diff --git a/404.html b/404.html new file mode 100644 index 0000000..faf7e23 --- /dev/null +++ b/404.html @@ -0,0 +1,23 @@ +--- +layout: default +--- + + + +
+

404

+

Page not found :(

+

The requested page could not be found.

+
diff --git a/CNAME b/CNAME new file mode 100644 index 0000000..8739ba4 --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +eips.ethereum.org \ No newline at end of file diff --git a/EIPS/eip-1.md b/EIPS/eip-1.md new file mode 100644 index 0000000..1bc9cec --- /dev/null +++ b/EIPS/eip-1.md @@ -0,0 +1,402 @@ +--- +eip: 1 +title: EIP Purpose and Guidelines +status: Living +type: Meta +author: Martin Becze , Hudson Jameson , et al. +created: 2015-10-27 +--- + +## What is an EIP? + +EIP stands for Ethereum Improvement Proposal. An EIP is a design document providing information to the Ethereum community, or describing a new feature for Ethereum or its processes or environment. The EIP should provide a concise technical specification of the feature and a rationale for the feature. The EIP author is responsible for building consensus within the community and documenting dissenting opinions. + +## EIP Rationale + +We intend EIPs to be the primary mechanisms for proposing new features, for collecting community technical input on an issue, and for documenting the design decisions that have gone into Ethereum. Because the EIPs are maintained as text files in a versioned repository, their revision history is the historical record of the feature proposal. + +For Ethereum implementers, EIPs are a convenient way to track the progress of their implementation. Ideally each implementation maintainer would list the EIPs that they have implemented. This will give end users a convenient way to know the current status of a given implementation or library. + +## EIP Types + +There are three types of EIP: + +- A **Standards Track EIP** describes any change that affects most or all Ethereum implementations, such as—a change to the network protocol, a change in block or transaction validity rules, proposed application standards/conventions, or any change or addition that affects the interoperability of applications using Ethereum. Standards Track EIPs consist of three parts—a design document, an implementation, and (if warranted) an update to the [formal specification](https://github.com/ethereum/yellowpaper). Furthermore, Standards Track EIPs can be broken down into the following categories: + - **Core**: improvements requiring a consensus fork (e.g. [EIP-5](./eip-5.md), [EIP-101](./eip-101.md)), as well as changes that are not necessarily consensus critical but may be relevant to [“core dev” discussions](https://github.com/ethereum/pm) (for example, [EIP-90], and the miner/node strategy changes 2, 3, and 4 of [EIP-86](./eip-86.md)). + - **Networking**: includes improvements around [devp2p](https://github.com/ethereum/devp2p/blob/readme-spec-links/rlpx.md) ([EIP-8](./eip-8.md)) and [Light Ethereum Subprotocol](https://ethereum.org/en/developers/docs/nodes-and-clients/#light-node), as well as proposed improvements to network protocol specifications of [whisper](https://github.com/ethereum/go-ethereum/issues/16013#issuecomment-364639309) and [swarm](https://github.com/ethereum/go-ethereum/pull/2959). + - **Interface**: includes improvements around client [API/RPC](https://github.com/ethereum/execution-apis#README) specifications and standards, and also certain language-level standards like method names ([EIP-6](./eip-6.md)) and [contract ABIs](https://docs.soliditylang.org/en/develop/abi-spec.html). The label “interface” aligns with the [interfaces repo] and discussion should primarily occur in that repository before an EIP is submitted to the EIPs repository. + - **ERC**: application-level standards and conventions, including contract standards such as token standards ([ERC-20](./eip-20.md)), name registries ([ERC-137](./eip-137.md)), URI schemes, library/package formats, and wallet formats. + +- A **Meta EIP** describes a process surrounding Ethereum or proposes a change to (or an event in) a process. Process EIPs are like Standards Track EIPs but apply to areas other than the Ethereum protocol itself. They may propose an implementation, but not to Ethereum's codebase; they often require community consensus; unlike Informational EIPs, they are more than recommendations, and users are typically not free to ignore them. Examples include procedures, guidelines, changes to the decision-making process, and changes to the tools or environment used in Ethereum development. Any meta-EIP is also considered a Process EIP. + +- An **Informational EIP** describes an Ethereum design issue, or provides general guidelines or information to the Ethereum community, but does not propose a new feature. Informational EIPs do not necessarily represent Ethereum community consensus or a recommendation, so users and implementers are free to ignore Informational EIPs or follow their advice. + +It is highly recommended that a single EIP contain a single key proposal or new idea. The more focused the EIP, the more successful it tends to be. A change to one client doesn't require an EIP; a change that affects multiple clients, or defines a standard for multiple apps to use, does. + +An EIP must meet certain minimum criteria. It must be a clear and complete description of the proposed enhancement. The enhancement must represent a net improvement. The proposed implementation, if applicable, must be solid and must not complicate the protocol unduly. + +### Special requirements for Core EIPs + +If a **Core** EIP mentions or proposes changes to the EVM (Ethereum Virtual Machine), it should refer to the instructions by their mnemonics and define the opcodes of those mnemonics at least once. A preferred way is the following: + +``` +REVERT (0xfe) +``` + +## EIP Work Flow + +### Shepherding an EIP + +Parties involved in the process are you, the champion or *EIP author*, the [*EIP editors*](#eip-editors), and the [*Ethereum Core Developers*](https://github.com/ethereum/pm). + +Before you begin writing a formal EIP, you should vet your idea. Ask the Ethereum community first if an idea is original to avoid wasting time on something that will be rejected based on prior research. It is thus recommended to open a discussion thread on [the Ethereum Magicians forum](https://ethereum-magicians.org/) to do this. + +Once the idea has been vetted, your next responsibility will be to present (by means of an EIP) the idea to the reviewers and all interested parties, invite editors, developers, and the community to give feedback on the aforementioned channels. You should try and gauge whether the interest in your EIP is commensurate with both the work involved in implementing it and how many parties will have to conform to it. For example, the work required for implementing a Core EIP will be much greater than for an ERC and the EIP will need sufficient interest from the Ethereum client teams. Negative community feedback will be taken into consideration and may prevent your EIP from moving past the Draft stage. + +### Core EIPs + +For Core EIPs, given that they require client implementations to be considered **Final** (see "EIPs Process" below), you will need to either provide an implementation for clients or convince clients to implement your EIP. + +The best way to get client implementers to review your EIP is to present it on an AllCoreDevs call. You can request to do so by posting a comment linking your EIP on an [AllCoreDevs agenda GitHub Issue](https://github.com/ethereum/pm/issues). + +The AllCoreDevs call serves as a way for client implementers to do three things. First, to discuss the technical merits of EIPs. Second, to gauge what other clients will be implementing. Third, to coordinate EIP implementation for network upgrades. + +These calls generally result in a "rough consensus" around what EIPs should be implemented. This "rough consensus" rests on the assumptions that EIPs are not contentious enough to cause a network split and that they are technically sound. + +:warning: The EIPs process and AllCoreDevs call were not designed to address contentious non-technical issues, but, due to the lack of other ways to address these, often end up entangled in them. This puts the burden on client implementers to try and gauge community sentiment, which hinders the technical coordination function of EIPs and AllCoreDevs calls. If you are shepherding an EIP, you can make the process of building community consensus easier by making sure that [the Ethereum Magicians forum](https://ethereum-magicians.org/) thread for your EIP includes or links to as much of the community discussion as possible and that various stakeholders are well-represented. + +*In short, your role as the champion is to write the EIP using the style and format described below, shepherd the discussions in the appropriate forums, and build community consensus around the idea.* + +### EIP Process + +The following is the standardization process for all EIPs in all tracks: + +![EIP Status Diagram](../assets/eip-1/EIP-process-update.jpg) + +**Idea** - An idea that is pre-draft. This is not tracked within the EIP Repository. + +**Draft** - The first formally tracked stage of an EIP in development. An EIP is merged by an EIP Editor into the EIP repository when properly formatted. + +**Review** - An EIP Author marks an EIP as ready for and requesting Peer Review. + +**Last Call** - This is the final review window for an EIP before moving to `Final`. An EIP editor will assign `Last Call` status and set a review end date (`last-call-deadline`), typically 14 days later. + +If this period results in necessary normative changes it will revert the EIP to `Review`. + +**Final** - This EIP represents the final standard. A Final EIP exists in a state of finality and should only be updated to correct errata and add non-normative clarifications. + +A PR moving an EIP from Last Call to Final SHOULD contain no changes other than the status update. Any content or editorial proposed change SHOULD be separate from this status-updating PR and committed prior to it. + +**Stagnant** - Any EIP in `Draft` or `Review` or `Last Call` if inactive for a period of 6 months or greater is moved to `Stagnant`. An EIP may be resurrected from this state by Authors or EIP Editors through moving it back to `Draft` or it's earlier status. If not resurrected, a proposal may stay forever in this status. + +>*EIP Authors are notified of any algorithmic change to the status of their EIP* + +**Withdrawn** - The EIP Author(s) have withdrawn the proposed EIP. This state has finality and can no longer be resurrected using this EIP number. If the idea is pursued at later date it is considered a new proposal. + +**Living** - A special status for EIPs that are designed to be continually updated and not reach a state of finality. This includes most notably EIP-1. + +## What belongs in a successful EIP? + +Each EIP should have the following parts: + +- Preamble - RFC 822 style headers containing metadata about the EIP, including the EIP number, a short descriptive title (limited to a maximum of 44 characters), a description (limited to a maximum of 140 characters), and the author details. Irrespective of the category, the title and description should not include EIP number. See [below](./eip-1.md#eip-header-preamble) for details. +- Abstract - Abstract is a multi-sentence (short paragraph) technical summary. This should be a very terse and human-readable version of the specification section. Someone should be able to read only the abstract to get the gist of what this specification does. +- Motivation *(optional)* - A motivation section 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. This section may be omitted if the motivation is evident. +- 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 (besu, erigon, ethereumjs, go-ethereum, nethermind, or others). +- 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 should discuss important objections or concerns raised during discussion around the EIP. +- Backwards Compatibility *(optional)* - All EIPs that introduce backwards incompatibilities must include a section describing these incompatibilities and their consequences. The EIP must explain how the author proposes to deal with these incompatibilities. This section may be omitted if the proposal does not introduce any backwards incompatibilities, but this section must be included if backward incompatibilities exist. +- Test Cases *(optional)* - Test cases for an implementation are mandatory for EIPs that are affecting consensus changes. Tests should either be inlined in the EIP as data (such as input/expected output pairs, or included in `../assets/eip-###/`. This section may be omitted for non-Core proposals. +- Reference Implementation *(optional)* - An optional section that contains a reference/example implementation that people can use to assist in understanding or implementing this specification. This section may be omitted for all EIPs. +- Security Considerations - All EIPs must contain a section that discusses the security implications/considerations relevant to the proposed change. Include information that might be important for security discussions, surfaces risks and can be used throughout the life-cycle of the proposal. E.g. include security-relevant design decisions, concerns, important discussions, implementation-specific guidance and pitfalls, an outline of threats and risks and how they are being addressed. EIP submissions missing the "Security Considerations" section will be rejected. An EIP cannot proceed to status "Final" without a Security Considerations discussion deemed sufficient by the reviewers. +- Copyright Waiver - All EIPs must be in the public domain. The copyright waiver MUST link to the license file and use the following wording: `Copyright and related rights waived via [CC0](../LICENSE.md).` + +## EIP Formats and Templates + +EIPs should be written in [markdown](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet) format. There is a [template](https://github.com/ethereum/EIPs/blob/master/eip-template.md) to follow. + +## EIP Header Preamble + +Each EIP must begin with an [RFC 822](https://www.ietf.org/rfc/rfc822.txt) style header preamble, preceded and followed by three hyphens (`---`). This header is also termed ["front matter" by Jekyll](https://jekyllrb.com/docs/front-matter/). The headers must appear in the following order. + +`eip`: *EIP number* (this is determined by the EIP editor) + +`title`: *The EIP title is a few words, not a complete sentence* + +`description`: *Description is one full (short) sentence* + +`author`: *The list of the author's or authors' name(s) and/or username(s), or name(s) and email(s). Details are below.* + +`discussions-to`: *The url pointing to the official discussion thread* + +`status`: *Draft, Review, Last Call, Final, Stagnant, Withdrawn, Living* + +`last-call-deadline`: *The date last call period ends on* (Optional field, only needed when status is `Last Call`) + +`type`: *One of `Standards Track`, `Meta`, or `Informational`* + +`category`: *One of `Core`, `Networking`, `Interface`, or `ERC`* (Optional field, only needed for `Standards Track` EIPs) + +`created`: *Date the EIP was created on* + +`requires`: *EIP number(s)* (Optional field) + +`withdrawal-reason`: *A sentence explaining why the EIP was withdrawn.* (Optional field, only needed when status is `Withdrawn`) + +Headers that permit lists must separate elements with commas. + +Headers requiring dates will always do so in the format of ISO 8601 (yyyy-mm-dd). + +### `author` header + +The `author` header lists the names, email addresses or usernames of the authors/owners of the EIP. Those who prefer anonymity may use a username only, or a first name and a username. The format of the `author` header value must be: + +> Random J. User <address@dom.ain> + +or + +> Random J. User (@username) + +or + +> Random J. User (@username) <address@dom.ain> + +if the email address and/or GitHub username is included, and + +> Random J. User + +if neither the email address nor the GitHub username are given. + +At least one author must use a GitHub username, in order to get notified on change requests and have the capability to approve or reject them. + +### `discussions-to` header + +While an EIP is a draft, a `discussions-to` header will indicate the URL where the EIP is being discussed. + +The preferred discussion URL is a topic on [Ethereum Magicians](https://ethereum-magicians.org/). The URL cannot point to Github pull requests, any URL which is ephemeral, and any URL which can get locked over time (i.e. Reddit topics). + +### `type` header + +The `type` header specifies the type of EIP: Standards Track, Meta, or Informational. If the track is Standards please include the subcategory (core, networking, interface, or ERC). + +### `category` header + +The `category` header specifies the EIP's category. This is required for standards-track EIPs only. + +### `created` header + +The `created` header records the date that the EIP was assigned a number. Both headers should be in yyyy-mm-dd format, e.g. 2001-08-14. + +### `requires` header + +EIPs may have a `requires` header, indicating the EIP numbers that this EIP depends on. If such a dependency exists, this field is required. + +A `requires` dependency is created when the current EIP cannot be understood or implemented without a concept or technical element from another EIP. Merely mentioning another EIP does not necessarily create such a dependency. + +## Linking to External Resources + +Other than the specific exceptions listed below, links to external resources **SHOULD NOT** be included. External resources may disappear, move, or change unexpectedly. + +The process governing permitted external resources is described in [EIP-5757](./eip-5757.md). + +### Consensus Layer Specifications + +Links to specific commits of files within the Ethereum Consensus Layer Specifications may be included using normal markdown syntax, such as: + +```markdown +[Beacon Chain](https://github.com/ethereum/consensus-specs/blob/26695a9fdb747ecbe4f0bb9812fedbc402e5e18c/specs/sharding/beacon-chain.md) +``` + +Which renders to: + +[Beacon Chain](https://github.com/ethereum/consensus-specs/blob/26695a9fdb747ecbe4f0bb9812fedbc402e5e18c/specs/sharding/beacon-chain.md) + +Permitted Consensus Layer Specifications URLs must anchor to a specific commit, and so must match this regular expression: + +```regex +^https://github.com/ethereum/consensus-specs/blob/[0-9a-f]{40}/.*$ +``` + +### Networking Specifications + +Links to specific commits of files within the Ethereum Networking Specifications may be included using normal markdown syntax, such as: + +```markdown +[Ethereum Wire Protocol](https://github.com/ethereum/devp2p/blob/40ab248bf7e017e83cc9812a4e048446709623e8/caps/eth.md) +``` + +Which renders as: + +[Ethereum Wire Protocol](https://github.com/ethereum/devp2p/blob/40ab248bf7e017e83cc9812a4e048446709623e8/caps/eth.md) + +Permitted Networking Specifications URLs must anchor to a specific commit, and so must match this regular expression: + +```regex +^https://github.com/ethereum/devp2p/blob/[0-9a-f]{40}/.*$ +``` + +### Digital Object Identifier System + +Links qualified with a Digital Object Identifier (DOI) may be included using the following syntax: + +````markdown +This is a sentence with a footnote.[^1] + +[^1]: + ```csl-json + { + "type": "article", + "id": 1, + "author": [ + { + "family": "Jameson", + "given": "Hudson" + } + ], + "DOI": "00.0000/a00000-000-0000-y", + "title": "An Interesting Article", + "original-date": { + "date-parts": [ + [2022, 12, 31] + ] + }, + "URL": "https://sly-hub.invalid/00.0000/a00000-000-0000-y", + "custom": { + "additional-urls": [ + "https://example.com/an-interesting-article.pdf" + ] + } + } + ``` +```` + +Which renders to: + + + + +This is a sentence with a footnote.[^1] + +[^1]: + ```csl-json + { + "type": "article", + "id": 1, + "author": [ + { + "family": "Jameson", + "given": "Hudson" + } + ], + "DOI": "00.0000/a00000-000-0000-y", + "title": "An Interesting Article", + "original-date": { + "date-parts": [ + [2022, 12, 31] + ] + }, + "URL": "https://sly-hub.invalid/00.0000/a00000-000-0000-y", + "custom": { + "additional-urls": [ + "https://example.com/an-interesting-article.pdf" + ] + } + } + ``` + + + +See the [Citation Style Language Schema](https://resource.citationstyles.org/schema/v1.0/input/json/csl-data.json) for the supported fields. In addition to passing validation against that schema, references must include a DOI and at least one URL. + +The top-level URL field must resolve to a copy of the referenced document which can be viewed at zero cost. Values under `additional-urls` must also resolve to a copy of the referenced document, but may charge a fee. + +## Linking to other EIPs + +References to other EIPs should follow the format `EIP-N` where `N` is the EIP number you are referring to. Each EIP that is referenced in an EIP **MUST** be accompanied by a relative markdown link the first time it is referenced, and **MAY** be accompanied by a link on subsequent references. The link **MUST** always be done via relative paths so that the links work in this GitHub repository, forks of this repository, the main EIPs site, mirrors of the main EIP site, etc. For example, you would link to this EIP as `./eip-1.md`. + +## Auxiliary Files + +Images, diagrams and auxiliary files should be included in a subdirectory of the `assets` folder for that EIP as follows: `assets/eip-N` (where **N** is to be replaced with the EIP number). When linking to an image in the EIP, use relative links such as `../assets/eip-1/image.png`. + +## Transferring EIP Ownership + +It occasionally becomes necessary to transfer ownership of EIPs to a new champion. In general, we'd like to retain the original author as a co-author of the transferred EIP, but that's really up to the original author. A good reason to transfer ownership is because the original author no longer has the time or interest in updating it or following through with the EIP process, or has fallen off the face of the 'net (i.e. is unreachable or isn't responding to email). A bad reason to transfer ownership is because you don't agree with the direction of the EIP. We try to build consensus around an EIP, but if that's not possible, you can always submit a competing EIP. + +If you are interested in assuming ownership of an EIP, send a message asking to take over, addressed to both the original author and the EIP editor. If the original author doesn't respond to the email in a timely manner, the EIP editor will make a unilateral decision (it's not like such decisions can't be reversed :)). + +## EIP Editors + +The current EIP editors are + +- Alex Beregszaszi (@axic) +- Gavin John (@Pandapip1) +- Greg Colvin (@gcolvin) +- Matt Garnett (@lightclient) +- Sam Wilson (@SamWilsn) +- Zainan Victor Zhou (@xinbenlv) +- Gajinder Singh (@g11tech) + +Emeritus EIP editors are + +- Casey Detrio (@cdetrio) +- Hudson Jameson (@Souptacular) +- Martin Becze (@wanderer) +- Micah Zoltu (@MicahZoltu) +- Nick Johnson (@arachnid) +- Nick Savers (@nicksavers) +- Vitalik Buterin (@vbuterin) + +If you would like to become an EIP editor, please check [EIP-5069](./eip-5069.md). + +## EIP Editor Responsibilities + +For each new EIP that comes in, an editor does the following: + +- Read the EIP to check if it is ready: sound and complete. The ideas must make technical sense, even if they don't seem likely to get to final status. +- The title should accurately describe the content. +- Check the EIP for language (spelling, grammar, sentence structure, etc.), markup (GitHub flavored Markdown), code style + +If the EIP isn't ready, the editor will send it back to the author for revision, with specific instructions. + +Once the EIP is ready for the repository, the EIP editor will: + +- Assign an EIP number (generally the PR number, but the decision is with the editors) +- Merge the corresponding [pull request](https://github.com/ethereum/EIPs/pulls) +- Send a message back to the EIP author with the next step. + +Many EIPs are written and maintained by developers with write access to the Ethereum codebase. The EIP editors monitor EIP changes, and correct any structure, grammar, spelling, or markup mistakes we see. + +The editors don't pass judgment on EIPs. We merely do the administrative & editorial part. + +## Style Guide + +### Titles + +The `title` field in the preamble: + +- Should not include the word "standard" or any variation thereof; and +- Should not include the EIP's number. + +### Descriptions + +The `description` field in the preamble: + +- Should not include the word "standard" or any variation thereof; and +- Should not include the EIP's number. + +### EIP numbers + +When referring to an EIP with a `category` of `ERC`, it must be written in the hyphenated form `ERC-X` where `X` is that EIP's assigned number. When referring to EIPs with any other `category`, it must be written in the hyphenated form `EIP-X` where `X` is that EIP's assigned number. + +### RFC 2119 and RFC 8174 + +EIPs are encouraged to follow [RFC 2119](https://www.ietf.org/rfc/rfc2119.html) and [RFC 8174](https://www.ietf.org/rfc/rfc8174.html) for terminology and to insert the following at the beginning of the Specification section: + +> The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. + +## History + +This document was derived heavily from [Bitcoin's BIP-0001](https://github.com/bitcoin/bips) written by Amir Taaki which in turn was derived from [Python's PEP-0001](https://peps.python.org/). In many places text was simply copied and modified. Although the PEP-0001 text was written by Barry Warsaw, Jeremy Hylton, and David Goodger, they are not responsible for its use in the Ethereum Improvement Process, and should not be bothered with technical questions specific to Ethereum or the EIP. Please direct all comments to the EIP editors. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-100.md b/EIPS/eip-100.md new file mode 100644 index 0000000..78b9400 --- /dev/null +++ b/EIPS/eip-100.md @@ -0,0 +1,39 @@ +--- +eip: 100 +title: Change difficulty adjustment to target mean block time including uncles +author: Vitalik Buterin (@vbuterin) +type: Standards Track +category: Core +status: Final +created: 2016-04-28 +--- + +### Specification + +Currently, the formula to compute the difficulty of a block includes the following logic: + +``` python +adj_factor = max(1 - ((timestamp - parent.timestamp) // 10), -99) +child_diff = int(max(parent.difficulty + (parent.difficulty // BLOCK_DIFF_FACTOR) * adj_factor, min(parent.difficulty, MIN_DIFF))) +... +``` + +If `block.number >= BYZANTIUM_FORK_BLKNUM`, we change the first line to the following: + +``` python +adj_factor = max((2 if len(parent.uncles) else 1) - ((timestamp - parent.timestamp) // 9), -99) +``` +### Rationale + +This new formula ensures that the difficulty adjustment algorithm targets a constant average rate of blocks produced including uncles, and so ensures a highly predictable issuance rate that cannot be manipulated upward by manipulating the uncle rate. A formula that accounts for the exact number of included uncles: +``` python +adj_factor = max(1 + len(parent.uncles) - ((timestamp - parent.timestamp) // 9), -99) +``` +can be fairly easily seen to be (to within a tolerance of ~3/4194304) mathematically equivalent to assuming that a block with `k` uncles is equivalent to a sequence of `k+1` blocks that all appear with the exact same timestamp, and this is likely the simplest possible way to accomplish the desired effect. But since the exact formula depends on the full block and not just the header, we are instead using an approximate formula that accomplishes almost the same effect but has the benefit that it depends only on the block header (as you can check the uncle hash against the blank hash). + +Changing the denominator from 10 to 9 ensures that the block time remains roughly the same (in fact, it should decrease by ~3% given the current uncle rate of 7%). + +### References + +1. EIP 100 issue and discussion: https://github.com/ethereum/EIPs/issues/100 +2. https://bitslog.wordpress.com/2016/04/28/uncle-mining-an-ethereum-consensus-protocol-flaw/ diff --git a/EIPS/eip-101.md b/EIPS/eip-101.md new file mode 100644 index 0000000..19053a6 --- /dev/null +++ b/EIPS/eip-101.md @@ -0,0 +1,83 @@ +--- +eip: 101 +title: Serenity Currency and Crypto Abstraction +author: Vitalik Buterin (@vbuterin) +status: Stagnant +type: Standards Track +category: Core +created: 2015-11-15 +--- + +### Specification + +1. Accounts now have only two fields in their RLP encoding: **code** and **storage**. +2. Ether is no longer stored in account objects directly; instead, at address `0`, we premine a contract which contains all ether holdings. The `eth.getBalance` command in web3 is remapped appropriately. +3. `msg.value` no longer exists as an opcode. +4. A transaction now only has four fields: **to**, **startgas**, **data** and **code**. +5. Aside from an RLP validity check, and checking that the **to** field is twenty bytes long, the **startgas** is an integer, and **code** is either empty or hashes to the **to** address, there are no other validity constraints; anything goes. However, the block gas limit remains, so miners are disincentivized from including junk. +6. Gas is charged for bytes in **code** at the same rate as **data**. +7. When a transaction is sent, if the receiving account does not yet exist, the account is created, and its code is set to the code provided in the transaction; otherwise the code is ignored. +8. A `tx.gas` opcode is added alongside the existing `msg.gas` at index `0x5c`; this new opcode allows the transaction to access the original amount of gas allotted for the transaction + +Note that `ECRECOVER`, sequence number/nonce incrementing and ether are now nowhere in the bottom-level spec (NOTE: ether is going to continue to have a privileged role in Casper PoS). To replicate existing functionality under the new model, we do the following. + +Simple user accounts can have the following default standardized code: + +```python +# We assume that data takes the following schema: +# bytes 0-31: v (ECDSA sig) +# bytes 32-63: r (ECDSA sig) +# bytes 64-95: s (ECDSA sig) +# bytes 96-127: sequence number (formerly called "nonce") +# bytes 128-159: gasprice +# bytes 172-191: to +# bytes 192+: data + +# Get the hash for transaction signing +~mstore(0, msg.gas) +~calldatacopy(32, 96, ~calldatasize() - 96) +h = sha3(96, ~calldatasize() - 96) +# Call ECRECOVER contract to get the sender +~call(5000, 3, [h, ~calldataload(0), ~calldataload(32), ~calldataload(64)], 128, ref(addr), 32) +# Check sender correctness +assert addr == 0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1 +# Check sequence number correctness +assert ~calldataload(96) == self.storage[-1] +# Increment sequence number +self.storage[-1] += 1 +# Make the sub-call and discard output +~call(msg.gas - 50000, ~calldataload(160), 192, ~calldatasize() - 192, 0, 0) +# Pay for gas +~call(40000, 0, [SEND, block.coinbase, ~calldataload(128) * (tx.gas - msg.gas + 50000)], 96, 0, 0) +``` + +This essentially implements signature and nonce checking, and if both checks pass then it uses all remaining gas minus 50000 to send the actual desired call, and then finally pays for gas. + +Miners can follow the following algorithm upon receiving transactions: + +1. Run the code for a maximum of 50000 gas, stopping if they see an operation or call that threatens to go over this limit +2. Upon seeing that operation, make sure that it leaves at last 50000 gas to spare (either by checking that the static gas consumption is small enough or by checking that it is a call with `msg.gas - 50000` as its gas limit parameter) +3. Pattern-match to make sure that gas payment code at the end is *exactly* the same as in the code above. + +This process ensures that miners *waste* at most 50000 gas before knowing whether or not it will be worth their while to include the transaction, and is also highly general so users can experiment with new cryptography (eg. ed25519, Lamport), ring signatures, quasi-native multisig, etc. Theoretically, one can even create an account for which the *valid signature* type is a valid Merkle branch of a receipt, creating a quasi-native alarm clock. + +If someone wants to send a transaction with nonzero value, instead of the current `msg.sender` approach, we compile into a three step process: + +1. In the outer scope just before calling, call the ether contract to create a cheque for the desired amount +2. In the inner scope, if a contract uses the `msg.value` opcode anywhere in the function that is being called, then we have the contract cash out the cheque at the start of the function call and store the amount cashed out in a standardized address in memory +3. In the outer scope just after calling, send a message to the ether contract to disable the cheque if it has not yet been cashed + +### Rationale + +This allows for a large increase in generality, particularly in a few +areas: + +1. Cryptographic algorithms used to secure accounts (we could reasonably say that Ethereum is quantum-safe, as one is perfectly free to secure one's account with Lamport signatures). The nonce-incrementing approach is now also open to revision on the part of account holders, allowing for experimentation in k-parallelizable nonce techniques, UTXO schemes, etc. +2. Moving ether up a level of abstraction, with the particular benefit of allowing ether and sub-tokens to be treated similarly by contracts +3. Reducing the level of indirection required for custom-policy accounts such as multisigs + +It also substantially simplifies and *purifies* the underlying Ethereum protocol, reducing the minimal consensus implementation complexity. + +### Implementation + +Coming soon. diff --git a/EIPS/eip-1010.md b/EIPS/eip-1010.md new file mode 100644 index 0000000..794fee1 --- /dev/null +++ b/EIPS/eip-1010.md @@ -0,0 +1,68 @@ +--- +eip: 1010 +title: Uniformity Between 0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B and 0x15E55EF43efA8348dDaeAa455F16C43B64917e3c +author: Anderson Wesley (@andywesley) +discussions-to: https://github.com/andywesley/EIPs/issues/1 +status: Stagnant +type: Standards Track +category: Core +created: 2018-04-18 +--- + +## Simple Summary + +This document proposes to improve the uniformity of ether distribution +between wallet address `0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B` and +wallet address `0x15E55EF43efA8348dDaeAa455F16C43B64917e3c` which are +currently experiencing a significant non-uniformity. + +## Abstract + +As of the date of this EIP, the difference in balance between +address `0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B` and address +`0x15E55EF43efA8348dDaeAa455F16C43B64917e3c` is far from equitable +or uniform, with the former having more than 365,000 ether +more than the latter. The distribution of ether between these two +addresses must be improved in order to protect the Ethereum economy +from centralized control. This will be accomplished by transferring +100,000 ether from the former address to the latter. This is a properly +motivated improvement in keeping with the core Ethereum philosophy of +decentralization. + +## Motivation + +This proposal is necessary because the Ethereum protocol does not allow +the owner of an address which does not own an equitable amount of ether +to claim their share of ether from an address which owns a dangerously +centralized quantity. Rather than proposing an overly complicated generic +mechanism for any user to claim ether to which they believe they are +equitably entitled, this proposal will take the simple route of a one-time +transfer of 100,000 ether from `0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B` +to `0x15E55EF43efA8348dDaeAa455F16C43B64917e3c`. This avoids duplicating +the effort of other proposals and provides a net improvement to the +Ethereum project and community. + +## Specification + +The balance of `0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B` will be decreased +by 100,000 ether. The balance of `0x15E55EF43efA8348dDaeAa455F16C43B64917e3c` +will be increased by 100,000 ether. No net change in the amount of extant +ether will occur unless at the time of implementation the former address does not +contain sufficient ether for such a deduction. + +## Rationale + +The value 100,000 was chosen after careful technically sound analysis of various economic theories +developed over the past century. In spite of the fact that it is a convenient round +number, it is actually the exact output of a complex statistical process iterated to +determine the optimal distribution of ether between these addresses. + +## Backwards Compatibility + +Clients that fail to implement this change will not be aware of the correct balances +for these addresses. This will create a hard fork. The implementation of this change +consistently among all clients as intended by the proposal process will be sufficient +to ensure that backwards compatibility is not a concern. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1011.md b/EIPS/eip-1011.md new file mode 100644 index 0000000..84c3844 --- /dev/null +++ b/EIPS/eip-1011.md @@ -0,0 +1,440 @@ +--- +eip: 1011 +title: Hybrid Casper FFG +status: Stagnant +type: Standards Track +category: Core +author: Danny Ryan (@djrtwo), Chih-Cheng Liang (@ChihChengLiang) +discussions-to: https://github.com/djrtwo/EIPs/issues/5 +created: 2018-04-20 +--- + +## Simple Summary + +Specification of the first step to transition Ethereum main net from Proof of Work (PoW) to Proof of Stake (PoS). The resulting consensus model is a PoW/PoS hybrid. + +## Abstract + +This EIP specifies a hybrid PoW/PoS consensus model for Ethereum main net. Existing PoW mechanics are used for new block creation, and a novel PoS mechanism called Casper the Friendly Finality Gadget (FFG) is layered on top using a smart contract. + +Through the use of Ether deposits, slashing conditions, and a modified fork choice, FFG allows the underlying PoW blockchain to be finalized. As network security is greatly shifted from PoW to PoS, PoW block rewards are reduced. + +This EIP does not contain safety and liveness proofs or validator implementation details, but these can be found in the [Casper FFG paper](https://arxiv.org/abs/1710.09437) and [Validator Implementation Guide](https://github.com/ethereum/casper/blob/master/VALIDATOR_GUIDE.md) respectively. + +## Glossary + +* **epoch**: The span of blocks between checkpoints. Epochs are numbered starting at the hybrid casper fork, incrementing by one at the start of each epoch. +* **finality**: The point at which a block has been decided upon by a client to _never_ revert. Proof of Work does not have the concept of finality, only of further deep block confirmations. +* **checkpoint**: The block/hash under consideration for finality for a given epoch. This block is the _last_ block of the previous epoch. Rather than dealing with every block, Casper FFG only considers checkpoints for finalization. When a checkpoint is explicitly finalized, all ancestor blocks of the checkpoint are implicitly finalized. +* **validator**: A participant in the Casper FFG consensus that has deposited ether in the casper contract and has the responsibility to vote and finalize checkpoints. +* **validator set**: The set of validators in the casper contract at any given time. +* **dynasty**: The number of finalized checkpoints in the chain from root to the parent of a block. The dynasty is used to define when a validator starts and ends validating. The current dynasty only increments when a checkpoint is finalized as opposed to epoch numbers that increment regardless of finality. +* **slash**: The burning of some amount of a validator's deposit along with an immediate logout from the validator set. Slashing occurs when a validator signs two conflicting `vote` messages that violate a slashing condition. For an in-depth discussion of slashing conditions, see the [Casper FFG Paper](https://arxiv.org/abs/1710.09437). + +## Motivation + +Transitioning the Ethereum network from PoW to PoS has been on the roadmap and in the [Yellow Paper](https://github.com/ethereum/yellowpaper) since the launch of the protocol. Although effective in coming to a decentralized consensus, PoW consumes an incredible amount of energy, has no economic finality, and has no effective strategy in resisting cartels. Excessive energy consumption, issues with equal access to mining hardware, mining pool centralization, and an emerging market of ASICs each provide a distinct motivation to make the transition as soon as possible. + +Until recently, the proper way to make this transition was still an open area of research. In October of 2017 [Casper the Friendly Finality Gadget](https://arxiv.org/abs/1710.09437) was published, solving open questions of economic finality through validator deposits and crypto-economic incentives. For a detailed discussion and proofs of "accountable safety" and "plausible liveness", see the [Casper FFG](https://arxiv.org/abs/1710.09437) paper. + +The Casper FFG contract can be layered on top of any block proposal mechanism, providing finality to the underlying chain. This EIP proposes layering FFG on top of the existing PoW block proposal mechanism as a conservative step-wise approach in the transition to full PoS. The new FFG staking mechanism requires minimal changes to the protocol, allowing the Ethereum network to fully test and evaluate Casper FFG on top of PoW before moving to a validator based block proposal mechanism. + +## Parameters + +* `HYBRID_CASPER_FORK_BLKNUM`: TBD +* `CASPER_ADDR`: TBD +* `CASPER_CODE`: see below +* `CASPER_BALANCE`: 1.25e24 wei (1,250,000 ETH) +* `MSG_HASHER_ADDR`: TBD +* `MSG_HASHER_CODE`: see below +* `PURITY_CHECKER_ADDR`: TBD +* `PURITY_CHECKER_CODE`: see below +* `NULL_SENDER`: `2**160 - 1` +* `NEW_BLOCK_REWARD`: 6e17 wei (0.6 ETH) +* `REWARD_STEPDOWN_BLOCK_COUNT`: 5.5e5 blocks (~3 months) +* `CASPER_INIT_DATA`: TBD +* `VOTE_BYTES`: `0xe9dc0614` +* `INITIALIZE_EPOCH_BYTES`: `0x5dcffc17` +* `NON_REVERT_MIN_DEPOSIT`: amount in wei configurable by client + +### Casper Contract Parameters + +* `EPOCH_LENGTH`: 50 blocks +* `WARM_UP_PERIOD`: 1.8e5 blocks (~1 month) +* `WITHDRAWAL_DELAY`: 1.5e4 epochs +* `DYNASTY_LOGOUT_DELAY`: 700 dynasties +* `BASE_INTEREST_FACTOR`: 7e-3 +* `BASE_PENALTY_FACTOR`: 2e-7 +* `MIN_DEPOSIT_SIZE`: 1.5e21 wei (1500 ETH) + + +## Specification + +#### Deploying Casper Contract + +If `block.number == HYBRID_CASPER_FORK_BLKNUM`, then when processing the block before processing any transactions: + +* set the code of `MSG_HASHER_ADDR` to `MSG_HASHER_CODE` +* set the code of `PURITY_CHECKER_ADDR` to `PURITY_CHECKER_CODE` +* set the code of `CASPER_ADDR` to `CASPER_CODE` +* set balance of `CASPER_ADDR` to `CASPER_BALANCE` + +Then execute a `CALL` with the following parameters before executing any normal block transactions: + +* `SENDER`: `NULL_SENDER` +* `GAS`: 3141592 +* `TO`: `CASPER_ADDR` +* `VALUE`: 0 +* `NONCE`: 0 +* `GASPRICE`: 0 +* `DATA`: `CASPER_INIT_DATA` + +This `CALL` utilizes no gas and does not increment the nonce of `NULL_SENDER` + +#### Initialize Epochs + +If `block.number >= (HYBRID_CASPER_FORK_BLKNUM + WARM_UP_PERIOD)` and `block.number % EPOCH_LENGTH == 0`, execute a `CALL` with the following parameters before executing any normal block transactions: + +* `SENDER`: `NULL_SENDER` +* `GAS`: 3141592 +* `TO`: `CASPER_ADDR` +* `VALUE`: 0 +* `NONCE`: 0 +* `GASPRICE`: 0 +* `DATA`: `INITIALIZE_EPOCH_BYTES` followed by the 32-byte encoding of `floor(block.number / EPOCH_LENGTH)` + +This `CALL` utilizes no gas and does not increment the nonce of `NULL_SENDER` + +#### Casper Votes + +A `vote` transaction is defined as a transaction with the following parameters: + +* `TO`: `CASPER_ADDR` +* `DATA`: Begins with `VOTE_BYTES` + +If `block.number >= HYBRID_CASPER_FORK_BLKNUM`, then: + +* A valid `vote` transaction to `CASPER_ADDR` must satisfy each of the following: + * Must have the following signature `(CHAIN_ID, 0, 0)` (ie. `r = s = 0, v = CHAIN_ID`) + * Must have `value == nonce == gasprice == 0` +* When producing and validating a block, when handling `vote` transactions to `CASPER_ADDR`: + * Only include "valid" `vote` transactions as defined above + * Place all `vote` transactions at the end of the block + * Track cumulative gas used by votes separately from cumulative gas used by normal transactions via `vote_gas_used` + * Total `vote_gas_used` of `vote` transactions cannot exceed the `block_gas_limit`, independent of gas used by normal block transactions +* When applying `vote` transactions to `CASPER_ADDR` to vm state: + * Set sender to `NULL_SENDER` + * Count gas of `vote` toward `vote_gas_used` + * Do not count gas of `vote` toward the normal `gas_used`. For all `vote` transaction receipts, cumulative gas used is equal to last non-`vote` transaction receipt + * Do not increment the nonce of `NULL_SENDER` +* All unsuccessful `vote` transactions to `CASPER_ADDR` are invalid and must not be included in the block + + +#### Fork Choice and Finalization + +If `block.number >= HYBRID_CASPER_FORK_BLKNUM`, the fork choice rule is the logic represented by the following pseudocode. Note that options `--casper-fork-choice` and `--exclude` are discussed below in "Client Settings". + +```python +def handle_block(new_block): + if not is_new_head(new_block): + return + + set_head(new_block) + if --casper-fork-choice is on: + check_and_finalize_new_checkpoint(new_block) + + +def is_new_head(new_block): + if --casper-fork-choice is off + # old pure PoW chain scoring rule + return new_block.total_difficuty > current_head.total_difficulty + + if new_block is in --exclude list or one of its descendants + return false + + # don't revert finalized blocks + if db.last_finalized_block is not in new_block.ancestors: + return false + + # new casper chain scoring rule + return highest_justified_epoch(new_block) * 10**40 + new_block.total_difficuty > + highest_justified_epoch(current_head) * 10**40 + current_head.total_difficulty + + +def highest_justified_epoch(block): + casper = block.post_state.casper_contract + return casper.highest_justified_epoch(NON_REVERT_MIN_DEPOSITS) + + +def check_and_finalize_new_checkpoint(new_block): + casper = new_block.post_state.casper_contract + + # If no finalized blocks, db.last_finalized_epoch initialized to -1 + + finalized_epoch = casper.highest_finalized_epoch(NON_REVERT_MIN_DEPOSITS) + if finalized_epoch > db.last_finalized_epoch: + finalized_hash = casper.checkpoint_hashes(finalized_epoch) + + # ensure not trivially finalized + if finalized_hash == b'\x00' * 32: + return + + db.last_finalized_epoch = finalized_epoch + db.last_finalized_block = finalized_hash +``` + +The new chain scoring rule queries the casper contract to find the highest justified epoch that meets the client's minimum deposit requirement (`NON_REVERT_MIN_DEPOSITS`). The `10**40` multiplier ensures that the justified epoch takes precedence over block mining difficulty. `total_difficulty` only serves as a tie breaker if the two blocks in question have an equivalent `highest_justified_epoch`. + +_Note_: If the client has no justified checkpoints, the contract returns `highest_justified_epoch` as `0` essentially reverting the fork choice rule to pure PoW. + +When assessing a new block as the chain's head, clients must _never revert finalized blocks_ as seen by the code commented as "don't revert finalized blocks". + +When a new block is added as the chain's head, clients then check for a new finalized block. This is handled by the `check_and_finalized_new_checkpoint(new_block)` method above. If the highest finalized epoch in the casper contract is greater than the previous finalized epoch, then the client finalizes the block with the hash `casper.checkpoint_hashes(finalized_epoch)`, storing this block and the related epoch number in the client database as finalized. + +Clients only consider checkpoints justified or finalized if deposits were greater than `NON_REVERT_MIN_DEPOSIT` _during the epoch in question_. This logic is encapsulated in `casper.highest_justified_epoch(NON_REVERT_MIN_DEPOSIT)` and `casper.highest_finalized_epoch(NON_REVERT_MIN_DEPOSIT)`, respectively. + + +#### Block Reward + +If `block.number >= HYBRID_CASPER_FORK_BLKNUM`, then `block_reward` is defined by the following logic and utilizes the same formulas for ommer rewards but with the updated `block_reward`. + +```python +if block.number < HYBRID_CASPER_FORK_BLKNUM + REWARD_STEPDOWN_BLOCK_COUNT: + block_reward = 5 * NEW_BLOCK_REWARD +elif block.number < HYBRID_CASPER_FORK_BLKNUM + 2*REWARD_STEPDOWN_BLOCK_COUNT: + block_reward = 4 * NEW_BLOCK_REWARD +elif block.number < HYBRID_CASPER_FORK_BLKNUM + 3*REWARD_STEPDOWN_BLOCK_COUNT: + block_reward = 3 * NEW_BLOCK_REWARD +elif block.number < HYBRID_CASPER_FORK_BLKNUM + 4*REWARD_STEPDOWN_BLOCK_COUNT: + block_reward = 2 * NEW_BLOCK_REWARD +else: + block_reward = NEW_BLOCK_REWARD +``` + + +#### Validators + +The mechanics and responsibilities of validators are not specified in this EIP because they rely upon network transactions to the contract at `CASPER_ADDR` rather than on protocol level implementation and changes. +See the [Validator Implementation Guide](https://github.com/ethereum/casper/blob/master/VALIDATOR_GUIDE.md) for validator details. + +#### MSG_HASHER_CODE + +The source code for `MSG_HASHER_CODE` is located [here](https://github.com/ethereum/casper/blob/master/casper/contracts/msg_hash.se.py). +The source is to be migrated to Vyper LLL before the bytecode is finalized for this EIP. + +The EVM init code is: +``` +TBD +``` + +The EVM bytecode that the contract should be set to is: +``` +TBD +``` + +#### PURITY_CHECKER_CODE + +The source code for `PURITY_CHECKER_CODE` is located [here](https://github.com/ethereum/research/blob/master/impurity/check_for_impurity.se). +The source is to be migrated to Vyper LLL before the bytecode is finalized for this EIP. + +The EVM init code is: +``` +TBD +``` + +The EVM bytecode that the contract should be set to is: +``` +TBD +``` + +#### CASPER_CODE + +The source code for `CASPER_CODE` is located at +[here](https://github.com/ethereum/casper/blob/master/casper/contracts/simple_casper.v.py). +The contract is to be formally verified and further tested before the bytecode is finalized for this EIP. + +The EVM init code with the above specified params is: +``` +TBD +``` + +The EVM bytecode that the contract should be set to is: +``` +TBD +``` + +#### Client Settings +Clients should be implemented with the following configurable settings: + +##### Enable Casper Fork Choice +The ability to enable/disable the Casper Fork Choice. A suggested implementation is `--casper-fork-choice`. + +This setting should ship as default disabled in client versions during the initial casper fork. This setting should ship as default enabled in subsequent client versions. + +##### NON_REVERT_MIN_DEPOSIT +The minimum size of total deposits that the client must observe in the FFG contract for the state of the contract to affect the client's fork choice. A suggested implementation is `--non-revert-min-deposit WEI_VALUE`. + +The suggested default value that clients should ship with is at least 2e23 wei (200K ETH). + +See "Fork Choice" more details. + +##### Exclusion +The ability to exclude a specified blockhash and all of its descendants from a client's fork choice. A suggested implementation is `--exclude BLOCKHASHES`, where `BLOCK_HASHES` is a comma delimited list of blockhashes to exclude. + +Note: this _can_ by design override a client's forkchoice and revert finalized blocks. + +##### Join Fork +The ability to manually join a fork specified by a blockhash. A suggested implementation is `--join-fork BLOCKHASH` where the client automatically sets the head to the block defined by`BLOCKHASH` and locally finalizes it. + +Note: this _can_ by design override a client's forkchoice and revert finalized blocks. + +##### Monitor Votes +The ability to monitor incoming `vote` transactions for slashing conditions and submit proof to the casper contract for a finder's fee if found. A suggested implementation is `--monitor-votes`. + +The setting should default to disabled. + +The following pseudocode defines when two `vote` messages violate a slashing condition. A `vote` message is the singular argument included in a `vote` transaction. +```python +def decode_rlp_list(vote_msg): + # [validator_index, target_hash, target_epoch, source_epoch, signature] + return RLPList(vote_msg, [int, bytes, int, int, bytes]) + +def same_target_epoch(vote_msg_1, vote_msg_2): + decoded_values_1 = decode_rlp_msg(vote_msg_1) + target_epoch_1 = decoded_values_1[2] + + decoded_values_2 = decode_rlp_msg(vote_msg_2) + target_epoch_2 = decoded_values_2[2] + + return target_epoch_1 == target_epoch_2 + +def surrounds(vote_msg_1, vote_msg_2): + decoded_values_1 = decode_rlp_msg(vote_msg_1) + target_epoch_1 = decoded_values_1[2] + source_epoch_1 = decoded_values_1[3] + + decoded_values_2 = decode_rlp_msg(vote_msg_2) + target_epoch_2 = decoded_values_2[2] + source_epoch_1 = decoded_values_1[3] + + vote_1_surrounds_vote_2 = target_epoch_1 > target_epoch_2 and source_epoch_1 < source_epoch_2 + vote_2_surrounds_vote_1 = target_epoch_2 > target_epoch_1 and source_epoch_2 < source_epoch_1 + + return vote_1_surrounds_vote_2 or vote_2_surrounds_vote_1 + +def violates_slashing_condition(vote_msg_1, vote_msg_2): + return same_target_epoch(vote_msg_1, vote_msg_2) or surrounds(vote_msg_1, vote_msg_2) +``` + +The casper contract also provides a helper method `slashable(vote_msg_1, vote_msg_2)` to check if two votes violate a slashing condition. Clients should use the above pseudocode in combination with `casper.slashable()` as a final check when deciding whether to submit a `slash` to the contract. + +The `--monitor-votes` setting is to be used for clients that wish to monitor vote transactions for slashing conditions. If a slashing condition is found, the client creates and sends a transaction to `slash` on the casper contract. The first transaction to include the slashing condition proof slashes the validator in question and sends a 4% finder's fee to the transaction sender. + +## Rationale + +Naive PoS specifications and implementations have existed since early blockchain days, but most are vulnerable to serious attacks and do not hold up under crypto-economic analysis. Casper FFG solves problems such as "Nothing at Stake" and "Long Range Attacks" through requiring validators to post slashable deposits and through defining economic finality. + +#### Minimize Consensus Changes +The finality gadget is designed to minimize changes across clients. For this reason, FFG is implemented within the EVM, so that the contract byte code encapsulates most of the complexity of the fork. + +Most other decisions were made to minimize changes across clients. For example, it would be possible to allow `CASPER_ADDR` to mint Ether each time it paid rewards (as compared to creating the contract with `CASPER_BALANCE`), but this would be more invasive and error-prone than relying on existing EVM mechanics. + +#### Deploying Casper Contract +The `MSG_HASHER_CODE` and `PURITY_CHECKER_CODE` both do not require any initialization so the EVM bytecode can simply be placed at `MSG_HASHER_ADDR` and `PURITY_CHECKER_ADDR`. On the other hand, the casper contract _does_ require passing in parameters and initialization of state. This initialization would normally occur by the EVM init code interacting with the CREATE opcode. Due to the nature of this contract being deployed outside of normal block transactions and to a particular address, the EVM init code/CREATE method requires client specific "hacks" to make it work. For simplicity of specifying across clients, the EVM bytecode -- `CASPER_CODE` -- is placed at `CASPER_ADDR` followed by an explicit `CALL` to a one-time `init` method on the casper contract. `init` handles all of the logic that a constructor normally would, accepting contract parameters as arguments and setting initial variable values, and can only be run _once_. + +`CASPER_INIT_DATA` is composed of the the byte signature of the `init` method of the casper contract concatenated with the 32-byte encodings of the following variables in the following order: + + * `EPOCH_LENGTH` + * `WITHDRAWAL_DELAY` + * `DYNASTY_LOGOUT_DELAY` + * `MSG_HASHER_ADDR` + * `PURITY_CHECKER_ADDR` + * `BASE_INTEREST_FACTOR` + * `BASE_PENALTY_FACTOR` + * `MIN_DEPOSIT_SIZE` + +The entirety of this data is provided as a bytestring -- `CASPER_INIT_DATA` -- to reduce the chance of encoding errors across clients, especially regarding fixed decimal types which are new in vyper and not yet supported by all clients. + +#### Casper Contract Params + +`EPOCH_LENGTH` is set to 50 blocks as a balance between time to finality and message overhead. + +`WARM_UP_PERIOD` is set to 1.8e5 blocks to provide validators with an approximate 1 month period to make initial deposits before full contract functionality and voting begin. This helps prevent degenerate cases such as having very few or even just one validator in the initial dynasty. This 1 month period also gives the network time to observe on the order of how many validators will initially be participating in consensus. If for some reason there is an unexpectedly low turnout, the community might choose to delay validation and consider design alternatives. + +`WITHDRAWAL_DELAY` is set to 15000 epochs to freeze a validator's funds for approximately 4 months after logout. This allows for at least a 4 month window to identify and slash a validator for attempting to finalize two conflicting checkpoints. This also defines the window of time with which a client must log on to sync the network due to weak subjectivity. + +`DYNASTY_LOGOUT_DELAY` is set to 700 dynasties to prevent immediate logout in the event of an attack from being a viable strategy. + +`BASE_INTEREST_FACTOR` is set to 7e-3 such that if there are ~10M ETH in total deposits, then validators earn approximately 5% per year in ETH rewards under optimal FFG conditions. + +`BASE_PENALTY_FACTOR` is set to 2e-7 such that if 50% of deposits go offline, then offline validators lose half of their deposits in approximately 3 weeks, at which the online portion of validators becomes a 2/3 majority and can begin finalizing checkpoints again. + +`MIN_DEPOSIT_SIZE` is set to 1500 ETH to form a natural upper bound on the total number of validators, bounding the overhead due to `vote` messages. Using formulas found [here](https://medium.com/@VitalikButerin/parametrizing-casper-the-decentralization-finality-time-overhead-tradeoff-3f2011672735) under "From validator count to minimum staking ETH", we estimate that with 1500 ETH minimum deposit at an assumed ~10M in total deposits there will be approximately 900 validators at any given time. `vote`s are only sent after the first quarter of an epoch so 900 votes have to fit into 37 blocks or ~24 `vote`s per block. We have experimented with more dynamic models for `MIN_DEPOSIT_SIZE`, but these tend to introduce significant complexities and without data from a live network seem to be premature optimizations. + +#### Initialize Epochs +The call to the method at `INITIALIZE_EPOCH_BYTES` at `CASPER_ADDR` at the start of each epoch is a call to the `initialize_epoch` method in the casper contract. This method can only be called once per epoch and is guaranteed by the protocol to be called at the start block of each epoch by `NULL_SENDER`. This method performs a number of bookkeeping tasks around incrementing variables, updating rewards, etc. + +Any call to this method fails prior to the end of the `WARM_UP_PERIOD`. Thus the protocol does not begin executing `initialize_epoch` calls until `block.number >= HYBRID_CASPER_FORK_BLKNUM + WARM_UP_PERIOD`. + +#### Issuance +A fixed amount of 1.25M ETH was chosen as `CASPER_BALANCE` to fund the casper contract. This gives the contract enough runway to operate for approximately 2 years (assuming ~10M ETH in validator deposits). Acting similarly to the "difficulty bomb", this "funding crunch" forces the network to hardfork in the relative near future to further fund the contract. This future hardfork is an opportunity to upgrade the contract and transition to full PoS. + +The PoW block reward is reduced from 3.0 to 0.6 ETH/block over the course of approximately one year because the security of the chain is greatly shifted from PoW difficulty to PoS finality and because rewards are now issued to both validators and miners. Rewards are stepped down by 0.6 ETH/block every 3 months (`REWARD_STEPDOWN_BLOCK_COUNT`) to provide for a conservative transition period from full PoW to hybrid PoS/PoW. This gives validators time to become familiar with the new technology and begin logging on and also provides the network with more leeway in case of any unforeseen issues. If any major issues do arise, the Ethereum network will still have substantial PoW security to rely upon while decisions are made and/or patches are deployed. See [here](https://gist.github.com/djrtwo/bc864c0d0a275170183803814b207b9a) for further analysis of the current PoW security and of the effect of PoW block reward reduction in the context of Hybrid Casper FFG. + +In addition to block rewards, miners now receive an issuance reward for including successful `vote` transactions into the block on time. This reward is equal to 1/8th that of the reward the validator receives for a successful `vote` transaction. Under optimal FFG conditions after group validator reward adjustments are made, miners receive approximately 1/5th of the total ETH issued by the Casper contract. + +Below is a table of deposit sizes with associated annual interest rate and approximate time until funding crunch: + +| Deposit Size | Annual Validator Interest | Funding Crunch | +| -------- | -------- | -------- | +| 2.5M ETH | 10.12% | ~4 years | +| 10M ETH | 5.00% | ~2 years | +| 20M ETH | 3.52% | ~1.4 years | +| 40M ETH | 2.48% | ~1 year | + +#### Gas Changes +Normal block transactions cannot affect casper `vote` validation results, but casper `vote` validation results can affect normal block transaction execution. Due to this asymmetrical relationship, `vote` transactions can be processed in parallel with normal block transactions if `vote` transactions are placed after all normal block transactions. Because `vote` transactions can be processed in parallel to normal block transactions, `vote` transactions cost 0 gas for validators, ensuring that validators can submit votes even in highly congested or high gas-price periods. + +`vote_gas_used` is introduced to ensure that `vote` transactions do not put an undue burden on block processing. The additional overhead from `vote` transactions is capped at the same limit as normal block transactions so that, when run in parallel, neither sets of transactions exceed the overhead defined by the `block_gas_limit`. + +The call to `initialize_epoch` at the beginning of each epoch requires 0 gas so that this protocol state transition does not take any gas allowance away from normal transactions. + +#### NULL_SENDER and Account Abstraction +This EIP implements a limited version of account abstraction for validators' `vote` transactions. The general design was borrowed from [EIP-86](./eip-86.md). Rather than relying upon native transaction signatures, each validator specifies a signature contract when sending their `deposit` to `CASPER_ADDR`. When casting a `vote`, the validator bundles and signs the parameters of their `vote` according to the requirements of their signature contract. The `vote` method of the casper contract checks the signature of the parameters against the validator's signature contract, exiting the transaction as unsuccessful if the signature is not successfully verified. + +This allows validators to customize their own signing scheme for votes. Use cases include: +* quantum-secure signature schemes +* multisig wallets +* threshold schemes + +For more details on validator account abstraction, see the [Validator Implementation Guide](https://github.com/ethereum/casper/blob/master/VALIDATOR_GUIDE.md). + +#### Client Settings +##### Enable Casper Fork Choice +Releasing client versions with the casper fork choice as initially default disabled allows for a more conservative transition to hybrid Casper FFG. Under normal operating conditions there are no disparities between the PoW fork choice and the hybrid Casper FFG fork choice. The two fork choice rules can only diverge if either 51% of miners or 51% of validators are faulty. + +Validators will begin to log on, vote, and finalize the FFG contract before the majority of the network begins explicitly relying upon the new finality mechanism. Once a significant number of validators have logged on and the finality mechanism has been tested on the live network, new client software versions that change the default to enabled will be released. + +##### NON_REVERT_MIN_DEPOSIT +`NON_REVERT_MIN_DEPOSIT` is defined and configurable locally by each client. Clients are in charge of deciding upon the minimum deposits (security) at which they will accept the chain as finalized. In the general case, differing values in the choice of this local constant will not create any fork inconsistencies because clients with very strict finalization requirements will revert to follow the longest PoW chain. + +Arguments have been made to hardcode a value into clients or the contract, but we cannot reasonably define security required for all clients especially in the context of massive fluctuations in the value of ETH. + +##### Exclusion +This setting is useful in coordinating minority forks in cases of majority collusion. + +##### Join Fork +This setting is to be used by new clients that are syncing the network for the first time. Due to weak subjectivity, a blockhash should be supplied to successfully sync the network when initially starting a node. + +This setting is also useful for coordinating minority forks in cases of majority collusion. + +##### Monitor Votes +Monitoring the network for slashing conditions is key to Casper FFG's "accountable safety" as submitting proof of nefarious activity burns a validator's deposit. + +This setting is suggested default disabled because the block producer will almost certainly frontrun anyone else submitting a `slash` transaction. To prevent every client on the network from submitting a `slash` transaction in the event of a slashing condition, this setting should only be enabled by block producers and those clients who explicitly choose to monitor votes. + +## Backwards Compatibility +This EIP is not forward compatible and introduces backwards incompatibilities in the state, fork choice rule, block reward, transaction validity, and gas calculations on certain transactions. Therefore, all changes should be included in a scheduled hardfork at `HYBRID_CASPER_FORK_BLKNUM`. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1013.md b/EIPS/eip-1013.md new file mode 100644 index 0000000..0c7b04a --- /dev/null +++ b/EIPS/eip-1013.md @@ -0,0 +1,38 @@ +--- +eip: 1013 +title: "Hardfork Meta: Constantinople" +author: Nick Savers (@nicksavers) +type: Meta +status: Final +created: 2018-04-20 +requires: 145, 609, 1014, 1052, 1234, 1283 +--- + +## Abstract + +This meta-EIP specifies the changes included in the Ethereum hardfork named Constantinople. + +## Specification + +- Codename: Constantinople +- Aliases: Metropolis/Constantinople, Metropolis part 2 +- Activation: + - `Block >= 7_280_000` on the Ethereum Mainnet + - `Block >= 4,230,000` on the Ropsten testnet + - `Block >= 9_200_000` on the Kovan testnet + - `Block >= 3_660_663` on the Rinkeby testnet +- Included EIPs: + - [EIP-145](./eip-145.md): Bitwise shifting instructions in EVM + - [EIP-1014](./eip-1014.md): Skinny CREATE2 + - [EIP-1052](./eip-1052.md): EXTCODEHASH Opcode + - [EIP-1234](./eip-1234.md): Delay difficulty bomb, adjust block reward + - [EIP-1283](./eip-1283.md): Net gas metering for SSTORE without dirty maps + +## References + +1. The list above includes the EIPs discussed as candidates for Constantinople at the All Core Dev [Constantinople Session #1](https://github.com/ethereum/pm/issues/55). See also [Constantinople Progress Tracker](https://github.com/ethereum/pm/wiki/Constantinople-Progress-Tracker). +2. https://blog.ethereum.org/2019/02/22/ethereum-constantinople-st-petersburg-upgrade-announcement/ + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1014.md b/EIPS/eip-1014.md new file mode 100644 index 0000000..31d4d62 --- /dev/null +++ b/EIPS/eip-1014.md @@ -0,0 +1,109 @@ +--- +eip: 1014 +title: Skinny CREATE2 +author: Vitalik Buterin (@vbuterin) +category: Core +type: Standards Track +status: Final +created: 2018-04-20 +--- + +### Specification + +Adds a new opcode (`CREATE2`) at `0xf5`, which takes 4 stack arguments: endowment, memory_start, memory_length, salt. Behaves identically to `CREATE` (`0xf0`), 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. + +### 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](./eip-161.md) + +> 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` (`0xff`) 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` + +Example 6 +* address `0x0000000000000000000000000000000000000000` +* salt `0x0000000000000000000000000000000000000000000000000000000000000000` +* init_code `0x` +* gas (assuming no mem expansion): `32000` +* result: `0xE33C0C7F7df4809055C3ebA6c09CFe4BaF1BD9e0` + diff --git a/EIPS/eip-1015.md b/EIPS/eip-1015.md new file mode 100644 index 0000000..829cc00 --- /dev/null +++ b/EIPS/eip-1015.md @@ -0,0 +1,119 @@ +--- +eip: 1015 +title: Configurable On Chain Issuance +author: Alex Van de Sande +discussions-to: https://ethereum-magicians.org/t/eip-dynamic-block-rewards-with-governance-contract/204 +status: Stagnant +type: Standards Track +category: Core +created: 2018-04-20 +--- + + +## Simple Summary + +This EIP changes the block reward step by instead of setting it to be hard coded on the clients and to be given to the miner/validator etherbase, it should instead go to an address decided by an on-chain contract, with hard limits on how it would be issued (six month lock-in; issuance can only decrease or be maintained, but not increase;). A decision method is suggested but not essential to the notion of this EIP. This would **not be a generic governance solution**, which is a much broader and harder topic, would **not** affect technical upgrade decisions or other hard forks, but seen as *a forum to attempt to prevent contentious hard forks* that can be solved with the issuance. + +## Summary +### Thesis: many controversial issues boil down to resources +These are current EIPs that are being developed or debated. They might seem unrelated but they have something in common, that they can be resolved by proper channel of funds. + +#### Casper and PoS + +Moving to PoS has been on the roadmap since day 0 for ethereum, along with a reduction in issuance to a cost equivalent to the Validator's cost of security (considered to be more eficient than PoW). But the exact issuance necessary for PoS has yet to be determined and is currently being researched. Casper validation will be an on chain contract and therefore it will need to be funded. It's unlikely that a definitive final answer on how much issuance is needed for validation will be reached in the next years as new research will uncover new arguments, so it would make sense to allow some flexibility on this matter + +#### Issuance Cap at 120 Million + +[EIP-960](https://github.com/ethereum/EIPs/issues/960), Vitalik's not so jokey april's fool has been taken seriously. It proposes the issuance to be slowly reduced until it reaches 120 million ether. One of the main counterpoints by Vlad can be simplified by [we don't know enough to know what that ether can be used for](https://medium.com/@Vlad_Zamfir/against-vitaliks-fixed-supply-eip-eip-960-18e182a7e5bd) and Vitalik's counterpoint is that [reducing emissions can be a way to reduce future abuse of these funds by finding a schelling point at 0](https://medium.com/@VitalikButerin/to-be-clear-im-not-necessarily-wedded-to-a-finite-supply-cap-a7aa48ab880c). Issuance has already been reduced once, from 5 ether to the current 3 ether per block. The main point of a hard cap is that a lot of people consider *not issuing* as having a positive contribution, that can outweigh other actions. Burning ether is also a valid issuance decision. + +#### Asics and advantages of PoW + +[EIP-960](https://eips.ethereum.org/EIPS/eip-969) proposes a change in algorithm to avoid mining being dominated by ASICS. Counter arguments by Phil Daian argue among others than [resisting economies of scale is futile and there might be specific security advantages to specialized hardware](https://pdaian.com/blog/anti-asic-forks-considered-harmful/). One of the main arguments for PoW mining, even when it doesn't provide security, it is useful as a fair distribution mechanism, that **PoW allows any person with a computer, internet access and electricity to obtain currency without having to deal with government imposed currency controls**. + +#### Recovery Forks + +After the Parity Multisig library self destruction, three different strategies have been attempted to recover the funds: [a general protocol improvement to allow reviving self destructed contracts](https://gist.github.com/5chdn/a9bb8617cc8523a030126a3d1c60baf3) (which was considered dangerous), a [general process to recover funds](https://github.com/ethereum/EIPs/pull/867) and a [specific recovery of the multisig library](./eip-999.md). The latter two are finding a lot of resistance from the community, but it's unlikely that these issues are going away soon. The affected parties have a large incentive (fluctuating at almost half a billion dollars) to keep trying, and it's an issue that is likely to occur again in the future. If they get reimbursed, [there are many other special cases of ether provably burnt or stuck](https://github.com/ethereum/EIPs/issues/156) that might deserve the same treatment. If they get shut down, they have an incentive to move forward a fork implementation: even if they are a minority chain, it's likely they'll recover an amount larger than 0, which is what they would otherwise, and it means the main ethereum community might lose a valuable team of developers. + + +#### Other Public Goods + +There are many other types of public goods that could be funded by issuance. By *Public Good*, I'm using a strict definition of something that brings value to everyone, both those who funded it and free-loaders, making it hard to fund it exclusively by traditional private incentives. They can be research, whole network security, [incentivize full clients and networking](./eip-908.md), fair distribution of tokens etc. + +## Proposed Solution +### Issuance Contract + +This EIP proposes a future hard fork where block reward is not issued to miners/validators etherbase, but instead to a single contract, that then will activate the default function (with a fixed amount of gas) and then it will forward the ether to other contracts which will finally distribute to their final destinations. + +#### Limits on the contract + +##### It can only deal with issuance + +It's not meant to be a general governance contract. The contract **should NOT be used to** to decide software updates, to freeze funds, change contracts balances or anything on the consensus layer other than the strict definition of *where block rewards go*. It should be seen as a platform to settle disputes to avoid the implementation of contentious hard forks, not as a mean to remove the power of users and developers to execute them. + +##### It cannot only decrease issuance, and once decreased it cannot be increased again + +In order to reduce future abuse and uncertainty, **once issuance is reduced, it cannot be increased**. To prevent a single action reducing it to 0, the reduction is limited up to a percentage per time, so if the **decision assembly** is aggressively to reduce issuance to zero, it would take a known number of years. + +##### Results are locked for six months + +Whenever a new decision on either reducing the issuance or changing the target is made, the effects will have a six month delay to it. Once a decision is made it is final, it will take place six months after that, but if it's shortly reversed, then that change will be short lived. The rationale behind is that it allows time to anyone disagreeing with the decision to: + +* Sell their coins so that if a bad actor takes over they will have a reduced reward +* Attempt to revert the decision as soon as possible, to reduce the duration that the change will take place +* Organize to create counter measures against the decision +* Implement a hard fork changing the decision contract or to remove the issuance contract altogether, as a last resort measure + +##### Interface + +It would have the following interface: + +`function targetContract() returns (address)` + +There's a single target contract that should ether issued to. At each new block, that contract would have the default function called so it would forward to the intended addresses. + +`function decisionAssembly() returns (address)` + +A contract that would represent the internal governance of decisions. More on this later. + +`function reduceIssuance(uint)` + +Can only be called by **decision assembly**. The contract is allowed to reduce or maintain the issuance per block. **Change is not executed immediately, effects only happen six months later, but once a decision is committed it is irrevocable**. + +`function changeTargetContract(address)` + +Can only be called by **decision assembly**. It changes the contract that will receive the funds in the future. Only one contract can be appointed, but if the community desires to split issuance or even burn a part of it (in a non-irrevocable manner) it can be done in that contract. Change is not executed immediately, **effects only happen six months later**, but once a decision is committed it is certain, even if it's later reverted: if a new target contract is changed after a few hours, then it still means that in six month's time, it would issue for that contract for a few hours. + +`function executeDecision(uint)` + +Can be called by anyone: it executes a change to issuance amount or target that had been scheduled six months prior. + +##### Decision Assembly + +A contract that has the power to decide the changes to issuance, the core of the governance of these decisions. The exact format of this governance is open to debate, but what follows is a suggestion: + +The decision would be made by multiple signalling contracts, each one implemented by separate groups and representing one aspect of the community or one sort of measurement. Each signaling process would have a `int` bound in which they could vote and they would have their own internal process to decide that. As new governance methods are tested and debated, new signalling contracts should be added and removed. Suggested signalling contracts: + +* Futarchy and prediction markets based on multiple measures +* Votes weighted by ether balance (optionally with multipliers if the voters where committed to locking votes) +* Token votes, weighted by their own relative ether exchange rate +* Votes by individual humans if a good sybil resistance, coercion mechanism is developed +* Block-signalling, as a way to measure validators/miners choices +* Some sort of signalling representing developers, exchanges or other stakeholders +* Any other proposed manner + +Since adding and removing signalling contracts, as well as changing their total weight would be controlled by the contract itself, it would be crucial that the first signalling contracts were a diverse set of interests, and that they were open to adding more signals as new governance is experimented as well as removing signals that stop representing the community. + +### Questions to be debated + +A lot of things are suggested in this EIP, so I would like to propose these questions to be debated: + +1. Do we want to have dynamically changing block rewards, instead of having them be hard coded in the protocol? +2. If the answer above is yes, then what would be the best governance process to decide it, and what sorts of limits would we want that governance contract to have? +3. If the answer is a multi-signalling contract, then what sorts of signals would we want, what sort of relative weight should they have and what would be the process to add and remove them? + + + + + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1046.md b/EIPS/eip-1046.md new file mode 100644 index 0000000..c4e3856 --- /dev/null +++ b/EIPS/eip-1046.md @@ -0,0 +1,221 @@ +--- +eip: 1046 +title: tokenURI Interoperability +description: Extends ERC-20 with an ERC-721-like tokenURI, and extends ERC-721 and ERC-1155 with interoperability +author: Tommy Nicholas (@tomasienrbc), Matt Russo (@mateosu), John Zettler (@JohnZettler), Matt Condon (@shrugs), Gavin John (@Pandapip1) +discussions-to: https://ethereum-magicians.org/t/eip-1046-erc-20-metadata-extension/13036 +status: Final +type: Standards Track +category: ERC +created: 2018-04-13 +requires: 20, 721, 1155 +--- + +## Abstract + +[ERC-721](./eip-721.md) introduced a `tokenURI` function for non-fungible tokens to handle miscellaneous metadata such as: + +- thumbnail image +- title +- description +- special asset properties +- etc. + +This ERC adds a `tokenURI` function to [ERC-20](./eip-20.md), and extends [ERC-721](./eip-721.md) and [ERC-1155](./eip-1155.md) to enable interoperability between all three types of token URI. + +## Motivation + +See the note about the metadata extension in [ERC-721](./eip-721.md#rationale). The same arguments apply to ERC-20. + +Being able to use similar mechanisms to extract metadata for ERC-20, ERC-721, ERC-1155, and future standards is useful for determining: + +- What type of token a contract is (if any); +- How to display a token to a user, either in an asset listing page or on a dedicated token page; and +- Determining the capabilities of the token + +## Specification + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. + +### Interoperability Metadata + +The following TypeScript interface is used in later sections: + +```typescript +/** + * Interoperability metadata. + * This can be extended by other proposals. + * + * All fields MUST be optional. + * **Not every field has to be a boolean.** Any optional JSON-serializable object can be used by extensions. + */ +interface InteroperabilityMetadata { + /** + * This MUST be true if this is ERC-1046 Token Metadata, otherwise, this MUST be omitted. + * Setting this to true indicates to wallets that the address should be treated as an ERC-20 token. + **/ + erc1046?: boolean | undefined; + + /** + * This MUST be true if this is ERC-721 Token Metadata, otherwise, this MUST be omitted. + * Setting this to true indicates to wallets that the address should be treated as a ERC-721 token. + **/ + erc721?: boolean | undefined; + + /** + * This MUST be true if this is ERC-1155 Token Metadata, otherwise, this MUST be omitted. + * Setting this to true indicates to wallets that the address should be treated as a ERC-1155 token. + **/ + erc1155?: boolean | undefined; +} +``` + +### ERC-20 Extension + +#### ERC-20 Interface Extension + +Compliant contracts MUST implement the following Solidity interface: + +```solidity +pragma solidity ^0.8.0; + +/// @title ERC-20 Metadata Extension +interface ERC20TokenMetadata /* is ERC20 */ { + /// @notice Gets an ERC-721-like token URI + /// @dev The resolved data MUST be in JSON format and support ERC-1046's ERC-20 Token Metadata Schema + function tokenURI() external view returns (string); +} +``` + +#### ERC-20 Token Metadata Schema + +The resolved JSON of the `tokenURI` described in the ERC-20 Interface Extension section MUST conform to the following TypeScript interface: + +```typescript +/** + * Asset Metadata + */ +interface ERC20TokenMetadata { + /** + * Interoperabiliy, to differentiate between different types of tokens and their corresponding URIs. + **/ + interop: InteroperabilityMetadata; + + /** + * The name of the ERC-20 token. + * If the `name()` function is present in the ERC-20 token and returns a nonempty string, these MUST be the same value. + */ + name?: string; + + /** + * The symbol of the ERC-20 token. + * If the `symbol()` function is present in the ERC-20 token and returns a nonempty string, these MUST be the same value. + */ + symbol?: string; + + /** + * The decimals of the ERC-20 token. + * If the `decimals()` function is present in the ERC-20 token, these MUST be the same value. + * Defaults to 18 if neither this parameter nor the ERC-20 `decimals()` function are present. + */ + decimals?: number; + + /** + * Provides a short one-paragraph description of the ERC-20 token, without any markup or newlines. + */ + description?: string; + + /** + * A URI pointing to a resource with mime type `image/*` that represents this token. + * If the image is a bitmap, it SHOULD have a width between 320 and 1080 pixels + * The image SHOULD have an aspect ratio between 1.91:1 and 4:5 inclusive. + */ + image?: string; + + /** + * One or more URIs each pointing to a resource with mime type `image/*` that represents this token. + * If an image is a bitmap, it SHOULD have a width between 320 and 1080 pixels + * Images SHOULD have an aspect ratio between 1.91:1 and 4:5 inclusive. + */ + images?: string[]; + + /** + * One or more URIs each pointing to a resource with mime type `image/*` that represent an icon for this token. + * If an image is a bitmap, it SHOULD have a width between 320 and 1080 pixels, and MUST have a height equal to its width + * Images MUST have an aspect ratio of 1:1, and use a transparent background + */ + icons?: string[]; +} +``` + +### ERC-721 Extension + +#### Extension to the ERC-721 Metadata Schema + +Contracts that implement ERC-721 and use its token metadata URI SHOULD to use the following TypeScript extension to the metadata URI: + +```typescript +interface ERC721TokenMetadataInterop extends ERC721TokenMetadata { + /** + * Interoperabiliy, to avoid confusion between different token URIs + **/ + interop: InteroperabilityMetadata; +} +``` + +### ERC-1155 Extension + +#### ERC-1155 Interface Extension + +[ERC-1155](./eip-1155.md)-compliant contracts using the metadata extension SHOULD implement the following Solidity interface: + +```solidity +pragma solidity ^0.8.0; + +/// @title ERC-1155 Metadata URI Interoperability Extension +interface ERC1155TokenMetadataInterop /* is ERC1155Metadata */ { + /// @notice Gets an ERC-1046-compliant ERC-1155 token URI + /// @dev The resolved data MUST be in JSON format and support ERC-1046's Extension to the ERC-1155 Token Metadata Schema + /// This MUST be the same uri as the `uri()` function + function tokenURI() external view returns (string); +} +``` + +#### Extension to the ERC-1155 Metadata Schema + +Contracts that implement ERC-1155 and use its token metadata URI are RECOMMENDED to use the following extension to the metadata URI. Contracts that implement the interface described in the ERC-1155 Interface Extension section MUST use the following TypeScript extension: + +```typescript +interface ERC1155TokenMetadataInterop extends ERC1155TokenMetadata { + /** + * Interoperabiliy, to avoid confusion between different token URIs + **/ + interop: InteroperabilityMetadata; +} +``` + +### Miscellaneous Recommendations + +To save gas, it is RECOMMENDED for compliant contracts not to implement the optional `name()`, `symbol()`, or `decimals()` functions, and instead to only include them in the metadata URI. Additionally, if the decimals is `18`, then it is NOT RECOMMENDED to include the `decimals` field in the metadata. + +## Rationale + +This ERC makes adding metadata to ERC-20 tokens more straightforward for developers, with minimal to no disruption to the overall ecosystem. Using the same parameter name makes it easier to reuse code. + +Additionally, the recommendations not to use ERC-20's `name`, `symbol`, and `decimals` functions save gas. + +Built-in interoperability is useful as otherwise it might not be easy to differentiate the type of the token. Interoperability could be done using [ERC-165](./eip-165.md), but static calls are time-inefficient for wallets and websites, and is generally inflexible. Instead, including interoperability data in the token URI increases flexibility while also giving a performance increase. + +## Backwards Compatibility + +This EIP is fully backwards compatible as its implementation simply extends the functionality of ERC-20 tokens and is optional. Additionally, it makes backward compatible recommendations for ERC-721 and ERC-1155 tokens. + +## Security Considerations + +### Server-Side Request Forgery (SSRF) + +Wallets should be careful about making arbitrary requests to URLs. As such, it is recommended for wallets to sanitize the URI by whitelisting specific schemes and ports. A vulnerable wallet could be tricked into, for example, modifying data on a locally-hosted redis database. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1051.md b/EIPS/eip-1051.md new file mode 100644 index 0000000..c6b1a7e --- /dev/null +++ b/EIPS/eip-1051.md @@ -0,0 +1,59 @@ +--- +eip: 1051 +title: Overflow checking for the EVM +author: Nick Johnson +discussions-to: https://ethereum-magicians.org/t/eip-arithmetic-overflow-detection-for-the-evm/261 +status: Stagnant +type: Standards Track +category: Core +created: 2018-05-02 +--- + +## Abstract +This EIP adds overflow checking for EVM arithmetic operations, and two new opcodes that check and clear the overflow flags. + +## Motivation +The correct functioning of many contracts today is dependent on detecting and preventing overflow of arithmetic operations. Since the EVM operates on mod 2^256 integers and provides no built-in overflow detection or prevention, this requires manual checks on every arithmetic operation. + +In the interests of facilitating efficient and secure contracts, we propose new opcodes that permit efficient detection of overflows, which can be checked periodically rather than after each operation. + +## Specification + +Two new flags are added to the EVM state: overflow (`ovf`) and signed overflow (`sovf`). + +The `ovf` flag is set in the following circumstances: + + - When an `ADD` (`0x01`) opcode, with both inputs treated as unsigned integers, produces an ideal output in excess of 2^256 - 1. + - When a `SUB` (`0x03`) opcode, with both inputs treated as unsigned integers, produces an ideal output less than 0. + - When a `MUL`(`0x02`) opcode, with both inputs treated as unsigned integers, produces an ideal output in excess of 2^256 - 1. + +The `sovf` flag is set whenever the `ovf` flag is set, and additionally in the following circumstances: + + - When an `ADD` opcode with both inputs having the same MSB results in the output having a different MSB (eg, `(+a) + (+b) = (-c)` or `(-a) + (-b) = (+c)`). + - When a `SUB` opcode occurs and the result has the same MSB as the subtractand (second argument) (eg, `(+a) - (-b) = (-c)` or `(-a) - (+b) = (+c)`. + - When a `MUL` opcode with both inputs being positive has a negative output. + - When a `MUL` opcode with both inputs being negative has a negative output. + - When a `MUL` opcode with one negative input and one positive input has a positive output. + +A new opcode, `OFV` is added, with number `0x0c`. This opcode takes 0 arguments from the stack. When executed, it pushes `1` if the `ovf` flag is set, and `0` otherwise. It then sets the `ovf` flag to false. + +A new opcode, `SOVF` is added, with number `0x0d`. This opcode takes 0 arguments from the stack. When executed, it pushes `1` if the `sovf` flag is set, and `0` otherwise. It then sets the `sovf` flag to false. + +## Rationale +Any change to implement overflow protection needs to preserve behaviour of existing contracts, which precludes many changes to the arithmetic operations themselves. One option would be to provide an opcode that enables overflow protection, causing a throw or revert if an overflow happens. However, this limits the manner in which overflows can be handled. + +Instead, we replicate functionality from real world CPUs, which typically implement 'carry' and 'overflow' flags. + +Separate flags for signed and unsigned overflow are necessary due to the fact that a signed overflow may not result in an unsigned overflow. + +## Backwards Compatibility +This EIP introduces no backwards compatibility issues. + +## Test Cases +TBD + +## Implementation +TBD + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1052.md b/EIPS/eip-1052.md new file mode 100644 index 0000000..73e00bb --- /dev/null +++ b/EIPS/eip-1052.md @@ -0,0 +1,78 @@ +--- +eip: 1052 +title: EXTCODEHASH opcode +author: Nick Johnson , Paweł Bylica +discussions-to: https://ethereum-magicians.org/t/extcodehash-opcode/262 +status: Final +type: Standards Track +category: Core +created: 2018-05-02 +requires: 161 +--- + +## Abstract +This EIP specifies a new opcode, which returns the keccak256 hash of a contract's code. + +## Motivation +Many contracts need to perform checks on a contract's bytecode, but do not necessarily need the bytecode itself. For instance, a contract may want to check if another contract's bytecode is one of a set of permitted implementations, or it may perform analyses on code and whitelist any contract with matching bytecode if the analysis passes. + +Contracts can presently do this using the `EXTCODECOPY` (`0x3c`) opcode, but this is expensive, especially for large contracts, in cases where only the hash is required. As a result, we propose a new opcode, `EXTCODEHASH`, which returns the keccak256 hash of a contract's bytecode. + +## Specification + +A new opcode, `EXTCODEHASH`, is introduced, with number `0x3f`. The `EXTCODEHASH` +takes one argument from the stack, zeros the first 96 bits +and pushes to the stack the keccak256 hash of the code of the account +at the address being the remaining 160 bits. + +In case the account does not exist or is empty (as defined by [EIP-161](./eip-161.md)) `0` is pushed to the stack. + +In case the account does not have code the keccak256 hash of empty data +(i.e. `c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470`) +is pushed to the stack. + +The gas cost of the `EXTCODEHASH` is 400. + + +## Rationale + +As described in the motivation section, this opcode is widely useful, and saves +on wasted gas in many cases. + +The gas cost is the same as the gas cost for the `BALANCE` opcode because the +execution of the `EXTCODEHASH` requires the same account lookup as in `BALANCE`. + +Only the 20 last bytes of the argument are significant (the first 12 bytes are +ignored) similarly to the semantics of the `BALANCE` (`0x31`), `EXTCODESIZE` (`0x3b`) and +`EXTCODECOPY` (`0x3c`). + +The `EXTCODEHASH` distincts accounts without code and non-existing accounts. +This is consistent with the way accounts are represented in the state trie. +This also allows smart contracts to check whenever an account exists. + + +## Backwards Compatibility + +There are no backwards compatibility concerns. + + +## Test Cases + +1. The `EXTCODEHASH` of the account without code is `c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470` + what is the keccack256 hash of empty data. +2. The `EXTCODEHASH` of non-existent account is `0`. +3. The `EXTCODEHASH` of an precompiled contract is either `c5d246...` or `0`. +4. If `EXTCODEHASH` of `A` is `X`, then `EXTCODEHASH` of `A + 2**160` is `X`. +5. The `EXTCODEHASH` of an account that selfdestructed in the current transaction. +6. The `EXTCODEHASH` of an account that selfdestructed and later the selfdestruct has been reverted. +7. The `EXTCODEHASH` of an account created in the current transaction. +8. The `EXTCODEHASH` of an account that has been newly created and later the creation has been reverted. +9. The `EXTCODEHASH` of an account that firstly does not exist and later is empty. +10. The `EXTCODEHASH` of an empty account that is going to be cleared by the state clearing rule. + + +## Implementation +TBD + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1056.md b/EIPS/eip-1056.md new file mode 100644 index 0000000..cb468d8 --- /dev/null +++ b/EIPS/eip-1056.md @@ -0,0 +1,286 @@ +--- +eip: 1056 +title: Ethereum Lightweight Identity +author: Pelle Braendgaard , Joel Torstensson +type: Standards Track +category: ERC +discussions-to: https://github.com/ethereum/EIPs/issues/1056 +status: Stagnant +created: 2018-05-03 +--- + +## Simple Summary + +A registry for key and attribute management of lightweight blockchain identities. + +## Abstract + +This ERC describes a standard for creating and updating identities with a limited use of blockchain resources. An identity can have an unlimited number of `delegates` and `attributes` associated with it. Identity creation is as simple as creating a regular key pair ethereum account, which means that it's free (no gas costs) and all ethereum accounts are valid identities. Furthermore this ERC is fully [DID compliant](https://w3c-ccg.github.io/did-spec/). + +## Motivation + +As we have been developing identity systems for the last couple of years at uPort it has become apparent that the cost of identity creation is a large issue. The previous Identity proposal [ERC-725](./eip-725.md) faces this exact issue. Our requirements when creating this ERC is that identity creation should be free, and should be possible to do in an offline environment (e.g. refugee scenario). However it must also be possible to rotate keys without changing the primary identifier of the identity. The identity system should be fit to use off-chain as well as on-chain. + +## Definitions + +* `Identifier`: a piece of data that uniquely identifies the identity, an ethereum address + +* `delegate`: an address that is delegated for a specific time to perform some sort of function on behalf of an identity + +* `delegateType`: the type of a delegate, is determined by a protocol or application higher up + Examples: + + * `did-jwt` + * `raiden` + +* `attribute`: a piece of data associated with the identity + +## Specification + +This ERC specifies a contract called `EthereumDIDRegistry` that is deployed once and can then be commonly used by everyone. + +### Identity ownership + +By default an identity is owned by itself, meaning whoever controls the ethereum account with that address. The owner can be updated to a new key pair account or to a multisig account etc. + +#### identityOwner + +Returns the owner of the given identity. + +```js +function identityOwner(address identity) public view returns(address); +``` + +#### changeOwner + +Sets the owner of the given identity to another ethereum account. + +```js +function changeOwner(address identity, address newOwner) public; +``` + +#### changeOwnerSigned + +Same as above but with raw signature. + + +```js +function changeOwnerSigned(address identity, uint8 sigV, bytes32 sigR, bytes32 sigS, address newOwner) public; +``` + +### Delegate management + +Delegates can be used both on- and off-chain. They all have a `delegateType` which can be used to specify the purpose of the delegate. + +#### validDelegate + +Returns true if the given `delegate` is a delegate with type `delegateType` of `identity`. + +```js +function validDelegate(address identity, bytes32 delegateType, address delegate) public view returns(bool); +``` + +#### addDelegate + +Adds a new delegate with the given type. `validity` indicates the number of seconds that the delegate will be valid for, after which it will no longer be a delegate of `identity`. + +```js +function addDelegate(address identity, bytes32 delegateType, address delegate, uint validity) public; +``` + + +#### addDelegateSigned + +Same as above but with raw signature. + + +```js +function addDelegateSigned(address identity, uint8 sigV, bytes32 sigR, bytes32 sigS, bytes32 delegateType, address delegate, uint validity) public; +``` + + +#### revokeDelegate + +Revokes the given `delegate` for the given `identity`. + + +```js +function revokeDelegate(address identity, bytes32 delegateType, address delegate) public; +``` + + +#### revokeDelegateSigned + +Same as above but with raw signature. + + +```js +function revokeDelegateSigned(address identity, uint8 sigV, bytes32 sigR, bytes32 sigS, bytes32 delegateType, address delegate) public; +``` + + +### Attribute management + +Attributes contain simple data about the identity. They can be managed only by the owner of the identity. + + +#### setAttribute + +Sets an attribute with the given `name` and `value`, valid for `validity` seconds. + + +```js +function setAttribute(address identity, bytes32 name, bytes value, uint validity) public; +``` + + +#### setAttributeSigned + +Same as above but with raw signature. + + +```js +function setAttributeSigned(address identity, uint8 sigV, bytes32 sigR, bytes32 sigS, bytes32 name, bytes value, uint validity) public; +``` + + +#### revokeAttrubte + +Revokes an attribute. + + +```js +function revokeAttribute(address identity, bytes32 name, bytes value) public; +``` + + +#### revokeAttributeSigned + +Same as above but with raw signature. + + +```js +function revokeAttributeSigned(address identity, uint8 sigV, bytes32 sigR, bytes32 sigS, bytes32 name, bytes value) public; +``` + + +### Events + +#### DIDOwnerChanged + +MUST be triggered when `changeOwner` or `changeOwnerSigned` was successfully called. + + +```js +event DIDOwnerChanged( + address indexed identity, + address owner, + uint previousChange +); +``` + + +#### DIDDelegateChanged + +MUST be triggered when a change to a delegate was successfully made. + + +```js +event DIDDelegateChanged( + address indexed identity, + bytes32 delegateType, + address delegate, + uint validTo, + uint previousChange +); +``` + + +#### DIDAttritueChanged + +MUST be triggered when a change to an attribute was successfully made. + + +```js +event DIDAttributeChanged( + address indexed identity, + bytes32 name, + bytes value, + uint validTo, + uint previousChange +); +``` + + +### Efficient lookup of events through linked identity events + +Contract Events are a useful feature for storing data from smart contracts exclusively for off-chain use. Unfortunately current ethereum implementations provide a very inefficient lookup mechanism. By using linked events that always link to the previous block with a change for the identity, we can solve this problem with much improved performance. Each identity has its previously changed block stored in the `changed` mapping. + + + +1. Lookup `previousChange` block for identity + +2. Lookup all events for given identity address using web3, but only for the `previousChange` block + +3. Do something with event + +4. Find `previousChange` from the event and repeat + + + +Example code: + + +```js +const history = [] +previousChange = await didReg.changed(identity) +while (previousChange) { + const filter = await didReg.allEvents({topics: [identity], fromBlock: previousChange, toBlock: previousChange}) + const events = await getLogs(filter) + previousChange = undefined + for (let event of events) { + history.unshift(event) + previousChange = event.args.previousChange + } +} +``` + + +### Building a DID document for an identity + +The primary owner key should be looked up using `identityOwner(identity)`. This should be the first of the publicKeys listed. Iterate through the `DIDDelegateChanged` events to build a list of additional keys and authentication sections as needed. The list of delegateTypes to include is still to be determined. Iterate through `DIDAttributeChanged` events for service entries, encryption public keys and other public names. The attribute names are still to be determined. + + +## Rationale + +For on-chain interactions Ethereum has a built in account abstraction that can be used regardless of whether the account is a smart contract or a key pair. Any transaction has a `msg.sender` as the verified send of the transaction. + + +Since each Ethereum transaction has to be funded, there is a growing trend of on-chain transactions that are authenticated via an externally created signature and not by the actual transaction originator. This allows 3rd party funding services or receiver pays without any fundamental changes to the underlying Ethereum architecture. These kinds of transactions have to be signed by an actual key pair and thus can not be used to represent smart contract based Ethereum accounts. + + +We propose a way of a Smart Contract or regular key pair delegating signing for various purposes to externally managed key pairs. This allows a smart contract to be represented both on-chain as well as off-chain or in payment channels through temporary or permanent delegates. + + +## Backwards Compatibility + +All ethereum accounts are valid identities (and DID compatible) using this standard. This means that any wallet provider that uses key pair accounts already supports the bare minimum of this standard, and can implement `delegate` and `attribute` functionality by simply using the `ethr-did` referenced below. As the **DID Auth** standard solidifies it also means that all of these wallets will be compatible with the [DID decentralized login system](https://github.com/decentralized-identity). + + +## Implementation + +[ethr-did-registry](https://github.com/uport-project/ethr-did-registry/blob/develop/contracts/EthereumDIDRegistry.sol) (`EthereumDIDRegistry` contract implementation) + +[ethr-did-resolver](https://github.com/uport-project/ethr-did-resolver) (DID compatible resolver) + +[ethr-did](https://github.com/uport-project/ethr-did) (javascript library for using the identity) + + +### Deployment + +The address for the `EthereumDIDRegistry` is `0xdca7ef03e98e0dc2b855be647c39abe984fcf21b` on Mainnet, Ropsten, Rinkeby and Kovan. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). + diff --git a/EIPS/eip-1057.md b/EIPS/eip-1057.md new file mode 100644 index 0000000..dc39734 --- /dev/null +++ b/EIPS/eip-1057.md @@ -0,0 +1,575 @@ +--- +eip: 1057 +title: ProgPoW, a Programmatic Proof-of-Work +author: Greg Colvin , Andrea Lanfranchi (@AndreaLanfranchi), Michael Carter (@bitsbetrippin), IfDefElse +discussions-to: https://ethereum-magicians.org/t/eip-progpow-a-programmatic-proof-of-work/272 +status: Stagnant +type: Standards Track +category: Core +created: 2018-05-02 +--- + +## Simple Summary + +A new Proof-of-Work algorithm to replace Ethash that utilizes almost all parts of commodity GPUs. + +## Abstract + +ProgPoW is a proof-of-work algorithm designed to close the efficiency gap available to specialized ASICs. It utilizes almost all parts of commodity hardware (GPUs), and comes pre-tuned for the most common hardware utilized in the Ethereum network. + +This document presents an overview of the algorithm and examines what it means to be “ASIC-resistant.” Next, we compare existing PoW designs by analyzing how each algorithm executes in hardware. Finally, we present the detailed implementation by walking through the code. + +## Motivation + +Ever since the first bitcoin mining ASIC was released, many new Proof of Work algorithms have been created with the intention of being “ASIC-resistant”. The goal of “ASIC-resistance” is to resist the centralization of PoW mining power such that these coins couldn’t be so easily manipulated by a few players. + +Ethereum's approach is to incentivize a geographically-distributed community of miners with a low barrier to entry on commodity hardware. As stated in the [Yellow Paper](https://ethereum.github.io/yellowpaper/paper.pdf): + +> 11.5. Mining Proof-of-Work. The mining proof-ofwork (PoW) exists as a cryptographically secure nonce that proves beyond reasonable doubt that a particular amount of computation has been expended in the determination of some token value n. It is utilised to enforce the blockchain security by giving meaning and credence to the notion of difficulty (and, by extension, total difficulty). However, since mining new blocks comes with an attached reward, the proof-of-work not only functions as a method of securing confidence that the blockchain will remain canonical into the future, but also as a wealth distribution mechanism. + +> For both reasons, there are two important goals of the proof-of-work function; firstly, it should be as accessible as possible to as many people as possible. The requirement of, or reward from, specialised and uncommon hardware should be minimised. This makes the distribution model as open as possible, and, ideally, makes the act of mining a simple swap from electricity to Ether at roughly the same rate for anyone around the world. + +> Secondly, it should not be possible to make super-linear profits, and especially not so with a high initial barrier. Such a mechanism allows a well-funded adversary to gain a troublesome amount of the network’s total mining power and as such gives them a super-linear reward (thus skewing distribution in their favour) as well as reducing the network security... + +> ... While ASICs exist for a proof-of-work function, both goals are placed in jeopardy. Because of this, a proof-of-work function that is ASIC-resistant (i.e. difficult or economically inefficient to implement in specialised compute hardware) has been identified as the proverbial silver bullet. + +It is from these premises that Ethash was designed as an ASIC-resistant proof-of-work: + +> Two directions exist for ASIC resistance; firstly make it sequential memory-hard, i.e. engineer the function such that the determination of the nonce requires a lot of memory and bandwidth such that the memory cannot be used in parallel to discover multiple nonces simultaneously. The second is to make the type of computation it would need to do general-purpose; the meaning of “specialised hardware” for a general-purpose task set is, naturally, general purpose hardware and as such commodity desktop computers are likely to be pretty close to “specialised hardware” for the task. For Ethereum 1.0 we have chosen the first path. + +5 years of experience with the Ethereum blockchain have demonstrated the success of our approach. This success cannot be taken for granted. +* 11 years of experience with PoW Blockchains have shown a centralization in hardware development, resulting in a [few companies](https://www.asicminervalue.com/) controlling the lifecycle of new hardware with limited distribution. +* New ASICs for Ethash are providing higher efficiency than GPUs, such as the [Antminer E3](https://shop.bitmain.com/product/detail?pid=00020181031134626816gh0zYNKC06A3). +* As much as 40% of the Ethereum network may now be secured by ASICs. + +ProgPow restores Ethash' ASIC-resistance by extending Ethash with a GPU-specific approach to the second path — making the “specialised hardware” for the PoW task commodity hardware. + +### ProgPoW Overview +The design goal of ProgPoW is to have the algorithm’s requirements match what is available on commodity GPUs: If the algorithm were to be implemented on a custom ASIC there should be little opportunity for efficiency gains compared to a commodity GPU. + +The main elements of the algorithm are: +* Changes keccak_f1600 (with 64-bit words) to keccak_f800 (with 32-bit words) to reduce impact on total power +* Increases mix state. +* Adds a random sequence of math in the main loop. +* Adds reads from a small, low-latency cache that supports random addresses. +* Increases the DRAM read from 128 bytes to 256 bytes. + +The random sequence changes every `PROGPOW_PERIOD` (about 2 to 12 minutes depending on the configured value). When mining source code is generated for the random sequence and compiled on the host CPU. The GPU will execute the compiled code where what math to perform and what mix state to use are already resolved. + +While a custom ASIC to implement this algorithm is still possible, the efficiency gains available are minimal. The majority of a commodity GPU is required to support the above elements. The only optimizations available are: +* Remove the graphics pipeline (displays, geometry engines, texturing, etc) +* Remove floating point math +* A few ISA tweaks, like instructions that exactly match the merge() function + +These would result in minimal, roughly 1.1-1.2x, efficiency gains. This is much less than the 2x for Ethash or 50x for Cryptonight. + +### Rationale for PoW on Commodity Hardware +With the growth of large mining pools, the control of hashing power has been delegated to the top few pools to provide a steadier economic return for small miners. While some have made the argument that large centralized pools defeats the purpose of “ASIC resistance,” it’s important to note that ASIC based coins are even more centralized for several reasons. + +1. No natural distribution: There isn’t an economic purpose for ultra-specialized hardware outside of mining and thus no reason for most people to have it. +2. No reserve group: Thus, there’s no reserve pool of hardware or reserve pool of interested parties to jump in when coin price is volatile and attractive for manipulation. +3. High barrier to entry: Initial miners are those rich enough to invest capital and ecological resources on the unknown experiment a new coin may be. Thus, initial coin distribution through mining will be very limited causing centralized economic bias. +4. Delegated centralization vs implementation centralization: While pool centralization is delegated, hardware monoculture is not: only the limiter buyers of this hardware can participate so there isn’t even the possibility of divesting control on short notice. +5. No obvious decentralization of control even with decentralized mining: Once large custom ASIC makers get into the game, designing back-doored hardware is trivial. ASIC makers have no incentive to be transparent or fair in market participation. + +While the goal of “ASIC resistance” is valuable, the entire concept of “ASIC resistance” is a bit of a fallacy. CPUs and GPUs are themselves ASICs. Any algorithm that can run on a commodity ASIC (a CPU or GPU) by definition can have a customized ASIC created for it with slightly less functionality. Some algorithms are intentionally made to be “ASIC friendly” - where an ASIC implementation is drastically more efficient than the same algorithm running on general purpose hardware. The protection that this offers when the coin is unknown also makes it an attractive target for a dedicate mining ASIC company as soon as it becomes useful. + +Therefore, ASIC resistance is: the efficiency difference of specialized hardware versus hardware that has a wider adoption and applicability. A smaller efficiency difference between custom vs general hardware mean higher resistance and a better algorithm. This efficiency difference is the proper metric to use when comparing the quality of PoW algorithms. Efficiency could mean absolute performance, performance per watt, or performance per dollar - they are all highly correlated. If a single entity creates and controls an ASIC that is drastically more efficient, they can gain 51% of the network hashrate and possibly stage an attack. + +### Review of Existing PoW Algorithms + +#### SHA256 +* Potential ASIC efficiency gain ~ 1000X + +The SHA algorithm is a sequence of simple math operations - additions, logical ops, and rotates. + +To process a single op on a CPU or GPU requires fetching and decoding an instruction, reading data from a register file, executing the instruction, and then writing the result back to a register file. This takes significant time and power. + +A single op implemented in an ASIC takes a handful of transistors and wires. This means every individual op takes negligible power, area, or time. A hashing core is built by laying out the sequence of required ops. + +The hashing core can execute the required sequence of ops in much less time, and using less power or area, than doing the same sequence on a CPU or GPU. A bitcoin ASIC consists of a number of identical hashing cores and some minimal off-chip communication. + +#### Scrypt and NeoScrypt +* Potential ASIC efficiency gain ~ 1000X + +Scrypt and NeoScrypt are similar to SHA in the arithmetic and bitwise operations used. Unfortunately, popular coins such as Litecoin only use a scratchpad size between 32kb and 128kb for their PoW mining algorithm. This scratch pad is small enough to trivially fit on an ASIC next to the math core. The implementation of the math core would be very similar to SHA, with similar efficiency gains. + +#### X11 and X16R +* Potential ASIC efficiency gain ~ 1000X + +X11 (and similar X##) require an ASIC that has 11 unique hashing cores pipelined in a fixed sequence. Each individual hashing core would have similar efficiency to an individual SHA core, so the overall design will have the same efficiency gains. + +X16R requires the multiple hashing cores to interact through a simple sequencing state machine. Each individual core will have similar efficiency gains and the sequencing logic will take minimal power, area, or time. + +The Baikal BK-X is an existing ASIC with multiple hashing cores and a programmable sequencer. It has been upgraded to enable new algorithms that sequence the hashes in different orders. + +#### Equihash +* Potential ASIC efficiency gain ~ 100X + +The ~150mb of state is large but possible on an ASIC. The binning, sorting, and comparing of bit strings could be implemented on an ASIC at extremely high speed. + +#### Cuckoo Cycle +* Potential ASIC efficiency gain ~ 100X + +The amount of state required on-chip is not clear as there are Time/Memory Tradeoff attacks. A specialized graph traversal core would have similar efficiency gains to a SHA compute core. + +#### CryptoNight +* Potential ASIC efficiency gain ~ 50X + +Compared to Scrypt, CryptoNight does much less compute and requires a full 2mb of scratch pad (there is no known Time/Memory Tradeoff attack). The large scratch pad will dominate the ASIC implementation and limit the number of hashing cores, limiting the absolute performance of the ASIC. An ASIC will consist almost entirely of just on-die SRAM. + +#### Ethash +* Potential ASIC efficiency gain ~ 2X + +Ethash requires external memory due to the large size of the DAG. However that is all that it requires - there is minimal compute that is done on the result loaded from memory. As a result a custom ASIC could remove most of the complexity, and power, of a GPU and be just a memory interface connected to a small compute engine. + +## Specification + +ProgPoW can be tuned using the following parameters. The proposed settings have been tuned for a range of existing, commodity GPUs: + +* `PROGPOW_PERIOD`: Number of blocks before changing the random program +* `PROGPOW_LANES`: The number of parallel lanes that coordinate to calculate a single hash instance +* `PROGPOW_REGS`: The register file usage size +* `PROGPOW_DAG_LOADS`: Number of uint32 loads from the DAG per lane +* `PROGPOW_CACHE_BYTES`: The size of the cache +* `PROGPOW_CNT_DAG`: The number of DAG accesses, defined as the outer loop of the algorithm (64 is the same as Ethash) +* `PROGPOW_CNT_CACHE`: The number of cache accesses per loop +* `PROGPOW_CNT_MATH`: The number of math operations per loop + +The values of these parameters have been tweaked between the original version and the version proposed here for Ethereum adoption. See [this medium post](https://medium.com/@ifdefelse/progpow-progress-da5bb31a651b) for details. + +| Parameter | 0.9.2 | 0.9.3 | +|-----------------------|-------|-------| +| `PROGPOW_PERIOD` | `50` | `10` | +| `PROGPOW_LANES` | `16` | `16` | +| `PROGPOW_REGS` | `32` | `32` | +| `PROGPOW_DAG_LOADS` | `4` | `4` | +| `PROGPOW_CACHE_BYTES` | `16x1024` | `16x1024` | +| `PROGPOW_CNT_DAG` | `64` | `64` | `64` | +| `PROGPOW_CNT_CACHE` | `12` | `11` | `11` | +| `PROGPOW_CNT_MATH` | `20` | `18` | `18` | + +| DAG Parameter | 0.9.2 | 0.9.3 | +|--------------------------|-------|-------| +| `ETHASH_DATASET_PARENTS` | `256` | `256` | + + +The random program changes every `PROGPOW_PERIOD` blocks (default `10`, roughly 2 minutes) to ensure the hardware executing the algorithm is fully programmable. If the program only changed every DAG epoch (roughly 5 days) certain miners could have time to develop hand-optimized versions of the random sequence, giving them an undue advantage. + +Sample code is written in C++, this should be kept in mind when evaluating the code in the specification. +All numerics are computed using unsigned 32 bit integers. Any overflows are trimmed off before proceeding to the next computation. Languages that use numerics not fixed to bit lengths (such as Python and JavaScript) or that only use signed integers (such as Java) will need to keep their languages' quirks in mind. The extensive use of 32 bit data values aligns with modern GPUs internal data architectures. + +ProgPoW uses a 32-bit variant of **FNV1a** for merging data. The existing Ethash uses a similar variant of FNV1 for merging, but FNV1a provides better distribution properties. + +Test vectors can be found [in the test vectors file](../assets/eip-1057/test-vectors.md#fnv1a). + +```cpp +const uint32_t FNV_PRIME = 0x1000193; +const uint32_t FNV_OFFSET_BASIS = 0x811c9dc5; + +uint32_t fnv1a(uint32_t h, uint32_t d) +{ + return (h ^ d) * FNV_PRIME; +} +``` + +ProgPow uses [KISS99](https://en.wikipedia.org/wiki/KISS_(algorithm)) for random number generation. This is the simplest (fewest instruction) random generator that passes the TestU01 statistical test suite. A more complex random number generator like Mersenne Twister can be efficiently implemented on a specialized ASIC, providing an opportunity for efficiency gains. + +Test vectors can be found [in the test vectors file](../assets/eip-1057/test-vectors.md#kiss99). + +```cpp +typedef struct { + uint32_t z, w, jsr, jcong; +} kiss99_t; + +// KISS99 is simple, fast, and passes the TestU01 suite +// https://en.wikipedia.org/wiki/KISS_(algorithm) +// http://www.cse.yorku.ca/~oz/marsaglia-rng.html +uint32_t kiss99(kiss99_t &st) +{ + st.z = 36969 * (st.z & 65535) + (st.z >> 16); + st.w = 18000 * (st.w & 65535) + (st.w >> 16); + uint32_t MWC = ((st.z << 16) + st.w); + st.jsr ^= (st.jsr << 17); + st.jsr ^= (st.jsr >> 13); + st.jsr ^= (st.jsr << 5); + st.jcong = 69069 * st.jcong + 1234567; + return ((MWC^st.jcong) + st.jsr); +} +``` + +The `fill_mix` function populates an array of `int32` values used by each lane in the hash calculations. + +Test vectors can be found [in the test vectors file](../assets/eip-1057/test-vectors.md#fill_mix). + +```cpp +void fill_mix( + uint64_t seed, + uint32_t lane_id, + uint32_t mix[PROGPOW_REGS] +) +{ + // Use FNV to expand the per-warp seed to per-lane + // Use KISS to expand the per-lane seed to fill mix + uint32_t fnv_hash = FNV_OFFSET_BASIS; + kiss99_t st; + st.z = fnv1a(fnv_hash, seed); + st.w = fnv1a(fnv_hash, seed >> 32); + st.jsr = fnv1a(fnv_hash, lane_id); + st.jcong = fnv1a(fnv_hash, lane_id); + for (int i = 0; i < PROGPOW_REGS; i++) + mix[i] = kiss99(st); +} +``` + +Like Ethash Keccak is used to seed the sequence per-nonce and to produce the final result. The keccak-f800 variant is used as the 32-bit word size matches the native word size of modern GPUs. The implementation is a variant of SHAKE with width=800, bitrate=576, capacity=224, output=256, and no padding. The result of keccak is treated as a 256-bit big-endian number - that is result byte 0 is the MSB of the value. + +As with Ethash the input and output of the keccak function are fixed and relatively small. This means only a single "absorb" and "squeeze" phase are required. For a pseudo-code implementation of the `keccak_f800_round` function see the `Round[b](A,RC)` function in the "Pseudo-code description of the permutations" section of the [official Keccak specs](https://keccak.team/keccak_specs_summary.html). + +```cpp +hash32_t keccak_f800_progpow(uint32_t* state) +{ + // keccak_f800 call for the single absorb pass + for (int r = 0; r < 22; r++) + keccak_f800_round(st, r); + + // Squeeze phase for fixed 8 words of output + hash32_t ret; + for (int i=0; i<8; i++) + ret.uint32s[i] = st[i]; + + return ret; +} +``` + +The inner loop uses FNV and KISS99 to generate a random sequence from the `prog_seed`. This random sequence determines which mix state is accessed and what random math is performed. + +Since the `prog_seed` changes only once per `PROGPOW_PERIOD` (10 blocks or about 2 minutes) it is expected that while mining `progPowLoop` will be evaluated on the CPU to generate source code for that period's sequence. The source code will be compiled on the CPU before running on the GPU. You can see an example sequence and generated source code in [kernel.cu](https://github.com/ifdefelse/ProgPOW/blob/824cd791634204c4cc7e31f84bb76c0c84895bd3/test/kernel.cu). + +Test vectors can be found [in the test vectors file](../assets/eip-1057/test-vectors.md#progpowinit). + +```cpp +kiss99_t progPowInit(uint64_t prog_seed, int mix_seq_dst[PROGPOW_REGS], int mix_seq_src[PROGPOW_REGS]) +{ + kiss99_t prog_rnd; + prog_rnd.z = fnv1a(FNV_OFFSET_BASIS, prog_seed); + prog_rnd.w = fnv1a(prog_rnd.z, prog_seed >> 32); + prog_rnd.jsr = fnv1a(prog_rnd.w, prog_seed); + prog_rnd.jcong = fnv1a(prog_rnd.jsr, prog_seed >> 32); + // Create a random sequence of mix destinations for merge() and mix sources for cache reads + // guarantees every destination merged once + // guarantees no duplicate cache reads, which could be optimized away + // Uses Fisher-Yates shuffle + for (int i = 0; i < PROGPOW_REGS; i++) + { + mix_seq_dst[i] = i; + mix_seq_src[i] = i; + } + for (int i = PROGPOW_REGS - 1; i > 0; i--) + { + int j; + j = kiss99(prog_rnd) % (i + 1); + swap(mix_seq_dst[i], mix_seq_dst[j]); + j = kiss99(prog_rnd) % (i + 1); + swap(mix_seq_src[i], mix_seq_src[j]); + } + return prog_rnd; +} +``` + +The math operations that merges values into the mix data are ones chosen to maintain entropy. + +Test vectors can be found [in the test vectors file](../assets/eip-1057/test-vectors.md#math). + +```cpp +// Merge new data from b into the value in a +// Assuming A has high entropy only do ops that retain entropy +// even if B is low entropy +// (IE don't do A&B) +uint32_t merge(uint32_t a, uint32_t b, uint32_t r) +{ + switch (r % 4) + { + case 0: return (a * 33) + b; + case 1: return (a ^ b) * 33; + // prevent rotate by 0 which is a NOP + case 2: return ROTL32(a, ((r >> 16) % 31) + 1) ^ b; + case 3: return ROTR32(a, ((r >> 16) % 31) + 1) ^ b; + } +} +``` + +The math operations chosen for the random math are ones that are easy to implement in CUDA and OpenCL, the two main programming languages for commodity GPUs. The [mul_hi](https://www.khronos.org/registry/OpenCL/sdk/1.1/docs/man/xhtml/mul_hi.html), [min](https://www.khronos.org/registry/OpenCL/sdk/2.0/docs/man/xhtml/integerMax.html), [clz](https://www.khronos.org/registry/OpenCL/sdk/1.1/docs/man/xhtml/clz.html), and [popcount](https://www.khronos.org/registry/OpenCL/sdk/2.0/docs/man/xhtml/popcount.html) functions match the corresponding OpenCL functions. ROTL32 matches the OpenCL [rotate](https://www.khronos.org/registry/OpenCL/sdk/1.0/docs/man/xhtml/rotate.html) function. ROTR32 is rotate right, which is equivalent to `rotate(i, 32-v)`. + +Test vectors can be found [in the test vectors file](../assets/eip-1057/test-vectors.md#math). + +```cpp +// Random math between two input values +uint32_t math(uint32_t a, uint32_t b, uint32_t r) +{ + switch (r % 11) + { + case 0: return a + b; + case 1: return a * b; + case 2: return mul_hi(a, b); + case 3: return min(a, b); + case 4: return ROTL32(a, b); + case 5: return ROTR32(a, b); + case 6: return a & b; + case 7: return a | b; + case 8: return a ^ b; + case 9: return clz(a) + clz(b); + case 10: return popcount(a) + popcount(b); + } +} +``` + +The flow of the inner loop is: +* Lane `(loop % LANES)` is chosen as the leader for that loop iteration +* The leader's `mix[0]` value modulo the number of 256-byte DAG entries is is used to select where to read from the full DAG +* Each lane reads `DAG_LOADS` sequential words, using `(lane ^ loop) % LANES` as the starting offset within the entry. +* The random sequence of math and cache accesses is performed +* The DAG data read at the start of the loop is merged at the end of the loop + +`prog_seed` and `loop` come from the outer loop, corresponding to the current program seed (which is block_number/PROGPOW_PERIOD) and the loop iteration number. `mix` is the state array, initially filled by `fill_mix`. `dag` is the bytes of the Ethash DAG grouped into 32 bit unsigned ints in litte-endian format. On little-endian architectures this is just a normal int32 pointer to the existing DAG. + +`DAG_BYTES` is set to the number of bytes in the current DAG, which is generated identically to the existing Ethash algorithm. + +Test vectors can be found [in the test vectors file](../assets/eip-1057/test-vectors.md#progpowloop). + +```cpp +void progPowLoop( + const uint64_t prog_seed, + const uint32_t loop, + uint32_t mix[PROGPOW_LANES][PROGPOW_REGS], + const uint32_t *dag) +{ + // dag_entry holds the 256 bytes of data loaded from the DAG + uint32_t dag_entry[PROGPOW_LANES][PROGPOW_DAG_LOADS]; + // On each loop iteration rotate which lane is the source of the DAG address. + // The source lane's mix[0] value is used to ensure the last loop's DAG data feeds into this loop's address. + // dag_addr_base is which 256-byte entry within the DAG will be accessed + uint32_t dag_addr_base = mix[loop%PROGPOW_LANES][0] % + (DAG_BYTES / (PROGPOW_LANES*PROGPOW_DAG_LOADS*sizeof(uint32_t))); + for (int l = 0; l < PROGPOW_LANES; l++) + { + // Lanes access DAG_LOADS sequential words from the dag entry + // Shuffle which portion of the entry each lane accesses each iteration by XORing lane and loop. + // This prevents multi-chip ASICs from each storing just a portion of the DAG + size_t dag_addr_lane = dag_addr_base * PROGPOW_LANES + (l ^ loop) % PROGPOW_LANES; + for (int i = 0; i < PROGPOW_DAG_LOADS; i++) + dag_entry[l][i] = dag[dag_addr_lane * PROGPOW_DAG_LOADS + i]; + } + + // Initialize the program seed and sequences + // When mining these are evaluated on the CPU and compiled away + int mix_seq_dst[PROGPOW_REGS]; + int mix_seq_src[PROGPOW_REGS]; + int mix_seq_dst_cnt = 0; + int mix_seq_src_cnt = 0; + kiss99_t prog_rnd = progPowInit(prog_seed, mix_seq_dst, mix_seq_src); + + int max_i = max(PROGPOW_CNT_CACHE, PROGPOW_CNT_MATH); + for (int i = 0; i < max_i; i++) + { + if (i < PROGPOW_CNT_CACHE) + { + // Cached memory access + // lanes access random 32-bit locations within the first portion of the DAG + int src = mix_seq_src[(mix_seq_src_cnt++)%PROGPOW_REGS]; + int dst = mix_seq_dst[(mix_seq_dst_cnt++)%PROGPOW_REGS]; + int sel = kiss99(prog_rnd); + for (int l = 0; l < PROGPOW_LANES; l++) + { + uint32_t offset = mix[l][src] % (PROGPOW_CACHE_BYTES/sizeof(uint32_t)); + mix[l][dst] = merge(mix[l][dst], dag[offset], sel); + } + } + if (i < PROGPOW_CNT_MATH) + { + // Random Math + // Generate 2 unique sources + int src_rnd = kiss99(prog_rnd) % (PROGPOW_REGS * (PROGPOW_REGS-1)); + int src1 = src_rnd % PROGPOW_REGS; // 0 <= src1 < PROGPOW_REGS + int src2 = src_rnd / PROGPOW_REGS; // 0 <= src2 < PROGPOW_REGS - 1 + if (src2 >= src1) ++src2; // src2 is now any reg other than src1 + int sel1 = kiss99(prog_rnd); + int dst = mix_seq_dst[(mix_seq_dst_cnt++)%PROGPOW_REGS]; + int sel2 = kiss99(prog_rnd); + for (int l = 0; l < PROGPOW_LANES; l++) + { + uint32_t data = math(mix[l][src1], mix[l][src2], sel1); + mix[l][dst] = merge(mix[l][dst], data, sel2); + } + } + } + // Consume the global load data at the very end of the loop to allow full latency hiding + // Always merge into mix[0] to feed the offset calculation + for (int i = 0; i < PROGPOW_DAG_LOADS; i++) + { + int dst = (i==0) ? 0 : mix_seq_dst[(mix_seq_dst_cnt++)%PROGPOW_REGS]; + int sel = kiss99(prog_rnd); + for (int l = 0; l < PROGPOW_LANES; l++) + mix[l][dst] = merge(mix[l][dst], dag_entry[l][i], sel); + } +} +``` + +The flow of the overall algorithm is: +* A keccak hash of the header + nonce to create a digest of 256 bits from keccak_f800 (padding is consistent with custom one in ethash) +* Use first two words of digest as seed to generate initial mix data +* Loop multiple times, each time hashing random loads and random math into the mix data +* Hash all the mix data into a single 256-bit value +* A final keccak hash using carry-over digest from initial data + mix_data final 256 bit value (padding is consistent with custom one in ethash) +* When mining this final value is compared against a `hash32_t` target + +```cpp +hash32_t progPowHash( + const uint64_t prog_seed, // value is (block_number/PROGPOW_PERIOD) + const uint64_t nonce, + const hash32_t header, + const uint32_t *dag // gigabyte DAG located in framebuffer - the first portion gets cached +) +{ + hash32_t hash_init; + hash32_t hash_final; + + uint32_t mix[PROGPOW_LANES][PROGPOW_REGS]; + + /* + ======================================== + Absorb phase for initial keccak pass + ======================================== + */ + + { + uint32_t state[25] = {0x0}; + // 1st fill with header data (8 words) + for (int i = 0; i < 8; i++) + state[i] = header.uint32s[i]; + + // 2nd fill with nonce (2 words) + state[8] = nonce; + state[9] = nonce >> 32; + + // 3rd apply padding + state[10] = 0x00000001; + state[18] = 0x80008081; + + // keccak(header..nonce) + hash_init = keccak_f800_progpow(state); + + // get the seed to initialize mix + seed = ((uint64_t)hash_init.uint32s[1] << 32) | hash_init.uint32s[0]); + } + + // initialize mix for all lanes + for (int l = 0; l < PROGPOW_LANES; l++) + fill_mix(seed, l, mix[l]); + + // execute the randomly generated inner loop + for (int i = 0; i < PROGPOW_CNT_DAG; i++) + progPowLoop(prog_seed, i, mix, dag); + + // Reduce mix data to a per-lane 32-bit digest + uint32_t digest_lane[PROGPOW_LANES]; + for (int l = 0; l < PROGPOW_LANES; l++) + { + digest_lane[l] = FNV_OFFSET_BASIS; + for (int i = 0; i < PROGPOW_REGS; i++) + digest_lane[l] = fnv1a(digest_lane[l], mix[l][i]); + } + // Reduce all lanes to a single 256-bit digest + for (int i = 0; i < 8; i++) + digest.uint32s[i] = FNV_OFFSET_BASIS; + for (int l = 0; l < PROGPOW_LANES; l++) + digest.uint32s[l%8] = fnv1a(digest.uint32s[l%8], digest_lane[l]); + + /* + ======================================== + Absorb phase for final keccak pass + ======================================== + */ + + { + uint32_t state[25] = {0x0}; + + // 1st fill with hash_init (8 words) + for (int i = 0; i < 8; i++) + state[i] = hash_init.uint32s[i]; + + // 2nd fill with digest from main loop + for (int i = 8; i < 16; i++) + state[i] = digest.uint32s[i - 8]; + + // 3rd apply padding + state[17] = 0x00000001; + state[24] = 0x80008081; + + // keccak(header..nonce) + hash_final = keccak_f800_progpow(state); + } + + // Compare hash final to target + [...] + +} +``` + +## Security Considerations + +This proposal has been software and hardware audited: +* [Least Authority — ProgPoW Software Audit PDF](https://leastauthority.com/static/publications/LeastAuthority-ProgPow-Algorithm-Final-Audit-Report.pdf) +* [Bob Rao - ProgPoW Hardware Audit PDF](https://github.com/ethereum-cat-herders/progpow-audit/raw/master/Bob%20Rao%20-%20ProgPOW%20Hardware%20Audit%20Report%20Final.pdf) + +Least Authority in their findings suggest a change to DAG generation -- modification of `ETHASH_DATASET_PARENTS` from a value of 256 to the new value of 512 -- in order to mitigate vulnerability to a "Light Evaluation" attack. Due to this the DAG memory file used by ProgPoW is would no longer compatible with the one used by Ethash (epoch length and size increase ratio remain the same though). + +We do not recommend implementing this fix at this time. Ethash will not be exploitable for years, and it's not clear ProgPoW will ever be exploitable. It's better to deploy the audited code. + +After the completion of the audits a clever finding by [Kik](https://github.com/kik/) disclosed a vulnerability to [bypassing ProgPoW memory hardness](https://github.com/kik/progpow-exploit). The vulnerability is present in Ethash as well but is near-impossible to exploit. In progPoW it is not possible to exploit -- it assumes the ability to create variants of the candidate block's header hash in a fashion similar to bitcoin, which is actually not possible in Ethereum. An attacker would need modified block headers, would need customized nodes able to accept the modified block headers, and uses extraNonce/extraData as entropy -- which isn’t the standard. And the required brute-force search would be difficult to accomplish in one blocktime. And even if supported by a customized node the block propagation of such mined blocks would be immediately blocked by other peers as the header hash is invalid. + +The author's have since found another vulnerability similar to Kik's, but it adds too much overhead to be ASIC-friendly. See Lanfranchi's full explanation [here](https://github.com/ifdefelse/ProgPOW/issues/51#issuecomment-690155355). To completely prevent such exploits we could change the condition modifying the input state of the last keccak pass from +* header (256 bits) + +* seed for mix initiator (64 bits) + +* mix from main loop (256 bits) +* no padding + +to +* digest from initial keccak (256 bits) + +* mix from main loop (256 bits) + +* padding + +thus widening the constraint to target in keccak [brute force keccak linear searches](https://github.com/kik/progpow-exploit) from 64 to 256 bits. + +This fix is available as a PR to the reference implementation. Again, we do not recommend implementing this fix at this time. Kik's vulnerability and others like it cannot be exploited now and likely never will be. It's better to deploy the audited code. + +Note that these vulnerabilities cannot be exploited to deny service, double spend, or otherwise damage the network. They could at worst give their deployer an efficiency advantage over other miners. + +## Test Cases + +The random sequence generated for block 30,000 (prog_seed 3,000) can been seen in [kernel.cu](https://github.com/ifdefelse/ProgPOW/blob/824cd791634204c4cc7e31f84bb76c0c84895bd3/test/kernel.cu). + +The algorithm run on block 30,000 produces the following digest and result: +``` +Header : 0xffeeddccbbaa9988776655443322110000112233445566778899aabbccddeeff +Nonce : 0x123456789abcdef0 +Hash init : 0xee304846ddd0a47b98179e96b60ec5ceeae2727834367e593de780e3e6d1892f +Mix seed : 0x7ba4d0dd464830ee +Mix hash : 0x493c13e9807440571511b561132834bbd558dddaa3b70c09515080a6a1aff6d0 +Hash final : 0x46b72b75f238bea3fcfd227e0027dc173dceaa1fb71744bd3d5e030ed2fed053 +``` + +Additional test vectors can be found [in the test vectors file](../assets/eip-1057/test-vectors.md#progpowhash). + +Machine-readable test vectors (T.B.D) + + +## Implementation + +The reference ProgPoW mining implementation is located at [the @ifdefelse ProgPOW repository](https://github.com/ifdefelse/ProgPOW). + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). + +The reference ProgPoW mining implementation located at [ProgPOW](https://github.com/ifdefelse/ProgPOW) is a derivative of ethminer so retains the GPL license. diff --git a/EIPS/eip-1062.md b/EIPS/eip-1062.md new file mode 100644 index 0000000..0f304b6 --- /dev/null +++ b/EIPS/eip-1062.md @@ -0,0 +1,84 @@ +--- +eip: 1062 +title: Formalize IPFS hash into ENS(Ethereum Name Service) resolver +author: Phyrex Tsai , Portal Network Team +discussions-to: https://ethereum-magicians.org/t/eip-1062-formalize-ipfs-hash-into-ens-ethereum-name-service-resolver/281 +status: Stagnant +type: Standards Track +category: ERC +created: 2018-05-02 +--- + +## Simple Summary +To specify the mapping protocol between resources stored on IPFS and ENS(Ethereum Naming Service). + +## Abstract +The following standard details the implementation of how to combine the IPFS cryptographic hash unique fingerprint with ENS public resolver. This standard provides a functionality to get and set IPFS online resources to ENS resolver. + +We think that this implementation is not only aim to let more developers and communities to provide more use cases, but also leverage the human-readable features to gain more user adoption accessing decentralized resources. We considered the IPFS ENS resolver mapping standard a cornerstone for building future Web3.0 service. + +## Motivation +To build fully decentralized web service, it’s necessary to have a decentralized file storage system. Here comes the IPFS, for three following advantages : +- Address large amounts of data, and has unique cryptographic hash for every record. +- Since IPFS is also based on peer to peer network, it can be really helpful to deliver large amounts of data to users, with safer way and lower the millions of cost for the bandwidth. +- IPFS stores files in high efficient way via tracking version history for every file, and removing the duplications across the network. + +Those features makes perfect match for integrating into ENS, and these make users can easily access content through ENS, and show up in the normal browser. + + +## Specification +The condition now is that the IPFS file fingerprint using base58 and in the meantime, the Ethereum uses hex in API to encode the binary data. So that need a way to process the condition requires not only we need to transfer from IPFS to Ethereum, but also need to convert it back. + +To solve these requirements, we can use binary buffer bridging that gap. +When mapping the IPFS base58 string to ENS resolver, first we convert the Base58 to binary buffer, turn the buffer to hex encrypted format, and save to the contract. Once we want to get the IPFS resources address represented by the specific ENS, we can first find the mapping information stored as hex format before, extract the hex format to binary buffer, and finally turn that to IPFS Base58 address string. + + +## Rationale +To implement the specification, need two methods from ENS public resolver contract, when we want to store IPFS file fingerprint to contract, convert the Base58 string identifier to the hex format and invoke the `setMultihash` method below : + +```solidity +function setMultihash(bytes32 node, bytes hash) public only_owner(node); +``` + +Whenever users need to visit the ENS content, we call the `multihash` method to get the IPFS hex data, transfer to the Base58 format, and return the IPFS resources to use. + +```solidity +function multihash(bytes32 node) public view returns (bytes); +``` + +## Test Cases + +To implement the way to transfer from base58 to hex format and the reverse one, using the ‘multihashes’ library to deal with the problem. +The library link : [https://www.npmjs.com/package/multihashes](https://www.npmjs.com/package/multihashes) +To implement the method transfer from IPFS(Base58) to hex format : + +```javascript +import multihash from 'multihashes' + +export const toHex = function(ipfsHash) { + let buf = multihash.fromB58String(ipfsHash); + return '0x' + multihash.toHexString(buf); +} +``` + +To implement the method transfer from hex format to IPFS(Base58) : + +```javascript +import multihash from 'multihashes' + +export const toBase58 = function(contentHash) { + let hex = contentHash.substring(2) + let buf = multihash.fromHexString(hex); + return multihash.toB58String(buf); +} +``` + +## Implementation +The use case can be implemented as browser extension. Users can easily download the extension, and easily get decentralized resources by just typing the ENS just like we normally type the DNS to browser the website. Solve the current pain for normal people can not easily visit the total decentralized website. + +The workable implementation repository : [https://github.com/PortalNetwork/portal-network-browser-extension](https://github.com/PortalNetwork/portal-network-browser-extension) + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). + + diff --git a/EIPS/eip-1066.md b/EIPS/eip-1066.md new file mode 100644 index 0000000..5b29e2d --- /dev/null +++ b/EIPS/eip-1066.md @@ -0,0 +1,598 @@ +--- +eip: 1066 +title: Status Codes +author: Brooklyn Zelenka (@expede), Tom Carchrae (@carchrae), Gleb Naumenko (@naumenkogs) +discussions-to: https://ethereum-magicians.org/t/erc-1066-ethereum-status-codes-esc/ +status: Stagnant +type: Standards Track +category: ERC +created: 2018-05-05 +--- + +## Simple Summary + +Broadly applicable status codes for smart contracts. + +## Abstract + +This standard outlines a common set of status codes in a similar vein to HTTP statuses. This provides a shared set of signals to allow smart contracts to react to situations autonomously, expose localized error messages to users, and so on. + +The current state of the art is to either `revert` on anything other than a clear success (ie: require human intervention), or return a low-context `true` or `false`. Status codes are similar-but-orthogonal to `revert`ing with a reason, but aimed at automation, debugging, and end-user feedback (including translation). _They are fully compatible with both `revert` and `revert`-with-reason._ + +As is the case with HTTP, having a standard set of known codes has many benefits for developers. They remove friction from needing to develop your own schemes for every contract, makes inter-contract automation easier, and makes it easier to broadly understand which of the finite states your request produced. Importantly, it makes it much easier to distinguish between expected errors states, truly exceptional conditions that require halting execution, normal state transitions, and various success cases. + +## Motivation + +### Semantic Density + +HTTP status codes are widely used for this purpose. BEAM languages use atoms and tagged tuples to signify much the same information. Both provide a lot of information both to the programmer (debugging for instance), and to the program that needs to decide what to do next. + +Status codes convey a much richer set of information [than Booleans](https://existentialtype.wordpress.com/2011/03/15/boolean-blindness/), and are able to be reacted to autonomously unlike arbitrary strings. + +### User Experience (UX) + +_End users get little to no feedback, and there is no translation layer._ + +Since ERC1066 status codes are finite and known in advance, we can leverage [ERC-1444](./eip-1444.md) to provide global, human-readable sets of status messages. These may also be translated into any language, differing levels of technical detail, added as `revert` messages, natspecs, and so on. + +Status codes convey a much richer set of information than Booleans, and are able to be reacted to autonomously unlike arbitrary strings. + +### Developer Experience (DX) + +_Developers currently have very little context exposed by their smart contracts._ + +At time of writing, other than stepping through EVM execution and inspecting memory dumps directly, it is very difficult to understand what is happening during smart contract execution. By returning more context, developers can write well-decomposed tests and assert certain codes are returned as an expression of where the smart contract got to. This includes status codes as bare values, `event`s, and `revert`s. + +Having a fixed set of codes also makes it possible to write common helper functions to react in common ways to certain signals. This can live off- or on-chain library, lowering the overhead in building smart contracts, and helping raise code quality with trusted shared components. + +We also see a desire for this [in transactions](./eip-658.md), and there's no reason that these status codes couldn't be used by the EVM itself. + +### Smart Contract Autonomy + +_Smart contracts don’t know much about the result of a request beyond pass/fail; they can be smarter with more context._ + +Smart contracts are largely intended to be autonomous. While each contract may define a specific interface, having a common set of semantic codes can help developers write code that can react appropriately to various situations. + +While clearly related, status codes are complementary to `revert`-with-reason. Status codes are not limited to rolling back the transaction, and may represent known error states without halting execution. They may also represent off-chain conditions, supply a string to revert, signal time delays, and more. + +All of this enables contracts to share a common vocabulary of state transitions, results, and internal changes, without having to deeply understand custom status enums or the internal business logic of collaborator contracts. + +## Specification + +### Format + +Codes are returned either on their own, or as the first value of a multiple return. + +```solidity +// Status only + +function isInt(uint num) public pure returns (byte status) { + return hex"01"; +} + +// Status and value + +uint8 private counter; + +function safeIncrement(uint8 interval) public returns (byte status, uint8 newCounter) { + uint8 updated = counter + interval; + + if (updated >= counter) { + counter = updated; + return (hex"01", updated); + } else { + return (hex"00", counter); + } +} +``` + +### Code Table + +Codes break nicely into a 16x16 matrix, represented as a 2-digit hex number. The high nibble represents the code's kind or "category", and the low nibble contains the state or "reason". We present them below as separate tables per range for explanatory and layout reasons. + +**NB: Unspecified codes are _not_ free for arbitrary use, but rather open for further specification.** + +#### `0x0*` Generic + +General codes. These double as bare "reasons", since `0x01 == 1`. + +| Code | Description | +|--------|-----------------------------------------| +| `0x00` | Failure | +| `0x01` | Success | +| `0x02` | Awaiting Others | +| `0x03` | Accepted | +| `0x04` | Lower Limit or Insufficient | +| `0x05` | Receiver Action Requested | +| `0x06` | Upper Limit | +| `0x07` | [reserved] | +| `0x08` | Duplicate, Unnecessary, or Inapplicable | +| `0x09` | [reserved] | +| `0x0A` | [reserved] | +| `0x0B` | [reserved] | +| `0x0C` | [reserved] | +| `0x0D` | [reserved] | +| `0x0E` | [reserved] | +| `0x0F` | Informational or Metadata | + +#### `0x1*` Permission & Control + +Also used for common state machine actions (ex. "stoplight" actions). + +| Code | Description | +|--------|---------------------------------------------------| +| `0x10` | Disallowed or Stop | +| `0x11` | Allowed or Go | +| `0x12` | Awaiting Other's Permission | +| `0x13` | Permission Requested | +| `0x14` | Too Open / Insecure | +| `0x15` | Needs Your Permission or Request for Continuation | +| `0x16` | Revoked or Banned | +| `0x17` | [reserved] | +| `0x18` | Not Applicable to Current State | +| `0x19` | [reserved] | +| `0x1A` | [reserved] | +| `0x1B` | [reserved] | +| `0x1C` | [reserved] | +| `0x1D` | [reserved] | +| `0x1E` | [reserved] | +| `0x1F` | Permission Details or Control Conditions | + +#### `0x2*` Find, Inequalities & Range + +This range is broadly intended for finding and matching. Data lookups and order matching are two common use cases. + +| Code | Description | +|--------|-------------------------------------| +| `0x20` | Not Found, Unequal, or Out of Range | +| `0x21` | Found, Equal or In Range | +| `0x22` | Awaiting Match | +| `0x23` | Match Request Sent | +| `0x24` | Below Range or Underflow | +| `0x25` | Request for Match | +| `0x26` | Above Range or Overflow | +| `0x27` | [reserved] | +| `0x28` | Duplicate, Conflict, or Collision | +| `0x29` | [reserved] | +| `0x2A` | [reserved] | +| `0x2B` | [reserved] | +| `0x2C` | [reserved] | +| `0x2D` | [reserved] | +| `0x2E` | [reserved] | +| `0x2F` | Matching Meta or Info | + +#### `0x3*` Negotiation & Governance + +Negotiation, and very broadly the flow of such transactions. Note that "other party" may be more than one actor (not necessarily the sender). + +| Code | Description | +|--------|-----------------------------------------| +| `0x30` | Sender Disagrees or Nay | +| `0x31` | Sender Agrees or Yea | +| `0x32` | Awaiting Ratification | +| `0x33` | Offer Sent or Voted | +| `0x34` | Quorum Not Reached | +| `0x35` | Receiver's Ratification Requested | +| `0x36` | Offer or Vote Limit Reached | +| `0x37` | [reserved] | +| `0x38` | Already Voted | +| `0x39` | [reserved] | +| `0x3A` | [reserved] | +| `0x3B` | [reserved] | +| `0x3C` | [reserved] | +| `0x3D` | [reserved] | +| `0x3E` | [reserved] | +| `0x3F` | Negotiation Rules or Participation Info | + +#### `0x4*` Availability & Time + +Service or action availability. + +| Code | Description | +|--------|------------------------------------------------------| +| `0x40` | Unavailable | +| `0x41` | Available | +| `0x42` | Paused | +| `0x43` | Queued | +| `0x44` | Not Available Yet | +| `0x45` | Awaiting Your Availability | +| `0x46` | Expired | +| `0x47` | [reserved] | +| `0x48` | Already Done | +| `0x49` | [reserved] | +| `0x4A` | [reserved] | +| `0x4B` | [reserved] | +| `0x4C` | [reserved] | +| `0x4D` | [reserved] | +| `0x4E` | [reserved] | +| `0x4F` | Availability Rules or Info (ex. time since or until) | + +#### `0x5*` Tokens, Funds & Finance + +Special token and financial concepts. Many related concepts are included in other ranges. + +| Code | Description | +|--------|---------------------------------| +| `0x50` | Transfer Failed | +| `0x51` | Transfer Successful | +| `0x52` | Awaiting Payment From Others | +| `0x53` | Hold or Escrow | +| `0x54` | Insufficient Funds | +| `0x55` | Funds Requested | +| `0x56` | Transfer Volume Exceeded | +| `0x57` | [reserved] | +| `0x58` | Funds Not Required | +| `0x59` | [reserved] | +| `0x5A` | [reserved] | +| `0x5B` | [reserved] | +| `0x5C` | [reserved] | +| `0x5D` | [reserved] | +| `0x5E` | [reserved] | +| `0x5F` | Token or Financial Information | + +#### `0x6*` TBD + +Currently unspecified. (Full range reserved) + +#### `0x7*` TBD + +Currently unspecifie. (Full range reserved) + +#### `0x8*` TBD + +Currently unspecified. (Full range reserved) + +#### `0x9*` TBD + +Currently unspecified. (Full range reserved) + +#### `0xA*` Application-Specific Codes + +Contracts may have special states that they need to signal. This proposal only outlines the broadest meanings, but implementers may have very specific meanings for each, as long as they are coherent with the broader definition. + +| Code | Description | +|--------|----------------------------------------| +| `0xA0` | App-Specific Failure | +| `0xA1` | App-Specific Success | +| `0xA2` | App-Specific Awaiting Others | +| `0xA3` | App-Specific Acceptance | +| `0xA4` | App-Specific Below Condition | +| `0xA5` | App-Specific Receiver Action Requested | +| `0xA6` | App-Specific Expiry or Limit | +| `0xA7` | [reserved] | +| `0xA8` | App-Specific Inapplicable Condition | +| `0xA9` | [reserved] | +| `0xAA` | [reserved] | +| `0xAB` | [reserved] | +| `0xAC` | [reserved] | +| `0xAD` | [reserved] | +| `0xAE` | [reserved] | +| `0xAF` | App-Specific Meta or Info | + +#### `0xB*` TBD + +Currently unspecified. (Full range reserved) + +#### `0xC*` TBD + +Currently unspecified. (Full range reserved) + +#### `0xD*` TBD + +Currently unspecified. (Full range reserved) + +#### `0xE*` Encryption, Identity & Proofs + +Actions around signatures, cryptography, signing, and application-level authentication. + +The meta code `0xEF` is often used to signal a payload describing the algorithm or process used. + +| Code | Description | +|--------|-------------------------------------| +| `0xE0` | Decrypt Failure | +| `0xE1` | Decrypt Success | +| `0xE2` | Awaiting Other Signatures or Keys | +| `0xE3` | Signed | +| `0xE4` | Unsigned or Untrusted | +| `0xE5` | Signature Required | +| `0xE6` | Known to be Compromised | +| `0xE7` | [reserved] | +| `0xE8` | Already Signed or Not Encrypted | +| `0xE9` | [reserved] | +| `0xEA` | [reserved] | +| `0xEB` | [reserved] | +| `0xEC` | [reserved] | +| `0xED` | [reserved] | +| `0xEE` | [reserved] | +| `0xEF` | Cryptography, ID, or Proof Metadata | + +#### `0xF*` Off-Chain + +For off-chain actions. Much like th `0x0*: Generic` range, `0xF*` is very general, and does little to modify the reason. + +Among other things, the meta code `0xFF` may be used to describe what the off-chain process is. + +| Code | Description | +|--------|-----------------------------------| +| `0xF0` | Off-Chain Failure | +| `0xF1` | Off-Chain Success | +| `0xF2` | Awaiting Off-Chain Process | +| `0xF3` | Off-Chain Process Started | +| `0xF4` | Off-Chain Service Unreachable | +| `0xF5` | Off-Chain Action Required | +| `0xF6` | Off-Chain Expiry or Limit Reached | +| `0xF7` | [reserved] | +| `0xF8` | Duplicate Off-Chain Request | +| `0xF9` | [reserved] | +| `0xFA` | [reserved] | +| `0xFB` | [reserved] | +| `0xFC` | [reserved] | +| `0xFD` | [reserved] | +| `0xFE` | [reserved] | +| `0xFF` | Off-Chain Info or Meta | + +### As a Grid + +| | `0x0*` General | `0x1*` Permission & Control | `0x2*` Find, Inequalities & Range | `0x3*` Negotiation & Governance | `0x4*` Availability & Time | `0x5*` Tokens, Funds & Finance | `0x6*` TBD | `0x7*` TBD | `0x8*` TBD | `0x9*` TBD | `0xA*` Application-Specific Codes | `0xB*` TBD | `0xC*` TBD | `0xD*` TBD | `0xE*` Encryption, Identity & Proofs | `0xF*` Off-Chain | +|--------|------------------------------------------------|----------------------------------------------------------|--------------------------------------------|------------------------------------------------|-------------------------------------------------------------|----------------------------------------|-------------------|-------------------|-------------------|-------------------|-----------------------------------------------|-------------------|-------------------|-------------------|--------------------------------------------|------------------------------------------| +| `0x*0` | `0x00` Failure | `0x10` Disallowed or Stop | `0x20` Not Found, Unequal, or Out of Range | `0x30` Sender Disagrees or Nay | `0x40` Unavailable | `0x50` Transfer Failed | `0x60` [reserved] | `0x70` [reserved] | `0x80` [reserved] | `0x90` [reserved] | `0xA0` App-Specific Failure | `0xB0` [reserved] | `0xC0` [reserved] | `0xD0` [reserved] | `0xE0` Decrypt Failure | `0xF0` Off-Chain Failure | +| `0x*1` | `0x01` Success | `0x11` Allowed or Go | `0x21` Found, Equal or In Range | `0x31` Sender Agrees or Yea | `0x41` Available | `0x51` Transfer Successful | `0x61` [reserved] | `0x71` [reserved] | `0x81` [reserved] | `0x91` [reserved] | `0xA1` App-Specific Success | `0xB1` [reserved] | `0xC1` [reserved] | `0xD1` [reserved] | `0xE1` Decrypt Success | `0xF1` Off-Chain Success | +| `0x*2` | `0x02` Awaiting Others | `0x12` Awaiting Other's Permission | `0x22` Awaiting Match | `0x32` Awaiting Ratification | `0x42` Paused | `0x52` Awaiting Payment From Others | `0x62` [reserved] | `0x72` [reserved] | `0x82` [reserved] | `0x92` [reserved] | `0xA2` App-Specific Awaiting Others | `0xB2` [reserved] | `0xC2` [reserved] | `0xD2` [reserved] | `0xE2` Awaiting Other Signatures or Keys | `0xF2` Awaiting Off-Chain Process | +| `0x*3` | `0x03` Accepted | `0x13` Permission Requested | `0x23` Match Request Sent | `0x33` Offer Sent or Voted | `0x43` Queued | `0x53` Hold or Escrow | `0x63` [reserved] | `0x73` [reserved] | `0x83` [reserved] | `0x93` [reserved] | `0xA3` App-Specific Acceptance | `0xB3` [reserved] | `0xC3` [reserved] | `0xD3` [reserved] | `0xE3` Signed | `0xF3` Off-Chain Process Started | +| `0x*4` | `0x04` Lower Limit or Insufficient | `0x14` Too Open / Insecure | `0x24` Below Range or Underflow | `0x34` Quorum Not Reached | `0x44` Not Available Yet | `0x54` Insufficient Funds | `0x64` [reserved] | `0x74` [reserved] | `0x84` [reserved] | `0x94` [reserved] | `0xA4` App-Specific Below Condition | `0xB4` [reserved] | `0xC4` [reserved] | `0xD4` [reserved] | `0xE4` Unsigned or Untrusted | `0xF4` Off-Chain Service Unreachable | +| `0x*5` | `0x05` Receiver Action Required | `0x15` Needs Your Permission or Request for Continuation | `0x25` Request for Match | `0x35` Receiver's Ratification Requested | `0x45` Awaiting Your Availability | `0x55` Funds Requested | `0x65` [reserved] | `0x75` [reserved] | `0x85` [reserved] | `0x95` [reserved] | `0xA5` App-Specific Receiver Action Requested | `0xB5` [reserved] | `0xC5` [reserved] | `0xD5` [reserved] | `0xE5` Signature Required | `0xF5` Off-Chain Action Required | +| `0x*6` | `0x06` Upper Limit | `0x16` Revoked or Banned | `0x26` Above Range or Overflow | `0x36` Offer or Vote Limit Reached | `0x46` Expired | `0x56` Transfer Volume Exceeded | `0x66` [reserved] | `0x76` [reserved] | `0x86` [reserved] | `0x96` [reserved] | `0xA6` App-Specific Expiry or Limit | `0xB6` [reserved] | `0xC6` [reserved] | `0xD6` [reserved] | `0xE6` Known to be Compromised | `0xF6` Off-Chain Expiry or Limit Reached | +| `0x*7` | `0x07` [reserved] | `0x17` [reserved] | `0x27` [reserved] | `0x37` [reserved] | `0x47` [reserved] | `0x57` [reserved] | `0x67` [reserved] | `0x77` [reserved] | `0x87` [reserved] | `0x97` [reserved] | `0xA7` [reserved] | `0xB7` [reserved] | `0xC7` [reserved] | `0xD7` [reserved] | `0xE7` [reserved] | `0xF7` [reserved] | +| `0x*8` | `0x08` Duplicate, Unnecessary, or Inapplicable | `0x18` Not Applicable to Current State | `0x28` Duplicate, Conflict, or Collision | `0x38` Already Voted | `0x48` Already Done | `0x58` Funds Not Required | `0x68` [reserved] | `0x78` [reserved] | `0x88` [reserved] | `0x98` [reserved] | `0xA8` App-Specific Inapplicable Condition | `0xB8` [reserved] | `0xC8` [reserved] | `0xD8` [reserved] | `0xE8` Already Signed or Not Encrypted | `0xF8` Duplicate Off-Chain Request | +| `0x*9` | `0x09` [reserved] | `0x19` [reserved] | `0x29` [reserved] | `0x39` [reserved] | `0x49` [reserved] | `0x59` [reserved] | `0x69` [reserved] | `0x79` [reserved] | `0x89` [reserved] | `0x99` [reserved] | `0xA9` [reserved] | `0xB9` [reserved] | `0xC9` [reserved] | `0xD9` [reserved] | `0xE9` [reserved] | `0xF9` [reserved] | +| `0x*A` | `0x0A` [reserved] | `0x1A` [reserved] | `0x2A` [reserved] | `0x3A` [reserved] | `0x4A` [reserved] | `0x5A` [reserved] | `0x6A` [reserved] | `0x7A` [reserved] | `0x8A` [reserved] | `0x9A` [reserved] | `0xAA` [reserved] | `0xBA` [reserved] | `0xCA` [reserved] | `0xDA` [reserved] | `0xEA` [reserved] | `0xFA` [reserved] | +| `0x*B` | `0x0B` [reserved] | `0x1B` [reserved] | `0x2B` [reserved] | `0x3B` [reserved] | `0x4B` [reserved] | `0x5B` [reserved] | `0x6B` [reserved] | `0x7B` [reserved] | `0x8B` [reserved] | `0x9B` [reserved] | `0xAB` [reserved] | `0xBB` [reserved] | `0xCB` [reserved] | `0xDB` [reserved] | `0xEB` [reserved] | `0xFB` [reserved] | +| `0x*C` | `0x0C` [reserved] | `0x1C` [reserved] | `0x2C` [reserved] | `0x3C` [reserved] | `0x4C` [reserved] | `0x5C` [reserved] | `0x6C` [reserved] | `0x7C` [reserved] | `0x8C` [reserved] | `0x9C` [reserved] | `0xAC` [reserved] | `0xBC` [reserved] | `0xCC` [reserved] | `0xDC` [reserved] | `0xEC` [reserved] | `0xFC` [reserved] | +| `0x*D` | `0x0D` [reserved] | `0x1D` [reserved] | `0x2D` [reserved] | `0x3D` [reserved] | `0x4D` [reserved] | `0x5D` [reserved] | `0x6D` [reserved] | `0x7D` [reserved] | `0x8D` [reserved] | `0x9D` [reserved] | `0xAD` [reserved] | `0xBD` [reserved] | `0xCD` [reserved] | `0xDD` [reserved] | `0xED` [reserved] | `0xFD` [reserved] | +| `0x*E` | `0x0E` [reserved] | `0x1E` [reserved] | `0x2E` [reserved] | `0x3E` [reserved] | `0x4E` [reserved] | `0x5E` [reserved] | `0x6E` [reserved] | `0x7E` [reserved] | `0x8E` [reserved] | `0x9E` [reserved] | `0xAE` [reserved] | `0xBE` [reserved] | `0xCE` [reserved] | `0xDE` [reserved] | `0xEE` [reserved] | `0xFE` [reserved] | +| `0x*F` | `0x0F` Informational or Metadata | `0x1F` Permission Details or Control Conditions | `0x2F` Matching Meta or Info | `0x3F` Negotiation Rules or Participation Info | `0x4F` Availability Rules or Info (ex. time since or until) | `0x5F` Token or Financial Information | `0x6F` [reserved] | `0x7F` [reserved] | `0x8F` [reserved] | `0x9F` [reserved] | `0xAF` App-Specific Meta or Info | `0xBF` [reserved] | `0xCF` [reserved] | `0xDF` [reserved] | `0xEF` Cryptography, ID, or Proof Metadata | `0xFF` Off-Chain Info or Meta | + +### Example Function Change + +```solidity +uint256 private startTime; +mapping(address => uint) private counters; + +// Before +function increase() public returns (bool _available) { + if (now < startTime && counters[msg.sender] == 0) { + return false; + }; + + counters[msg.sender] += 1; + return true; +} + +// After +function increase() public returns (byte _status) { + if (now < start) { return hex"44"; } // Not yet available + if (counters[msg.sender] == 0) { return hex"10"; } // Not authorized + + counters[msg.sender] += 1; + return hex"01"; // Success +} +``` + +### Example Sequence Diagrams + +``` +0x03 = Waiting +0x31 = Other Party (ie: not you) Agreed +0x41 = Available +0x44 = Not Yet Available + + + Exchange + + +AwesomeCoin DEX TraderBot + + + + + | | buy(AwesomeCoin) | + | | <------------------------+ + | buy() | | + | <---------------------+ | + | | | + | Status [0x44] | | + +---------------------> | Status [0x44] | + | +------------------------> | + | | | + | | isDoneYet() | + | | <------------------------+ + | | | + | | Status [0x44] | + | +------------------------> | + | | | + | | | + | Status [0x41] | | + +---------------------> | | + | | | + | buy() | | + | <---------------------+ | + | | | + | | | + | Status [0x31] | | + +---------------------> | Status [0x31] | + | +------------------------> | + | | | + | | | + | | | + | | | + + + + +``` + + + +``` +0x01 = Generic Success +0x10 = Disallowed +0x11 = Allowed + + Token Validation + + + Buyer RegulatedToken TokenValidator IDChecker SpendLimiter + + + + + + + | buy() | | | | + +------------------------> | check() | | | + | +-----------------------> | check() | | + | | +-----------------------> | | + | | | | | + | | | Status [0x10] | | + | | Status [0x10] | <-----------------------+ | + | revert() | <-----------------------+ | | + | <------------------------+ | | | + | | | | | ++---------------------------+ | | | | +| | | | | | +| Updates ID with provider | | | | | +| | | | | | ++---------------------------+ | | | | + | | | | | + | buy() | | | | + +------------------------> | check() | | | + | +-----------------------> | check() | | + | | +-----------------------> | | + | | | | | + | | | Status [0x11] | | + | | | <-----------------------+ | + | | | | | + | | | | check() | + | | +-------------------------------------------> | + | | | | | + | | | | Status [0x11] | + | | Status [0x11] | <-------------------------------------------+ + | Status [0x01] | <-----------------------+ | | + | <------------------------+ | | | + | | | | | + | | | | | + | | | | | + + + + + + +``` + +## Rationale + +### Encoding + +Status codes are encoded as a `byte`. Hex values break nicely into high and low nibbles: `category` and `reason`. For instance, `0x01` stands for general success (ie: `true`) and `0x00` for general failure (ie: `false`). + +As a general approach, all even numbers are blocking conditions (where the receiver does not have control), and odd numbers are nonblocking (the receiver is free to contrinue as they wish). This aligns both a simple bit check with the common encoding of Booleans. + +`bytes1` is very lightweight, portable, easily interoperable with `uint8`, cast from `enum`s, and so on. + +#### Alternatives + +Alternate schemes include `bytes32` and `uint8`. While these work reasonably well, they have drawbacks. + +`uint8` feels even more similar to HTTP status codes, and enums don't require as much casting. However does not break as evenly as a square table (256 doesn't look as nice in base 10). + +Packing multiple codes into a single `bytes32` is nice in theory, but poses additional challenges. Unused space may be interpreted as `0x00 Failure`, you can only efficiently pack four codes at once, and there is a challenge in ensuring that code combinations are sensible. Forcing four codes into a packed representation encourages multiple status codes to be returned, which is often more information than strictly necessarily. This can lead to paradoxical results (ex `0x00` and `0x01` together), or greater resources allocated to interpreting 2564 (4.3 billion) permutations. + +### Multiple Returns + +While there may be cases where packing a byte array of status codes may make sense, the simplest, most forwards-compatible method of transmission is as the first value of a multiple return. + +Familiarity is also a motivating factor. A consistent position and encoding together follow the principle of least surprise. It is both viewable as a "header" in the HTTP analogy, or like the "tag" in BEAM tagged tuples. + +### Human Readable + +Developers should not be required to memorize 256 codes. However, they break nicely into a table. Cognitive load is lowered by organizing the table into categories and reasons. `0x10` and `0x11` belong to the same category, and `0x04` shares a reason with `0x24` + +While this repository includes helper enums, we have found working directly in the hex values to be quite natural. Status code `0x10` is just as comfortable as HTTP 401, for example. + +#### Localizations + +One commonly requested application of this spec is human-readable translations of codes. This has been moved to its own proposal: [ERC-1444](./eip-1444.md), primarily due to a desire to keep both specs focused. + +### Extensibility + +The `0xA` category is reserved for application-specific statuses. In the case that 256 codes become insufficient, `bytes1` may be embedded in larger byte arrays. + +### EVM Codes + +The EVM also returns a status code in transactions; specifically `0x00` and `0x01`. This proposal both matches the meanings of those two codes, and could later be used at the EVM level. + +### Empty Space + +Much like how HTTP status codes have large unused ranges, there are totally empty sections in this proposal. The intent is to not impose a complete set of codes up front, and to allow users to suggest uses for these spaces as time progresses. + +### Beyond Errors + +This spec is intended to be much more than a set of common errors. One design goal is to enable easier contract-to-contract communication, protocols built on top of status codes, and flows that cross off-chain. Many of these cases include either expected kinds of exception state (as opposed to true errors), neutral states, time logic, and various successes. + +Just like how HTTP 200 has a different meaning from HTTP 201, ERC-1066 status codes can relay information between contract beyond simply pass or fail. They can be thought of as the edges in a graph that has smart contracts as nodes. + +### Fully `revert`able + +_This spec is fully compatible with `revert`-with-reason and does not intend to supplant it in any way._ Both by reverting with a common code, the developer can determine what went wrong from a set of known error states. + +Further, by leveraging ERC-1066 and a translation table (such as in ERC-1444) in conjunction, developers and end users alike can receive fully automated human-readable error messages in the language and phrasing of their choice. + +### Nibble Order + +Nibble order makes no difference to the machine, and is purely mnemonic. This design was originally in opposite order, but changed it for a few convenience factors. Since it's a different scheme from HTTP, it may feel strange initially, but becomes very natural after a couple hours of use. + +#### Short Forms + +Generic is `0x0*`, general codes are consistent with their integer representations + +```solidity +hex"1" == hex"01" == 1 // with casting +``` + +#### Contract Categories + +Many applications will always be part of the same category. For instance, validation will generally be in the `0x10` range. + +```solidity +contract Whitelist { + mapping(address => bool) private whitelist; + uint256 private deadline; + byte constant private prefix = hex"10"; + + check(address _, address _user) returns (byte _status) { + if (now >= deadline) { return prefix | 5; } + if (whitelist[_user]) { return prefix | 1; } + return prefix; + } +} +``` + +#### Helpers + +This above also means that working with app-specific enums is slightly easier, and also saves gas (fewer operations required). + +```solidity +enum Sleep { + Awake, + Asleep, + BedOccupied, + WindingDown +} + +// From the helper library + +function appCode(Sleep _state) returns (byte code) { + return byte(160 + _state); // 160 = 0xA0 +} + +// Versus + +function appCode(Sleep _state) returns (byte code) { + return byte((16 * _state) + 10); // 10 = 0xA +} +``` + +## Implementation + +Reference cases and helper libraries (Solidity and JS) can be found at: +* [Source Code](https://github.com/fission-suite/fission-codes/) +* [Package on npm](https://www.npmjs.com/package/fission-codes/) + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-107.md b/EIPS/eip-107.md new file mode 100644 index 0000000..3920c20 --- /dev/null +++ b/EIPS/eip-107.md @@ -0,0 +1,617 @@ +--- +eip: 107 +title: safe "eth_sendTransaction" authorization via html popup +author: Ronan Sandford (@wighawag) +created: 2016-06-05 +status: Stagnant +type: Standards Track +category: Interface +--- + +Abstract +======== +This draft EIP describes the details of an authorization method that if provided by rpc enabled ethereum nodes would allow regular websites to send transactions (via ```eth_sendTransaction```) without the need to enable CORS. Instead, user would be asked to confirm the transaction via an html popup. + +Every read only rpc call the dapp wants to perform is redirected to an invisible iframe from the node's domain and for every transaction that the dapp wish to execute, an html popup is presented to the user to allow him/her to cancel or confirm the transaction. This allows the dapp to connect to the node's rpc api without being granted any kind of privileges. This allows users to safely interact with dapps running in their everyday web browser while their accounts are unlocked. In case the account is not unlocked, and the node has allowed the "personal" api via rpc,the html page also allow the user to enter their password to unlock the account for the scope of the transaction. + +Motivation +========== +Currently, if a user navigates to a dapp running on a website using her/his everyday browser, the dapp will by default have no access to the rpc api for security reasons. The user will have to enable CORS for the website's domain in order for the dapp to work. Unfortunately if the user does so, the dapp will be able to send transactions from any unlocked account without the need for any user consent. In other words, not only does the user need to change the node's default setting, but the user is also forced to trust the dapp in order to use it. This is of course not acceptable and forces existing dapps to rely on the use of workarounds like: +- if the transaction is a plain ether transfer, the user is asked to enter it in a dedicated trusted wallet like "Mist" +- For more complex case, the user is asked to enter the transaction manually via the node command line interface. + + +This proposal aims to provide a safe and user friendly alternative. + +Here are some screenshots of the provided implementation of that html popup: + +Account unlocked : +----------------- +When the account is already unlocked, the user is presented with the following popup for every transaction that the dapp attempts to make: + +![](../assets/eip-107/authorization.png) + +Account locked and no "personal" api exposed via rpc: +----------------- +When the account is locked, and the node does not provide access to account unlocking via its rpc interface, the following popup will be presented. This is not ideal since this requires the user to know how to unlock an account: + +![](../assets/eip-107/authorization-locked.png) + +Account locked but node exposing the "personal" api via rpc : +----------------- +A better option is to ask the user for their password, but this is only possible if the node allows access to the "personal" api via rpc. In such case, the following dialog will be presented to the user so he/she can accept the transaction by providing the password required to unlock the account: + +![](../assets/eip-107/authorization-password.png) + + +Specification +============= +In order for the mechanism to work, the node needs to serve an html file via http at the url \/authorization.html + +This file will then be used by the dapp in 2 different modes (invisible iframe and popup window). + +The invisible iframe will be embedded in the dapp to allow the dapp to send its read-only rpc call without having to enable CORS for the dapp's website domain. This is done by sending message to the iframe (via javascript ```window.postMessage```) which in turn execute the rpc call. This works since the iframe and the node share the same domain/port. + +In the iframe mode, the html file's javascript code will ensure that no call requiring an unlocked key can be made. This is to prevent dapps from embedding the invisible iframe and tricking the user into clicking the confirm button. +If the dapp requires an ```eth_sendTransaction``` call, the dapp will instead open a new window using the same url. + +In this popup window mode, the html file's javascript code will allow ```eth_sendTransaction``` (but not ```eth_sign```, as there is no way to display to the user the meaningful content of the transaction to sign in a safe way) to be called. But instead of sending the call to the node directly, a confirmation dialog will be presented showing the sender and recipient addresses, as well as the amount being transferred along with the potential gas cost. Upon the user approving, the request will be sent and the result returned to the dapp. An error will be returned in case the user cancel the request. + +The html page also checks for the availability of the "personal" api and if so, will ask the user to unlock the account if necessary. The unlocking is temporary (3s) so the password will be asked again if a transaction is attempted before the end of this short time. + +In both iframe mode and window mode, the communication with the dapp is achieved using ```window.postMessage```. +The fist message the iframe/window sends is a message containing the string "ready" to let the dapp know that it now accepts messages. Then the dapp can start performing rpc call by sending message using the following object : +``` +{ + id:, //so responses can be match as there is no guarantee of the order of the response + payload: //this is the exact object that usually send to the node +} +``` + +For ```eth_sendTransaction``` the "gas", "gasPrice" and "from" field need to be set in the rpc parameter so that the window can display the correct value. If not all of these are passed in, the window will return an error. + +Upon receiving such message, the iframe will perform the actual rpc call to the node but only if such a call is a read only call (not requiring an unlocked key). If it is not it will return a error. The window on the other will only accept ```eth_sendTransaction``` calls but will display a dialog so the user can accept or cancel the request. + +In all the cases, the iframe/window will send a message back to the dapp using the following object: +``` +{ + id:, + result:, + error: +} +``` + +the error object cannot be a javascript Error object due to postMessage limitation. Instead it is +``` +{ + message:, + type: //type="cancel" means the user cancel the transaction +} +``` + + +Rationale +========= +The design for that proposal was chosen for its simplicity and security. A previous idea was to use an oauth-like protocol in order for the user to accept or deny a transaction request. It would have required deeper code change in the node and some geth contributors argues that such change did not fit into geth code base as it would have required dapp aware code. +The current design, instead has a very simple implementation (self contained html file that can be shared across node's implementation) and its safeness is guarantess by browsers' cross domain policies. + +The use of iframe/ window was required to have both security and user friendliness. The invisible iframe allows the dapp to execute read only calls without the need for user input, and the window ensures user approval before making a call. While we could have made it without the window mode by making the iframe confirmation use the native browser ```window.confirm``` dialog, this would have prevented the use of a more elegant confirmation popup that the current design allows. It also happens to be that the ```window.confirm``` is not safe in some browsers, as it gives focus to the accept option and can be triggered automatically (https://bugs.chromium.org/p/chromium/issues/detail?id=260653). + + +Implementations +=============== +In order to implement this design, the following html file or an equivalent one needs to be served at the url \/authorization.html + +That's it. + + +``` + + + + Ethereum Authorization + + + + + + + +
+
+

Please wait...

+
+ +
+
+

+

+ + +
+ + + + + + +``` +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1077.md b/EIPS/eip-1077.md new file mode 100644 index 0000000..1efe6a1 --- /dev/null +++ b/EIPS/eip-1077.md @@ -0,0 +1,229 @@ +--- +eip: 1077 +title: Gas relay for contract calls +author: Alex Van de Sande , Ricardo Guilherme Schmidt (@3esmit) +discussions-to: https://ethereum-magicians.org/t/erc1077-and-1078-the-magic-of-executable-signed-messages-to-login-and-do-actions/351 +status: Stagnant +type: Standards Track +category: ERC +created: 2018-05-04 +requires: 20, 191, 1271, 1344 +--- + + +## Simple Summary + +A standard interface for gas abstraction in top of smart contracts. + +Allows users to offer [EIP-20] token for paying the gas used in a call. + +## Abstract + +A main barrier for adoption of DApps is the requirement of multiple tokens for executing in chain actions. Allowing users to sign messages to show intent of execution, but allowing a third party relayer to execute them can circumvent this problem, while ETH will always be required for ethereum transactions, it's possible for smart contract to take [EIP-191] signatures and forward a payment incentive to an untrusted party with ETH for executing the transaction. + +## Motivation + +Standardizing a common format for them, as well as a way in which the user allows the transaction to be paid in tokens, gives app developers a lot of flexibility and can become the main way in which app users interact with the Blockchain. + + +## Specification + +### Methods + +#### executeGasRelay + +Executes `_execData` with current `lastNonce()` and pays `msg.sender` the gas used in specified `_gasToken`. + +```solidity +function executeGasRelay(bytes calldata _execData, uint256 _gasPrice, uint256 _gasLimit, address _gasToken, address _gasRelayer, bytes calldata _signature) external; +``` + +### executeGasRelayMsg + +Returns the `executeGasRelay` message used for signing messages.. + +```solidity +function executeGasRelayMsg(uint256 _nonce, bytes memory _execData, uint256 _gasPrice, uint256 _gasLimit, address _gasToken, address _gasRelayer) public pure returns (bytes memory); +``` + +#### executeGasRelayERC191Msg + +Returns the [EIP-191] of `executeGasRelayMsg` used for signing messages and for verifying the execution. + +```solidity +function executeGasRelayERC191Msg(uint256 _nonce, bytes memory _execData, uint256 _gasPrice, uint256 _gasLimit, address _gasToken, address _gasRelayer) public view returns (bytes memory); +``` + +#### lastNonce + +Returns the current nonce for the gas relayed messages. + +```solidity +function lastNonce() public returns (uint nonce); +``` + +### Signed Message + +The signed message require the following fields: + +* Nonce: A nonce *or* a timestamp; +* Execute Data: the bytecode to be executed by the account contract; +* Gas Price: The gas price (paid in the selected token); +* Gas Limit: The gas reserved to the relayed execution; +* Gas Token: A token in which the gas will be paid (leave 0 for ether); +* Gas Relayer: the beneficiary of gas refund for this call (leave 0 for `block.coinbase`) . + +#### Signing the message + +The message **MUST** be signed as [EIP-191] standard, and the called contract **MUST** also implement [EIP-1271] which must validate the signed messages. + +Messages **MUST** be signed by the owner of the account contract executing. If the owner is a contract, it must implement [EIP-1271] interface and forward validation to it. + +In order to be compliant, the transaction **MUST** request to sign a "messageHash" that is a concatenation of multiple fields. + +The fields **MUST** be constructed as this method: + +The first and second fields are to make it [EIP-191] compliant. Starting a transaction with `byte(0x19)` ensure the signed data from being a [valid ethereum transaction](https://github.com/ethereum/wiki/wiki/RLP). The second argument is a version control byte. The third being the validator address (the account contract address) according to version 0 of [EIP-191]. The remaining arguments being the application specific data for the gas relay: chainID as per [EIP-1344], execution nonce, execution data, agreed gas Price, gas limit of gas relayed call, gas token to pay back and gas relayer authorized to receive reward. + +The [EIP-191] message must be constructed as following: +```solidity +keccak256( + abi.encodePacked( + byte(0x19), //ERC-191 - the initial 0x19 byte + byte(0x0), //ERC-191 - the version byte + address(this), //ERC-191 - version data (validator address) + chainID, + bytes4( + keccak256("executeGasRelay(uint256,bytes,uint256,uint256,address,address)") + ), + _nonce, + _execData, + _gasPrice, + _gasLimit, + _gasToken, + _gasRelayer + ) +) +``` + +## Rationale + +User pain points: + +* users don't want to think about ether +* users don't want to think about backing up private keys or seed phrases +* users want to be able to pay for transactions using what they already have on the system, be apple pay, xbox points or even a credit card +* Users don’t want to sign a new transaction at every move +* Users don’t want to download apps/extensions (at least on the desktop) to connect to their apps + +App developer pain points: +* Many apps use their own token and would prefer to use those as the main accounting +* Apps want to be able to have apps in multiple platforms without having to share private keys between devices or have to spend transaction costs moving funds between them +* Token developers want to be able for their users to be able to move funds and pay fees in the token +* While the system provides fees and incentives for miners, there are no inherent business model for wallet developers (or other apps that initiate many transactions) + +Using signed messages, specially combined with an account contract that holds funds, and multiple disposable ether-less keys that can sign on its behalf, solves many of these pain points. + +### Multiple signatures + +More than one signed transaction with the same parameter can be executed by this function at the same time, by passing all signatures in the `messageSignatures` field. That field will split the signature in multiple 72 character individual signatures and evaluate each one. This is used for cases in which one action might require the approval of multiple parties, in a single transaction. + +If multiple signatures are required, then all signatures should then be *ordered by account* and the account contract should implement signatures checks locally (`JUMP`) on [EIP-1271] interface which might forward (`STATIC_CALL`) the [EIP-1271] signature check to owner contract. + +### Keep track of nonces: + +Note that `executeGasRelay` function does not take a `_nonce` as parameter. The contract knows what is the current nonce, and can only execute the transactions in order, therefore there is no reason + +Nonces work similarly to normal ethereum transactions: a transaction can only be executed if it matches the last nonce + 1, and once a transaction has occurred, the `lastNonce` will be updated to the current one. This prevents transactions to be executed out of order or more than once. + +Contracts may accept transactions without nonce (nonce = 0). The contract then must keep the full hash of the transaction to prevent it from being replayed. This would allows contracts to have more flexibilities as you can sign a transaction that can be executed out of order or not at all, but it uses more memory for each transaction. It can be used, for instance, for transactions that the user wants to schedule in the future but cannot know its future nonce, or transactions that are made for state channel contracts that are not guaranteed to be executed or are only executed when there's some dispute. + +### Execute transaction + +After signature validation, the evaluation of `_execBytes` is up to the account contract implementation, it's role of the wallet to properly use the account contract and it's gas relay method. +A common pattern is to expose an interface which can be only called by the contract itself. The `_execBytes` could entirely forward the call in this way, as example: `address(this).call.gas(_gasLimit)(_execData);` +Where `_execData` could call any method of the contract itself, for example: + +- `call(address to, uint256 value, bytes data)`: allow any type of ethereum call be performed; +- `create(uint256 value, bytes deployData)`: allows create contract +- `create2(uint256 value, bytes32 salt, bytes deployData)`: allows create contract with deterministic address +- `approveAndCall(address token, address to, uint256 value, bytes data)`: allows safe approve and call of an ERC20 token. +- `delegatecall(address codeBase, bytes data)`: allows executing code stored on other contract +- `changeOwner(address newOwner)`: Some account contracts might allow change of owner +- `foo(bytes bar)`: Some account contracts might have custom methods of any format. + +The standardization of account contracts is not scope of this ERC, and is presented here only for illustration on possible implementations. +Using a self call to evaluate `_execBytes` is not mandatory, depending on the account contract logic, the evaluation could be done locally. + +### Gas accounting and refund + +The implementing contract must keep track of the gas spent. One way to do it is to first call `gasLeft()` at the beginning of the function and then after executing the desired action and compare the difference. + +The contract then will make a token transfer (or ether, if `tokenAddress` is nil) in the value of `gasSpent * gasPrice` to the `_gasRelayer`, that is the account that deployed the message. + +If `_gasRelayer` is zero, then the funds **MUST** go to `block.coinbase`. + +If there are not enough funds, or if the total surpasses `gasLimit` then the transaction **MUST** revert. + +If the executed transaction fails internally, nonces should still be updated and gas needs to be paid. + +Contracts are not obligated to support ether or any other token they don’t want and can be implemented to only accept refunds in a few tokens of their choice. + +### Usage examples + +This scheme opens up a great deal of possibilities on interaction as well as different experiments on business models: + +* Apps can create individual identities contract for their users which holds the actual funds and then create a different private key for each device they log into. Other apps can use the same identity and just ask to add permissioned public keys to manage the device, so that if one individual key is lost, no ether is lost. +* An app can create its own token and only charge their users in its internal currency for any ethereum transaction. The currency units can be rounded so it looks more similar to to actual amount of transactions: a standard transaction always costs 1 token, a very complex transaction costs exactly 2, etc. Since the app is the issuer of the transactions, they can do their own Sybil verifications and give a free amount of currency units to new users to get them started. +* A game company creates games with a traditional monthly subscription, either by credit card or platform-specific microtransactions. Private keys never leave the device and keep no ether and only the public accounts are sent to the company. The game then signs transactions on the device with gas price 0, sends them to the game company which checks who is an active subscriber and batches all transactions and pays the ether themselves. If the company goes bankrupt, the gamers themselves can set up similar subscription systems or just increase the gas price. End result is a **ethereum based game in which gamers can play by spending apple, google or xbox credits**. +* A standard token is created that doesn’t require its users to have ether, and instead allows tokens to be transferred by paying in tokens. A wallet is created that signs messages and send them via whisper to the network, where other nodes can compete to download the available transactions, check the current gas price, and select those who are paying enough tokens to cover the cost. **The result is a token that the end users never need to keep any ether and can pay fees in the token itself.** +* A DAO is created with a list of accounts of their employees. Employees never need to own ether, instead they sign messages, send them to whisper to a decentralized list of relayers which then deploy the transactions. The DAO contract then checks if the transaction is valid and sends ether to the deployers. Employees have an incentive not to use too many of the companies resources because they’re identifiable. The result is that the users of the DAO don't need to keep ether, and **the contract ends up paying for it's own gas usage**. + +## Backwards Compatibility + +There is no issues with backwards compatibility, however for future upgrades, as `_execData` contains arbitrary data evaluated by the account contract, it's up to the contract to handle properly this data and therefore contracts can gas relay any behavior with the current interface. + +## Test Cases + +TBD + +## Implementation + +One initial implementation of such a contract can be found at [Status.im account-contracts repository](https://github.com/status-im/account-contracts/blob/develop/contracts/account/AccountGasAbstract.sol) + +Other version is implemented as Gnosis Safe variant in: https://github.com/status-im/safe-contracts + +### Similar implementations + +The idea of using signed messages as executable intent has been around for a while and many other projects are taking similar approaches, which makes it a great candidate for a standard that guarantees interoperability: + +* [EIP-877](https://github.com/ethereum/EIPs/pull/877) An attempt of doing the same but with a change in the protocol +* [Status](https://github.com/status-im/ideas/issues/73) +* [Aragon](https://github.com/aragonlabs/pay-protocol) (this might not be the best link to show their work in this area) +* [Token Standard Functions for Preauthorized Actions](https://github.com/ethereum/EIPs/issues/662) +* [Token Standard Extension 865](https://github.com/ethereum/EIPs/issues/865) +* [Iuri Matias: Transaction Relay](https://github.com/iurimatias/TransactionRelay) +* [uPort: Meta transactions](https://github.com/uport-project/uport-identity#send-a-meta-tx) +* [uPort: safe Identities](https://github.com/uport-project/uport-identity/blob/develop/docs/txRelay.md) +* [Gnosis safe contracts](https://github.com/gnosis/safe-contracts) + +Swarm city uses a similar proposition for etherless transactions, called [Gas Station Service](https://github.com/swarmcity/SCLabs-gasstation-service), but it's a different approach. Instead of using signed messages, a traditional ethereum transaction is signed on an etherless account, the transaction is then sent to a service that immediately sends the exact amount of ether required and then publishes the transaction. + +## Security Considerations + +Deployers of transactions (relayers) should be able to call untrusted contracts, which provides no guarantees that the contract they are interacting with correctly implements the standard and they will be reimbursed for gas. To prevent being fooled by bad implementations, relayers must **estimate the outcome of a transaction**, and only include/sign transactions which have a desired outcome. + +Is also interest of relayers to maintaining a private reputation of contracts they interact with, as well as keep track of which tokens and for which `gasPrice` they’re willing to deploy transactions. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). + +## References + +* [Universal Logins talk at UX Unconf, Toronto](https://www.youtube.com/watch?v=qF2lhJzngto) + +[EIP-20]: ./eip-20.md +[EIP-191]: ./eip-191.md +[EIP-1271]: ./eip-1271.md +[EIP-1344]: ./eip-1344.md diff --git a/EIPS/eip-1078.md b/EIPS/eip-1078.md new file mode 100644 index 0000000..7990475 --- /dev/null +++ b/EIPS/eip-1078.md @@ -0,0 +1,121 @@ +--- +eip: 1078 +title: Universal login / signup using ENS subdomains +author: Alex Van de Sande +discussions-to: https://ethereum-magicians.org/t/erc1077-and-1078-the-magic-of-executable-signed-messages-to-login-and-do-actions/351 +status: Stagnant +type: Standards Track +category: ERC +created: 2018-05-04 +requires: 191, 681, 725, 1077 +--- + +## Abstract + +This presents a method to replace the usual signup/login design pattern with a minimal ethereum native scheme, that doesn’t require passwords, backing up private keys nor typing seed phrases. From the user point of view it will be very similar to patterns they’re already used to with second factor authentication (without relying in a central server), but for dapp developers it requires a new way to think about ethereum transactions. + + +## Simple Summary + +The unique identifier of the user is a contract which implements both Identity and the Executable Signed Messages ERCs. The user should not need provide this address directly, only a ens name pointing to it. These types of contracts are indirectly controlled by private keys that can sign messages indicating intents, which are then deployed to the contract by a third party (or a decentralized network of deployers). + +In this context, therefore, a device "logging into" an app using an identity, means that the device will generate a private key locally and then request an authorization to add that key as one of the signers of that identity, with a given set of permissions. Since that private key is only used for signing messages, it is not required to hold ether, tokens or assets, and if lost, it can be simply be replaced by a new one – the user's funds are kept on the identity contract. + +In this context, ethereum accounts are used in a manner more similar to auth tokens, rather than unique keys. + +The login process is as follows: + +#### 1) Request a name from the user + +The first step of the process is to request from the user the ENS name that points to their identity. If the user doesn’t have a login set up, the app should–if they have an integrated identity manager–provide an option to provide a subdomain or a name they own. + +**UX Note:** there are many ways to provide this interface, the app can ask if they want to signup/login before hand or simply directly ask them to type the name. Note that since it’s trivial to verify if a username exists, your app should adapt to it gracefully and not require the user to type their name twice. If they ask to signup and provide a name that exists then ask them if they want to login using that name, or similarly if they ask to connect to an existing name but type a non-existent name show them a nice alert and ask them if they want to create that name now. Don’t force them to type the same name twice in two different fields. + +#### 2.a) Create a new identity + +If the user doesn’t have an identity, the app should provide the option to create one for them. Each app must have one or more domains they control which they can create immediate subdomains on demand. The app therefore will make these actions on the background: + +1. Generate a private key which it will keep saved locally on the device or browser, the safest way possible. +2. Create (or set up) an identity contract which supports both ERC720 and ERC1077 +3. Register the private key created on step 1 as the *only* admin key of the contract (the app must not add any app-controlled key, except as recovery option - see 5) +4. Register the requested subdomain and transfer its ownership to the contract (while the app controls the main domain and may keep the option to reassign them at will, the ownership of the subdomain itself should belong to the identity, therefore allowing them to transfer it) +5. (Optionally) Register a recovery method on the contract, which allows the user to regain access to the contract in case the main key is lost. + +All those steps can be designed to be set up in a single ethereum transaction. Since this step is not free, the app reserves the right to charge for registering users, or require the user to be verified in a sybil resistant manner of the app’s choosing (captcha, device ID registration, proof of work, etc) + +The user shouldn’t be forced to wait for transaction confirmation times. Instead, have an indicator somewhere on the app the shows the progress and then allow the user to interact with your app normally. It’s unlikely that they’ll need the identity in the first few minutes and if something goes wrong (username gets registered at the same time), you can then ask the user for an action. + +**Implementation note:** in order to save gas, some of these steps can be done in advance. The app can automatically deploy a small number of contracts when the gas price is low, and set up all their main variables to be 0xFFFFFF...FFFFF. These should be considered ‘vacant’ and when the user registers one, they will get a gas discount for freeing up space on the chain. This has the added benefit of allowing the user a choice in contract address/icon. + +#### 2.b) Connect to an existing identity + +If the user wants to connect with an existing identity, then the first thing the app needs to understand is what level of privilege it’s going to ask for: + +**Manager** the higher level, allows the key to initiate or sign transactions that change the identity itself, like adding or removing keys. An app should only require this level if it integrates an identity manager. Depending on how the identity is set up, it might require signature from more keys before these transactions can be deployed. + +**Action** this level allows the key to initiate or sign transactions on address other than itself. It can move funds, ether, assets etc. An app should only require this level of privilege if it’s a general purpose wallet or browser for sending ethereum transactions. Depending on how the identity is set up, it might require signature from more keys before these transactions can be deployed. + +**Encryption** the lower level has no right to initiate any transactions, but it can be used to represent the user in specific instances or off-chain signed messages. It’s the ideal level of privilege for games, chat or social media apps, as they can be used to sign moves, send messages, etc. If a game requires actual funds (say, to start a game with funds in stake) then it should still use the encryption level, and then require the main wallet/browser of the user to sign messages using the ethereum URI standard. + +Once the desired level is known, the app must take these steps: + +1. **Generate a private key** which it will keep saved locally on the device or browser, the safest way possible. +2. **Query ens** to figure the existing address of the identity +3. **Generate the bytecode** for a transaction calling the function `addKey(PUBLICKEY,LEVEL)`. +4. **Broadcast a transaction request on a whisper channel** or some other decentralized network of peers. Details on this step require further discussions +1. **If web3 is available** then attempt calling web3.eth.sendTransaction. This can be automatic or prompted by user action. +1. **Attempt calling a URI** if the app supports [URL format for transaction requests EIP](./eip-681.md) then attempt calling this. This can be automatic or prompted by user action. +1. **Show a QR code**: with an EIP681 formatted URL. That QR code can be clickable to attempt to retry the other options, but it should be done last: if step 1 works, the user should receive a notification on their compatible device and won't need to use the QR code. + +Here's an example of a EIP681 compatible address to add a public key generated locally in the app: + +`ethereum:bob.example.eth?function=addKey(address='0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef',uint=1)` + +If adding the new key requires multiple signatures, or if the app receiving that request exclusiveky deals with executable signed messages and has no ether on itself, then it should follow the steps in the next section on how to request transactions. + +As before, the user shouldn’t be forced to wait for transaction confirmation times. Instead, have an indicator somewhere on the app the shows the progress and then allow the user to interact with your app normally. + + + +#### 3) Request transactions + +After step 2, the end result should be that your app should have the identity address of the user, their main ens name and a private key, whose public account is listed on the identity as one of their keys, with roles being either manager, action or encryption. Now it can start using that information to sign and execute transactions. + +**Not all transactions need to be on chain**, actually most common uses of signed messages should be off chain. If you have a chat app, for instance, you can use the local key for signing messages and sending it to the other parties, and they can just query the identity contract to see if that key actually comes from the user. If you have a game with funds at stake, only the first transaction moving funds and setting up the initial game needs to be executed by the identity: at each turn the players can sign a hash of the current state of the board and at the end, the last two plays can be used to determine the winner. Notice that keys can be revoked at any time, so your app should take that in consideration, for instance saving all keys at the start of the game. Keys that only need this lower level of privilege, should be set at level 4 (encryption). + +Once you decided you actually need an on-chain transaction, follow these steps: + +1. **Figure out the TO, FROM, VALUE and DATA**. These are the basics of any ethereum transaction. `from` is the compatible contract you want the transaction to be deployed from. +2. **Check the privilege level you need:** if the `to` and `from` fields are the same contract, ie, if the transaction requires the identity to act upon itself (for instance, when adding or removing a key) then you need level 1 (management), otherwise it's 2 (action). Verify if the key your app owns correspond to the required level. +3. **Verify how many keys are required** by calling `requiredSignatures(uint level)` on the target contract +4. **Figure out gasLimit**: Estimate the gas cost of the desired transaction, and add a margin (recommended: add 100k gas) +5. **Figure out gasToken and gasPrice**: Check the current gas price considering network congestions and the market price of the token the user is going to pay with. Leave gasToken as 0 for ether. Leave gasPrice as 0 if you are deploying it yourself and subsidizing the costs elsewhere. +6. **Sign an executable signed transaction** by following that standard. + +After having all the signed executable message, we need to deploy it to the chain. If the transaction only requires a single signature, then the app provider can deploy it themselves. Send the transaction to the `from` address and attempt to call the function `executeSigned`, using the parameters and signature you just collected. + +If the transaction need to collect more signatures or the app doesn't have a deployable server, the app should follow these steps: + +1. **Broadcast the transaction on a whisper channel** or some other decentralized network of peers. Details on this step require further discussions +2. **If web3 is available** then attempt calling web3.eth.personal_sign. This can be automatic or prompted by user action. +3. **Show a QR code**: with the signed transaction and the new data to be signed. That QR code can be clickable to attempt to retry the other options, but it should be done last: if step 1 works, the user should receive a notification on their compatible device and won't need to use the QR code. + +The goal is to keep broadcasting signatures via whisper until a node that is willing to deploy them is able to collect all messages. + +Once you've followed the above steps, watch the transaction pool to any transaction to that address and then take the user to your app. Once you seen the desired transaction, you can stop showing the QRcode and proceed with the app, while keeping some indication that the transaction is in progress. Subscribe to the event `ExecutedSigned` of the desired contract: once you see the transaction with the nonce, you can call it a success. If you see a different transaction with the same or higher nonce (or timestamp) then you consider the transaction permanently failed and restart the process. + + +### Implementation + +No working examples of this implementation exists, but many developers have expressed interest in adopting it. This section will be edited in the future to reflect that. + +### Conclusion and future improvements + +This scheme would allow much more lighter apps, that don't require to hold ether, and can keep unlocked private keys on the device to be able to send messages and play games without requesting user prompt every time. More work is needed to standardize common decentralized messaging protocols as well as open source tools for deployment nodes, in order to create a decentralized and reliable layer for message deployment. + +### References + +* [Universal Logins talk at UX Unconf, Toronto](https://www.youtube.com/watch?v=qF2lhJzngto) + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1080.md b/EIPS/eip-1080.md new file mode 100644 index 0000000..d704fc8 --- /dev/null +++ b/EIPS/eip-1080.md @@ -0,0 +1,184 @@ +--- +eip: 1080 +title: Recoverable Token +author: Bradley Leatherwood +discussions-to: https://ethereum-magicians.org/t/erc-1080-recoverabletoken-standard/364 +status: Stagnant +type: Standards Track +category: ERC +created: 2018-05-02 +--- + +## Simple Summary + +A standard interface for tokens that support chargebacks, theft prevention, and lost & found resolutions. + +## Abstract + +The following standard allows for the implementation of a standard API for tokens extending ERC-20 or ERC-791. This standard provides basic functionality to recover stolen or lost accounts, as well as provide for the chargeback of tokens. + +## Motivation + +To mitigate the effects of reasonably provable token or asset loss or theft and to help resolve other conflicts. Ethereum's protocol should not be modified because of loss, theft, or conflicts, but it is possible to solve these problems in the smart contract layer. + +## Specification + +## RecoverableToken + +### Methods + +#### claimLost + +Reports the `lostAccount` address as being lost. MUST trigger the `AccountClaimedLost` event. + +After the time configured in `getLostAccountRecoveryTimeInMinutes` the implementer MUST provide a mechanism for determining the correct owner of the tokens held and moving the tokens to a new account. + +Account recoveries must trigger the `AccountRecovered` event. + +``` js +function claimLost(address lostAccount) returns (bool success) +``` + +#### cancelLostClaim + +Reports the `msg.sender`'s account as being not being lost. MUST trigger the `AccountClaimedLostCanceled` event. + +MUST fail if an account recovery process has already begun. + +Otherwise, this method MUST stop a dispute from being started to recover funds. + +``` js +function claimLost() returns (bool success) +``` + +#### reportStolen + +Reports the current address as being stolen. MUST trigger the `AccountFrozen` event. +Successful calls MUST result in the `msg.sender`'s tokens being frozen. + +The implementer MUST provide a mechanism for determining the correct owner of the tokens held and moving the tokens to a new account. + +Account recoveries must trigger the `AccountRecovered` event. + +``` js +function reportStolen() returns (bool success) +``` + +#### chargeback + +Requests a reversal of transfer on behalf of `msg.sender`. + +The implementer MUST provide a mechanism for determining the correct owner of the tokens disputed and moving the tokens to the correct account. + +MUST comply with sender's chargeback window as value configured by `setPendingTransferTimeInMinutes`. + +``` js +function chargeback(uint256 pendingTransferNumber) returns (bool success) +``` + +#### getPendingTransferTimeInMinutes + +Get the time an account has to chargeback a transfer. + +``` js +function getPendingTransferTime(address account) view returns (uint256 minutes) +``` + +#### setPendingTransferTimeInMinutes + +Sets the time `msg.sender`'s account has to chargeback a transfer. + +MUST NOT change the time if the account has any pending transfers. + +``` js +function setPendingTransferTime(uint256 minutes) returns (bool success) +``` + +#### getLostAccountRecoveryTimeInMinutes + +Get the time account has to wait before a lost account dispute can start. + +``` js +function getLostAccountRecoveryTimeInMinutes(address account) view returns (uint256 minutes) +``` + +#### setLostAccountRecoveryTimeInMinutes + +Sets the time `msg.sender`'s account has to sit before a lost account dispute can start. + +MUST NOT change the time if the account has open disputes. + +``` js +function setLostAccountRecoveryTimeInMinutes(uint256 minutes) returns (bool success) +``` + +### Events + +#### AccountRecovered + +The recovery of an account that was lost or stolen. + +``` js +event AccountClaimedLost(address indexed account, address indexed newAccount) +``` + +#### AccountClaimedLostCanceled + +An account claimed as being lost. + +``` js +event AccountClaimedLost(address indexed account) +``` + +#### AccountClaimedLost + +An account claimed as being lost. + +``` js +event AccountClaimedLost(address indexed account) +``` + +#### PendingTransfer + +A record of a transfer pending. + +``` js +event PendingTransfer(address indexed from, address indexed to, uint256 value, uint256 pendingTransferNumber) +``` + +#### ChargebackRequested + +A record of a chargeback being requested. + +``` js +event ChargebackRequested(address indexed from, address indexed to, uint256 value, uint256 pendingTransferNumber) +``` + +#### Chargeback + +A record of a transfer being reversed. + +``` js +event Chargeback(address indexed from, address indexed to, uint256 value, uint256 indexed pendingTransferNumber) +``` + +#### AccountFrozen + +A record of an account being frozen. MUST trigger when an account is frozen. + +``` js +event AccountFrozen(address indexed reported) +``` + +## Rationale + +* A recoverable token standard can provide configurable safety for users or contracts who desire this safety. +* Implementations of this standard will give users the ability to select a dispute resolution process on an opt-in basis and benefit the community by decreasing the necessity of consideration of token recovery actions. + + +## Implementation + +Pending. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1081.md b/EIPS/eip-1081.md new file mode 100644 index 0000000..f73b2c7 --- /dev/null +++ b/EIPS/eip-1081.md @@ -0,0 +1,121 @@ +--- +eip: 1081 +title: Standard Bounties +author: Mark Beylin , Kevin Owocki , Ricardo Guilherme Schmidt (@3esmit) +discussions-to: https://gitter.im/bounties-network/Lobby +status: Stagnant +type: Standards Track +category: ERC +created: 2018-05-14 +requires: 20 +--- + +## Simple Summary +A standard contract and interface for issuing bounties on Ethereum, usable for any type of task, paying in any ERC20 token or in ETH. + +## Abstract +In order to encourage cross-platform interoperability of bounties on Ethereum, and for easier reputational tracking, StandardBounties can facilitate the administration of funds in exchange for deliverables corresponding to a completed task, in a publicly auditable and immutable fashion. + +## Motivation +In the absence of a standard for bounties on Ethereum, it would be difficult for platforms to collaborate and share the bounties which users create (thereby recreating the walled gardens which currently exist on Web2.0 task outsourcing platforms). A standardization of these interactions across task types also makes it far easier to track various reputational metrics (such as how frequently you pay for completed submissions, or how frequently your work gets accepted). + +## Specification +After studying bounties as they've existed for thousands of years (and after implementing and processing over 300 of them on main-net in beta), we've discovered that there are 3 core steps to every bounty: +- a bounty is **issued**: an `issuer` specifies the requirements for the task, describing the desired outcome, and how much they would be willing to pay for the completion of that task (denoted in one or several tokens). +- a bounty is **fulfilled**: a bounty `fulfiller` may see the bounty, complete the task, and produce a deliverable which is itself the desired outcome of the task, or simply a record that it was completed. Hashes of these deliverables should be stored immutably on-chain, to serve as proof after the fact. +- a fulfillment is **accepted**: a bounty `issuer` or `arbiter` may select one or more submissions to be accepted, thereby releasing payment to the bounty fulfiller(s), and transferring ownership over the given deliverable to the `issuer`. + +To implement these steps, a number of functions are needed: +- `initializeBounty(address _issuer, address _arbiter, string _data, uint _deadline)`: This is used when deploying a new StandardBounty contract, and is particularly useful when applying the proxy design pattern, whereby bounties cannot be initialized in their constructors. Here, the data string should represent an IPFS hash, corresponding to a JSON object which conforms to the schema (described below). +- `fulfillBounty(address[] _fulfillers, uint[] _numerators, uint _denomenator, string _data)`: This is called to submit a fulfillment, submitting a string representing an IPFS hash which contains the deliverable for the bounty. Initially fulfillments could only be submitted by one individual at a time, however users consistently told us they desired to be able to collaborate on fulfillments, thereby allowing the credit for submissions to be shared by several parties. The lines along which eventual payouts are split are determined by the fractions of the submission credited to each fulfiller (using the array of numerators and single denominator). Here, a bounty platform may also include themselves as a collaborator to collect a small fee for matching the bounty with fulfillers. +- `acceptFulfillment(uint _fulfillmentId, StandardToken[] _payoutTokens, uint[] _tokenAmounts)`: This is called by the `issuer` or the `arbiter` to pay out a given fulfillment, using an array of tokens, and an array of amounts of each token to be split among the contributors. This allows for the bounty payout amount to move as it needs to based on incoming contributions (which may be transferred directly to the contract address). It also allows for the easy splitting of a given bounty's balance among several fulfillments, if the need should arise. + - `drainBounty(StandardToken[] _payoutTokens)`: This may be called by the `issuer` to drain a bounty of it's funds, if the need should arise. +- `changeBounty(address _issuer, address _arbiter, string _data, uint _deadline)`: This may be called by the `issuer` to change the `issuer`, `arbiter`, `data`, and `deadline` fields of their bounty. +- `changeIssuer(address _issuer)`: This may be called by the `issuer` to change to a new `issuer` if need be +- `changeArbiter(address _arbiter)`: This may be called by the `issuer` to change to a new `arbiter` if need be +- `changeData(string _data)`: This may be called by the `issuer` to change just the `data` +- `changeDeadline(uint _deadline)`: This may be called by the `issuer` to change just the `deadline` + +Optional Functions: +- `acceptAndFulfill(address[] _fulfillers, uint[] _numerators, uint _denomenator, string _data, StandardToken[] _payoutTokens, uint[] _tokenAmounts)`: During the course of the development of this standard, we discovered the desire for fulfillers to avoid paying gas fees on their own, entrusting the bounty's `issuer` to make the submission for them, and at the same time accept it. This is useful since it still immutably stores the exchange of tokens for completed work, but avoids the need for new bounty fulfillers to have any ETH to pay for gas costs in advance of their earnings. +- `changeMasterCopy(StandardBounty _masterCopy)`: For `issuer`s to be able to change the masterCopy which their proxy contract relies on, if the proxy design pattern is being employed. +- `refundableContribute(uint[] _amounts, StandardToken[] _tokens)`: While non-refundable contributions may be sent to a bounty simply by transferring those tokens to the address where it resides, one may also desire to contribute to a bounty with the option to refund their contribution, should the bounty never receive a correct submission which is paid out. +`refundContribution(uint _contributionId)`: If a bounty hasn't yet paid out to any correct submissions and is past it's deadline, those individuals who employed the `refundableContribute` function may retrieve their funds from the contract. + +**Schemas** +Persona Schema: +``` +{ + name: // optional - A string representing the name of the persona + email: // optional - A string representing the preferred contact email of the persona + githubUsername: // optional - A string representing the github username of the persona + address: // required - A string web3 address of the persona +} +``` +Bounty issuance `data` Schema: +``` +{ + payload: { + title: // A string representing the title of the bounty + description: // A string representing the description of the bounty, including all requirements + issuer: { + // persona for the issuer of the bounty + }, + funders:[ + // array of personas of those who funded the issue. + ], + categories: // an array of strings, representing the categories of tasks which are being requested + tags: // an array of tags, representing various attributes of the bounty + created: // the timestamp in seconds when the bounty was created + tokenSymbol: // the symbol for the token which the bounty pays out + tokenAddress: // the address for the token which the bounty pays out (0x0 if ETH) + + // ------- add optional fields here ------- + sourceFileName: // A string representing the name of the file + sourceFileHash: // The IPFS hash of the file associated with the bounty + sourceDirectoryHash: // The IPFS hash of the directory which can be used to access the file + webReferenceURL: // The link to a relevant web reference (ie github issue) + }, + meta: { + platform: // a string representing the original posting platform (ie 'gitcoin') + schemaVersion: // a string representing the version number (ie '0.1') + schemaName: // a string representing the name of the schema (ie 'standardSchema' or 'gitcoinSchema') + } +} +``` +Bounty `fulfillment` data Schema: + +``` +{ + payload: { + description: // A string representing the description of the fulfillment, and any necessary links to works + sourceFileName: // A string representing the name of the file being submitted + sourceFileHash: // A string representing the IPFS hash of the file being submitted + sourceDirectoryHash: // A string representing the IPFS hash of the directory which holds the file being submitted + fulfillers: { + // personas for the individuals whose work is being submitted + } + + // ------- add optional fields here ------- + }, + meta: { + platform: // a string representing the original posting platform (ie 'gitcoin') + schemaVersion: // a string representing the version number (ie '0.1') + schemaName: // a string representing the name of the schema (ie 'standardSchema' or 'gitcoinSchema') + } +} +``` +## Rationale +The development of this standard began a year ago, with the goal of encouraging interoperability among bounty implementations on Ethereum. The initial version had significantly more restrictions: a bounty's `data` could not be changed after issuance (it seemed unfair for bounty `issuer`s to change the requirements after work is underway), and the bounty payout could not be changed (all funds needed to be deposited in the bounty contract before it could accept submissions). + +The initial version was also far less extensible, and only allowed for fixed payments to a given set of fulfillments. This new version makes it possible for funds to be split among several correct submissions, for submissions to be shared among several contributors, and for payouts to not only be in a single token as before, but in as many tokens as the `issuer` of the bounty desires. These design decisions were made after the 8+ months which Gitcoin, the Bounties Network, and Status Open Bounty have been live and meaningfully facilitating bounties for repositories in the Web3.0 ecosystem. + +## Test Cases +Tests for our implementation can be found here: https://github.com/Bounties-Network/StandardBounties/tree/develop/test + +## Implementation +A reference implementation can be found here: https://github.com/Bounties-Network/StandardBounties/blob/develop/contracts/StandardBounty.sol +**Although this code has been tested, it has not yet been audited or bug-bountied, so we cannot make any assertions about it's correctness, nor can we presently encourage it's use to hold funds on the Ethereum mainnet.** + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1087.md b/EIPS/eip-1087.md new file mode 100644 index 0000000..5df4223 --- /dev/null +++ b/EIPS/eip-1087.md @@ -0,0 +1,81 @@ +--- +eip: 1087 +title: Net gas metering for SSTORE operations +author: Nick Johnson (@arachnid) +discussions-to: https://ethereum-magicians.org/t/eip-net-storage-gas-metering-for-the-evm/383 +status: Stagnant +type: Standards Track +category: Core +created: 2018-05-17 +--- + +## Abstract +This EIP proposes a change to how gas is charged for EVM `SSTORE` operations, in order to reduce excessive gas costs in situations where these are unwarranted, and to enable new use-cases for contract storage. + +## Motivation +Presently, `SSTORE` (`0x55`) operations are charged as follows: + + - 20,000 gas to set a slot from 0 to non-0 + - 5,000 gas for any other change + - A 10,000 gas refund when a slot is set from non-0 to 0. Refunds are applied at the end of the transaction. + +In situations where a single update is made to a storage value in a transaction, these gas costs have been determined to fairly reflect the resources consumed by the operation. However, this results in excessive gas costs for sequences of operations that make multiple updates. + +Some examples to illustrate the problem: + + - If a contract with empty storage sets slot 0 to 1, then back to 0, it will be charged `20000 + 5000 - 10000 = 15000` gas, despite this sequence of operations not requiring any disk writes. + - A contract with empty storage that increments slot 0 5 times will be charged `20000 + 5 * 5000 = 45000` gas, despite this sequence of operations requiring no more disk activity than a single write, charged at 20000 gas. + - A balance transfer from account A to account B followed by a transfer from B to C, with all accounts having nonzero starting and ending balances, will cost `5000 * 4 = 20000` gas. + +Addressing this issue would also enable new use-cases that are currently cost-prohibitive, where a sequence of operations results in no net change to storage at the end of the transaction. For instance, mutexes to prevent reentrancy, or context information passed between multiple calls to the same contract. One such example is an `approveAndCall` operation, which would permit sending to and calling a contract in a single transaction, without that contract having to be updated for a new token standard. + +## Specification +The following changes are made to the EVM: + + - A 'dirty map' for each transaction is maintained, tracking all storage slots in all contracts that have been modified in the current transaction. The dirty map is scoped in the same manner as updates to storage, meaning that changes to the dirty map in a call that later reverts are not retained. + - When a storage slot is written to with the value it already contains, 200 gas is deducted. + - When a storage slot's value is changed for the first time, the slot is marked as dirty. If the slot was previously set to 0, 20000 gas is deducted; otherwise, 5000 gas is deducted. + - When a storage slot that is already in the dirty map is written to, 200 gas is deducted. + - At the end of the transaction, for each slot in the dirty map: + - If the slot was 0 before the transaction and is 0 now, refund 19800 gas. + - If the slot was nonzero before the transaction and its value has not changed, refund 4800 gas. + - If the slot was nonzero before the transaction and is 0 now, refund 15000 gas. + +After these changes, transactions that make only a single change to a storage slot will retain their existing costs. However, contracts that make multiple changes will see significantly reduced costs. Repeating the examples from the Motivation section: + + - If a contract with empty storage sets slot 0 to 1, then back to 0, it will be charged `20000 + 200 - 19800 = 400` gas, down from 15000. + - A contract with empty storage that increments slot 0 5 times will be charged `20000 + 5 * 200 = 21000` gas, down from 45000. + - A balance transfer from account A to account B followed by a transfer from B to C, with all accounts having nonzero starting and ending balances, will cost `5000 * 3 + 200 - 4800 = 10400` gas, down from 20000. + +## Rationale +We believe the proposed mechanism represents the simplest way to reduce storage gas costs in situations where they do not reflect the actual costs borne by nodes. Several alternative designs were considered and dismissed: + + - Charging a flat 200 gas for `SSTORE` operations, and an additional 19800 / 4800 at the end of a transaction for new or modified values is simpler, and removes the need for a dirty map, but pushes a significant source of gas consumption out of the EVM stack and applies it at the end of the transaction, which is likely to complicate debugging and reduces contracts' ability to limit the gas consumption of callees, as well as introducing a new mechanism to the EVM. + - Keeping a separate refund counter for storage gas refunds would avoid the issue of refunds being limited to half the gas consumed (not necessary here), but would introduce additional complexity in tracking this value. + - Refunding gas each time a storage slot is set back to its initial value would introduce a new mechanism (instant refunds) and complicate gas accounting for contracts calling other contracts; it would also permit the possibility of a contract call with negative execution cost. + +## Backwards Compatibility +This EIP requires a hard fork to implement. + +No contract should see an increase in gas cost for this change, and many will see decreased gas consumption, so no contract-layer backwards compatibility issues are anticipated. + +## Test Cases + + - Writing x to a storage slot that contains 0, where x != 0 (20k gas, no refund) + - Writing y to a storage slot that contained x, where x != y and x != 0 (5k gas, no refund) + - Writing 0 to a storage slot that contains x, where x != 0 (5k gas, 10k refund) + - Writing 0 to a storage slot that already contains zero (200 gas, no refund) + - Writing x to a storage slot that already contains x, where x != 0 (200 gas, no refund) + - Writing x, then y to a storage slot that contains 0, where x != y (20200 gas, no refund) + - Writing x, then y to a storage slot that contains 0, where x != y != z and x != 0 (5200 gas, no refund) + - Writing x, then 0 to a storage slot that contains 0, where x != 0 (20200 gas, 19800 refund) + - Writing x, then y to a storage slot that contains y, where x != y != 0 (5200 gas, 4800 refund) + - Writing x, then 0 to a storage slot that contains 0, then reverting the stack frame in which the writes occurred (20200 gas, no refund) + - Writing x, then y to a storage slot that contains y, then reverting the stack frame in which the writes occurred (5200 gas, no refund) + - In a nested frame, writing x to a storage slot that contains 0, then returning, and writing 0 to that slot (20200 gas, 19800 refund) + +## Implementation +TBD + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1102.md b/EIPS/eip-1102.md new file mode 100644 index 0000000..6e7e743 --- /dev/null +++ b/EIPS/eip-1102.md @@ -0,0 +1,138 @@ +--- +eip: 1102 +title: Opt-in account exposure +author: Paul Bouchon , Erik Marks (@rekmarks) +discussions-to: https://ethereum-magicians.org/t/eip-1102-opt-in-provider-access/414 +status: Stagnant +type: Standards Track +category: Interface +created: 2018-05-04 +requires: 1474 +--- + +## Simple summary + +This proposal describes a communication protocol between dapps and Ethereum-enabled DOM environments that allows the Ethereum-enabled DOM environment to choose what information to supply the dapp with and when. + +## Abstract + +The previous generation of Ethereum-enabled DOM environments follows a pattern of injecting a provider populated with accounts without user consent. This puts users of such environments at risk because malicious websites can use these accounts to view detailed account information and to arbitrarily initiate unwanted transactions on a user's behalf. + +This proposal outlines a protocol in which Ethereum-enabled DOM environments can choose to expose no accounts until the user approves account access. + +## Specification + +### Concepts + +#### RFC-2119 + +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). + +#### `eth_requestAccounts` + +Providers exposed by Ethereum-enabled DOM environments define a new RPC method: `eth_requestAccounts`. Calling this method may trigger a user interface that allows the user to approve or reject account access for a given dapp. This method returns a `Promise` that is resolved with an `Array` of accounts or is rejected with an `Error` if accounts are not available. + +```typescript +ethereum.send('eth_requestAccounts'): Promise> +``` + +#### Provider#enable (DEPRECATED) + +**Note: This method is deprecated in favor of the RPC method [`eth_requestAccounts`](#eth_requestaccounts).** + +Providers exposed by Ethereum-enabled DOM environments define a new RPC method: `ethereum.enable()`. Calling this method triggers a user interface that allows the user to approve or reject account access for a given dapp. This method returns a `Promise` that is resolved with an `Array` of accounts if the user approves access or rejected with an `Error` if the user rejects access. + +```typescript +ethereum.enable(): Promise +``` + +### Protocol + +#### Legacy dapp initialization + +``` +START dapp +IF web3 is defined + CONTINUE dapp +IF web3 is undefined + STOP dapp +``` + +#### Proposed dapp initialization + +``` +START dapp +IF provider is defined + REQUEST[1] account access + IF user approves + RESOLVE[2] account access + CONTINUE dapp + IF user rejects + REJECT[3] account access + STOP dapp +IF provider is undefined + STOP dapp +``` + +##### `[1] REQUEST` + +Dapps **MUST** request accounts by calling the `eth_requestAccounts` RPC method on the provider exposed at `window.ethereum`. Calling this method **MAY** trigger a user interface that allows the user to approve or reject account access for a given dapp. This method **MUST** return a `Promise` that is resolved with an array of one or more user accounts or rejected if no accounts are available (e.g., the user rejected account access). + +##### `[2] RESOLVE` + +The `Promise` returned when calling the `eth_requestAccounts` RPC method **MUST** be resolved with an `Array` of user accounts. + +##### `[3] REJECT` + +The `Promise` returned when calling the `eth_requestAccounts` RPC method **MUST** be rejected with an informative `Error` if no accounts are available for any reason. + +### Example initialization + +```js +try { + // Request account access if needed + const accounts = await ethereum.send('eth_requestAccounts'); + // Accounts now exposed, use them + ethereum.send('eth_sendTransaction', { from: accounts[0], /* ... */ }) +} catch (error) { + // User denied account access +} +``` + +### Constraints + +* Browsers **MUST** expose a provider at `window.ethereum` . +* Browsers **MUST** define an `eth_requestAccounts` RPC method. +* Browsers **MAY** wait for a user interaction before resolving/rejecting the `eth_requestAccounts` promise. +* Browsers **MUST** include at least one account if the `eth_requestAccounts` promise is resolved. +* Browsers **MUST** reject the promise with an informative error if no accounts are available. + +## Rationale + +The pattern of automatic account exposure followed by the previous generation of Ethereum-enabled DOM environments fails to protect user privacy and fails to maintain safe user experience: untrusted websites can both view detailed account information and arbitrarily initiate transactions on a user's behalf. Even though most users may reject unsolicited transactions on untrusted websites, a protocol for account access should make such unsolicited requests impossible. + +This proposal establishes a new pattern wherein dapps must request access to user accounts. This protocol directly strengthens user privacy by allowing the browser to hide user accounts and preventing unsolicited transaction requests on untrusted sites. + +### Immediate value-add + +* Users can reject account access on untrusted sites to hide accounts. +* Users can reject account access on untrusted sites to prevent unsolicited transactions. + +### Long-term value-add + +* Dapps could request specific account information based on user consent. +* Dapps could request specific user information based on user consent (uPort, DIDs). +* Dapps could request a specific network based on user consent. +* Dapps could request multiple instances of the above based on user consent. + +## Backwards compatibility + +This proposal impacts dapp developers and requires that they request access to user accounts following the protocol outlined above. Similarly, this proposal impacts dapp browser developers and requires that they only expose user accounts following the protocol defined above. + +## Implementation + +The MetaMask team [has implemented](https://github.com/MetaMask/metamask-extension/pull/4703) the strategy described above. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1108.md b/EIPS/eip-1108.md new file mode 100644 index 0000000..85bc480 --- /dev/null +++ b/EIPS/eip-1108.md @@ -0,0 +1,139 @@ +--- +eip: 1108 +title: Reduce alt_bn128 precompile gas costs +author: Antonio Salazar Cardozo (@shadowfiend), Zachary Williamson (@zac-williamson) +discussions-to: https://ethereum-magicians.org/t/eip-1108-reduce-alt-bn128-precompile-gas-costs/3206 +status: Final +type: Standards Track +category: Core +created: 2018-05-21 +requires: 196, 197 +--- + +## Simple Summary + +The elliptic curve arithmetic precompiles are currently overpriced. Re-pricing the precompiles would greatly assist a number of privacy solutions and scaling solutions on Ethereum. + +## Abstract + +Changes in 2018 to the underlying library used by the official Go reference +implementation led to significant performance gains for the `ECADD`, `ECMUL`, +and pairing check precompiled contracts on the `alt_bn128` elliptic curve. + +In the Parity client, field operations used by the precompile algorithms were optimized in 2018, +and recent changes to the pairing algorithm used by the `bn` crate have brought considerable speedups. + +Faster operations on Ethereum clients should be reflected in reduced gas costs. + +## Motivation + +Recently, the underlying library used by the [official Go reference +implementation](https://github.com/ethereum/go-ethereum) to implement the +`ECADD` (at address `0x06`), `ECMUL` (at address `0x07`), and pairing check (at +address `0x08`) precompiled contracts was shifted to [Cloudflare's bn256 +library](https://github.com/cloudflare/bn256). Based on the [initial PR that +introduced this change](https://github.com/ethereum/go-ethereum/pull/16203), +and corroborated in [a later +note](https://github.com/ethereum/go-ethereum/pull/16301#issuecomment-372687543), +the computational cost of `ECADD`, `ECMUL`, and pairing checks (excepting the +constant) has dropped roughly an order of magnitude across the board. + +Also, optimizations in the bn library [in 2018](https://github.com/paritytech/bn/pull/9) and [2019](https://github.com/paritytech/bn/pull/14) +used by the [Parity client](https://github.com/paritytech/parity-ethereum) led to a +significant performance boost we +[benchmarked](https://gist.github.com/zac-williamson/838410a3da179d47d31b25b586c15e53) +and compared against the [previous +results](https://gist.github.com/pdyraga/4649b74436940a01e8221d85e80bfeef). + + +## Specification + +Following is a table with the current gas cost and new gas cost: + +| Contract | Address | Current Gas Cost | Updated Gas Cost | +| ------------- | --------- | ----------------------------- | ------------------- | +| `ECADD` | `0x06` | 500[1] | 150 | +| `ECMUL` | `0x07` | 40 000[1] | 6 000 | +| Pairing check | `0x08` | 80 000 * k + 100 000[2]| 34 000 * k + 45 000 | + +The gas costs for `ECADD` and `ECMUL` are updates to the costs listed in +EIP-196, while the gas costs for the pairing check are updates to the cost +listed in EIP-197. Updated gas costs have been adjusted to the less performant +client which is Parity, according to benchmarks[3]. + +To come up with these updates gas costs, the performance of the `ecrecover` precompile +was measured at 116 microseconds per `ecrecover` invocation. Assuming the `ecrecover` +gas price is fair at 3,000 gas, we get a price of 25.86 gas per microsecond of a precompile +algorithm's runtime. With this in mind, the pairing precompile took 3,037 microseconds to +compute 1 pairing, and 14,663 microseconds to compute 10 pairings. From this, the pairing +algorithm has a fixed 'base' run-time of 1,745 microseconds, plus 1,292 microseconds per +pairing. We can split the run-time into 'fixed cost' and 'linear cost per pairing' +components because of the structure of the algorithm. + +Thus using a 'fair' price of 25.86 gas per microsecond, we get a gas formula of +~`35,000 * k + 45,000` gas, where `k` is the number of pairings being computed. [4] + +[1]- Per [EIP-196](./eip-196.md). + +[2]- Per [EIP-197](./eip-197.md). + +[3]- [Parity benchmarks.](https://gist.github.com/zac-williamson/838410a3da179d47d31b25b586c15e53) + +[4]- [PR comment clarifying gas cost math](https://github.com/ethereum/EIPs/pull/1987#discussion_r280977066). + +## Rationale + +### Existing protocols would benefit immensely from cheaper elliptic curve cryptography + +Fast elliptic curve cryptography is a keystone of a growing number of protocols built on top of Ethereum. To list a few: + +* [The AZTEC protocol](https://github.com/AztecProtocol/AZTEC) utilizes the elliptic curve precompiles to construct private tokens, with zero-knowledge transaction logic, via the [ERC-1723](https://github.com/ethereum/EIPs/issues/1723) and [ERC-1724](https://github.com/ethereum/EIPs/issues/1724) standard. +* [Matter Labs](https://github.com/matter-labs/matter-network) utilizes the precompiles to implement Ignis, a scaling solution with a throughput of 500txns per second +* [Rollup](https://github.com/rollup/rollup) utilizes the precompiles to create L2 scaling solutions, where the correctness of transactions is guaranteed by main-net, without an additional consensus layer +* [ZEther](https://crypto.stanford.edu/~buenz/papers/zether.pdf) uses precompiles `ECADD` and `ECMUL` to construct confidential transactions + +These are all technologies that have been, or are in the process of being, deployed to main-net. There protocols would all benefit from reducing the gas cost of the precompiles. + +To give a concrete example, it currently costs `820,000` gas to validate the cryptography in a typical AZTEC confidential transaction. If the gas schedule for the precompiles correctly reflected their load on the Ethereum network, this cost would be `197,000` gas. This significantly increases the potential use cases for private assets on Ethereum. AZTEC is planning to deploy several cryptographic protocols Ethereum, but these are at the limits of what is practical given the current precompile costs: + +* Confidential weighted voting +* Partial-order filling over encrypted orders, for private decentralized exchanges +* Anonymous identity sharing proofs (e.g. proving you are on a whitelist, without revealing who you are) +* Many-to-one payments and one-to-many confidential payments, as encrypted communication channels between main-net and L2 applications + +For zk-SNARK based protocols on Ethereum, EIP-1108 will not only reduce the gas costs of verifying zk-SNARKs substantially, but can also aid in [batching together multiple zk-SNARK proofs](https://github.com/matter-labs/Groth16BatchVerifier). This is also a technique that can be used to split up monolithic zk-SNARK circuits into a batch of zk-SNARKs with smaller individual circuit sizes, which makes zk-SNARKs both easier to construct and deploy. + +ZEther transactions currently cost ~`6,000,000` gas. This EIP would reduce this to ~`1,000,000` gas, which makes the protocol more practical. + +To summarise, there are several protocols that currently exist on main-net, that would benefit immensely from this EIP. Elliptic curve cryptography can provide valuable solutions for Ethereum, such as scaling and privacy, and the scope and scale of these solutions can be increased if the gas costs for the `bn128` precompiles accurately reflects their computational load on the network. + +### Cheaper elliptic curve cryptography can be used to trade storage for computation + +Solutions such as Rollup and Ignis can be used to batch groups of individual transactions into a zk-SNARK proof, with the on-chain state being represented by a small Merkle root, instead of multiple account balances. + +If zk-SNARK verification costs are decreased, these solutions can be deployed for a wider range of use cases and more Rollup-style transactions can be processed per block. + +### Parity and Geth already have fast algorithms that justify reduced gas costs + +This EIP does not require Parity or Geth to deploy new cryptographic libraries, as fast bn128 algorithms have already been integrated into these clients. This goal of proposing this EIP for Istanbul, is to supplement [EIP-1829](./eip-1829.md) (arithmetic over generic elliptic curves), providing an immediate solution to the pressing problem of expensive cryptography, while more advanced solutions are developed, defined and deployed. + + +## Test Cases + +As no underlying algorithms are being changed, there are no additional test cases to specify. + +## Implementation + +Both the Parity and Geth clients have already implemented cryptographic libraries that are fast enough to justify reducing the precompile gas costs. As a reference, here are a list of elliptic curve libraries, in `C++`, `golang` and `rust`, that support the `bn128` curve, and have run-times that are equal to or faster than the Parity benchmarks. + +* [Parity bn crate (rust)](https://github.com/paritytech/bn) +* [Geth bn256 library (golang)](https://github.com/ethereum/go-ethereum/tree/master/crypto/bn256/cloudflare) +* [MCL, a portable C++ pairing library](https://github.com/herumi/mcl) +* [Libff, a C++ pairing library used in many zk-SNARK libraries](https://github.com/scipr-lab/libff) + +## Additional References + +@vbuterin independently proposed a similar reduction after this EIP was originally created, with similar rationale, as [ethereum/EIPs#1187](https://github.com/ethereum/EIPs/issues/1187). + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1109.md b/EIPS/eip-1109.md new file mode 100644 index 0000000..f9df787 --- /dev/null +++ b/EIPS/eip-1109.md @@ -0,0 +1,87 @@ +--- +eip: 1109 +title: PRECOMPILEDCALL opcode (Remove CALL costs for precompiled contracts) +author: Jordi Baylina (@jbaylina) +discussions-to: https://ethereum-magicians.org/t/eip-1109-remove-call-costs-for-precompiled-contracts/447 +status: Stagnant +type: Standards Track +category: Core +created: 2018-05-22 +--- + +## Simple Summary + +This EIP creates a specific opcode named `PRECOMPILEDCALL` to call Precompiled contracts without the costs of a normal `CALL`. + +## Abstract + +This EIP tries to resolve the problem of high gas consumption when calling precompiled contracts with a small gas cost. Using this opcode for calling precompiled contracts allows to define precompiled contracts whose effective cost it is less than 700. + +## Motivation + +Each precompiled contract has an already defined cost for calling it. It does not make sense to add the implicit extra gas cost of the CALL opcode. + +As an example, SHA256 precompiled contract costs 60 and ECADD costs 500 (proposed to costs only 50 in [EIP-1108](./eip-1108.md) . When a precompiled contract is called, 700 gas is consumed just for the CALL opcode besides the costs of the precompiled contract. + +This makes no sense, and right now it's impossible to define a precompiled contract whose effective cost for using it, is less than 700. + +## Specification + +If `block.number >= XXXXX`, define a new opcode named `PRECOMPILEDCALL` with code value `0xfb`. + +The gas cost of the OPCODE is 2 (Gbase) plus the Specific gas cost defined for each specific precompiled smart contract. + +The OPCODE takes 5 words from the stack and returns 1 word to the stack. + +The input stack values are: + +mu_s[0] = The address of the precompiled smart contract that is called. +mu_s[1] = Pointer to memory for the input parameters. +mu_s[2] = Length of the input parameters in bytes. +mu_s[3] = Pointer to memory where the output is stored +mu_s[4] = Length of the output buffer. + + +The return will be 1 in case of success call and 0 in any of the next cases: + +1.- mu_s[0] is an address of an undefined precompiled smart contract. +2.- The precompiled smart contract fails (as defined on each smart contract). Invalid input parameters for example. + +Precompiled smart contracs, does not execute opcodes, so there is no need to pass a gas parameter as a normal `CALL` (`0xf1`). If the available gas is less that 2 plus the required gas required for the specific precompiled smart cotract, the context just STOPS executing with an "Out of Gas" error. + +There is no stack check for this call. + +The normal `CALL`s to the precompiled smart contracts continue to work with the exact same behavior. + +A `PRECOMPILEDCALL` to a regular address or regular smart contract, is considered a call to an "undefined smart contract", so the VM MUST not execute it and the opcode must return 0x0 . + + +## Rationale + +There was a first proposal for removing the gast consts for the `CALL`, but it looks that it's easier to implement and test a new opcode just for that. + +The code is just the next opcode available after the `STATICCALL` opcode. + +## Backwards Compatibility + +This EIP is backwards compatible. Smart contracts that call precompiled contracts using this new opcode will cost less from now on. + +Old contracts that call precompiled smart contracts with the `CALL` method, will continue working. + +## Test Cases + +- Normal call to a defined precompiled contract. +- Call to undefined precompiled contract. +- Call to a regular contract +- Call to a regular account +- Call to 0x0 smart contract (Does not exists). +- Call with large values for the offste pointers and lengths +- Call with the exact gas remaining needed to call smart contract. +- Call with the exact gas remaining minus one needed to call smart contract. + +## Implementation + +Not implemented yet. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1123.md b/EIPS/eip-1123.md new file mode 100644 index 0000000..06ca527 --- /dev/null +++ b/EIPS/eip-1123.md @@ -0,0 +1,1881 @@ +--- +eip: 1123 +title: Revised Ethereum Smart Contract Packaging Standard +author: g. nicholas d’andrea (@gnidan), Piper Merriam (@pipermerriam), Nick Gheorghita (@njgheorghita), Danny Ryan (@djrtwo) +discussions-to: https://github.com/ethereum/EIPs/issues/1123 +status: Withdrawn +type: Standards Track +category: ERC +created: 2018-06-01 +--- + +This ERC has been abandoned in favor of the EthPM V3 smart contract packaging standard defined in [ERC-2678](./eip-2678.md) + +Simple Summary +============== + +A data format describing a smart contract software package. + + +Abstract +========== + +This EIP defines a data format for *package manifest* documents, +representing a package of one or more smart contracts, optionally +including source code and any/all deployed instances across multiple +networks. Package manifests are minified JSON objects, to be distributed +via content addressable storage networks, such as IPFS. + +This document presents a natural language description of a formal +specification for version **2** of this format. + + +Motivation +========== + +This standard aims to encourage the Ethereum development ecosystem +towards software best practices around code reuse. By defining an open, +community-driven package data format standard, this effort seeks to +provide support for package management tools development by offering a +general-purpose solution that has been designed with observed common +practices in mind. + +As version 2 of this specification, this standard seeks to address a +number of areas of improvement found for the previous version (defined +in +[EIP-190](./eip-190.md)). +This version: + +- Generalizes storage URIs to represent any content addressable URI + scheme, not only IPFS. + +- Renames *release lockfile* to *package manifest*. + +- Adds support for languages other than Solidity by generalizing the + compiler information format. + +- Redefines link references to be more flexible, to represent + arbitrary gaps in bytecode (besides only addresses), in a more + straightforward way. + +- Forces format strictness, requiring that package manifests contain + no extraneous whitespace, and sort object keys in alphabetical + order, to prevent hash mismatches. + + +
+ +Specification +============= + +This document defines the specification for an EthPM package manifest. A +package manifest provides metadata about a [Package](#term-package), and +in most cases should provide sufficient information about the packaged +contracts and its dependencies to do bytecode verification of its +contracts. + +> **Note** +> +> A [hosted +> version](https://ethpm.github.io/ethpm-spec) of this +> specification is available via GitHub Pages. This EIP and the hosted +> HTML document were both autogenerated from the same documentation +> source. + + +Guiding Principles +------------------ + +This specification makes the following assumptions about the document +lifecycle. + +1. Package manifests are intended to be generated programmatically by + package management software as part of the release process. + +2. Package manifests will be consumed by package managers during tasks + like installing package dependencies or building and deploying new + releases. + +3. Package manifests will typically **not** be stored alongside the + source, but rather by package registries *or* referenced by package + registries and stored in something akin to IPFS. + + +Conventions +----------- + + +### RFC2119 + +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. + +- + + +### Prefixed vs Unprefixed + +A [prefixed](#term-prefixed) hexadecimal value begins with `0x`. +[Unprefixed](#term-unprefixed) values have no prefix. Unless otherwise +specified, all hexadecimal values **should** be represented with the +`0x` prefix. + + ++++ + + + + + + + + + + +

Prefixed

0xdeadbeef

Unprefixed

deadbeef

+ + +Document Format +--------------- + +The canonical format is a single JSON object. Packages **must** conform +to the following serialization rules. + +- The document **must** be tightly packed, meaning no linebreaks or + extra whitespace. + +- The keys in all objects must be sorted alphabetically. + +- Duplicate keys in the same object are invalid. + +- The document **must** use + [UTF-8](https://en.wikipedia.org/wiki/UTF-8) + encoding. + +- The document **must** not have a trailing newline. + + +Document Specification +---------------------- + +The following fields are defined for the package. Custom fields **may** +be included. Custom fields **should** be prefixed with `x-` to prevent +name collisions with future versions of the specification. + + ++++ + + + + + + + + + + +

See Also

Formalized (JSON-Schema) version of this specification: package.spec.json

Jump To

Definitions

+ + +
+ +### EthPM Manifest Version: `manifest_version` + +The `manifest_version` field defines the specification version that this +document conforms to. Packages **must** include this field. + + ++++ + + + + + + + + + + + + + + + + + + +

Required

Yes

Key

manifest_version

Type

String

Allowed Values

2

+ + +
+ +### Package Name: `package_name` + +The `package_name` field defines a human readable name for this package. +Packages **must** include this field. Package names **must** begin with +a lowercase letter and be comprised of only lowercase letters, numeric +characters, and the dash character `-`. Package names **must** not +exceed 214 characters in length. + + ++++ + + + + + + + + + + + + + + + + + + +

Required

Yes

Key

package_name

Type

String

Format

must match the regular expression ^[a-zA-Z][a-zA-Z0-9_]{0,255}$

+ + +### Package Meta: `meta` + +The `meta` field defines a location for metadata about the package which +is not integral in nature for package installation, but may be important +or convenient to have on-hand for other reasons. This field **should** +be included in all Packages. + + ++++ + + + + + + + + + + + + + + +

Required

No

Key

meta

Type

Package Meta Object

+ + +### Version: `version` + +The `version` field declares the version number of this release. This +value **must** be included in all Packages. This value **should** +conform to the [semver](https://semver.org/) version +numbering specification. + + ++++ + + + + + + + + + + + + + + +

Required

Yes

Key

version

Type

String

+ + +### Sources: `sources` + +The `sources` field defines a source tree that **should** comprise the +full source tree necessary to recompile the contracts contained in this +release. Sources are declared in a key/value mapping. + + ++++ + + + + + + + + + + + + + + +

Key

sources

Type

Object (String: String)

Format

See Below.

+ + +#### Format + +Keys **must** be relative filesystem paths beginning with a `./`. + +Paths **must** resolve to a path that is within the current working +directory. + +Values **must** conform to *one of* the following formats. + +- Source string. + +- [Content Addressable URI](#term-content-addressable-uri). + +When the value is a source string the key should be interpreted as a +file path. + +- If the resulting document is a directory the key should be + interpreted as a directory path. + +- If the resulting document is a file the key should be interpreted as + a file path. + + +### Contract Types: `contract_types` + +The `contract_types` field holds the [Contract +Types](#term-contract-type) which have been included in this release. +[Packages](#term-package) **should** only include contract types that +can be found in the source files for this package. Packages **should +not** include contract types from dependencies. Packages **should not** +include abstract contracts in the contract types section of a release. + + ++++ + + + + + + + + + + + + + + +

Key

contract_types

Type

Object (String: Contract Type Object)

Format

Keys must be valid Contract Aliases.

+

Values must conform to the Contract Type Object definition.

+ + +### Deployments: `deployments` + +The `deployments` field holds the information for the chains on which +this release has [Contract Instances](#term-contract-instance) as well +as the [Contract Types](#term-contract-type) and other deployment +details for those deployed contract instances. The set of chains defined +by the `*BIP122 URI <#bip122-uris>*` keys for this object **must** be +unique. + + ++++ + + + + + + + + + + + + + + +

Key

deployments

Type

Object (String: Object(String: Contract Instance Object))

Format

See Below.

+ + +#### Format + +Keys **must** be a valid BIP122 URI chain definition. + +Values **must** be objects which conform to the following format. + +- Keys **must** be valid [Contract Instance + Names](#term-contract-instance-name). + +- Values **must** be a valid [Contract Instance + Object](#contract-instance-object). + + +### Build Dependencies: `build_dependencies` + +The `build_dependencies` field defines a key/value mapping of Ethereum +[Packages](#term-package) that this project depends on. + + ++++ + + + + + + + + + + + + + + + + + + +

Required

No

Key

build_dependencies

Type

Object (String: String)

Format

Keys must be valid package names matching the regular expression [a-z][-a-z0-9]{0,213}.

+

Values must be valid IPFS URIs which resolve to a valid package.

+ + +Definitions +----------- + +Definitions for different objects used within the Package. All objects +allow custom fields to be included. Custom fields **should** be prefixed +with `x-` to prevent name collisions with future versions of the +specification. + + + + +### The *Link Reference* Object + +A [Link Reference](#term-link-reference) object has the following +key/value pairs. All link references are assumed to be associated with +some corresponding [Bytecode](#term-bytecode). + + +#### Offsets: `offsets` + +The `offsets` field is an array of integers, corresponding to each of +the start positions where the link reference appears in the bytecode. +Locations are 0-indexed from the beginning of the bytes representation +of the corresponding bytecode. This field is invalid if it references a +position that is beyond the end of the bytecode. + + ++++ + + + + + + + + + + +

Required

Yes

Type

Array

+ + +#### Length: `length` + +The `length` field is an integer which defines the length in bytes of +the link reference. This field is invalid if the end of the defined link +reference exceeds the end of the bytecode. + + ++++ + + + + + + + + + + +

Required

Yes

Type

Integer

+ + +#### Name: `name` + +The `name` field is a string which **must** be a valid +[Identifier](#term-identifier). Any link references which **should** be +linked with the same link value **should** be given the same name. + + ++++ + + + + + + + + + + + + + + +

Required

No

Type

String

Format

must conform to the Identifier format.

+ + + + +### The *Link Value* Object + +Describes a single [Link Value](#term-link-value). + +A **Link Value object** is defined to have the following key/value +pairs. + + +
+ +#### Offsets: `offsets` + +The `offsets` field defines the locations within the corresponding +bytecode where the `value` for this link value was written. These +locations are 0-indexed from the beginning of the bytes representation +of the corresponding bytecode. + + ++++ + + + + + + + + + + + + + + +

Required

Yes

Type

Integer

Format

See Below.

+ +**Format** + +Array of integers, where each integer **must** conform to all of the +following. + +- greater than or equal to zero + +- strictly less than the length of the unprefixed hexadecimal + representation of the corresponding bytecode. + + +#### Type: `type` + +The `type` field defines the `value` type for determining what is +encoded when [linking](#term-linking) the corresponding bytecode. + + ++++ + + + + + + + + + + + + + + +

Required

Yes

Type

String

Allowed Values

"literal" for bytecode literals

+

"reference" for named references to a particular Contract Instance

+ + +#### Value: `value` + +The `value` field defines the value which should be written when +[linking](#term-linking) the corresponding bytecode. + + ++++ + + + + + + + + + + + + + + +

Required

Yes

Type

String

Format

Determined based on type, see below.

+ +**Format** + +For static value *literals* (e.g. address), value **must** be a *byte +string* + +To reference the address of a [Contract +Instance](#term-contract-instance) from the current package the value +should be the name of that contract instance. + +- This value **must** be a valid contract instance name. + +- The chain definition under which the contract instance that this + link value belongs to must contain this value within its keys. + +- This value **may not** reference the same contract instance that + this link value belongs to. + +To reference a contract instance from a [Package](#term-package) from +somewhere within the dependency tree the value is constructed as +follows. + +- Let `[p1, p2, .. pn]` define a path down the dependency tree. + +- Each of `p1, p2, pn` **must** be valid package names. + +- `p1` **must** be present in keys of the `build_dependencies` for the + current package. + +- For every `pn` where `n > 1`, `pn` **must** be present in the keys + of the `build_dependencies` of the package for `pn-1`. + +- The value is represented by the string + `::<...>::` where all of ``, + ``, `` are valid package names and `` is + a valid [Contract Name](#term-contract-name). + +- The `` value **must** be a valid [Contract + Instance Name](#term-contract-instance-name). + +- Within the package of the dependency defined by ``, all of the + following must be satisfiable: + + - There **must** be *exactly* one chain defined under the + `deployments` key which matches the chain definition that this + link value is nested under. + + - The `` value **must** be present in the keys + of the matching chain. + + +### The *Bytecode* Object + +A bytecode object has the following key/value pairs. + + +#### Bytecode: `bytecode` + +The `bytecode` field is a string containing the `0x` prefixed +hexadecimal representation of the bytecode. + + ++++ + + + + + + + + + + + + + + +

Required

Yes

Type

String

Format

0x prefixed hexadecimal.

+ + +#### Link References: `link_references` + +The `link_references` field defines the locations in the corresponding +bytecode which require [linking](#term-linking). + + ++++ + + + + + + + + + + + + + + +

Required

No

Type

Array

Format

All values must be valid Link Reference objects. See also below.

+ +**Format** + +This field is considered invalid if *any* of the [Link +References](#term-link-reference) are invalid when applied to the +corresponding `bytecode` field, *or* if any of the link references +intersect. + +Intersection is defined as two link references which overlap. + + +#### Link Dependencies: `link_dependencies` + +The `link_dependencies` defines the [Link Values](#term-link-value) that +have been used to link the corresponding bytecode. + + ++++ + + + + + + + + + + + + + + +

Required

No

Type

Array

Format

All values must be valid Link Value objects. See also below.

+ +**Format** + +Validation of this field includes the following: + +- Two link value objects **must not** contain any of the same values + for `offsets`. + +- Each [link value object](#link-value-object) **must** have a + corresponding [link reference object](#link-reference-object) under + the `link_references` field. + +- The length of the resolved `value` **must** be equal to the `length` + of the corresponding [Link Reference](#term-link-reference). + + +
+ +### The *Package Meta* Object + +The *Package Meta* object is defined to have the following key/value +pairs. + + +#### Authors: `authors` + +The `authors` field defines a list of human readable names for the +authors of this package. Packages **may** include this field. + + ++++ + + + + + + + + + + + + + + +

Required

No

Key

authors

Type

Array (String)

+ + +#### License: `license` + +The `license` field declares the license under which this package is +released. This value **should** conform to the +[SPDX](https://en.wikipedia.org/wiki/Software_Package_Data_Exchange) +format. Packages **should** include this field. + + ++++ + + + + + + + + + + + + + + +

Required

No

Key

license

Type

String

+ + +#### Description: `description` + +The `description` field provides additional detail that may be relevant +for the package. Packages **may** include this field. + + ++++ + + + + + + + + + + + + + + +

Required

No

Key

description

Type

String

+ + +#### Keywords: `keywords` + +The `keywords` field provides relevant keywords related to this package. + + ++++ + + + + + + + + + + + + + + +

Required

No

Key

keywords

Type

List of Strings

+ + +#### Links: `links` + +The `links` field provides URIs to relevant resources associated with +this package. When possible, authors **should** use the following keys +for the following common resources. + +- `website`: Primary website for the package. + +- `documentation`: Package Documentation + +- `repository`: Location of the project source code. + + ++++ + + + + + + + + + + +

Key

links

Type

Object (String: String)

+ + +
+ +### The *Contract Type* Object + +A *Contract Type* object is defined to have the following key/value +pairs. + + +#### Contract Name: `contract_name` + +The `contract_name` field defines the [Contract +Name](#term-contract-name) for this [Contract +Type](#term-contract-type). + + ++++ + + + + + + + + + + + + + + +

Required

If the Contract Name and Contract Alias are not the same.

Type

String

Format

must be a valid Contract Name.

+ + +#### Deployment Bytecode: `deployment_bytecode` + +The `deployment_bytecode` field defines the bytecode for this [Contract +Type](#term-contract-type). + + ++++ + + + + + + + + + + + + + + +

Required

No

Type

Object

Format

must conform to the Bytecode Object format.

+ + +#### Runtime Bytecode: `runtime_bytecode` + +The `runtime_bytecode` field defines the unlinked `0x`-prefixed runtime +portion of [Bytecode](#term-bytecode) for this [Contract +Type](#term-contract-type). + + ++++ + + + + + + + + + + + + + + +

Required

No

Type

Object

Format

must conform to the Bytecode Object format.

+ + +#### ABI: `abi` + + ++++ + + + + + + + + + + + + + + +

Required

No

Type

List

Format

must conform to the Ethereum Contract ABI JSON format.

+ + +#### Natspec: `natspec` + + ++++ + + + + + + + + + + + + + + +

Required

No

Type

Object

Format

The union of the UserDoc and DevDoc formats.

+ + +#### Compiler: `compiler` + + ++++ + + + + + + + + + + + + + + +

Required

No

Type

Object

Format

must conform to the Compiler Information object format.

+ + +
+ +### The *Contract Instance* Object + +A **Contract Instance Object** represents a single deployed [Contract +Instance](#term-contract-instance) and is defined to have the following +key/value pairs. + + +#### Contract Type: `contract_type` + +The `contract_type` field defines the [Contract +Type](#term-contract-type) for this [Contract +Instance](#term-contract-instance). This can reference any of the +contract types included in this [Package](#term-package) *or* any of the +contract types found in any of the package dependencies from the +`build_dependencies` section of the [Package +Manifest](#term-package-manifest). + + ++++ + + + + + + + + + + + + + + +

Required

Yes

Type

String

Format

See Below.

+ +**Format** + +Values for this field **must** conform to *one of* the two formats +herein. + +To reference a contract type from this Package, use the format +``. + +- The `` value **must** be a valid [Contract + Alias](#term-contract-alias). + +- The value **must** be present in the keys of the `contract_types` + section of this Package. + +To reference a contract type from a dependency, use the format +`:`. + +- The `` value **must** be present in the keys of the + `build_dependencies` of this Package. + +- The `` value **must** be be a valid [Contract + Alias](#term-contract-alias). + +- The resolved package for `` must contain the + `` value in the keys of the `contract_types` + section. + + +#### Address: `address` + +The `address` field defines the [Address](#term-address) of the +[Contract Instance](#term-contract-instance). + + ++++ + + + + + + + + + + + + + + +

Required

Yes

Type

String

Format

Hex encoded 0x prefixed Ethereum address matching the regular expression 0x[0-9a-fA-F]{40}.

+ + +#### Transaction: `transaction` + +The `transaction` field defines the transaction hash in which this +[Contract Instance](#term-contract-instance) was created. + + ++++ + + + + + + + + + + + + + + +

Required

No

Type

String

Format

0x prefixed hex encoded transaction hash.

+ + +#### Block: `block` + +The `block` field defines the block hash in which this the transaction +which created this *contract instance* was mined. + + ++++ + + + + + + + + + + + + + + +

Required

No

Type

String

Format

0x prefixed hex encoded block hash.

+ + +
+ +#### Runtime Bytecode: `runtime_bytecode` + +The `runtime_bytecode` field defines the runtime portion of bytecode for +this [Contract Instance](#term-contract-instance). When present, the +value from this field supersedes the `runtime_bytecode` from the +[Contract Type](#term-contract-type) for this [Contract +Instance](#term-contract-instance). + + ++++ + + + + + + + + + + + + + + +

Required

No

Type

Object

Format

must conform to the Bytecode Object format.

+ +Every entry in the `link_references` for this bytecode **must** have a +corresponding entry in the `link_dependencies` section. + + +#### Compiler: `compiler` + +The `compiler` field defines the compiler information that was used +during compilation of this [Contract Instance](#term-contract-instance). +This field **should** be present in all [Contract +Types](#term-contract-type) which include `bytecode` or +`runtime_bytecode`. + + ++++ + + + + + + + + + + + + + + +

Required

No

Type

Object

Format

must conform to the Compiler Information Object format.

+ + +
+ +### The *Compiler Information* Object + +The `compiler` field defines the compiler information that was used +during compilation of this [Contract Instance](#term-contract-instance). +This field **should** be present in all contract instances that locally +declare `runtime_bytecode`. + +A *Compiler Information* object is defined to have the following +key/value pairs. + + +#### Name `name` + +The `name` field defines which compiler was used in compilation. + + ++++ + + + + + + + + + + + + + + +

Required

Yes

Key

name

Type

String

+ + +#### Version: `version` + +The `version` field defines the version of the compiler. The field +**should** be OS agnostic (OS not included in the string) and take the +form of either the stable version in +[semver](https://semver.org/) format or if built on a +nightly should be denoted in the form of `-` ex: +`0.4.8-commit.60cc1668`. + + ++++ + + + + + + + + + + + + + + +

Required

Yes

Key

version

Type

String

+ + +#### Settings: `settings` + +The `settings` field defines any settings or configuration that was used +in compilation. For the `"solc"` compiler, this **should** conform to +the [Compiler Input and Output +Description](https://solidity.readthedocs.io/en/latest/using-the-compiler.html#compiler-input-and-output-json-description). + + ++++ + + + + + + + + + + + + + + +

Required

No

Key

settings

Type

Object

+ + +### BIP122 URIs + +BIP122 URIs are used to define a blockchain via a subset of the +[BIP-122](https://github.com/bitcoin/bips/blob/master/bip-0122.mediawiki) +spec. + + blockchain:///block/ + +The `` represents the blockhash of the first block on the +chain, and `` represents the hash of the +latest block that’s been reliably confirmed (package managers should be +free to choose their desired level of confirmations). + + +Rationale +========= + +The following use cases were considered during the creation of this +specification. + + ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

owned

A package which contains contracts which are not meant to be used by themselves but rather as base contracts to provide functionality to other contracts through inheritance.

transferable

A package which has a single dependency.

standard-token

A package which contains a reusable contract.

safe-math-lib

A package which contains deployed instance of one of the package contracts.

piper-coin

A package which contains a deployed instance of a reusable contract from a dependency.

escrow

A package which contains a deployed instance of a local contract which is linked against a deployed instance of a local library.

wallet

A package with a deployed instance of a local contract which is linked against a deployed instance of a library from a dependency.

wallet-with-send

A package with a deployed instance which links against a deep dependency.

+ +Each use case builds incrementally on the previous one. + +A full listing of [Use +Cases](https://ethpm.github.io/ethpm-spec/use-cases.html) +can be found on the hosted version of this specification. + + +Glossary +========== + + +
+ +ABI +--- + +The JSON representation of the application binary interface. See the +official +[specification](https://solidity.readthedocs.io/en/develop/abi-spec.html) +for more information. + + +
+ +Address +------- + +A public identifier for an account on a particular chain + + +
+ +Bytecode +-------- + +The set of EVM instructions as produced by a compiler. Unless otherwise +specified this should be assumed to be hexadecimal encoded, representing +a whole number of bytes, and [prefixed](#term-prefixed) with `0x`. + +Bytecode can either be linked or unlinked. (see +[Linking](#term-linking)) + + ++++ + + + + + + + + + + +

Unlinked Bytecode

The hexadecimal representation of a contract’s EVM instructions that contains sections of code that requires linking for the contract to be functional.

+

The sections of code which are unlinked must be filled in with zero bytes.

+

Example: 0x606060405260e06000730000000000000000000000000000000000000000634d536f

Linked Bytecode

The hexadecimal representation of a contract’s EVM instructions which has had all Link References replaced with the desired Link Values.

+

Example: 0x606060405260e06000736fe36000604051602001526040518160e060020a634d536f

+ + +
+ +Chain Definition +---------------- + +This definition originates from [BIP122 +URI](https://github.com/bitcoin/bips/blob/master/bip-0122.mediawiki). + +A URI in the format `blockchain:///block/` + +- `chain_id` is the unprefixed hexadecimal representation of the + genesis hash for the chain. + +- `block_hash` is the unprefixed hexadecimal representation of the + hash of a block on the chain. + +A chain is considered to match a chain definition if the the genesis +block hash matches the `chain_id` and the block defined by `block_hash` +can be found on that chain. It is possible for multiple chains to match +a single URI, in which case all chains are considered valid matches + + +
+ +Content Addressable URI +----------------------- + +Any URI which contains a cryptographic hash which can be used to verify +the integrity of the content found at the URI. + +The URI format is defined in RFC3986 + +It is **recommended** that tools support IPFS and Swarm. + + +
+ +Contract Alias +-------------- + +This is a name used to reference a specific [Contract +Type](#term-contract-type). Contract aliases **must** be unique within a +single [Package](#term-package). + +The contract alias **must** use *one of* the following naming schemes: + +- `` + +- `[]` + +The `` portion **must** be the same as the [Contract +Name](#term-contract-name) for this contract type. + +The `[]` portion **must** match the regular expression +`\[[-a-zA-Z0-9]{1,256}]`. + + +
+ +Contract Instance +----------------- + +A contract instance a specific deployed version of a [Contract +Type](#term-contract-type). + +All contract instances have an [Address](#term-address) on some specific +chain. + + +
+ +Contract Instance Name +---------------------- + +A name which refers to a specific [Contract +Instance](#term-contract-instance) on a specific chain from the +deployments of a single [Package](#term-package). This name **must** be +unique across all other contract instances for the given chain. The name +must conform to the regular expression `[a-zA-Z][a-zA-Z0-9_]{0,255}` + +In cases where there is a single deployed instance of a given [Contract +Type](#term-contract-type), package managers **should** use the +[Contract Alias](#term-contract-alias) for that contract type for this +name. + +In cases where there are multiple deployed instances of a given contract +type, package managers **should** use a name which provides some added +semantic information as to help differentiate the two deployed instances +in a meaningful way. + + +
+ +Contract Name +------------- + +The name found in the source code that defines a specific [Contract +Type](#term-contract-type). These names **must** conform to the regular +expression `[a-zA-Z][-a-zA-Z0-9_]{0,255}`. + +There can be multiple contracts with the same contract name in a +projects source files. + + +
+ +Contract Type +------------- + +Refers to a specific contract in the package source. This term can be +used to refer to an abstract contract, a normal contract, or a library. +Two contracts are of the same contract type if they have the same +bytecode. + +Example: + + contract Wallet { + ... + } + +A deployed instance of the `Wallet` contract would be of of type +`Wallet`. + + +
+ +Identifier +---------- + +Refers generally to a named entity in the [Package](#term-package). + +A string matching the regular expression `[a-zA-Z][-_a-zA-Z0-9]{0,255}` + + + + +Link Reference +-------------- + +A location within a contract’s bytecode which needs to be linked. A link +reference has the following properties. + + ++++ + + + + + + + + + + + + + + +

offset

Defines the location within the bytecode where the link reference begins.

length

Defines the length of the reference.

name

(optional.) A string to identify the reference

+ + + + +Link Value +---------- + +A link value is the value which can be inserted in place of a [Link +Reference](#term-link-reference) + + +
+ +Linking +------- + +The act of replacing [Link References](#term-link-reference) with [Link +Values](#term-link-value) within some [Bytecode](#term-bytecode). + + +
+ +Package +------- + +Distribution of an application’s source or compiled bytecode along with +metadata related to authorship, license, versioning, et al. + +For brevity, the term **Package** is often used metonymously to mean +[Package Manifest](#term-package-manifest). + + +
+ +Package Manifest +---------------- + +A machine-readable description of a package (See +[Specification](#package-specification) for information about the format +for package manifests.) + + +
+ +Prefixed +-------- + +[Bytecode](#term-bytecode) string with leading `0x`. + + ++++ + + + + + + +

Example

0xdeadbeef

+ + +
+ +Unprefixed +---------- + +Not [Prefixed](#term-prefixed). + + ++++ + + + + + + +

Example

deadbeef

+ + +Backwards Compatibility +======================= + +This specification supports backwards compatibility by use of the +[manifest\_version](#manifest-version) property. This +specification corresponds to version `2` as the value for that field. + + +Implementations +=============== + +This submission aims to coincide with development efforts towards +widespread implementation in commonly-used development tools. + +The following tools are known to have begun or are nearing completion of +a supporting implementation. + +- [Truffle](https://trufflesuite.com/) + +- [Populus](https://populus.readthedocs.io/en/latest/) + +- [Embark](https://embark.status.im/) + +Full support in implementation **may** require [Further +Work](#further-work), specified below. + + +Further Work +============ + +This EIP addresses only the data format for package descriptions. +Excluded from the scope of this specification are: + +- Package registry interface definition + +- Tooling integration, or how packages are stored on disk. + +These efforts **should** be considered separate, warranting future +dependent EIP submssions. + + +Acknowledgements +================ + +The authors of this document would like to thank the original authors of +[EIP-190](./eip-190.md), +[ETHPrize](http://ethprize.io/) for their funding +support, all community +[contributors](https://github.com/ethpm/ethpm-spec/graphs/contributors), +and the Ethereum community at large. + + +Copyright +========= + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1129.md b/EIPS/eip-1129.md new file mode 100644 index 0000000..ca6acdc --- /dev/null +++ b/EIPS/eip-1129.md @@ -0,0 +1,144 @@ +--- +eip: 1129 +title: Standardised DAPP announcements +author: Jan Turk (@ThunderDeliverer) +discussions-to: https://ethereum-magicians.org/t/eip-sda-standardised-dapp-announcements/508?u=thunderdeliverer +status: Stagnant +type: Standards Track +category: ERC +created: 2018-05-31 +--- + +## Simple Summary +Standardisation of announcements in DAPPs and services on Ethereum network. This ERC provides proposed mechanics to increase the quality of service provided by DAPP developers and service providers, by setting a framework for announcements. Be it transitioning to a new smart contract or just freezing the service for some reason. + +## Abstract +The proposed ERC defines format on how to post announcements about the service as well as how to remove them. It also defines mechanics on posting permissions and human friendly interface. + +## Motivation +Currently there are no guidelines on how to notify the users of the service status in the DAPPs. This is especially obvious in ERC20 and it's derivates. If the service is impeded by any reason it is good practice to have some sort of guidelines on how to announce that to the user. The standardisation would also provide traceability of the service's status. + +## Specification + +### Structures + +#### Announcer + +Stores information about the announcement maker. The `allowedToPost` stores posting permissions and is used for modifiers limiting announcement posting only to authorised entities. The `name` is used for human friendly identifier of the author to be stored. + +``` js +struct Announcer{ + bool allowedToPost; + string name; +} +``` + + +#### Announcement + +Stores information about the individual announcement. The human friendly author identifier is stored in `author`. Ethereum address associated with the author is stored in `authorAddress`. The announcement itself is stored in `post`. + +``` js +struct Announcement{ + string author; + address authorAddress; + string post; +} +``` + + + +### Methods +#### the number of ammouncements + +Returns the number of announcement currently active. + +OPTIONAL - this method can be used to provide quicker information for the UI, but could also be retrieved from `numberOfMessages` variable. + +``` js +function theNumberOfAnnouncements() public constant returns(uint256 _numberOfAnnouncements) +``` + + +#### read posts + +Returns the specified announcement as well as human friendly poster identificator (name or nickname). + +``` js +function readPosts(uint256 _postNumber) public constant returns(string _author, string _post) +``` + + +#### give posting permission + +Sets posting permissions of the address `_newAnnouncer` to `_postingPrivileges` and can also be used to revoke those permissions. The `_posterName` is human friendly author identificator used in the announcement data. + +``` js +function givePostingPermission(address _newAnnouncer, bool _postingPrivileges, string _posterName) public onlyOwner returns(bool success) +``` + + +#### can post + +Checks if the entity that wants to post an announcement has the posting privilieges. + +``` js +modifier canPost{ + require(posterData[msg.sender].allowedToPost); + _; +} +``` + + +#### post announcement + +Lets user post announcements, but only if they have their posting privileges set to `true`. The announcement is sent in `_message` variable. + +``` js +function postAnnouncement(string _message) public canPost +``` + + +#### remove announcement + +Removes an announcement with `_messageNumber` announcement identifier and rearranges the mapping so there are no empty slots. The `_removalReason` is used to update users if the issue that caused the announcement is resolved or what are the next steps from the service provider / DAPP development team. + +``` js +function removeAnnouncement(uint256 _messageNumber, string _removalReason) public +``` + + + +### Events + +#### New announcement + +MUST trigger when new announcement is created. + +Every time there is a new announcement it should be advertised in this event. It holds the information about author `author` and the announcement istelf `message`. + +``` js +event NewAnnouncement(string author, string message) +``` + + +#### Removed announcement + +MUST trigger when an announcement is removed. + +Every time an announcement is removed it should be advertised in this event. It holds the information about author `author`, the announcement itself `message`, the reason for removal or explanation of the solution `reason` and the address of the entity that removed the announcement `remover`. + +``` js +event RemovedAnnouncement(string author, string message, string reason, address remover); +``` + +## Rationale +The proposed solution was designed with UX in mind . It provides mechanics that serve to present the announcements in the user friendly way. It is meant to be deployed as a Solidity smart contract on Ethereum network. + +## Test Cases +The proposed version is deployed on Ropsten testnet all of the information can be found [here](https://ropsten.etherscan.io/address/0xb04f67172b9733837e59ebaf03d277279635c8e6#readContract). + +## Implementation + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1132.md b/EIPS/eip-1132.md new file mode 100644 index 0000000..373a0dd --- /dev/null +++ b/EIPS/eip-1132.md @@ -0,0 +1,152 @@ +--- +eip: 1132 +title: Extending ERC20 with token locking capability +author: nitika-goel +type: Standards Track +category: ERC +status: Stagnant +created: 2018-06-03 +discussions-to: https://github.com/ethereum/EIPs/issues/1132 +--- + +## Simple Summary + +An extension to the ERC20 standard with methods for time-locking of tokens within a contract. + +## Abstract + +This proposal provides basic functionality to time-lock tokens within an ERC20 smart contract for multiple utilities without the need of transferring tokens to an external escrow smart contract. It also allows fetching balance of locked and transferable tokens. + +Time-locking can also be achieved via staking (#900), but that requires transfer of tokens to an escrow contract / stake manager, resulting in the following six concerns: + +1. additional trust on escrow contract / stake manager +2. additional approval process for token transfer +3. increased ops costs due to gas requirements in transfers +4. tough user experience as the user needs to claim the amount back from external escrows +5. inability for the user to track their true token balance / token activity +6. inability for the user to utilize their locked tokens within the token ecosystem. + +## Motivation + +dApps often require tokens to be time-locked against transfers for letting members 1) adhere to vesting schedules and 2) show skin in the game to comply with the underlying business process. I realized this need while building Nexus Mutual and GovBlocks. + +In [Nexus Mutual](https://nexusmutual.io), claim assessors are required to lock their tokens before passing a vote for claims assessment. This is important as it ensures assessors’ skin in the game. The need here was that once a claim assessor locks his tokens for ‘n’ days, he should be able to cast multiple votes during that period of ‘n’ days, which is not feasible with staking mechanism. There are other scenarios like skills/identity verification or participation in gamified token curated registries where time-locked tokens are required as well. + +In [GovBlocks](https://govblocks.io), I wanted to allow dApps to lock member tokens for governance, while still allowing members to use those locked tokens for other activities within the dApp business. This is also the case with DGX governance model where they’ve proposed quarterly token locking for participation in governance activities of DGX. + +In addition to locking functionality, I have proposed a `Lock()` and `Unlock()` event, just like the `Transfer()` event , to track token lock and unlock status. From token holder’s perspective, it gets tough to manage token holdings if certain tokens are transferred to another account for locking, because whenever `balanceOf()` queries are triggered on token holder’s account – the result does not include locked tokens. A `totalBalanceOf()` function intends to solve this problem. + +The intention with this proposal is to enhance the ERC20 standard with token-locking capability so that dApps can time-lock tokens of the members without having to transfer tokens to an escrow / stake manager and at the same time allow members to use the locked tokens for multiple utilities. + +## Specification + +I’ve extended the ERC20 interface with the following enhancements: + +### Locking of tokens +```solidity +/** + * @dev Locks a specified amount of tokens against an address, + * for a specified reason and time + * @param _reason The reason to lock tokens + * @param _amount Number of tokens to be locked + * @param _time Lock time in seconds + */ +function lock(bytes32 _reason, uint256 _amount, uint256 _time) public returns (bool) +``` + +### Fetching number of tokens locked under each utility +```solidity +/** + * @dev Returns tokens locked for a specified address for a + * specified reason + * + * @param _of The address whose tokens are locked + * @param _reason The reason to query the lock tokens for + */ + tokensLocked(address _of, bytes32 _reason) view returns (uint256 amount) +``` + +### Fetching number of tokens locked under each utility at a future timestamp +```solidity +/** + * @dev Returns tokens locked for a specified address for a + * specified reason at a specific time + * + * @param _of The address whose tokens are locked + * @param _reason The reason to query the lock tokens for + * @param _time The timestamp to query the lock tokens for + */ + function tokensLockedAtTime(address _of, bytes32 _reason, uint256 _time) public view returns (uint256 amount) +``` + +### Fetching number of tokens held by an address +```solidity +/** + * @dev @dev Returns total tokens held by an address (locked + transferable) + * @param _of The address to query the total balance of + */ +function totalBalanceOf(address _of) view returns (uint256 amount) +``` + +### Extending lock period +```solidity +/** + * @dev Extends lock for a specified reason and time + * @param _reason The reason to lock tokens + * @param _time Lock extension time in seconds + */ + function extendLock(bytes32 _reason, uint256 _time) public returns (bool) +``` + +### Increasing number of tokens locked +```solidity +/** + * @dev Increase number of tokens locked for a specified reason + * @param _reason The reason to lock tokens + * @param _amount Number of tokens to be increased + */ + function increaseLockAmount(bytes32 _reason, uint256 _amount) public returns (bool) +``` +### Fetching number of unlockable tokens under each utility +```solidity +/** + * @dev Returns unlockable tokens for a specified address for a specified reason + * @param _of The address to query the the unlockable token count of + * @param _reason The reason to query the unlockable tokens for + */ + function tokensUnlockable(address _of, bytes32 _reason) public view returns (uint256 amount) + ``` +### Fetching number of unlockable tokens +```solidity +/** + * @dev Gets the unlockable tokens of a specified address + * @param _of The address to query the the unlockable token count of + */ + function getUnlockableTokens(address _of) public view returns (uint256 unlockableTokens) +``` +### Unlocking tokens +```solidity +/** + * @dev Unlocks the unlockable tokens of a specified address + * @param _of Address of user, claiming back unlockable tokens + */ + function unlock(address _of) public returns (uint256 unlockableTokens) +``` + +### Lock event recorded in the token contract +`event Locked(address indexed _of, uint256 indexed _reason, uint256 _amount, uint256 _validity)` + +### Unlock event recorded in the token contract +`event Unlocked(address indexed _of, uint256 indexed _reason, uint256 _amount)` + +## Test Cases + +Test cases are available at [https://github.com/nitika-goel/lockable-token](https://github.com/nitika-goel/lockable-token). + +## Implementation + +- Complete implementation available at https://github.com/nitika-goel/lockable-token +- [GovBlocks](https://govblocks.io) Project specific implementation available at https://github.com/somish/govblocks-protocol/blob/Locking/contracts/GBTStandardToken.sol + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1153.md b/EIPS/eip-1153.md new file mode 100644 index 0000000..3ea3068 --- /dev/null +++ b/EIPS/eip-1153.md @@ -0,0 +1,253 @@ +--- +eip: 1153 +title: Transient storage opcodes +description: Add opcodes for manipulating state that behaves identically to storage but is discarded after every transaction +author: Alexey Akhunov (@AlexeyAkhunov), Moody Salem (@moodysalem) +discussions-to: https://ethereum-magicians.org/t/eip-transient-storage-opcodes/553 +status: Last Call +last-call-deadline: 2022-12-08 +type: Standards Track +category: Core +created: 2018-06-15 +requires: 2200, 3529 +--- + +## Abstract + +This proposal introduces transient storage opcodes, which manipulate state that behaves identically to storage, except that transient storage is discarded after every transaction. In other words, the values of transient storage are never deserialized from storage or serialized to storage. Thus transient storage is cheaper since it never requires disk access. Transient storage is accessible to smart contracts via 2 new opcodes, `TLOAD` and `TSTORE`, where “T” stands for "transient:" + +``` +TLOAD (0xb3) +TSTORE (0xb4) +``` + +## Motivation + +Running a transaction in Ethereum can generate multiple nested frames of execution, each created by `CALL` (or similar) instructions. Contracts can be re-entered during the same transaction, in which case there are more than one frame belonging to one contract. Currently, these frames can communicate in two ways: via inputs/outputs passed via `CALL` instructions, and via storage updates. If there is an intermediate frame belonging to another untrusted contract, communication via inputs/outputs is not secure. Notable example is a reentrancy lock which cannot rely on the intermediate frame to pass through the state of the lock. Communication via storage (`SSTORE`/`SLOAD`) is costly. Transient storage is a dedicated and gas efficient solution to the problem of inter frame communication. + +Storage refunds accumulated due to inter frame communication are also limited to 20% of gas spent by a transaction due to [EIP-3529](./eip-3529.md) (introduced in the London hard fork). This greatly reduces the refunds for transiently-set storage slots in otherwise low-cost transactions. For example, in order to receive the full refund of one re-entrancy lock, the transaction must spend ~80k gas on other operations. + +Language support could be added in relatively easy way. For example, in Solidity, a qualifier `transient` can be introduced (similar to the existing qualifiers `memory` and `storage`, and Java's own `transient` keyword with a similar meaning). Since the addressing scheme of `TSTORE` and `TLOAD` is the same as for `SSTORE` and `SLOAD`, code generation routines that exist for storage variables, can be easily generalised to also support transient storage. + +Potential use cases enabled or improved by this EIP include: + +1. Reentrancy locks +2. On-chain computable CREATE2 addresses: constructor arguments are read from the factory contract instead of passed as part of init code hash +3. Single transaction [EIP-20](./eip-20.md) approvals, e.g. `#temporaryApprove(address spender, uint256 amount)` +4. Fee-on-transfer contracts: pay a fee to a token contract to unlock transfers for the duration of a transaction +5. "Till" pattern: allowing users to perform all actions as part of a callback, and checking the "till" is balanced at the end +6. Proxy call metadata: pass additional metadata to an implementation contract without using calldata, e.g. values of immutable proxy constructor arguments + +These opcodes are more efficient to execute than the `SSTORE` and `SLOAD` opcodes because the original value never needs to be loaded from storage (i.e. is always 0). The gas accounting rules are also simpler, since no refunds are required. + +## Specification + +Two new opcodes are added to EVM, `TLOAD` (`0xb3`) and `TSTORE` (`0xb4`). Note that previous drafts of this EIP specified the values `0x5c` and `0x5d` for `TLOAD` and `TSTORE` respectively, but these have been modified so as not to conflict with other draft EIPs. + +They use the same arguments on stack as `SLOAD` (`0x54`) and `SSTORE` (`0x55`). + +`TLOAD` pops one 32-byte word from the top of the stack, treats this value as the address, fetches 32-byte word from the transient storage at that address, and pops the value on top of the stack. + +`TSTORE` pops two 32-byte words from the top of the stack. The word on the top is the address, and the next is the value. `TSTORE` saves the value at the given address in the transient storage. + +Addressing is the same as `SLOAD` and `SSTORE`. i.e. each 32-byte address points to a unique 32-byte word. + +Gas cost for `TSTORE` is the same as a warm `SSTORE` of a dirty slot (i.e. original value is not new value and is not current value, currently 100 gas), and gas cost of `TLOAD` is the same as a hot `SLOAD` (value has been read before, currently 100 gas). Gas cost cannot be on par with memory access due to transient storage's interactions with reverts. + +All values in transient storage are discarded at the end of the transaction. + +Transient storage is private to the contract that owns it, in the same way as persistent storage. Only owning contract frames may access their transient storage. And when they do, all the frames access the same transient store, in the same way as persistent storage, but unlike memory. + +When transient storage is used in the context of `DELEGATECALL` or `CALLCODE`, then the owning contract of the transient storage is the contract that issued `DELEGATECALL` or `CALLCODE` instruction (the caller) as with persistent storage. When transient storage is used in the context of `CALL` or `STATICCALL`, then the owning contract of the transient storage is the contract that is the target of the `CALL` or `STATICCALL` instruction (the callee). + +If a frame reverts, all writes to transient storage that took place between entry to the frame and the return are reverted, including those that took place in inner calls. This mimics the behavior of persistent storage. + +If the `TSTORE` opcode is called within the context of a `STATICCALL`, it will result in an exception instead of performing the modification. `TLOAD` is allowed within the context of a `STATICCALL`. + +## Rationale + +Another option to solve the problem of inter-frame communication is repricing the `SSTORE` and `SLOAD` opcodes to be cheaper for the transient storage use case. This has already been done as of [EIP-2200](./eip-2200.md). However, [EIP-3529](./eip-3529.md) reduced the maximum refund to only 20% of the transaction gas cost, which means the use of transient storage is severely limited. + +Another approach is to keep the refund counter for transient storage separate from the refund counter for other storage uses, and remove the refund cap for transient storage. However, that approach is more complex to implement and understand. For example, the 20% refund cap must be applied to the gas used _after_ subtracting the uncapped gas refund. Otherwise, the refund amount available subject to the 20% refund cap could be increased by executing transient storage writes. Thus it is preferable to have a separate mechanism that does not interact with the refund counter. Future hard forks can remove the complex refund behavior meant to support the transient storage use case, encouraging migration to contracts that are more efficient for the Ethereum clients to execute. + +There is a known objection to the word-addressed storage-like interface of the `TSTORE` and `TLOAD` opcodes since transient storage is more akin to memory than storage in lifecycle. A byte-addressed memory-like interface is another option. The storage-like word-addressed interface is preferred due to the usefulness of mappings in combination with the transaction-scoped memory region. Often times, you will need to keep transient state with arbitrary keys, such as in the [EIP-20](./eip-20.md) temporary approval use case which uses a mapping of `(owner, spender)` to `allowance`. Mappings are difficult to implement using linear memory, and linear memory must also have dynamic gas costs. It is also more complicated to handle reverts with a linear memory. It is possible to have a memory-like interface while the underlying implementation uses a map to allow for storage in arbitrary offsets, but this would result in a third memory-storage hybrid interface that would require new code paths in compilers. + +Some think that a unique transaction identifier may obviate the need for transient storage as described in this EIP. This is a misconception: a transaction identifier used in combination with regular storage has all the same issues that motivate this EIP. The two features are orthogonal. + +Relative cons of this transient storage EIP: + +- Does not address transient usages of storage in existing contracts +- New code in the clients +- New concept for the yellow paper (more to update) + +Relative pros of this transient storage EIP: + +- Transient storage opcodes are considered separately in protocol upgrades and not inadvertently broken (e.g. [EIP-3529](./eip-3529.md)) +- Clients do not need to load the original value +- No upfront gas cost to account for non-transient writes +- Does not change the semantics of the existing operations +- No need to clear storage slots after usage +- Simpler gas accounting rules +- Future storage designs (e.g. Verkle tree) do not need to account for transient storage refunds + +## Backwards Compatibility + +This EIP requires a hard fork to implement. + +Since this EIP does not change behavior of any existing opcodes, it is backwards compatible with all existing smart contracts. + +## Reference Implementation + +Because the transient storage must behave identically to storage within the context of a single transaction with regards to revert behavior, it is necessary to be able to revert to a previous state of transient storage within a transaction. At the same time reverts are exceptional cases and loads, stores and returns should be cheap. + +A map of current state plus a journal of all changes and a list of checkpoints is recommended. This has the following time complexities: + +- On entry to a call frame, a call marker is added to the list - `O(1)` +- New values are written to the current state, and the previous value is written to the journal - `O(1)` +- When a call exits successfully, the marker to the journal index of when that call was entered is discarded - `O(1)` +- On revert all entries are reverted up to the last checkpoint, in reverse - `O(N)` where `N` = number of journal entries since last checkpoint + +```typescript +interface JournalEntry { + addr: string + key: string + prevValue: string +} + +type Journal = JournalEntry[] + +type Checkpoints = Journal['length'][] + +interface Current { + [addr: string]: { + [key: string]: string + } +} + +const EMPTY_VALUE = '0x0000000000000000000000000000000000000000000000000000000000000000' + +class TransientStorage { + /** + * The current state of transient storage. + */ + private current: Current = {} + /** + * All changes are written to the journal. On revert, we apply the changes in reverse to the last checkpoint. + */ + private journal: Journal = [] + /** + * The length of the journal at the time of each checkpoint + */ + private checkpoints: Checkpoints = [0] + + /** + * Returns the current value of the given contract address and key + * @param addr The address of the contract + * @param key The key of transient storage for the address + */ + public get(addr: string, key: string): string { + return this.current[addr]?.[key] ?? EMPTY_VALUE + } + + /** + * Set the current value in the map + * @param addr the address of the contract for which the key is being set + * @param key the slot to set for the address + * @param value the new value of the slot to set + */ + public put(addr: string, key: string, value: string) { + this.journal.push({ + addr, + key, + prevValue: this.get(addr, key), + }) + + this.current[addr] = this.current[addr] ?? {} + this.current[addr][key] = value; + } + + /** + * Commit all the changes since the last checkpoint + */ + public commit(): void { + if (this.checkpoints.length === 0) throw new Error('Nothing to commit') + this.checkpoints.pop() // The last checkpoint is discarded. + } + + /** + * To be called whenever entering a new context. If revert is called after checkpoint, all changes made after the latest checkpoint are reverted. + */ + public checkpoint(): void { + this.checkpoints.push(this.journal.length) + } + + /** + * Revert transient storage to the state from the last call to checkpoint + */ + public revert() { + const lastCheckpoint = this.checkpoints.pop() + if (typeof lastCheckpoint === 'undefined') throw new Error('Nothing to revert') + + for (let i = this.journal.length - 1; i >= lastCheckpoint; i--) { + const {addr, key, prevValue} = this.journal[i] + // we can assume it exists, since it was written in the journal + this.current[addr][key] = prevValue + } + this.journal.splice(lastCheckpoint, this.journal.length - lastCheckpoint) + } +} +``` + +The worst case time complexity can be produced by writing the maximum number of keys that can fit in one block, and then reverting. In this case, the client is required to do twice as many writes to apply all the entries in the journal. However, the same case applies to the state journaling implementation of existing clients, and cannot be DOS'd with the following code. + +```solidity +pragma solidity =0.8.13; + +contract TryDOS { + uint256 slot; + + constructor() { + slot = 1; + } + + function tryDOS() external { + uint256 i = 1; + while (gasleft() > 5000) { + unchecked { + slot = i++; + } + } + revert(); + } +} +``` + +## Security Considerations + +`TSTORE` presents a new way to allocate memory on a node with linear cost. In other words, each TSTORE allows the developer to store 32 bytes for 100 gas, excluding any other required operations to prepare the stack. Given 30 million gas, the maximum amount of memory that can be allocated using TSTORE is: + +``` +30M gas * 1 TSTORE / 100 gas * 32 bytes / 1 TSTORE * 1MB / 2^20 bytes ~= 9.15MB +``` + +Given the same amount of gas, the maximum amount of memory that can be allocated in a single context by `MSTORE` is ~3.75MB: + +``` +30M gas = 3x + x^2 / 512 => x = ~123,169 32-byte words +~123,169 words * 32 bytes/word * 1MB / 2^20 bytes = 3.75MB +``` + +However, if you only spend 1M gas allocating memory in each context, and make calls to reset the memory expansion cost, you can allocate ~700KB per million gas, for a total of ~20MB of memory allocated: + +``` +1M gas = 3x + x^2 / 512 => x = ~21,872 32-byte words +30M gas * ~21,872 words / 1M gas * 32 bytes/word * 1MB / 2^20 bytes = ~20MB +``` + +Smart contract developers should understand the lifetime of transient storage variables before use. Because transient storage is automatically cleared at the end of the transaction, smart contract developers may be tempted to avoid clearing slots as part of a call in order to save gas. However, this could prevent further interactions with the contract in the same transaction (e.g. in the case of re-entrancy locks) or cause other bugs, so smart contract developers should be careful to _only_ leave transient storage slots with nonzero values when those slots are intended to be used by future calls within the same transaction. Otherwise, these opcodes behave exactly the same as `SSTORE` and `SLOAD`, so all the usual security considerations apply especially in regard to reentrancy risk. + +Smart contract developers may also be tempted to use transient storage as an alternative to in-memory mappings. They should be aware that transient storage is not discarded when a call returns or reverts, as is memory, and should prefer memory for these use cases so as not to create unexpected behavior on reentrancy in the same transaction. The necessarily high cost of transient storage over memory should already discourage this usage pattern. Most usages of in-memory mappings can be better implemented with key-sorted lists of entries, and in-memory mappings are rarely required in smart contracts (i.e. the author knows of no known use cases in production). + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1154.md b/EIPS/eip-1154.md new file mode 100644 index 0000000..417ae86 --- /dev/null +++ b/EIPS/eip-1154.md @@ -0,0 +1,110 @@ +--- +eip: 1154 +title: Oracle Interface +author: Alan Lu (@cag) +discussions-to: https://github.com/ethereum/EIPs/issues/1161 +status: Withdrawn +type: Standards Track +category: ERC +created: 2018-06-13 +--- + +## Simple Summary +A standard interface for oracles. + +## Abstract +In order for ethereum smart contracts to interact with off-chain systems, oracles must be used. These oracles report values which are normally off-chain, allowing smart contracts to react to the state of off-chain systems. A distinction and a choice is made between push and pull based oracle systems. Furthermore, a standard interface for oracles is described here, allowing different oracle implementations to be interchangeable. + +## Motivation +The Ethereum ecosystem currently has many different oracle implementations available, but they do not provide a unified interface. Smart contract systems would be locked into a single set of oracle implementations, or they would require developers to write adapters/ports specific to the oracle system chosen in a given project. + +Beyond naming differences, there is also the issue of whether or not an oracle report-resolving transaction _pushes_ state changes by calling affected contracts, or changes the oracle state allowing dependent contracts to _pull_ the updated value from the oracle. These differing system semantics could introduce inefficiencies when adapting between them. + +Ultimately, the value in different oracle systems comes from their underlying resolution mechanics, and points where these systems are virtually identical should be standardized. + +These oracles may be used for answering questions about "real-world events", where each ID can be correlated with a specification of a question and its answers (so most likely for prediction markets, basically). + +Another use case could be for decision-making processes, where the results given by the oracle represent decisions made by the oracle (e.g. futarchies). DAOs may require their use in decision making processes. + +Both the ID and the results are intentionally unstructured so that things like time series data (via splitting the ID) and different sorts of results (like one of a few, any subset of up to 256, or some value in a range with up to 256 bits of granularity) can be represented. + +## Specification + +
+
Oracle
+
An entity which reports data to the blockchain.
+ +
Oracle consumer
+
A smart contract which receives data from an oracle.
+ +
ID
+
A way of indexing the data which an oracle reports. May be derived from or tied to a question for which the data provides the answer.
+ +
Result
+
Data associated with an id which is reported by an oracle. This data oftentimes will be the answer to a question tied to the id. Other equivalent terms that have been used include: answer, data, outcome.
+ +
Report
+
A pair (ID, result) which an oracle sends to an oracle consumer.
+
+ +```solidity +interface OracleConsumer { + function receiveResult(bytes32 id, bytes result) external; +} +``` + +`receiveResult` MUST revert if the `msg.sender` is not an oracle authorized to provide the `result` for that `id`. + +`receiveResult` MUST revert if `receiveResult` has been called with the same `id` before. + +`receiveResult` MAY revert if the `id` or `result` cannot be handled by the consumer. + +Consumers MUST coordinate with oracles to determine how to encode/decode results to and from `bytes`. For example, `abi.encode` and `abi.decode` may be used to implement a codec for results in Solidity. `receiveResult` SHOULD revert if the consumer receives a unexpected result format from the oracle. + +The oracle can be any Ethereum account. + +## Rationale +The specs are currently very similar to what is implemented by ChainLink (which can use any arbitrarily-named callback) and Oraclize (which uses `__callback`). + +With this spec, the oracle _pushes_ state to the consumer, which must react accordingly to the updated state. An alternate _pull_-based interface can be prescribed, as follows: + +### Alternate Pull-based Interface +Here are alternate specs loosely based on Gnosis prediction market contracts v1. Reality Check also exposes a similar endpoint (`getFinalAnswer`). + +```solidity +interface Oracle { + function resultFor(bytes32 id) external view returns (bytes result); +} +``` + +`resultFor` MUST revert if the result for an `id` is not available yet. + +`resultFor` MUST return the same result for an `id` after that result is available. + +### Push vs Pull +Note that push-based interfaces may be adapted into pull-based interfaces. Simply deploy an oracle consumer which stores the result received and implements `resultFor` accordingly. + +Similarly, every pull-based system can be adapted into a push-based system: just add a method on the oracle smart contract which takes an oracle consumer address and calls `receiveResult` on that address. + +In both cases, an additional transaction would have to be performed, so the choice to go with push or pull should be based on the dominant use case for these oracles. + +In the simple case where a single account has the authority to decide the outcome of an oracle question, there is no need to deploy an oracle contract and store the outcome on that oracle contract. Similarly, in the case where the outcome comes down to a vote, existing multisignature wallets can be used as the authorized oracle. + +#### Multiple Oracle Consumers +In the case that many oracle consumers depend on a single oracle result and all these consumers expect the result to be pushed to them, the push and pull adaptations mentioned before may be combined if the pushing oracle cannot be trusted to send the same result to every consumer (in a sense, this forwards the trust to the oracle adaptor implementation). + +In a pull-based system, each of the consumers would have to be called to pull the result from the oracle contract, but in the proposed push-based system, the adapted oracle would have to be called to push the results to each of the consumers. + +Transaction-wise, both systems are roughly equivalent in efficiency in this scenario, but in the push-based system, there's a need for the oracle consumers to store the results again, whereas in the pull-based system, the consumers may continue to refer to the oracle for the results. Although this may be somewhat less efficient, requiring the consumers to store the results can also provide security guarantees, especially with regards to result immutability. + +#### Result Immutability +In both the proposed specification and the alternate specification, results are immutable once they are determined. This is due to the expectation that typical consumers will require results to be immutable in order to determine a resulting state consistently. With the proposed push-based system, the consumer enforces the result immutability requirement, whereas in the alternate pull-based system, either the oracle would have to be trusted to implement the spec correctly and enforce the immutability requirement, or the consumer would also have to handle result immutability. + +For data which mutates over time, the `id` field may be structured to specify "what" and "when" for the data (using 128 bits to specify "when" is still safe for many millennia). + +## Implementation + +* [Tidbit](https://github.com/levelkdev/tidbit) tracks this EIP. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1155.md b/EIPS/eip-1155.md new file mode 100644 index 0000000..7444f76 --- /dev/null +++ b/EIPS/eip-1155.md @@ -0,0 +1,710 @@ +--- +eip: 1155 +title: Multi Token Standard +author: Witek Radomski , Andrew Cooke , Philippe Castonguay , James Therien , Eric Binet , Ronan Sandford +type: Standards Track +category: ERC +status: Final +created: 2018-06-17 +discussions-to: https://github.com/ethereum/EIPs/issues/1155 +requires: 165 +--- + +## Simple Summary + +A standard interface for contracts that manage multiple token types. A single deployed contract may include any combination of fungible tokens, non-fungible tokens or other configurations (e.g. semi-fungible tokens). + +## Abstract + +This standard outlines a smart contract interface that can represent any number of fungible and non-fungible token types. Existing standards such as ERC-20 require deployment of separate contracts per token type. The ERC-721 standard's token ID is a single non-fungible index and the group of these non-fungibles is deployed as a single contract with settings for the entire collection. In contrast, the ERC-1155 Multi Token Standard allows for each token ID to represent a new configurable token type, which may have its own metadata, supply and other attributes. + +The `_id` argument contained in each function's argument set indicates a specific token or token type in a transaction. + +## Motivation + +Tokens standards like ERC-20 and ERC-721 require a separate contract to be deployed for each token type or collection. This places a lot of redundant bytecode on the Ethereum blockchain and limits certain functionality by the nature of separating each token contract into its own permissioned address. With the rise of blockchain games and platforms like Enjin Coin, game developers may be creating thousands of token types, and a new type of token standard is needed to support them. However, ERC-1155 is not specific to games and many other applications can benefit from this flexibility. + +New functionality is possible with this design such as transferring multiple token types at once, saving on transaction costs. Trading (escrow / atomic swaps) of multiple tokens can be built on top of this standard and it removes the need to "approve" individual token contracts separately. It is also easy to describe and mix multiple fungible or non-fungible token types in a single contract. + +## Specification + +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. + +**Smart contracts implementing the ERC-1155 standard MUST implement all of the functions in the `ERC1155` interface.** + +**Smart contracts implementing the ERC-1155 standard MUST implement the ERC-165 `supportsInterface` function and MUST return the constant value `true` if `0xd9b67a26` is passed through the `interfaceID` argument.** + +```solidity +pragma solidity ^0.5.9; + +/** + @title ERC-1155 Multi Token Standard + @dev See https://eips.ethereum.org/EIPS/eip-1155 + Note: The ERC-165 identifier for this interface is 0xd9b67a26. + */ +interface ERC1155 /* is ERC165 */ { + /** + @dev Either `TransferSingle` or `TransferBatch` MUST emit when tokens are transferred, including zero value transfers as well as minting or burning (see "Safe Transfer Rules" section of the standard). + The `_operator` argument MUST be the address of an account/contract that is approved to make the transfer (SHOULD be msg.sender). + The `_from` argument MUST be the address of the holder whose balance is decreased. + The `_to` argument MUST be the address of the recipient whose balance is increased. + The `_id` argument MUST be the token type being transferred. + The `_value` argument MUST be the number of tokens the holder balance is decreased by and match what the recipient balance is increased by. + When minting/creating tokens, the `_from` argument MUST be set to `0x0` (i.e. zero address). + When burning/destroying tokens, the `_to` argument MUST be set to `0x0` (i.e. zero address). + */ + event TransferSingle(address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value); + + /** + @dev Either `TransferSingle` or `TransferBatch` MUST emit when tokens are transferred, including zero value transfers as well as minting or burning (see "Safe Transfer Rules" section of the standard). + The `_operator` argument MUST be the address of an account/contract that is approved to make the transfer (SHOULD be msg.sender). + The `_from` argument MUST be the address of the holder whose balance is decreased. + The `_to` argument MUST be the address of the recipient whose balance is increased. + The `_ids` argument MUST be the list of tokens being transferred. + The `_values` argument MUST be the list of number of tokens (matching the list and order of tokens specified in _ids) the holder balance is decreased by and match what the recipient balance is increased by. + When minting/creating tokens, the `_from` argument MUST be set to `0x0` (i.e. zero address). + When burning/destroying tokens, the `_to` argument MUST be set to `0x0` (i.e. zero address). + */ + event TransferBatch(address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values); + + /** + @dev MUST emit when approval for a second party/operator address to manage all tokens for an owner address is enabled or disabled (absence of an event assumes disabled). + */ + event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved); + + /** + @dev MUST emit when the URI is updated for a token ID. + URIs are defined in RFC 3986. + The URI MUST point to a JSON file that conforms to the "ERC-1155 Metadata URI JSON Schema". + */ + event URI(string _value, uint256 indexed _id); + + /** + @notice Transfers `_value` amount of an `_id` from the `_from` address to the `_to` address specified (with safety call). + @dev Caller must be approved to manage the tokens being transferred out of the `_from` account (see "Approval" section of the standard). + MUST revert if `_to` is the zero address. + MUST revert if balance of holder for token `_id` is lower than the `_value` sent. + MUST revert on any other error. + MUST emit the `TransferSingle` event to reflect the balance change (see "Safe Transfer Rules" section of the standard). + After the above conditions are met, this function MUST check if `_to` is a smart contract (e.g. code size > 0). If so, it MUST call `onERC1155Received` on `_to` and act appropriately (see "Safe Transfer Rules" section of the standard). + @param _from Source address + @param _to Target address + @param _id ID of the token type + @param _value Transfer amount + @param _data Additional data with no specified format, MUST be sent unaltered in call to `onERC1155Received` on `_to` + */ + function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external; + + /** + @notice Transfers `_values` amount(s) of `_ids` from the `_from` address to the `_to` address specified (with safety call). + @dev Caller must be approved to manage the tokens being transferred out of the `_from` account (see "Approval" section of the standard). + MUST revert if `_to` is the zero address. + MUST revert if length of `_ids` is not the same as length of `_values`. + MUST revert if any of the balance(s) of the holder(s) for token(s) in `_ids` is lower than the respective amount(s) in `_values` sent to the recipient. + MUST revert on any other error. + MUST emit `TransferSingle` or `TransferBatch` event(s) such that all the balance changes are reflected (see "Safe Transfer Rules" section of the standard). + Balance changes and events MUST follow the ordering of the arrays (_ids[0]/_values[0] before _ids[1]/_values[1], etc). + After the above conditions for the transfer(s) in the batch are met, this function MUST check if `_to` is a smart contract (e.g. code size > 0). If so, it MUST call the relevant `ERC1155TokenReceiver` hook(s) on `_to` and act appropriately (see "Safe Transfer Rules" section of the standard). + @param _from Source address + @param _to Target address + @param _ids IDs of each token type (order and length must match _values array) + @param _values Transfer amounts per token type (order and length must match _ids array) + @param _data Additional data with no specified format, MUST be sent unaltered in call to the `ERC1155TokenReceiver` hook(s) on `_to` + */ + function safeBatchTransferFrom(address _from, address _to, uint256[] calldata _ids, uint256[] calldata _values, bytes calldata _data) external; + + /** + @notice Get the balance of an account's tokens. + @param _owner The address of the token holder + @param _id ID of the token + @return The _owner's balance of the token type requested + */ + function balanceOf(address _owner, uint256 _id) external view returns (uint256); + + /** + @notice Get the balance of multiple account/token pairs + @param _owners The addresses of the token holders + @param _ids ID of the tokens + @return The _owner's balance of the token types requested (i.e. balance for each (owner, id) pair) + */ + function balanceOfBatch(address[] calldata _owners, uint256[] calldata _ids) external view returns (uint256[] memory); + + /** + @notice Enable or disable approval for a third party ("operator") to manage all of the caller's tokens. + @dev MUST emit the ApprovalForAll event on success. + @param _operator Address to add to the set of authorized operators + @param _approved True if the operator is approved, false to revoke approval + */ + function setApprovalForAll(address _operator, bool _approved) external; + + /** + @notice Queries the approval status of an operator for a given owner. + @param _owner The owner of the tokens + @param _operator Address of authorized operator + @return True if the operator is approved, false if not + */ + function isApprovedForAll(address _owner, address _operator) external view returns (bool); +} +``` + +### ERC-1155 Token Receiver + +**Smart contracts MUST implement all of the functions in the `ERC1155TokenReceiver` interface to accept transfers. See "Safe Transfer Rules" for further detail.** + +**Smart contracts MUST implement the ERC-165 `supportsInterface` function and signify support for the `ERC1155TokenReceiver` interface to accept transfers. See "ERC1155TokenReceiver ERC-165 rules" for further detail.** + +```solidity +pragma solidity ^0.5.9; + +/** + Note: The ERC-165 identifier for this interface is 0x4e2312e0. +*/ +interface ERC1155TokenReceiver { + /** + @notice Handle the receipt of a single ERC1155 token type. + @dev An ERC1155-compliant smart contract MUST call this function on the token recipient contract, at the end of a `safeTransferFrom` after the balance has been updated. + This function MUST return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` (i.e. 0xf23a6e61) if it accepts the transfer. + This function MUST revert if it rejects the transfer. + Return of any other value than the prescribed keccak256 generated value MUST result in the transaction being reverted by the caller. + @param _operator The address which initiated the transfer (i.e. msg.sender) + @param _from The address which previously owned the token + @param _id The ID of the token being transferred + @param _value The amount of tokens being transferred + @param _data Additional data with no specified format + @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` + */ + function onERC1155Received(address _operator, address _from, uint256 _id, uint256 _value, bytes calldata _data) external returns(bytes4); + + /** + @notice Handle the receipt of multiple ERC1155 token types. + @dev An ERC1155-compliant smart contract MUST call this function on the token recipient contract, at the end of a `safeBatchTransferFrom` after the balances have been updated. + This function MUST return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` (i.e. 0xbc197c81) if it accepts the transfer(s). + This function MUST revert if it rejects the transfer(s). + Return of any other value than the prescribed keccak256 generated value MUST result in the transaction being reverted by the caller. + @param _operator The address which initiated the batch transfer (i.e. msg.sender) + @param _from The address which previously owned the token + @param _ids An array containing ids of each token being transferred (order and length must match _values array) + @param _values An array containing amounts of each token being transferred (order and length must match _ids array) + @param _data Additional data with no specified format + @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` + */ + function onERC1155BatchReceived(address _operator, address _from, uint256[] calldata _ids, uint256[] calldata _values, bytes calldata _data) external returns(bytes4); +} +``` + +### Safe Transfer Rules + +To be more explicit about how the standard `safeTransferFrom` and `safeBatchTransferFrom` functions MUST operate with respect to the `ERC1155TokenReceiver` hook functions, a list of scenarios and rules follows. + +#### Scenarios + +**_Scenario#1 :_** The recipient is not a contract. +* `onERC1155Received` and `onERC1155BatchReceived` MUST NOT be called on an EOA (Externally Owned Account). + +**_Scenario#2 :_** The transaction is not a mint/transfer of a token. +* `onERC1155Received` and `onERC1155BatchReceived` MUST NOT be called outside of a mint or transfer process. + +**_Scenario#3 :_** The receiver does not implement the necessary `ERC1155TokenReceiver` interface function(s). +* The transfer MUST be reverted with the one caveat below. + - If the token(s) being sent are part of a hybrid implementation of another standard, that particular standard's rules on sending to a contract MAY now be followed instead. See "Backwards Compatibility" section. + +**_Scenario#4 :_** The receiver implements the necessary `ERC1155TokenReceiver` interface function(s) but returns an unknown value. +* The transfer MUST be reverted. + +**_Scenario#5 :_** The receiver implements the necessary `ERC1155TokenReceiver` interface function(s) but throws an error. +* The transfer MUST be reverted. + +**_Scenario#6 :_** The receiver implements the `ERC1155TokenReceiver` interface and is the recipient of one and only one balance change (e.g. `safeTransferFrom` called). +* The balances for the transfer MUST have been updated before the `ERC1155TokenReceiver` hook is called on a recipient contract. +* The transfer event MUST have been emitted to reflect the balance changes before the `ERC1155TokenReceiver` hook is called on the recipient contract. +* One of `onERC1155Received` or `onERC1155BatchReceived` MUST be called on the recipient contract. +* The `onERC1155Received` hook SHOULD be called on the recipient contract and its rules followed. + - See "onERC1155Received rules" for further rules that MUST be followed. +* The `onERC1155BatchReceived` hook MAY be called on the recipient contract and its rules followed. + - See "onERC1155BatchReceived rules" for further rules that MUST be followed. + +**_Scenario#7 :_** The receiver implements the `ERC1155TokenReceiver` interface and is the recipient of more than one balance change (e.g. `safeBatchTransferFrom` called). +* All balance transfers that are referenced in a call to an `ERC1155TokenReceiver` hook MUST be updated before the `ERC1155TokenReceiver` hook is called on the recipient contract. +* All transfer events MUST have been emitted to reflect current balance changes before an `ERC1155TokenReceiver` hook is called on the recipient contract. +* `onERC1155Received` or `onERC1155BatchReceived` MUST be called on the recipient as many times as necessary such that every balance change for the recipient in the scenario is accounted for. + - The return magic value for every hook call MUST be checked and acted upon as per "onERC1155Received rules" and "onERC1155BatchReceived rules". +* The `onERC1155BatchReceived` hook SHOULD be called on the recipient contract and its rules followed. + - See "onERC1155BatchReceived rules" for further rules that MUST be followed. +* The `onERC1155Received` hook MAY be called on the recipient contract and its rules followed. + - See "onERC1155Received rules" for further rules that MUST be followed. + +**_Scenario#8 :_** You are the creator of a contract that implements the `ERC1155TokenReceiver` interface and you forward the token(s) onto another address in one or both of `onERC1155Received` and `onERC1155BatchReceived`. +* Forwarding should be considered acceptance and then initiating a new `safeTransferFrom` or `safeBatchTransferFrom` in a new context. + - The prescribed keccak256 acceptance value magic for the receiver hook being called MUST be returned after forwarding is successful. +* The `_data` argument MAY be re-purposed for the new context. +* If forwarding fails the transaction MAY be reverted. + - If the contract logic wishes to keep the ownership of the token(s) itself in this case it MAY do so. + +**_Scenario#9 :_** You are transferring tokens via a non-standard API call i.e. an implementation specific API and NOT `safeTransferFrom` or `safeBatchTransferFrom`. +* In this scenario all balance updates and events output rules are the same as if a standard transfer function had been called. + - i.e. an external viewer MUST still be able to query the balance via a standard function and it MUST be identical to the balance as determined by `TransferSingle` and `TransferBatch` events alone. +* If the receiver is a contract the `ERC1155TokenReceiver` hooks still need to be called on it and the return values respected the same as if a standard transfer function had been called. + - However while the `safeTransferFrom` or `safeBatchTransferFrom` functions MUST revert if a receiving contract does not implement the `ERC1155TokenReceiver` interface, a non-standard function MAY proceed with the transfer. + - See "Implementation specific transfer API rules". + + +#### Rules + +**_safeTransferFrom rules:_** +* Caller must be approved to manage the tokens being transferred out of the `_from` account (see "Approval" section). +* MUST revert if `_to` is the zero address. +* MUST revert if balance of holder for token `_id` is lower than the `_value` sent to the recipient. +* MUST revert on any other error. +* MUST emit the `TransferSingle` event to reflect the balance change (see "TransferSingle and TransferBatch event rules" section). +* After the above conditions are met, this function MUST check if `_to` is a smart contract (e.g. code size > 0). If so, it MUST call `onERC1155Received` on `_to` and act appropriately (see "onERC1155Received rules" section). + - The `_data` argument provided by the sender for the transfer MUST be passed with its contents unaltered to the `onERC1155Received` hook function via its `_data` argument. + +**_safeBatchTransferFrom rules:_** +* Caller must be approved to manage all the tokens being transferred out of the `_from` account (see "Approval" section). +* MUST revert if `_to` is the zero address. +* MUST revert if length of `_ids` is not the same as length of `_values`. +* MUST revert if any of the balance(s) of the holder(s) for token(s) in `_ids` is lower than the respective amount(s) in `_values` sent to the recipient. +* MUST revert on any other error. +* MUST emit `TransferSingle` or `TransferBatch` event(s) such that all the balance changes are reflected (see "TransferSingle and TransferBatch event rules" section). +* The balance changes and events MUST occur in the array order they were submitted (_ids[0]/_values[0] before _ids[1]/_values[1], etc). +* After the above conditions are met, this function MUST check if `_to` is a smart contract (e.g. code size > 0). If so, it MUST call `onERC1155Received` or `onERC1155BatchReceived` on `_to` and act appropriately (see "onERC1155Received and onERC1155BatchReceived rules" section). + - The `_data` argument provided by the sender for the transfer MUST be passed with its contents unaltered to the `ERC1155TokenReceiver` hook function(s) via their `_data` argument. + +**_TransferSingle and TransferBatch event rules:_** +* `TransferSingle` SHOULD be used to indicate a single balance transfer has occurred between a `_from` and `_to` pair. + - It MAY be emitted multiple times to indicate multiple balance changes in the transaction, but note that `TransferBatch` is designed for this to reduce gas consumption. + - The `_operator` argument MUST be the address of an account/contract that is approved to make the transfer (SHOULD be msg.sender). + - The `_from` argument MUST be the address of the holder whose balance is decreased. + - The `_to` argument MUST be the address of the recipient whose balance is increased. + - The `_id` argument MUST be the token type being transferred. + - The `_value` argument MUST be the number of tokens the holder balance is decreased by and match what the recipient balance is increased by. + - When minting/creating tokens, the `_from` argument MUST be set to `0x0` (i.e. zero address). See "Minting/creating and burning/destroying rules". + - When burning/destroying tokens, the `_to` argument MUST be set to `0x0` (i.e. zero address). See "Minting/creating and burning/destroying rules". +* `TransferBatch` SHOULD be used to indicate multiple balance transfers have occurred between a `_from` and `_to` pair. + - It MAY be emitted with a single element in the list to indicate a singular balance change in the transaction, but note that `TransferSingle` is designed for this to reduce gas consumption. + - The `_operator` argument MUST be the address of an account/contract that is approved to make the transfer (SHOULD be msg.sender). + - The `_from` argument MUST be the address of the holder whose balance is decreased for each entry pair in `_ids` and `_values`. + - The `_to` argument MUST be the address of the recipient whose balance is increased for each entry pair in `_ids` and `_values`. + - The `_ids` array argument MUST contain the ids of the tokens being transferred. + - The `_values` array argument MUST contain the number of token to be transferred for each corresponding entry in `_ids`. + - `_ids` and `_values` MUST have the same length. + - When minting/creating tokens, the `_from` argument MUST be set to `0x0` (i.e. zero address). See "Minting/creating and burning/destroying rules". + - When burning/destroying tokens, the `_to` argument MUST be set to `0x0` (i.e. zero address). See "Minting/creating and burning/destroying rules". +* The total value transferred from address `0x0` minus the total value transferred to `0x0` observed via the `TransferSingle` and `TransferBatch` events MAY be used by clients and exchanges to determine the "circulating supply" for a given token ID. +* To broadcast the existence of a token ID with no initial balance, the contract SHOULD emit the `TransferSingle` event from `0x0` to `0x0`, with the token creator as `_operator`, and a `_value` of 0. +* All `TransferSingle` and `TransferBatch` events MUST be emitted to reflect all the balance changes that have occurred before any call(s) to `onERC1155Received` or `onERC1155BatchReceived`. + - To make sure event order is correct in the case of valid re-entry (e.g. if a receiver contract forwards tokens on receipt) state balance and events balance MUST match before calling an external contract. + +**_onERC1155Received rules:_** +- The `_operator` argument MUST be the address of an account/contract that is approved to make the transfer (SHOULD be msg.sender). +* The `_from` argument MUST be the address of the holder whose balance is decreased. + - `_from` MUST be 0x0 for a mint. +* The `_id` argument MUST be the token type being transferred. +* The `_value` argument MUST be the number of tokens the holder balance is decreased by and match what the recipient balance is increased by. +* The `_data` argument MUST contain the information provided by the sender for the transfer with its contents unaltered. + - i.e. it MUST pass on the unaltered `_data` argument sent via the `safeTransferFrom` or `safeBatchTransferFrom` call for this transfer. +* The recipient contract MAY accept an increase of its balance by returning the acceptance magic value `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` + - If the return value is `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` the transfer MUST be completed or MUST revert if any other conditions are not met for success. +* The recipient contract MAY reject an increase of its balance by calling revert. + - If the recipient contract throws/reverts the transaction MUST be reverted. +* If the return value is anything other than `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` the transaction MUST be reverted. +* `onERC1155Received` (and/or `onERC1155BatchReceived`) MAY be called multiple times in a single transaction and the following requirements must be met: + - All callbacks represent mutually exclusive balance changes. + - The set of all calls to `onERC1155Received` and `onERC1155BatchReceived` describes all balance changes that occurred during the transaction in the order submitted. +* A contract MAY skip calling the `onERC1155Received` hook function if the transfer operation is transferring the token to itself. + +**_onERC1155BatchReceived rules:_** +- The `_operator` argument MUST be the address of an account/contract that is approved to make the transfer (SHOULD be msg.sender). +* The `_from` argument MUST be the address of the holder whose balance is decreased. + - `_from` MUST be 0x0 for a mint. +* The `_ids` argument MUST be the list of tokens being transferred. +* The `_values` argument MUST be the list of number of tokens (matching the list and order of tokens specified in `_ids`) the holder balance is decreased by and match what the recipient balance is increased by. +* The `_data` argument MUST contain the information provided by the sender for the transfer with its contents unaltered. + - i.e. it MUST pass on the unaltered `_data` argument sent via the `safeBatchTransferFrom` call for this transfer. +* The recipient contract MAY accept an increase of its balance by returning the acceptance magic value `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` + - If the return value is `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` the transfer MUST be completed or MUST revert if any other conditions are not met for success. +* The recipient contract MAY reject an increase of its balance by calling revert. + - If the recipient contract throws/reverts the transaction MUST be reverted. +* If the return value is anything other than `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` the transaction MUST be reverted. +* `onERC1155BatchReceived` (and/or `onERC1155Received`) MAY be called multiple times in a single transaction and the following requirements must be met: + - All callbacks represent mutually exclusive balance changes. + - The set of all calls to `onERC1155Received` and `onERC1155BatchReceived` describes all balance changes that occurred during the transaction in the order submitted. +* A contract MAY skip calling the `onERC1155BatchReceived` hook function if the transfer operation is transferring the token(s) to itself. + +**_ERC1155TokenReceiver ERC-165 rules:_** +* The implementation of the ERC-165 `supportsInterface` function SHOULD be as follows: + ```solidity + function supportsInterface(bytes4 interfaceID) external view returns (bool) { + return interfaceID == 0x01ffc9a7 || // ERC-165 support (i.e. `bytes4(keccak256('supportsInterface(bytes4)'))`). + interfaceID == 0x4e2312e0; // ERC-1155 `ERC1155TokenReceiver` support (i.e. `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)")) ^ bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`). + } + ``` +* The implementation MAY differ from the above but: + - It MUST return the constant value `true` if `0x01ffc9a7` is passed through the `interfaceID` argument. This signifies ERC-165 support. + - It MUST return the constant value `true` if `0x4e2312e0` is passed through the `interfaceID` argument. This signifies ERC-1155 `ERC1155TokenReceiver` support. + - It MUST NOT consume more than 10,000 gas. + - This keeps it below the ERC-165 requirement of 30,000 gas, reduces the gas reserve needs and minimises possible side-effects of gas exhaustion during the call. + +**_Implementation specific transfer API rules:_** +* If an implementation specific API function is used to transfer ERC-1155 token(s) to a contract, the `safeTransferFrom` or `safeBatchTransferFrom` (as appropriate) rules MUST still be followed if the receiver implements the `ERC1155TokenReceiver` interface. If it does not the non-standard implementation SHOULD revert but MAY proceed. +* An example: + 1. An approved user calls a function such as `function myTransferFrom(address _from, address _to, uint256[] calldata _ids, uint256[] calldata _values);`. + 2. `myTransferFrom` updates the balances for `_from` and `_to` addresses for all `_ids` and `_values`. + 3. `myTransferFrom` emits `TransferBatch` with the details of what was transferred from address `_from` to address `_to`. + 4. `myTransferFrom` checks if `_to` is a contract address and determines that it is so (if not, then the transfer can be considered successful). + 5. `myTransferFrom` calls `onERC1155BatchReceived` on `_to` and it reverts or returns an unknown value (if it had returned `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` the transfer can be considered successful). + 6. At this point `myTransferFrom` SHOULD revert the transaction immediately as receipt of the token(s) was not explicitly accepted by the `onERC1155BatchReceived` function. + 7. If however `myTransferFrom` wishes to continue it MUST call `supportsInterface(0x4e2312e0)` on `_to` and if it returns the constant value `true` the transaction MUST be reverted, as it is now known to be a valid receiver and the previous acceptance step failed. + - NOTE: You could have called `supportsInterface(0x4e2312e0)` at a previous step if you wanted to gather and act upon that information earlier, such as in a hybrid standards scenario. + 8. If the above call to `supportsInterface(0x4e2312e0)` on `_to` reverts or returns a value other than the constant value `true` the `myTransferFrom` function MAY consider this transfer successful. + - __NOTE__: this MAY result in unrecoverable tokens if sent to an address that does not expect to receive ERC-1155 tokens. +* The above example is not exhaustive but illustrates the major points (and shows that most are shared with `safeTransferFrom` and `safeBatchTransferFrom`): + - Balances that are updated MUST have equivalent transfer events emitted. + - A receiver address has to be checked if it is a contract and if so relevant `ERC1155TokenReceiver` hook function(s) have to be called on it. + - Balances (and events associated) that are referenced in a call to an `ERC1155TokenReceiver` hook MUST be updated (and emitted) before the `ERC1155TokenReceiver` hook is called. + - The return values of the `ERC1155TokenReceiver` hook functions that are called MUST be respected if they are implemented. + - Only non-standard transfer functions MAY allow tokens to be sent to a recipient contract that does NOT implement the necessary `ERC1155TokenReceiver` hook functions. `safeTransferFrom` and `safeBatchTransferFrom` MUST revert in that case (unless it is a hybrid standards implementation see "Backwards Compatibility"). + +**_Minting/creating and burning/destroying rules:_** +* A mint/create operation is essentially a specialized transfer and MUST follow these rules: + - To broadcast the existence of a token ID with no initial balance, the contract SHOULD emit the `TransferSingle` event from `0x0` to `0x0`, with the token creator as `_operator`, and a `_value` of 0. + - The "TransferSingle and TransferBatch event rules" MUST be followed as appropriate for the mint(s) (i.e. singles or batches) however the `_from` argument MUST be set to `0x0` (i.e. zero address) to flag the transfer as a mint to contract observers. + - __NOTE:__ This includes tokens that are given an initial balance in the contract. The balance of the contract MUST also be able to be determined by events alone meaning initial contract balances (for eg. in construction) MUST emit events to reflect those balances too. +* A burn/destroy operation is essentially a specialized transfer and MUST follow these rules: + - The "TransferSingle and TransferBatch event rules" MUST be followed as appropriate for the burn(s) (i.e. singles or batches) however the `_to` argument MUST be set to `0x0` (i.e. zero address) to flag the transfer as a burn to contract observers. + - When burning/destroying you do not have to actually transfer to `0x0` (that is impl specific), only the `_to` argument in the event MUST be set to `0x0` as above. +* The total value transferred from address `0x0` minus the total value transferred to `0x0` observed via the `TransferSingle` and `TransferBatch` events MAY be used by clients and exchanges to determine the "circulating supply" for a given token ID. +* As mentioned above mint/create and burn/destroy operations are specialized transfers and so will likely be accomplished with custom transfer functions rather than `safeTransferFrom` or `safeBatchTransferFrom`. If so the "Implementation specific transfer API rules" section would be appropriate. + - Even in a non-safe API and/or hybrid standards case the above event rules MUST still be adhered to when minting/creating or burning/destroying. +* A contract MAY skip calling the `ERC1155TokenReceiver` hook function(s) if the mint operation is transferring the token(s) to itself. In all other cases the `ERC1155TokenReceiver` rules MUST be followed as appropriate for the implementation (i.e. safe, custom and/or hybrid). + + +##### A solidity example of the keccak256 generated constants for the various magic values (these MAY be used by implementation): + +```solidity +bytes4 constant public ERC1155_ERC165 = 0xd9b67a26; // ERC-165 identifier for the main token standard. +bytes4 constant public ERC1155_ERC165_TOKENRECEIVER = 0x4e2312e0; // ERC-165 identifier for the `ERC1155TokenReceiver` support (i.e. `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)")) ^ bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`). +bytes4 constant public ERC1155_ACCEPTED = 0xf23a6e61; // Return value from `onERC1155Received` call if a contract accepts receipt (i.e `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`). +bytes4 constant public ERC1155_BATCH_ACCEPTED = 0xbc197c81; // Return value from `onERC1155BatchReceived` call if a contract accepts receipt (i.e `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`). +``` + +### Metadata + +The URI value allows for ID substitution by clients. If the string `{id}` exists in any URI, clients MUST replace this with the actual token ID in hexadecimal form. This allows for a large number of tokens to use the same on-chain string by defining a URI once, for that large number of tokens. + +* The string format of the substituted hexadecimal ID MUST be lowercase alphanumeric: `[0-9a-f]` with no 0x prefix. +* The string format of the substituted hexadecimal ID MUST be leading zero padded to 64 hex characters length if necessary. + +Example of such a URI: `https://token-cdn-domain/{id}.json` would be replaced with `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json` if the client is referring to token ID 314592/0x4CCE0. + +#### Metadata Extensions + +The optional `ERC1155Metadata_URI` extension can be identified with the [ERC-165 Standard Interface Detection](./eip-165.md). + +If the optional `ERC1155Metadata_URI` extension is included: +* The ERC-165 `supportsInterface` function MUST return the constant value `true` if `0x0e89341c` is passed through the `interfaceID` argument. +* _Changes_ to the URI MUST emit the `URI` event if the change can be expressed with an event (i.e. it isn't dynamic/programmatic). + - An implementation MAY emit the `URI` event during a mint operation but it is NOT mandatory. An observer MAY fetch the metadata uri at mint time from the `uri` function if it was not emitted. +* The `uri` function SHOULD be used to retrieve values if no event was emitted. +* The `uri` function MUST return the same value as the latest event for an `_id` if it was emitted. +* The `uri` function MUST NOT be used to check for the existence of a token as it is possible for an implementation to return a valid string even if the token does not exist. + +```solidity +pragma solidity ^0.5.9; + +/** + Note: The ERC-165 identifier for this interface is 0x0e89341c. +*/ +interface ERC1155Metadata_URI { + /** + @notice A distinct Uniform Resource Identifier (URI) for a given token. + @dev URIs are defined in RFC 3986. + The URI MUST point to a JSON file that conforms to the "ERC-1155 Metadata URI JSON Schema". + @return URI string + */ + function uri(uint256 _id) external view returns (string memory); +} +``` + +#### ERC-1155 Metadata URI JSON Schema + +This JSON schema is loosely based on the "ERC721 Metadata JSON Schema", but includes optional formatting to allow for ID substitution by clients. If the string `{id}` exists in any JSON value, it MUST be replaced with the actual token ID, by all client software that follows this standard. + +* The string format of the substituted hexadecimal ID MUST be lowercase alphanumeric: `[0-9a-f]` with no 0x prefix. +* The string format of the substituted hexadecimal ID MUST be leading zero padded to 64 hex characters length if necessary. + +```json +{ + "title": "Token Metadata", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Identifies the asset to which this token represents" + }, + "decimals": { + "type": "integer", + "description": "The number of decimal places that the token amount should display - e.g. 18, means to divide the token amount by 1000000000000000000 to get its user representation." + }, + "description": { + "type": "string", + "description": "Describes the asset to which this token represents" + }, + "image": { + "type": "string", + "description": "A URI pointing to a resource with mime type image/* representing the asset to which this token represents. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive." + }, + "properties": { + "type": "object", + "description": "Arbitrary properties. Values may be strings, numbers, object or arrays." + } + } +} +``` + +An example of an ERC-1155 Metadata JSON file follows. The properties array proposes some SUGGESTED formatting for token-specific display properties and metadata. + +```json +{ + "name": "Asset Name", + "description": "Lorem ipsum...", + "image": "https:\/\/s3.amazonaws.com\/your-bucket\/images\/{id}.png", + "properties": { + "simple_property": "example value", + "rich_property": { + "name": "Name", + "value": "123", + "display_value": "123 Example Value", + "class": "emphasis", + "css": { + "color": "#ffffff", + "font-weight": "bold", + "text-decoration": "underline" + } + }, + "array_property": { + "name": "Name", + "value": [1,2,3,4], + "class": "emphasis" + } + } +} +``` + +##### Localization + +Metadata localization should be standardized to increase presentation uniformity across all languages. As such, a simple overlay method is proposed to enable localization. If the metadata JSON file contains a `localization` attribute, its content MAY be used to provide localized values for fields that need it. The `localization` attribute should be a sub-object with three attributes: `uri`, `default` and `locales`. If the string `{locale}` exists in any URI, it MUST be replaced with the chosen locale by all client software. + +##### JSON Schema + +```json +{ + "title": "Token Metadata", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Identifies the asset to which this token represents", + }, + "decimals": { + "type": "integer", + "description": "The number of decimal places that the token amount should display - e.g. 18, means to divide the token amount by 1000000000000000000 to get its user representation." + }, + "description": { + "type": "string", + "description": "Describes the asset to which this token represents" + }, + "image": { + "type": "string", + "description": "A URI pointing to a resource with mime type image/* representing the asset to which this token represents. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive." + }, + "properties": { + "type": "object", + "description": "Arbitrary properties. Values may be strings, numbers, object or arrays.", + }, + "localization": { + "type": "object", + "required": ["uri", "default", "locales"], + "properties": { + "uri": { + "type": "string", + "description": "The URI pattern to fetch localized data from. This URI should contain the substring `{locale}` which will be replaced with the appropriate locale value before sending the request." + }, + "default": { + "type": "string", + "description": "The locale of the default data within the base JSON" + }, + "locales": { + "type": "array", + "description": "The list of locales for which data is available. These locales should conform to those defined in the Unicode Common Locale Data Repository (http://cldr.unicode.org/)." + } + } + } + } +} +``` + +##### Localized Sample + +Base URI: +```json +{ + "name": "Advertising Space", + "description": "Each token represents a unique Ad space in the city.", + "localization": { + "uri": "ipfs://QmWS1VAdMD353A6SDk9wNyvkT14kyCiZrNDYAad4w1tKqT/{locale}.json", + "default": "en", + "locales": ["en", "es", "fr"] + } +} +``` + +es.json: +```json +{ + "name": "Espacio Publicitario", + "description": "Cada token representa un espacio publicitario único en la ciudad." +} +``` + +fr.json: +```json +{ + "name": "Espace Publicitaire", + "description": "Chaque jeton représente un espace publicitaire unique dans la ville." +} +``` + +### Approval + +The function `setApprovalForAll` allows an operator to manage one's entire set of tokens on behalf of the approver. To permit approval of a subset of token IDs, an interface such as [ERC-1761 Scoped Approval Interface](./eip-1761.md) is suggested. +The counterpart `isApprovedForAll` provides introspection into any status set by `setApprovalForAll`. + +An owner SHOULD be assumed to always be able to operate on their own tokens regardless of approval status, so should SHOULD NOT have to call `setApprovalForAll` to approve themselves as an operator before they can operate on them. + +## Rationale + +### Metadata Choices + +The `symbol` function (found in the ERC-20 and ERC-721 standards) was not included as we do not believe this is a globally useful piece of data to identify a generic virtual item / asset and are also prone to collisions. Short-hand symbols are used in tickers and currency trading, but they aren't as useful outside of that space. + +The `name` function (for human-readable asset names, on-chain) was removed from the standard to allow the Metadata JSON to be the definitive asset name and reduce duplication of data. This also allows localization for names, which would otherwise be prohibitively expensive if each language string was stored on-chain, not to mention bloating the standard interface. While this decision may add a small burden on implementers to host a JSON file containing metadata, we believe any serious implementation of ERC-1155 will already utilize JSON Metadata. + +### Upgrades + +The requirement to emit `TransferSingle` or `TransferBatch` on balance change implies that a valid implementation of ERC-1155 redeploying to a new contract address MUST emit events from the new contract address to replicate the deprecated contract final state. It is valid to only emit a minimal number of events to reflect only the final balance and omit all the transactions that led to that state. The event emit requirement is to ensure that the current state of the contract can always be traced only through events. To alleviate the need to emit events when changing contract address, consider using the proxy pattern, such as described in [EIP-2535](./eip-2535.md). This will also have the added benefit of providing a stable contract address for users. + +### Design decision: Supporting non-batch + +The standard supports `safeTransferFrom` and `onERC1155Received` functions because they are significantly cheaper for single token-type transfers, which is arguably a common use case. + +### Design decision: Safe transfers only + +The standard only supports safe-style transfers, making it possible for receiver contracts to depend on `onERC1155Received` or `onERC1155BatchReceived` function to be always called at the end of a transfer. + +### Guaranteed log trace + +As the Ethereum ecosystem continues to grow, many dapps are relying on traditional databases and explorer API services to retrieve and categorize data. The ERC-1155 standard guarantees that event logs emitted by the smart contract will provide enough data to create an accurate record of all current token balances. A database or explorer may listen to events and be able to provide indexed and categorized searches of every ERC-1155 token in the contract. + +### Approval + +The function `setApprovalForAll` allows an operator to manage one's entire set of tokens on behalf of the approver. It enables frictionless interaction with exchange and trade contracts. + +Restricting approval to a certain set of token IDs, quantities or other rules MAY be done with an additional interface or an external contract. The rationale is to keep the ERC-1155 standard as generic as possible for all use-cases without imposing a specific approval scheme on implementations that may not need it. Standard token approval interfaces can be used, such as the suggested [ERC-1761 Scoped Approval Interface](./eip-1761.md) which is compatible with ERC-1155. + +## Backwards Compatibility + +There have been requirements during the design discussions to have this standard be compatible with existing standards when sending to contract addresses, specifically ERC-721 at time of writing. +To cater for this scenario, there is some leeway with the revert logic should a contract not implement the `ERC1155TokenReceiver` as per "Safe Transfer Rules" section above, specifically "Scenario#3 : The receiver does not implement the necessary `ERC1155TokenReceiver` interface function(s)". + +Hence in a hybrid ERC-1155 contract implementation an extra call MUST be made on the recipient contract and checked before any hook calls to `onERC1155Received` or `onERC1155BatchReceived` are made. +Order of operation MUST therefore be: +1. The implementation MUST call the function `supportsInterface(0x4e2312e0)` on the recipient contract, providing at least 10,000 gas. +2. If the function call succeeds and the return value is the constant value `true` the implementation proceeds as a regular ERC-1155 implementation, with the call(s) to the `onERC1155Received` or `onERC1155BatchReceived` hooks and rules associated. +3. If the function call fails or the return value is NOT the constant value `true` the implementation can assume the recipient contract is not an `ERC1155TokenReceiver` and follow its other standard's rules for transfers. + +*__Note that a pure implementation of a single standard is recommended__* rather than a hybrid solution, but an example of a hybrid ERC-1155/ERC-721 contract is linked in the references section under implementations. + +An important consideration is that even if the tokens are sent with another standard's rules the *__ERC-1155 transfer events MUST still be emitted.__* This is so the balances can still be determined via events alone as per ERC-1155 standard rules. + +## Usage + +This standard can be used to represent multiple token types for an entire domain. Both fungible and non-fungible tokens can be stored in the same smart-contract. + +### Batch Transfers + +The `safeBatchTransferFrom` function allows for batch transfers of multiple token IDs and values. The design of ERC-1155 makes batch transfers possible without the need for a wrapper contract, as with existing token standards. This reduces gas costs when more than one token type is included in a batch transfer, as compared to single transfers with multiple transactions. + +Another advantage of standardized batch transfers is the ability for a smart contract to respond to the batch transfer in a single operation using `onERC1155BatchReceived`. + +It is RECOMMENDED that clients and wallets sort the token IDs and associated values (in ascending order) when posting a batch transfer, as some ERC-1155 implementations offer significant gas cost savings when IDs are sorted. See [Horizon Games - Multi-Token Standard](https://github.com/horizon-games/multi-token-standard) "packed balance" implementation for an example of this. + +### Batch Balance + +The `balanceOfBatch` function allows clients to retrieve balances of multiple owners and token IDs with a single call. + +### Enumerating from events + +In order to keep storage requirements light for contracts implementing ERC-1155, enumeration (discovering the IDs and values of tokens) must be done using event logs. It is RECOMMENDED that clients such as exchanges and blockchain explorers maintain a local database containing the token ID, Supply, and URI at the minimum. This can be built from each TransferSingle, TransferBatch, and URI event, starting from the block the smart contract was deployed until the latest block. + +ERC-1155 contracts must therefore carefully emit `TransferSingle` or `TransferBatch` events in any instance where tokens are created, minted, transferred or destroyed. + +### Non-Fungible Tokens + +The following strategies are examples of how you MAY mix fungible and non-fungible tokens together in the same contract. The standard does NOT mandate how an implementation must do this. + +##### Split ID bits + +The top 128 bits of the uint256 `_id` parameter in any ERC-1155 function MAY represent the base token ID, while the bottom 128 bits MAY represent the index of the non-fungible to make it unique. + +Non-fungible tokens can be interacted with using an index based accessor into the contract/token data set. Therefore to access a particular token set within a mixed data contract and a particular non-fungible within that set, `_id` could be passed as ``. + +To identify a non-fungible set/category as a whole (or a fungible) you COULD just pass in the base id via the `_id` argument as ``. If your implementation uses this technique this naturally means the index of a non-fungible SHOULD be 1-based. + +Inside the contract code the two pieces of data needed to access the individual non-fungible can be extracted with uint128(~0) and the same mask shifted by 128. + +```solidity +uint256 baseTokenNFT = 12345 << 128; +uint128 indexNFT = 50; + +uint256 baseTokenFT = 54321 << 128; + +balanceOf(baseTokenNFT, msg.sender); // Get balance of the base token for non-fungible set 12345 (this MAY be used to get balance of the user for all of this token set if the implementation wishes as a convenience). +balanceOf(baseTokenNFT + indexNFT, msg.sender); // Get balance of the token at index 50 for non-fungible set 12345 (should be 1 if user owns the individual non-fungible token or 0 if they do not). +balanceOf(baseTokenFT, msg.sender); // Get balance of the fungible base token 54321. +``` + +Note that 128 is an arbitrary number, an implementation MAY choose how they would like this split to occur as suitable for their use case. An observer of the contract would simply see events showing balance transfers and mints happening and MAY track the balances using that information alone. +For an observer to be able to determine type (non-fungible or fungible) from an ID alone they would have to know the split ID bits format on a implementation by implementation basis. + +The [ERC-1155 Reference Implementation](https://github.com/enjin/erc-1155) is an example of the split ID bits strategy. + +##### Natural Non-Fungible tokens + +Another simple way to represent non-fungibles is to allow a maximum value of 1 for each non-fungible token. This would naturally mirror the real world, where unique items have a quantity of 1 and fungible items have a quantity greater than 1. + +## References + +**Standards** +- [ERC-721 Non-Fungible Token Standard](./eip-721.md) +- [ERC-165 Standard Interface Detection](./eip-165.md) +- [ERC-1538 Transparent Contract Standard](./eip-1538.md) +- [JSON Schema](https://json-schema.org/) +- [RFC 2119 Key words for use in RFCs to Indicate Requirement Levels](https://www.ietf.org/rfc/rfc2119.txt) + +**Implementations** +- [ERC-1155 Reference Implementation](https://github.com/enjin/erc-1155) +- [Horizon Games - Multi-Token Standard](https://github.com/horizon-games/multi-token-standard) +- [Enjin Coin](https://enjincoin.io) ([GitHub](https://github.com/enjin)) +- [The Sandbox - Dual ERC-1155/721 Contract](https://github.com/pixowl/thesandbox-contracts/tree/master/src/Asset) + +**Articles & Discussions** +- [GitHub - Original Discussion Thread](https://github.com/ethereum/EIPs/issues/1155) +- [ERC-1155 - The Crypto Item Standard](https://blog.enjincoin.io/erc-1155-the-crypto-item-standard-ac9cf1c5a226) +- [Here Be Dragons - Going Beyond ERC-20 and ERC-721 To Reduce Gas Cost by ~80%](https://medium.com/horizongames/going-beyond-erc20-and-erc721-9acebd4ff6ef) +- [Blockonomi - Ethereum ERC-1155 Token Perfect for Online Games, Possibly More](https://blockonomi.com/erc1155-gaming-token/) +- [Beyond Gaming - Exploring the Utility of ERC-1155 Token Standard!](https://blockgeeks.com/erc-1155-token/) +- [ERC-1155: A new standard for The Sandbox](https://medium.com/sandbox-game/erc-1155-a-new-standard-for-the-sandbox-c95ee1e45072) + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1167.md b/EIPS/eip-1167.md new file mode 100644 index 0000000..ebb2d0a --- /dev/null +++ b/EIPS/eip-1167.md @@ -0,0 +1,115 @@ +--- +eip: 1167 +title: Minimal Proxy Contract +author: Peter Murray (@yarrumretep), Nate Welch (@flygoing), Joe Messerman (@JAMesserman) +discussions-to: https://github.com/optionality/clone-factory/issues/10 +status: Final +type: Standards Track +category: ERC +created: 2018-06-22 +requires: 211 +--- + +## Simple Summary +To simply and cheaply clone contract functionality in an immutable way, this standard specifies a minimal bytecode implementation that delegates all calls to a known, fixed address. +## Abstract +By standardizing on a known minimal bytecode redirect implementation, this standard allows users and third party tools (e.g. Etherscan) to (a) simply discover that a contract will always redirect in a known manner and (b) depend on the behavior of the code at the destination contract as the behavior of the redirecting contract. Specifically, tooling can interrogate the bytecode at a redirecting address to determine the location of the code that will run - and can depend on representations about that code (verified source, third-party audits, etc). This implementation forwards all calls and 100% of the gas to the implementation contract and then relays the return value back to the caller. In the case where the implementation reverts, the revert is passed back along with the payload data (for revert with message). + +## Motivation +This standard supports use-cases wherein it is desirable to clone exact contract functionality with a minimum of side effects (e.g. memory slot stomping) and with low gas cost deployment of duplicate proxies. + +## Specification +The exact bytecode of the standard clone contract is this: `363d3d373d3d3d363d73bebebebebebebebebebebebebebebebebebebebe5af43d82803e903d91602b57fd5bf3` wherein the bytes at indices 10 - 29 (inclusive) are replaced with the 20 byte address of the master functionality contract. + +A reference implementation of this can be found at the [optionality/clone-factory](https://github.com/optionality/clone-factory) github repo. + +## Rationale +The goals of this effort have been the following: +- inexpensive deployment (low gas to deploy clones) +- support clone initialization in creation transaction (through factory contract model) +- simple clone bytecode to encourage directly bytecode interrogation (see CloneProbe.sol in the clone-factory project) +- dependable, locked-down behavior - this is not designed to handle upgradability, nor should it as the representation we are seeking is stronger. +- small operational overhead - adds a single call cost to each call +- handles error return bubbling for revert messages + +## Backwards Compatibility +There are no backwards compatibility issues. There may be some systems that are using earlier versions of the proxy contract bytecode. They will not be compliant with this standard. + +## Test Cases +Test cases include: +- invocation with no arguments +- invocation with arguments +- invocation with fixed length return values +- invocation with variable length return values +- invocation with revert (confirming reverted payload is transferred) + +Tests for these cases are included in the reference implementation project. + +## Implementation +Deployment bytecode is not included in this specification. One approach is defined in the proxy-contract reference implementation. + +### Standard Proxy +The disassembly of the standard deployed proxy contract code (from r2 and edited to include stack visualization) + +``` +| 0x00000000 36 calldatasize cds +| 0x00000001 3d returndatasize 0 cds +| 0x00000002 3d returndatasize 0 0 cds +| 0x00000003 37 calldatacopy +| 0x00000004 3d returndatasize 0 +| 0x00000005 3d returndatasize 0 0 +| 0x00000006 3d returndatasize 0 0 0 +| 0x00000007 36 calldatasize cds 0 0 0 +| 0x00000008 3d returndatasize 0 cds 0 0 0 +| 0x00000009 73bebebebebe. push20 0xbebebebe 0xbebe 0 cds 0 0 0 +| 0x0000001e 5a gas gas 0xbebe 0 cds 0 0 0 +| 0x0000001f f4 delegatecall suc 0 +| 0x00000020 3d returndatasize rds suc 0 +| 0x00000021 82 dup3 0 rds suc 0 +| 0x00000022 80 dup1 0 0 rds suc 0 +| 0x00000023 3e returndatacopy suc 0 +| 0x00000024 90 swap1 0 suc +| 0x00000025 3d returndatasize rds 0 suc +| 0x00000026 91 swap2 suc 0 rds +| 0x00000027 602b push1 0x2b 0x2b suc 0 rds +| ,=< 0x00000029 57 jumpi 0 rds +| | 0x0000002a fd revert +| `-> 0x0000002b 5b jumpdest 0 rds +\ 0x0000002c f3 return + +``` + +NOTE: as an effort to reduce gas costs as much as possible, the above bytecode depends on EIP-211 specification that `returndatasize` returns zero prior to any calls within the call-frame. `returndatasize` uses 1 less gas than `dup*`. + +### Vanity Address Optimization +Proxy deployment can be further optimized by installing the master contract at a vanity contract deployment address with leading zero-bytes. By generating a master contract vanity address that includes Z leading 0 bytes in its address, you can shorten the proxy bytecode by replacing the `push20` opcode with `pushN` (where N is 20 - Z) followed by the N non-zero address bytes. The revert jump address is decremented by Z in this case. Here is an example where Z = 4: +``` +| 0x00000000 36 calldatasize cds +| 0x00000001 3d returndatasize 0 cds +| 0x00000002 3d returndatasize 0 0 cds +| 0x00000003 37 calldatacopy +| 0x00000004 3d returndatasize 0 +| 0x00000005 3d returndatasize 0 0 +| 0x00000006 3d returndatasize 0 0 0 +| 0x00000007 36 calldatasize cds 0 0 0 +| 0x00000008 3d returndatasize 0 cds 0 0 0 +| 0x00000009 6fbebebebebe. push16 0xbebebebe 0xbebe 0 cds 0 0 0 +| 0x0000001a 5a gas gas 0xbebe 0 cds 0 0 0 +| 0x0000001b f4 delegatecall suc 0 +| 0x0000001c 3d returndatasize rds suc 0 +| 0x0000001d 82 dup3 0 rds suc 0 +| 0x0000001e 80 dup1 0 0 rds suc 0 +| 0x0000001f 3e returndatacopy suc 0 +| 0x00000020 90 swap1 0 suc +| 0x00000021 3d returndatasize rds 0 suc +| 0x00000022 91 swap2 suc 0 rds +| 0x00000023 6027 push1 0x27 0x27 suc 0 rds +| ,=< 0x00000025 57 jumpi 0 rds +| | 0x00000026 fd revert +| `-> 0x00000027 5b jumpdest 0 rds +\ 0x00000028 f3 return +``` +This saves 4 bytes of proxy contract size (savings on each deployment) and has zero impact on runtime gas costs. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1175.md b/EIPS/eip-1175.md new file mode 100644 index 0000000..b56290a --- /dev/null +++ b/EIPS/eip-1175.md @@ -0,0 +1,533 @@ +--- +eip: 1175 +title: Wallet & shop standard for all tokens (erc20) +author: Jet Lim (@Nitro888) +discussions-to: https://github.com/ethereum/EIPs/issues/1182 +status: Stagnant +type: Standards Track +category: ERC +created: 2018-06-21 +requires: 20 +--- + +# All tokens go to heaven +## Simple Summary +Make wallets and shops created from certified contracts make erc20 tokens easy to use for commerce. + +![wallet](/assets/eip-1175/wallet.png) + +## Abstract +The mutual trust between the wallet and the shop created by the authenticated contract allows you to pay for and purchase items at a simple process. + +## Motivation +New standards with improvements have been released, but the majority of tokens currently being developed are erc20 tokens. So I felt I needed a proposal to use old tokens in commerce. + To use various erc20 tokens for trading, you need a custom contract. However, a single wallet with a variety of tokens, and a mutually trusted store, can make transactions that are simple and efficient. The erc20 token is traded through two calls, `approve (address _spender, uint256 _value)` and `transferFrom (address _from, address _to, uint256 _value)`, but when using the wallet contract, `paySafe (address _shop, uint256 _item)`will be traded only in one call. +And if you only reuse the store interface, you can also trade using `payUnsafe (address _shop, uint256 _item)`. + +## Specification +![workflow](/assets/eip-1175/workflow.png) +## WalletCenter +### Methods +#### createWallet +Create wallet contract and add to list. Returns the address of new wallet. + +``` js +function createWallet() public returns (address _wallet) +``` + +#### isWallet +Returns true or false value for test this address is a created by createWallet. + +``` js +function isWallet(address _wallet) public constant returns (bool) +``` + +#### createShop +Create Shop contract and add to list. Returns the address of new Shop with erc20 token address. + +``` js +function createShop(address _erc20) public returns (address _shop) +``` + +#### isShop +Returns true or false value for test this address is a created by createWallet. + +``` js +function isShop(address _shop) public constant returns (bool) +``` + +### Events +#### Wallet +Search for my wallet. +``` js +event Wallet(address indexed _owner, address indexed _wallet) +``` + +#### Shop +Search for my shop. +``` js +event Shop(address indexed _owner, address indexed _shop, address indexed _erc20) +``` + +## Wallet +Wallet must be created by wallet center. +### Methods +#### balanceOf +Returns the account balance of Wallet. +``` js +function balanceOf(address _erc20) public constant returns (uint256 balance) +``` + +#### withdrawal +withdrawal `_value` amount of `_erc20` token to `_owner`. +``` js +function withdrawal(address _erc20, uint256 _value) onlyOwner public returns (bool success) +``` + +#### paySafe +Pay for safe shop (created by contract) item with item index `_item`. +``` js +function paySafe(address _shop, uint256 _item) onlyOwner onlyShop(_shop) public payable returns (bool success) +``` + +#### payUnsafe +Pay for unsafe shop (did not created by contract) item with item index `_item`. +``` js +function payUnsafe(address _shop, uint256 _item) onlyOwner public payable returns (bool success) +``` + +#### payCancel +Cancel pay and refund. (only weekly model) +``` js +function payCancel(address _shop, uint256 _item) onlyOwner public returns (bool success) +``` + +#### refund +Refund from shop with item index `_item`. +``` js +function refund(uint256 _item, uint256 _value) public payable returns (bool success) +``` + +### Events +#### Pay +``` js +event Pay(address indexed _shop, uint256 indexed _item, uint256 indexed _value) +``` + +#### Refund +``` js +event Refund(address indexed _shop, uint256 indexed _item, uint256 indexed _value) +``` + +## Shop +Shop is created by wallet center or not. but Shop that created by wallet center is called safe shop. +### Methods +#### balanceOf +Returns the account balance of Shop. +``` js +function balanceOf(address _erc20) public constant returns (uint256 balance) +``` + +#### withdrawal +withdrawal `_value` amount of `_erc20` token to `_owner`. +``` js +function withdrawal(address _erc20, uint256 _value) onlyOwner public returns (bool success) +``` + +#### pay +Pay from buyer with item index `_item`. +``` js +function pay(uint256 _item) onlyWallet(msg.sender) public payable returns (bool success) +``` + +#### refund +refund token to `_to`. +``` js +function refund(address _buyer, uint256 _item, uint256 _value) onlyWallet(_buyer) onlyOwner public payable returns (bool success) +``` + +#### resister +Listing item for sell. +``` js +function resister(uint8 _category, uint256 _price, uint256 _stock) onlyOwner public returns (uint256 _itemId) +``` + +#### update +Update item state for sell. (change item `_price` or add item `_stock`) +``` js +function update(uint256 _item, uint256 _price, uint256 _stock) onlyOwner public +``` + +#### price +Get token address and price from buyer with item index `_item`. +``` js +function price(uint256 _item) public constant returns (address _erc20, uint256 _value) +``` + +#### canBuy +`_who` can Buy `_item`. +``` js +function canBuy(address _who, uint256 _item) public constant returns (bool _canBuy) +``` + +#### isBuyer +`_who` is buyer of `_item`. +``` js +function isBuyer(address _who, uint256 _item) public constant returns (bool _buyer) +``` + +#### info +Set shop information bytes. +``` js +function info(bytes _msgPack) +``` + +#### upVote +Up vote for this shop. +``` js +function upVote() +``` + +#### dnVote +Down vote for this shop. +``` js +function dnVote() +``` + +#### about +Get shop token, up vote and down vote. +``` js +function about() view returns (address _erc20, uint256 _up, uint256 _down) +``` + +#### infoItem +Set item information bytes. +``` js +function infoItem(uint256 _item, bytes _msgPack) +``` + +#### upVoteItem +Up vote for this item. +``` js +function upVoteItem(uint256 _item) +``` + +#### dnVoteItem +Down vote for this item. +``` js +function dnVoteItem(uint256 _item) +``` + +#### aboutItem +Get Item price, up vote and down vote. +``` js +function aboutItem(uint256 _item) view returns (uint256 _price, uint256 _up, uint256 _down) +``` + +### Events +#### Pay +``` js +event Pay(address indexed _buyer, uint256 indexed _item, uint256 indexed _value) +``` + +#### Refund +``` js +event Refund(address indexed _to, uint256 indexed _item, uint256 indexed _value) +``` + +#### Item +``` js +event Item(uint256 indexed _item, uint256 _price) +``` + +#### Info +``` js +event Info(bytes _msgPack) +``` + +#### InfoItem +``` js +event InfoItem(uint256 indexed _item, bytes _msgPack) +``` + +## Implementation +Sample token contract address is [0x393dd70ce2ae7b30501aec94727968c517f90d52](https://ropsten.etherscan.io/address/0x393dd70ce2ae7b30501aec94727968c517f90d52) + +WalletCenter contract address is [0x1fe0862a4a8287d6c23904d61f02507b5044ea31](https://ropsten.etherscan.io/address/0x1fe0862a4a8287d6c23904d61f02507b5044ea31) + +WalletCenter create shop contract address is [0x59117730D02Ca3796121b7975796d479A5Fe54B0](https://ropsten.etherscan.io/address/0x59117730D02Ca3796121b7975796d479A5Fe54B0) + +WalletCenter create wallet contract address is [0x39da7111844df424e1d0a0226183533dd07bc5c6](https://ropsten.etherscan.io/address/0x39da7111844df424e1d0a0226183533dd07bc5c6) + + +## Appendix +``` js +pragma solidity ^0.4.24; + +contract ERC20Interface { + function totalSupply() public constant returns (uint); + function balanceOf(address tokenOwner) public constant returns (uint balance); + function allowance(address tokenOwner, address spender) public constant returns (uint remaining); + function transfer(address to, uint tokens) public returns (bool success); + function approve(address spender, uint tokens) public returns (bool success); + function transferFrom(address from, address to, uint tokens) public returns (bool success); + + event Transfer(address indexed from, address indexed to, uint tokens); + event Approval(address indexed tokenOwner, address indexed spender, uint tokens); +} + +contract SafeMath { + function safeAdd(uint a, uint b) public pure returns (uint c) { + c = a + b; + require(c >= a); + } + function safeSub(uint a, uint b) public pure returns (uint c) { + require(b <= a); + c = a - b; + } + function safeMul(uint a, uint b) public pure returns (uint c) { + c = a * b; + require(a == 0 || c / a == b); + } + function safeDiv(uint a, uint b) public pure returns (uint c) { + require(b > 0); + c = a / b; + } +} + +contract _Base { + address internal owner; + address internal walletCenter; + + modifier onlyOwner { + require(owner == msg.sender); + _; + } + modifier onlyWallet(address _addr) { + require(WalletCenter(walletCenter).isWallet(_addr)); + _; + } + modifier onlyShop(address _addr) { + require(WalletCenter(walletCenter).isShop(_addr)); + _; + } + + function balanceOf(address _erc20) public constant returns (uint256 balance) { + if(_erc20==address(0)) + return address(this).balance; + return ERC20Interface(_erc20).balanceOf(this); + } + + function transfer(address _to, address _erc20, uint256 _value) internal returns (bool success) { + require((_erc20==address(0)?address(this).balance:ERC20Interface(_erc20).balanceOf(this))>=_value); + if(_erc20==address(0)) + _to.transfer(_value); + else + ERC20Interface(_erc20).approve(_to,_value); + return true; + } + + function withdrawal(address _erc20, uint256 _value) public returns (bool success); + + event Pay(address indexed _who, uint256 indexed _item, uint256 indexed _value); + event Refund(address indexed _who, uint256 indexed _item, uint256 indexed _value); + event Prize(address indexed _who, uint256 indexed _item, uint256 indexed _value); +} + +contract _Wallet is _Base { + constructor(address _who) public { + owner = _who; + walletCenter = msg.sender; + } + + function pay(address _shop, uint256 _item) private { + require(_Shop(_shop).canBuy(this,_item)); + + address _erc20; + uint256 _value; + (_erc20,_value) = _Shop(_shop).price(_item); + + transfer(_shop,_erc20,_value); + _Shop(_shop).pay(_item); + emit Pay(_shop,_item,_value); + } + + function paySafe(address _shop, uint256 _item) onlyOwner onlyShop(_shop) public payable returns (bool success) { + pay(_shop,_item); + return true; + } + function payUnsafe(address _shop, uint256 _item) onlyOwner public payable returns (bool success) { + pay(_shop,_item); + return true; + } + function payCancel(address _shop, uint256 _item) onlyOwner public returns (bool success) { + _Shop(_shop).payCancel(_item); + return true; + } + + function refund(address _erc20, uint256 _item, uint256 _value) public payable returns (bool success) { + require((_erc20==address(0)?msg.value:ERC20Interface(_erc20).allowance(msg.sender,this))==_value); + if(_erc20!=address(0)) + ERC20Interface(_erc20).transferFrom(msg.sender,this,_value); + emit Refund(msg.sender,_item,_value); + return true; + } + function prize(address _erc20, uint256 _item, uint256 _value) public payable returns (bool success) { + require((_erc20==address(0)?msg.value:ERC20Interface(_erc20).allowance(msg.sender,this))==_value); + if(_erc20!=address(0)) + ERC20Interface(_erc20).transferFrom(msg.sender,this,_value); + emit Prize(msg.sender,_item,_value); + return true; + } + + function withdrawal(address _erc20, uint256 _value) onlyOwner public returns (bool success) { + require((_erc20==address(0)?address(this).balance:ERC20Interface(_erc20).balanceOf(this))>=_value); + if(_erc20==address(0)) + owner.transfer(_value); + else + ERC20Interface(_erc20).transfer(owner,_value); + return true; + } +} + +contract _Shop is _Base, SafeMath{ + address erc20; + constructor(address _who, address _erc20) public { + owner = _who; + walletCenter = msg.sender; + erc20 = _erc20; + } + + struct item { + uint8 category; // 0 = disable, 1 = non Stock, non Expire, 2 = can Expire (after 1 week), 3 = stackable + uint256 price; + uint256 stockCount; + + mapping(address=>uint256) customer; + } + + uint index; + mapping(uint256=>item) items; + + function pay(uint256 _item) onlyWallet(msg.sender) public payable returns (bool success) { + require(canBuy(msg.sender, _item)); + require((erc20==address(0)?msg.value:ERC20Interface(erc20).allowance(msg.sender,this))==items[_item].price); + + if(erc20!=address(0)) + ERC20Interface(erc20).transferFrom(msg.sender,this,items[_item].price); + + if(items[_item].category==1 || items[_item].category==2 && now > safeAdd(items[_item].customer[msg.sender], 1 weeks)) + items[_item].customer[msg.sender] = now; + else if(items[_item].category==2 && now < safeAdd(items[_item].customer[msg.sender], 1 weeks) ) + items[_item].customer[msg.sender] = safeAdd(items[_item].customer[msg.sender], 1 weeks); + else if(items[_item].category==3) { + items[_item].customer[msg.sender] = safeAdd(items[_item].customer[msg.sender],1); + items[_item].stockCount = safeSub(items[_item].stockCount,1); + } + + emit Pay(msg.sender,_item,items[_item].customer[msg.sender]); + return true; + } + + function payCancel(uint256 _item) onlyWallet(msg.sender) public returns (bool success) { + require (items[_item].category==2&&safeAdd(items[_item].customer[msg.sender],2 weeks)>now&&balanceOf(erc20)>=items[_item].price); + + items[_item].customer[msg.sender] = safeSub(items[_item].customer[msg.sender],1 weeks); + transfer(msg.sender, erc20, items[_item].price); + _Wallet(msg.sender).refund(erc20,_item,items[_item].price); + emit Refund(msg.sender,_item,items[_item].price); + + return true; + } + function refund(address _to, uint256 _item) onlyWallet(_to) onlyOwner public payable returns (bool success) { + require(isBuyer(_to,_item)&&items[_item].category>0&&(items[_item].customer[_to]>0||(items[_item].category==2&&safeAdd(items[_item].customer[_to],2 weeks)>now))); + require((erc20==address(0)?address(this).balance:ERC20Interface(erc20).balanceOf(this))>=items[_item].price); + + if(items[_item].category==1) + items[_item].customer[_to] = 0; + else if(items[_item].category==2) + items[_item].customer[_to] = safeSub(items[_item].customer[_to],1 weeks); + else + items[_item].customer[_to] = safeSub(items[_item].customer[_to],1); + + transfer(_to, erc20, items[_item].price); + _Wallet(_to).refund(erc20,_item,items[_item].price); + emit Refund(_to,_item,items[_item].price); + + return true; + } + + event Item(uint256 indexed _item, uint256 _price); + function resister(uint8 _category, uint256 _price, uint256 _stock) onlyOwner public returns (uint256 _itemId) { + require(_category>0&&_category<4); + require(_price>0); + items[index] = item(_category,_price,_stock); + index = safeAdd(index,1); + emit Item(index,_price); + return safeSub(index,1); + } + function update(uint256 _item, uint256 _price, uint256 _stock) onlyOwner public { + require(items[_item].category>0); + require(_price>0); + uint256 temp = items[_item].price; + items[_item].price = _price; + items[_item].stockCount = safeAdd(items[_item].stockCount,_stock); + + if(temp!=items[_item].price) + emit Item(index,items[_item].price); + } + + function price(uint256 _item) public constant returns (address _erc20, uint256 _value) { + return (erc20,items[_item].price); + } + + function canBuy(address _who, uint256 _item) public constant returns (bool _canBuy) { + return (items[_item].category>0) && + !(items[_item].category==1&&items[_item].customer[_who]>0) && + (items[_item].stockCount>0); + } + + function isBuyer(address _who, uint256 _item) public constant returns (bool _buyer) { + return (items[_item].category==1&&items[_item].customer[_who]>0)||(items[_item].category==2&&safeAdd(items[_item].customer[_who],1 weeks)>now)||(items[_item].category==3&&items[_item].customer[_who]>0); + } + + uint lastWithdrawal; + function withdrawal(address _erc20, uint256 _value) onlyOwner public returns (bool success) { + require(safeAdd(lastWithdrawal,1 weeks)<=now); + require((_erc20==address(0)?address(this).balance:ERC20Interface(_erc20).balanceOf(this))>=_value); + if(_erc20==address(0)) + owner.transfer(_value); + else + ERC20Interface(_erc20).transfer(owner,_value); + lastWithdrawal = now; + return true; + } +} + +contract WalletCenter { + mapping(address=>bool) public wallet; + event Wallet(address indexed _owner, address indexed _wallet); + function createWallet() public returns (address _wallet) { + _wallet = new _Wallet(msg.sender); + wallet[_wallet] = true; + emit Wallet(msg.sender,_wallet); + return _wallet; + } + function isWallet(address _wallet) public constant returns (bool) { + return wallet[_wallet]; + } + mapping(address=>bool) public shop; + event Shop(address indexed _owner, address indexed _shop, address indexed _erc20); + function createShop(address _erc20) public returns (address _shop) { + _shop = new _Shop(msg.sender,_erc20); + shop[_shop] = true; + emit Shop(msg.sender,_shop,_erc20); + return _shop; + } + function isShop(address _shop) public constant returns (bool) { + return shop[_shop]; + } +} +``` +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1178.md b/EIPS/eip-1178.md new file mode 100644 index 0000000..06a740c --- /dev/null +++ b/EIPS/eip-1178.md @@ -0,0 +1,156 @@ +--- +eip: 1178 +title: Multi-class Token Standard +author: Albert Chon +discussions-to: https://github.com/ethereum/EIPs/issues/1179 +status: Stagnant +type: Standards Track +category: ERC +created: 2018-06-22 +--- + +## Simple Summary +A standard interface for multi-class fungible tokens. +## Abstract +This standard allows for the implementation of a standard API for multi-class fungible tokens (henceforth referred to as "MCFTs") within smart contracts. This standard provides basic functionality to track and transfer ownership of MCFTs. +## Motivation +Currently, there is no standard to support tokens that have multiple classes. In the real world, there are many situations in which defining distinct classes of the same token would be fitting (e.g. distinguishing between preferred/common/restricted shares of a company). Yet, such nuance cannot be supported in today's token standards. An ERC-20 token contract defines tokens that are all of one class while an ERC-721 token contract creates a class (defined by token_id) for each individual token. The ERC-1178 token standard proposes a new standard for creating multiple classes of tokens within one token contract. + +> Aside: In theory, while it is possible to implement tokens with classes using the properties of token structs in ERC-721 tokens, gas costs of implementing this in practice are prohibitive for any non-trivial application. + +## Specification +### ERC-20 Compatibility (partial) +**name** + +```solidity +function name() constant returns (string name) +``` + +*OPTIONAL - It is recommended that this method is implemented for enhanced usability with wallets and exchanges, but interfaces and other contracts MUST NOT depend on the existence of this method.* + +Returns the name of the aggregate collection of MCFTs managed by this contract. - e.g. `"My Company Tokens"`. + +**class name** + +```solidity +function className(uint256 classId) constant returns (string name) +``` + +*OPTIONAL - It is recommended that this method is implemented for enhanced usability with wallets and exchanges, but interfaces and other contracts MUST NOT depend on the existence of this method.* + +Returns the name of the class of MCFT managed by this contract. - e.g. `"My Company Preferred Shares Token"`. + +**symbol** +```solidity +function symbol() constant returns (string symbol) +``` + +*OPTIONAL - It is recommend that this method is implemented for enhanced usability with wallets and exchanges, but interfaces and other contracts MUST NOT depend on the existence of this method.* + +Returns a short string symbol referencing the entire collection of MCFT managed in this contract. e.g. "MUL". This symbol SHOULD be short (3-8 characters is recommended), with no whitespace characters or new-lines and SHOULD be limited to the uppercase latin alphabet (i.e. the 26 letters used in English). + +**totalSupply** +```solidity +function totalSupply() constant returns (uint256 totalSupply) +``` +Returns the total number of all MCFTs currently tracked by this contract. + +**individualSupply** +```solidity +function individualSupply(uint256 _classId) constant returns (uint256 individualSupply) +``` +Returns the total number of MCFTs of class `_classId` currently tracked by this contract. + +**balanceOf** +```solidity +function balanceOf(address _owner, uint256 _classId) constant returns (uint256 balance) +``` + +Returns the number of MCFTs of token class `_classId` assigned to address `_owner`. + +**classesOwned** +```solidity +function classesOwned(address _owner) constant returns (uint256[] classes) +``` + +Returns an array of `_classId`'s of MCFTs that address `_owner` owns in the contract. +> NOTE: returning an array is supported by `pragma experimental ABIEncoderV2` + +## Basic Ownership + +**approve** +```solidity +function approve(address _to, uint256 _classId, uint256 quantity) +``` +Grants approval for address `_to` to take possession `quantity` amount of the MCFT with ID `_classId`. This method MUST `throw` if `balanceOf(msg.sender, _classId) < quantity`, or if `_classId` does not represent an MCFT class currently tracked by this contract, or if `msg.sender == _to`. + +Only one address can "have approval" at any given time for a given address and `_classId`. Calling `approve` with a new address and `_classId` revokes approval for the previous address and `_classId`. Calling this method with 0 as the `_to` argument clears approval for any address and the specified `_classId`. + +Successful completion of this method MUST emit an `Approval` event (defined below) unless the caller is attempting to clear approval when there is no pending approval. In particular, an Approval event MUST be fired if the `_to` address is zero and there is some outstanding approval. Additionally, an Approval event MUST be fired if `_to` is already the currently approved address and this call otherwise has no effect. (i.e. An `approve()` call that "reaffirms" an existing approval MUST fire an event.) + + + +**transfer** +```solidity +function transfer(address _to, uint256 _classId, uint256 quantity) +``` +Assigns the ownership of `quantity` MCFT's with ID `_classId` to `_to` if and only if `quantity == balanceOf(msg.sender, _classId)`. A successful transfer MUST fire the `Transfer` event (defined below). + +This method MUST transfer ownership to `_to` or `throw`, no other outcomes can be possible. Reasons for failure include (but are not limited to): + +* `msg.sender` is not the owner of `quantity` amount of tokens of `_classId`'s. +* `_classId` does not represent an MCFT class currently tracked by this contract + +A conforming contract MUST allow the current owner to "transfer" a token to themselves, as a way of affirming ownership in the event stream. (i.e. it is valid for `_to == msg.sender` if `balanceOf(msg.sender, _classId) >= balance`.) This "no-op transfer" MUST be considered a successful transfer, and therefore MUST fire a `Transfer` event (with the same address for `_from` and `_to`). + +## Advanced Ownership and Exchange +```solidity +function approveForToken(uint256 classIdHeld, uint256 quantityHeld, uint256 classIdWanted, uint256 quantityWanted) +``` +Allows holder of one token to allow another individual (or the smart contract itself) to approve the exchange of their tokens of one class for tokens of another class at their specified exchange rate (see sample implementation for more details). This is equivalent to posting a bid in a marketplace. + +```solidity +function exchange(address to, uint256 classIdPosted, uint256 quantityPosted, uint256 classIdWanted, uint256 quantityWanted) +``` +Allows an individual to fill an existing bid (see above function) and complete the exchange of their tokens of one class for another. In the sample implementation, this function call should fail unless the callee has already approved the contract to transfer their tokens. Of course, it is possible to create an implementation where calling this function implicitly assumes approval and the transfer is completed in one step. + +```solidity +transferFrom(address from, address to, uint256 classId) +``` +Allows a third party to initiate a transfer of tokens from `from` to `to` assuming the approvals have been granted. + +## Events +**Transfer** + +This event MUST trigger when MCFT ownership is transferred via any mechanism. + +Additionally, the creation of new MCFTs MUST trigger a Transfer event for each newly created MCFTs, with a `_from` address of 0 and a `_to` address matching the owner of the new MCFT (possibly the smart contract itself). The deletion (or burn) of any MCFT MUST trigger a Transfer event with a `_to` address of 0 and a `_from` address of the owner of the MCFT (now former owner!). + +NOTE: A Transfer event with `_from == _to` is valid. See the `transfer()` documentation for details. + +```solidity +event Transfer(address indexed _from, address indexed _to, uint256 _classId) +``` + +**Approval** +This event MUST trigger on any successful call to `approve(_to, _classId, quantity)` (unless the caller is attempting to clear approval when there is no pending approval). + +```solidity +event Approval(address indexed _owner, address indexed _approved, uint256 _classId) +``` +## Rationale +### Current Limitations +The design of this project was motivated when I tried to create different classes of fungible ERC-721 tokens (an oxymoron) but ran into gas limits from having to create each tokens individually and maintain them in an efficient data structure for access. Using the maximum gas amount one can send with a transaction on Metamask (a popular web wallet), I was only able to create around 46 ERC-721 tokens before exhausting all gas. This experience motivated the creation of the multi-class fungible token standard. + + +## Backwards Compatibility +Adoption of the MCFT standard proposal would not pose backwards compatibility issues as it defines a new standard for token creation. This standard follows the semantics of ERC-721 as closely as possible, but can't be entirely compatible with it due to the fundamental differences between multi-class fungible and non-fungible tokens. For example, the `ownerOf`, `takeOwnership`, and `tokenOfOwnerByIndex` methods in the ERC-721 token standard cannot be implemented in this standard. Furthermore, the function arguments to `balanceOf`, `approve`, and `transfer` differ as well. + +## Implementation +A sample implementation can be found [here](https://github.com/achon22/ERC-1178/blob/master/erc1178-sample.sol) + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). + diff --git a/EIPS/eip-1185.md b/EIPS/eip-1185.md new file mode 100644 index 0000000..eab7b2b --- /dev/null +++ b/EIPS/eip-1185.md @@ -0,0 +1,80 @@ +--- +eip: 1185 +title: Storage of DNS Records in ENS +author: Jim McDonald (@mcdee) +status: Stagnant +type: Standards Track +category: ERC +created: 2018-06-26 +requires: 137 +discussions-to: https://ethereum-magicians.org/t/eip1185-dns-resolver-profile-for-ens/1589 +--- + +## Abstract +This EIP defines a resolver profile for ENS that provides features for storage and lookup of DNS records. This allows ENS to be used as a store of authoritative DNS information. + +## Motivation +ENS is a highly desirable store for DNS information. It provides the distributed authority of DNS without conflating ownership and authoritative serving of information. With ENS, the owner of a domain has full control over their own DNS records. Also, ENS has the ability (through smart contracts) for a domain's subdomains to be irrevocably assigned to another entity. + +## Specification + +The resolver profile to support DNS on ENS follows the resolver specification as defined in #137. + +Traditionally, DNS is a zone-based system in that all of the records for a zone are kept together in the same file. This has the benefit of simplicity and atomicity of zone updates, but when transposed to ENS can result in significant gas costs for simple changes. As a result, the resolver works on the basis of record sets. A record set is uniquely defined by the tuple (domain, name, resource record type), for example the tuple (example.com, www.example.com, A) defines the record set of A records for the name www.example.com in the domain example.com. A record set can contain 0 or more values, for example if www.example.com has A records 1.2.3.4 and 5.6.7.8 then the aforementioned tuple will have two values. + +The choice to work at the level of record sets rather than zones means that this specification cannot completely support some features of DNS, such as zone transfers and DNSSEC. It would be possible to build a different resolver profile that works at the zone level, however it would be very expensive to carry out updates and so is not considered further for this EIP. + +The DNS resolver interface consists of two functions to set DNS information and two functions to query DNS information. + +### setDNSRecords(bytes32 node, bytes data) + +`setDNSRecords()` sets, updates or clears 1 or more DNS records for a given node. It has function signature `0x0af179d7`. + +The arguments for the function are as follows: + - node: the namehash of the fully-qualified domain in ENS for which to set the records. Namehashes are defined in #137 + - data: 1 or more DNS records in DNS wire format. Any record that is supplied without a value will be cleared. Note that all records in the same RRset should be contiguous within the data; if not then the later RRsets will overwrite the earlier one(s) + +### clearDNSZone(bytes32 node) + +`clearDNSZone()` removes all DNS records for the domain. It has function signature `0xad5780af`. + +Although it is possible to clear records individually with `setDNSRecords()` as described above this requires the owner to know all of the records that have been set (as the resolver has no methods to iterate over the records for a given domain), and might require multiple transactions. `clearDNSZone()` removes all zone information in a single operation. + +The arguments for the function is as follows: + - node: the namehash of the fully-qualified domain in ENS for which to clear the records. Namehashes are defined in #137 + +### dnsRecords(bytes32 node, bytes32 name, uint16 resource) view returns (bytes) + +`dnsRecords()` obtains the DNS records for a given node, name and resource. It has function signature `0x2461e851`. + +The arguments for the function are as follows: + - node: the namehash of the fully-qualified domain in ENS for which to set the records. Namehashes are defined in #137 + - name: the `keccak256()` hash of the name of the record in DNS wire format. + - resource: the resource record ID. Resource record IDs are defined in https://en.wikipedia.org/wiki/List\_of\_DNS\_record\_types + +The function returns all matching records in DNS wire format. If there are no records present the function will return nothing. + +### hasDNSRecords(bytes32 node, bytes32 name) view returns (bool) + +`hasDNSRecords()` reports if there are any records for the provided name in the domain. It has function signature `0x4cbf6ba4`. + +This function is needed by DNS resolvers when working with wildcard resources as defined in https://tools.ietf.org/html/rfc4592 + +The arguments for the function are as follows: + - node: the namehash of the fully-qualified domain in ENS for which to set the records. Namehashes are defined in #137 + - name: the `keccak256()` hash of the name of the record in DNS wire format. + +The function returns `true` if there are any records for the provided node and name, otherwise `false`. + +## Backwards compatibility +Not applicable. + +## Implementation +The reference implementation of the DNS resolver is at https://github.com/wealdtech/wealdtech-solidity/blob/master/contracts/ens/DNSResolver.sol + +https://github.com/wealdtech/ethereal.git can be used to test the functionality of the resolver with the "dns set", "dns get" and "dns clear" commands. +## Test Cases +Test cases for the DNS resolver are at https://github.com/wealdtech/wealdtech-solidity/blob/master/test/ens/DNSResolver.js + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1186.md b/EIPS/eip-1186.md new file mode 100644 index 0000000..b97cf5a --- /dev/null +++ b/EIPS/eip-1186.md @@ -0,0 +1,138 @@ +--- +eip: 1186 +title: RPC-Method to get Merkle Proofs - eth_getProof +author: Simon Jentzsch , Christoph Jentzsch +discussions-to: https://github.com/ethereum/EIPs/issues/1186 +status: Stagnant +type: Standards Track +category: Interface +created: 2018-06-24 +requires: 1474 +--- + +## Simple Summary + +One of the great features of Ethereum is the fact, that you can verify all data of the state. But in order to allow verification of accounts outside the client, we need an additional function delivering us the required proof. These proofs are important to secure Layer2-Technologies. + +## Abstract + +Ethereum uses a [Merkle Tree](https://github.com/ethereum/wiki/wiki/Patricia-Tree) to store the state of accounts and their storage. This allows verification of each value by simply creating a Merkle Proof. But currently, the standard RPC-Interface does not give you access to these proofs. This EIP suggests an additional RPC-Method, which creates Merkle Proofs for Accounts and Storage Values. + +Combined with a stateRoot (from the blockheader) it enables offline verification of any account or storage-value. This allows especially IOT-Devices or even mobile apps which are not able to run a light client to verify responses from an untrusted source only given a trusted blockhash. + +## Motivation + +In order to create a MerkleProof access to the full state db is required. The current RPC-Methods allow an application to access single values (`eth_getBalance`,`eth_getTransactionCount`,`eth_getStorageAt`,`eth_getCode`), but it is impossible to read the data needed for a MerkleProof through the standard RPC-Interface. (There are implementations using leveldb and accessing the data via filesystems, but this can not be used for production systems since it requires the client to be stopped first - See https://github.com/zmitton/eth-proof) + +Today MerkleProofs are already used internally. For example, the [Light Client Protocol](https://github.com/zsfelfoldi/go-ethereum/wiki/Light-Ethereum-Subprotocol-%28LES%29#on-demand-data-retrieval) supports a function creating MerkleProof, which is used in order to verify the requested account or storage-data. + +Offering these already existing function through the RPC-Interface as well would enable Applications to store and send these proofs to devices which are not directly connected to the p2p-network and still are able to verify the data. This could be used to verify data in mobile applications or IOT-devices, which are currently only using a remote client. + + +## Specification + +As Part of the eth-Module, an additional Method called `eth_getProof` should be defined as follows: + +#### eth_getProof + +Returns the account- and storage-values of the specified account including the Merkle-proof. + +##### Parameters + +1. `DATA`, 20 Bytes - address of the account. +2. `ARRAY`, 32 Bytes - array of storage-keys which should be proofed and included. See [`eth_getStorageAt`](https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getstorageat) +3. `QUANTITY|TAG` - integer block number, or the string `"latest"` or `"earliest"`, see the [default block parameter](https://github.com/ethereum/wiki/wiki/JSON-RPC#the-default-block-parameter) + +##### Returns + +`Object` - A account object: + + - `balance`: `QUANTITY` - the balance of the account. See [`eth_getBalance`](https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getbalance) + - `codeHash`: `DATA`, 32 Bytes - hash of the code of the account. For a simple Account without code it will return `"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"` + - `nonce`: `QUANTITY`, - nonce of the account. See [`eth_getTransactionCount`](https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_gettransactioncount) + - `storageHash`: `DATA`, 32 Bytes - SHA3 of the StorageRoot. All storage will deliver a MerkleProof starting with this rootHash. + - `accountProof`: `ARRAY` - Array of rlp-serialized MerkleTree-Nodes, starting with the stateRoot-Node, following the path of the SHA3 (address) as key. + - `storageProof`: `ARRAY` - Array of storage-entries as requested. Each entry is a object with these properties: + + - `key`: `QUANTITY` - the requested storage key + - `value`: `QUANTITY` - the storage value + - `proof`: `ARRAY` - Array of rlp-serialized MerkleTree-Nodes, starting with the storageHash-Node, following the path of the SHA3 (key) as path. + + +##### Example + + +```json +{ + "id": 1, + "jsonrpc": "2.0", + "method": "eth_getProof", + "params": [ + "0x7F0d15C7FAae65896648C8273B6d7E43f58Fa842", + [ "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421" ], + "latest" + ] +} +``` + +The result will look like this: + +```json +{ + "id": 1, + "jsonrpc": "2.0", + "result": { + "accountProof": [ + "0xf90211a...0701bc80", + "0xf90211a...0d832380", + "0xf90211a...5fb20c80", + "0xf90211a...0675b80", + "0xf90151a0...ca08080" + ], + "balance": "0x0", + "codeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": "0x0", + "storageHash": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "storageProof": [ + { + "key": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "proof": [ + "0xf90211a...0701bc80", + "0xf90211a...0d832380" + ], + "value": "0x1" + } + ] + } +} +``` + +## Rationale + +This one Method actually returns 3 different important data points: + +1. The 4 fields of an account-object as specified in the yellow paper `[nonce, balance, storageHash, codeHash ]`, which allows storing a hash of the account-object in order to keep track of changes. +2. The MerkleProof for the account starting with a stateRoot from the specified block. +3. The MerkleProof for each requested storage entry starting with a storageHash from the account. + +Combining these in one Method allows the client to work very efficient since the required data are already fetched from the db. + +### Proofs for non existent values + +In case an address or storage-value does not exist, the proof needs to provide enough data to verify this fact. This means the client needs to follow the path from the root node and deliver until the last matching node. If the last matching node is a branch, the proof value in the node must be an empty one. In case of leaf-type, it must be pointing to a different relative-path in order to proof that the requested path does not exist. + +### possible Changes to be discussed: + +- instead of providing the blocknumber maybe the blockhash would be better since it would allow proofs of uncles-states. +- in order to reduce data, the account-object may only provide the `accountProof` and `storageProof`. The Fields `balance`, `nonce`, `storageHash` and `codeHash` could be taken from the last Node in the proof by deserializing it. + +## Backwards Compatibility + +Since this only adds a new Method there are no issues with Backwards Compatibility. + +## Test Cases + +TODO: Tests still need to be implemented, but the core function creating the proof already exists inside the clients and are well tested. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1191.md b/EIPS/eip-1191.md new file mode 100644 index 0000000..1820880 --- /dev/null +++ b/EIPS/eip-1191.md @@ -0,0 +1,123 @@ +--- +eip: 1191 +title: Add chain id to mixed-case checksum address encoding +author: Juliano Rizzo (@juli) +status: Last Call +last-call-deadline: 2019-11-18 +type: Standards Track +category: ERC +created: 2018-03-18 +requires: 55, 155 +discussions-to: https://github.com/ethereum/EIPs/issues/1121 +--- + +## Simple Summary +This EIP extends EIP-55 by optionally adding a chain id defined by EIP-155 to the checksum calculation. + +## Abstract +The EIP-55 was created to prevent users from losing funds by sending them to invalid addresses. This EIP extends EIP-55 to protect users from losing funds by sending them to addresses that are valid but that where obtained from a client of another network.For example, if this EIP is implemented, a wallet can alert the user that is trying to send funds to an Ethereum Testnet address from an Ethereum Mainnet wallet. + +## Motivation +The motivation of this proposal is to provide a mechanism to allow software to distinguish addresses from different Ethereum based networks. This proposal is necessary because Ethereum addresses are hashes of public keys and do not include any metadata. By extending the EIP-55 checksum algorithm it is possible to achieve this objective. + +## Specification +Convert the address using the same algorithm defined by EIP-55 but if a registered chain id is provided, add it to the input of the hash function. If the chain id passed to the function belongs to a network that opted for using this checksum variant, prefix the address with the chain id and the `0x` separator before calculating the hash. Then convert the address to hexadecimal, but if the ith digit is a letter (ie. it's one of `abcdef`) print it in uppercase if the 4*ith bit of the calculated hash is 1 otherwise print it in lowercase. + +## Rationale + Benefits: + - By means of a minimal code change on existing libraries, users are protected from losing funds by mixing addresses of different Ethereum based networks. + +## Implementation +```python +#!/usr/bin/python3 +from sha3 import keccak_256 +import random +""" + addr (str): Hexadecimal address, 40 characters long with 2 characters prefix + chainid (int): chain id from EIP-155 """ +def eth_checksum_encode(addr, chainid=1): + adopted_eip1191 = [30, 31] + hash_input = str(chainid) + addr.lower() if chainid in adopted_eip1191 else addr[2:].lower() + hash_output = keccak_256(hash_input.encode('utf8')).hexdigest() + aggregate = zip(addr[2:].lower(),hash_output) + out = addr[:2] + ''.join([c.upper() if int(a,16) >= 8 else c for c,a in aggregate]) + return out +``` + +## Test Cases +```python +eth_mainnet = [ +"0x27b1fdb04752bbc536007a920d24acb045561c26", +"0x3599689E6292b81B2d85451025146515070129Bb", +"0x42712D45473476b98452f434e72461577D686318", +"0x52908400098527886E0F7030069857D2E4169EE7", +"0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed", +"0x6549f4939460DE12611948b3f82b88C3C8975323", +"0x66f9664f97F2b50F62D13eA064982f936dE76657", +"0x8617E340B3D01FA5F11F306F4090FD50E238070D", +"0x88021160C5C792225E4E5452585947470010289D", +"0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb", +"0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB", +"0xde709f2102306220921060314715629080e2fb77", +"0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359", +] +rsk_mainnet = [ +"0x27b1FdB04752BBc536007A920D24ACB045561c26", +"0x3599689E6292B81B2D85451025146515070129Bb", +"0x42712D45473476B98452f434E72461577d686318", +"0x52908400098527886E0F7030069857D2E4169ee7", +"0x5aaEB6053f3e94c9b9a09f33669435E7ef1bEAeD", +"0x6549F4939460DE12611948B3F82B88C3C8975323", +"0x66F9664f97f2B50F62d13EA064982F936de76657", +"0x8617E340b3D01Fa5f11f306f4090fd50E238070D", +"0x88021160c5C792225E4E5452585947470010289d", +"0xD1220A0Cf47c7B9BE7a2e6ba89F429762E7B9adB", +"0xDBF03B407c01E7CD3cBea99509D93F8Dddc8C6FB", +"0xDe709F2102306220921060314715629080e2FB77", +"0xFb6916095cA1Df60bb79ce92cE3EA74c37c5d359", +] +rsk_testnet = [ +"0x27B1FdB04752BbC536007a920D24acB045561C26", +"0x3599689e6292b81b2D85451025146515070129Bb", +"0x42712D45473476B98452F434E72461577D686318", +"0x52908400098527886E0F7030069857D2e4169EE7", +"0x5aAeb6053F3e94c9b9A09F33669435E7EF1BEaEd", +"0x6549f4939460dE12611948b3f82b88C3c8975323", +"0x66f9664F97F2b50f62d13eA064982F936DE76657", +"0x8617e340b3D01fa5F11f306F4090Fd50e238070d", +"0x88021160c5C792225E4E5452585947470010289d", +"0xd1220a0CF47c7B9Be7A2E6Ba89f429762E7b9adB", +"0xdbF03B407C01E7cd3cbEa99509D93f8dDDc8C6fB", +"0xDE709F2102306220921060314715629080e2Fb77", +"0xFb6916095CA1dF60bb79CE92ce3Ea74C37c5D359", +] +test_cases = {30 : rsk_mainnet, 31 : rsk_testnet, 1 : eth_mainnet} + +for chainid, cases in test_cases.items(): + for addr in cases: + assert ( addr == eth_checksum_encode(addr,chainid) ) +``` +## Usage + +### Usage Table + +| Network | Chain id | Supports this EIP | +|-|-|-| +| RSK Mainnet | 30 | Yes | +| RSK Testnet | 31 | Yes | + +### Implementation Table +| Project | EIP Usage | Implementation | +|-|-|-| +| MyCrypto | Yes | [JavaScript](https://github.com/MyCryptoHQ/MyCrypto/blob/develop/common/utils/formatters.ts#L126) | +| MyEtherWallet | Yes | [JavaScript](https://github.com/MyEtherWallet/MyEtherWallet/blob/73c4a24f8f67c655749ac990c5b62efd92a2b11a/src/helpers/addressUtils.js#L22) | +| Ledger | Yes | [C](https://github.com/LedgerHQ/ledger-app-eth/blob/master/src_common/ethUtils.c#L203) | +| Trezor | Yes | [Python](https://github.com/trezor/trezor-core/blob/270bf732121d004a4cd1ab129adaccf7346ff1db/src/apps/ethereum/get_address.py#L32) and [C](https://github.com/trezor/trezor-crypto/blob/4153e662b60a0d83c1be15150f18483a37e9092c/address.c#L62) | +| Web3.js | Yes | [JavaScript](https://github.com/ethereum/web3.js/blob/aaf26c8806bc9fb60cf6dcb6658104963c6c7fc7/packages/web3-utils/src/Utils.js#L140) | +| EthereumJS-util | Yes | [JavaScript](https://github.com/ethereumjs/ethereumjs-util/pull/204/commits/cdf0b3c996b05ac5b1f758f17ea9f9ed1847c1eb) | +| ENS address-encoder | Yes | [TypeScript](https://github.com/ensdomains/address-encoder/commit/5bf53b13fa014646ea28c9e5f937361dc9b40590) | + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). + diff --git a/EIPS/eip-1193.md b/EIPS/eip-1193.md new file mode 100644 index 0000000..01cabe1 --- /dev/null +++ b/EIPS/eip-1193.md @@ -0,0 +1,576 @@ +--- +eip: 1193 +title: Ethereum Provider JavaScript API +author: Fabian Vogelsteller (@frozeman), Ryan Ghods (@ryanio), Victor Maia (@MaiaVictor), Marc Garreau (@marcgarreau), Erik Marks (@rekmarks) +discussions-to: https://github.com/ethereum/EIPs/issues/2319 +status: Final +type: Standards Track +category: Interface +created: 2018-06-30 +requires: 155, 695 +--- + +## Summary + +A JavaScript Ethereum Provider API for consistency across clients and applications. + +## Abstract + +A common convention in the Ethereum web application ("dapp") ecosystem is for key management software ("wallets") to expose their API via a JavaScript object in the web page. +This object is called "the Provider". + +Historically, Provider implementations have exhibited conflicting interfaces and behaviors between wallets. +This EIP formalizes an Ethereum Provider API to promote wallet interoperability. +The API is designed to be minimal, event-driven, and agnostic of transport and RPC protocols. +Its functionality is easily extended by defining new RPC methods and `message` event types. + +Historically, Providers have been made available as `window.ethereum` in web browsers, but this convention is not part of the specification. + +## Specification + +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). + +> Comments like this are non-normative. + +### Definitions + +_This section is non-normative._ + +- Provider + - A JavaScript object made available to a consumer, that provides access to Ethereum by means of a Client. +- Client + - An endpoint that receives Remote Procedure Call (RPC) requests from the Provider, and returns their results. +- Wallet + - An end-user application that manages private keys, performs signing operations, and acts as a middleware between the Provider and the Client. +- Remote Procedure Call (RPC) + - A Remote Procedure Call (RPC), is any request submitted to a Provider for some procedure that is to be processed by a Provider, its Wallet, or its Client. + +### Connectivity + +The Provider is said to be "connected" when it can service RPC requests to at least one chain. + +The Provider is said to be "disconnected" when it cannot service RPC requests to any chain at all. + +> To service an RPC request, the Provider must successfully submit the request to the remote location, and receive a response. +> In other words, if the Provider is unable to communicate with its Client, for example due to network issues, the Provider is disconnected. + +### API + +> The Provider API is specified using TypeScript. +> The authors encourage implementers to declare their own types and interfaces, using the ones in this section as a basis. +> +> For consumer-facing API documentation, see [Appendix I](#appendix-i-consumer-facing-api-documentation) + +The Provider **MUST** implement and expose the API defined in this section. +All API entities **MUST** adhere to the types and interfaces defined in this section. + +#### request + +> The `request` method is intended as a transport- and protocol-agnostic wrapper function for Remote Procedure Calls (RPCs). + +```typescript +interface RequestArguments { + readonly method: string; + readonly params?: readonly unknown[] | object; +} + +Provider.request(args: RequestArguments): Promise; +``` + +The Provider **MUST** identify the requested RPC method by the value of `RequestArguments.method`. + +If the requested RPC method takes any parameters, the Provider **MUST** accept them as the value of `RequestArguments.params`. + +RPC requests **MUST** be handled such that the returned Promise either resolves with a value per the requested RPC method's specification, or rejects with an error. + +If resolved, the Promise **MUST** resolve with a result per the RPC method's specification. The Promise **MUST NOT** resolve with any RPC protocol-specific response objects, unless the RPC method's return type is so defined. + +If the returned Promise rejects, it **MUST** reject with a `ProviderRpcError` as specified in the [RPC Errors](#rpc-errors) section below. + +The returned Promise **MUST** reject if any of the following conditions are met: + +- An error is returned for the RPC request. + - If the returned error is compatible with the `ProviderRpcError` interface, the Promise **MAY** reject with that error directly. +- The Provider encounters an error or fails to process the request for any reason. + +> If the Provider implements any kind of authorization logic, the authors recommend rejecting with a `4100` error in case of authorization failures. + +The returned Promise **SHOULD** reject if any of the following conditions are met: + +- The Provider is disconnected. + - If rejecting for this reason, the Promise rejection error `code` **MUST** be `4900`. +- The RPC request is directed at a specific chain, and the Provider is not connected to that chain, but is connected to at least one other chain. + - If rejecting for this reason, the Promise rejection error `code` **MUST** be `4901`. + +See the section [Connectivity](#connectivity) for the definitions of "connected" and "disconnected". + +### Supported RPC Methods + +A "supported RPC method" is any RPC method that may be called via the Provider. + +All supported RPC methods **MUST** be identified by unique strings. + +Providers **MAY** support whatever RPC methods required to fulfill their purpose, standardized or otherwise. + +If an RPC method defined in a finalized EIP is not supported, it **SHOULD** be rejected with a `4200` error per the [Provider Errors](#provider-errors) section below, or an appropriate error per the RPC method's specification. + +#### RPC Errors + +```typescript +interface ProviderRpcError extends Error { + code: number; + data?: unknown; +} +``` + +- `message` + - **MUST** be a human-readable string + - **SHOULD** adhere to the specifications in the [Error Standards](#error-standards) section below +- `code` + - **MUST** be an integer number + - **SHOULD** adhere to the specifications in the [Error Standards](#error-standards) section below +- `data` + - **SHOULD** contain any other useful information about the error + +##### Error Standards + +`ProviderRpcError` codes and messages **SHOULD** follow these conventions, in order of priority: + +1. The errors in the [Provider Errors](#provider-errors) section below + +2. Any errors mandated by the erroring RPC method's specification + +3. The [`CloseEvent` status codes](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes) + +#### Provider Errors + +| Status code | Name | Description | +| ----------- | --------------------- | ------------------------------------------------------------------------ | +| 4001 | User Rejected Request | The user rejected the request. | +| 4100 | Unauthorized | The requested method and/or account has not been authorized by the user. | +| 4200 | Unsupported Method | The Provider does not support the requested method. | +| 4900 | Disconnected | The Provider is disconnected from all chains. | +| 4901 | Chain Disconnected | The Provider is not connected to the requested chain. | + +> `4900` is intended to indicate that the Provider is disconnected from all chains, while `4901` is intended to indicate that the Provider is disconnected from a specific chain only. +> In other words, `4901` implies that the Provider is connected to other chains, just not the requested one. + +### Events + +The Provider **MUST** implement the following event handling methods: + +- `on` +- `removeListener` + +These methods **MUST** be implemented per the Node.js [`EventEmitter` API](https://nodejs.org/api/events.html). + +> To satisfy these requirements, Provider implementers should consider simply extending the Node.js `EventEmitter` class and bundling it for the target environment. + +#### message + +> The `message` event is intended for arbitrary notifications not covered by other events. + +When emitted, the `message` event **MUST** be emitted with an object argument of the following form: + +```typescript +interface ProviderMessage { + readonly type: string; + readonly data: unknown; +} +``` + +##### Subscriptions + +If the Provider supports Ethereum RPC subscriptions, e.g. [`eth_subscribe`](https://geth.ethereum.org/docs/rpc/pubsub), the Provider **MUST** emit the `message` event when it receives a subscription notification. + +If the Provider receives a subscription message from e.g. an `eth_subscribe` subscription, the Provider **MUST** emit a `message` event with a `ProviderMessage` object of the following form: + +```typescript +interface EthSubscription extends ProviderMessage { + readonly type: 'eth_subscription'; + readonly data: { + readonly subscription: string; + readonly result: unknown; + }; +} +``` + +#### connect + +See the section [Connectivity](#connectivity) for the definition of "connected". + +If the Provider becomes connected, the Provider **MUST** emit the event named `connect`. + +This includes when: + +- The Provider first connects to a chain after initialization. +- The Provider connects to a chain after the `disconnect` event was emitted. + +This event **MUST** be emitted with an object of the following form: + +```typescript +interface ProviderConnectInfo { + readonly chainId: string; +} +``` + +`chainId` **MUST** specify the integer ID of the connected chain as a hexadecimal string, per the [`eth_chainId`](./eip-695.md) Ethereum RPC method. + +#### disconnect + +See the section [Connectivity](#connectivity) for the definition of "disconnected". + +If the Provider becomes disconnected from all chains, the Provider **MUST** emit the event named `disconnect` with value `error: ProviderRpcError`, per the interfaced defined in the [RPC Errors](#rpc-errors) section. The value of the error's `code` property **MUST** follow the [status codes for `CloseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes). + +#### chainChanged + +If the chain the Provider is connected to changes, the Provider **MUST** emit the event named `chainChanged` with value `chainId: string`, specifying the integer ID of the new chain as a hexadecimal string, per the [`eth_chainId`](./eip-695.md) Ethereum RPC method. + +#### accountsChanged + +If the accounts available to the Provider change, the Provider **MUST** emit the event named `accountsChanged` with value `accounts: string[]`, containing the account addresses per the `eth_accounts` Ethereum RPC method. + +The "accounts available to the Provider" change when the return value of `eth_accounts` changes. + +## Rationale + +The purpose of a Provider is to _provide_ a consumer with access to Ethereum. +In general, a Provider must enable an Ethereum web application to do two things: + +- Make Ethereum RPC requests +- Respond to state changes in the Provider's Ethereum chain, Client, and Wallet + +The Provider API specification consists of a single method and five events. +The `request` method and the `message` event alone, are sufficient to implement a complete Provider. +They are designed to make arbitrary RPC requests and communicate arbitrary messages, respectively. + +The remaining four events can be separated into two categories: + +- Changes to the Provider's ability to make RPC requests + - `connect` + - `disconnect` +- Common Client and/or Wallet state changes that any non-trivial application must handle + - `chainChanged` + - `accountsChanged` + +These events are included due to the widespread production usage of related patterns, at the time of writing. + +## Backwards Compatibility + +Many Providers adopted a draft version of this specification before it was finalized. +The current API is designed to be a strict superset of the legacy version, and this specification is in that sense fully backwards compatible. +See [Appendix III](#appendix-iii-legacy-provider-api) for the legacy API. + +Providers that only implement this specification will not be compatible with Ethereum web applications that target the legacy API. + +## Implementations + +At the time of writing, the following projects have working implementations: + +- [buidler.dev](https://github.com/nomiclabs/buidler/pull/608) +- [ethers.js](https://github.com/ethers-io/ethers.js/blob/56af4413b1dd1787db68985e0b612b63d86fdf7c/packages/providers/src.ts/web3-provider.ts) +- [eth-provider](https://www.npmjs.com/package/eth-provider) +- [MetaMask](https://github.com/MetaMask/inpage-provider) +- [WalletConnect](https://github.com/WalletConnect/walletconnect-monorepo/blob/d33fd2070d7a67f74de50fd10ca4217f4e2f22f3/packages/providers/web3-provider/README.md) +- [web3.js](https://web3js.readthedocs.io/) + +## Security Considerations + +The Provider is intended to pass messages between an Ethereum Client and an Ethereum application. +It is _not_ responsible for private key or account management; it merely processes RPC messages and emits events. +Consequently, account security and user privacy need to be implemented in middlewares between the Provider and its Ethereum Client. +In practice, we call these middleware applications "Wallets," and they usually manage the user's private keys and accounts. +The Provider can be thought of as an extension of the Wallet, exposed in an untrusted environment, under the control of some third party (e.g. a website). + +### Handling Adversarial Behavior + +Since it is a JavaScript object, consumers can generally perform arbitrary operations on the Provider, and all its properties can be read or overwritten. +Therefore, it is best to treat the Provider object as though it is controlled by an adversary. +It is paramount that the Provider implementer protects the user, Wallet, and Client by ensuring that: + +- The Provider does not contain any private user data. +- The Provider and Wallet programs are isolated from each other. +- The Wallet and/or Client rate-limit requests from the Provider. +- The Wallet and/or Client validate all data sent from the Provider. + +### Chain Changes + +Since all Ethereum operations are directed at a particular chain, it's important that the Provider accurately reflects the Client's configured chain, per the `eth_chainId` Ethereum RPC method (see [EIP-695](./eip-695.md)). + +This includes ensuring that `eth_chainId` has the correct return value, and that the `chainChanged` event is emitted whenever that value changes. + +### User Account Exposure and Account Changes + +Many Ethereum write operations (e.g. `eth_sendTransaction`) require a user account to be specified. +Provider consumers access these accounts via the `eth_accounts` RPC method, and by listening for the `accountsChanged` event. + +As with `eth_chainId`, it is critical that `eth_accounts` has the correct return value, and that the `accountsChanged` event is emitted whenever that value changes. + +The return value of `eth_accounts` is ultimately controlled by the Wallet or Client. +In order to protect user privacy, the authors recommend not exposing any accounts by default. +Instead, Providers should support RPC methods for explicitly requesting account access, such as `eth_requestAccounts` (see [EIP-1102](./eip-1102.md)) or `wallet_requestPermissions` (see [EIP-2255](./eip-2255.md)). + +## References + +- [Initial discussion in `ethereum/interfaces`](https://github.com/ethereum/interfaces/issues/16) +- [Deprecated Ethereum Magicians thread](https://ethereum-magicians.org/t/eip-1193-ethereum-provider-javascript-api/640) +- [Continuing discussion](https://github.com/ethereum/EIPs/issues/2319) +- Related EIPs + - [EIP-1102: Opt-in Account Exposure](./eip-1102.md) + - [EIP-1474: Remote Procedure Call Specification](./eip-1474.md) + - [EIP-1767: GraphQL Interface to Ethereum Node Data](./eip-1767.md) + - [EIP-2255: Wallet Permissions](./eip-2255.md) + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). + +## Appendix I: Consumer-Facing API Documentation + +### request + +Makes an Ethereum RPC method call. + +```typescript +interface RequestArguments { + readonly method: string; + readonly params?: readonly unknown[] | object; +} + +Provider.request(args: RequestArguments): Promise; +``` + +The returned Promise resolves with the method's result or rejects with a [`ProviderRpcError`](#errors). For example: + +```javascript +Provider.request({ method: 'eth_accounts' }) + .then((accounts) => console.log(accounts)) + .catch((error) => console.error(error)); +``` + +Consult each Ethereum RPC method's documentation for its `params` and return type. +You can find a list of common methods [here](./eip-1474.md). + +#### RPC Protocols + +Multiple RPC protocols may be available. For examples, see: + +- [EIP-1474](./eip-1474.md), the Ethereum JSON-RPC API +- [EIP-1767](./eip-1767.md), the Ethereum GraphQL schema + +### Events + +Events follow the conventions of the Node.js [`EventEmitter` API](https://nodejs.org/api/events.html). + +#### connect + +The Provider emits `connect` when it: + +- first connects to a chain after being initialized. +- first connects to a chain, after the `disconnect` event was emitted. + +```typescript +interface ProviderConnectInfo { + readonly chainId: string; +} + +Provider.on('connect', listener: (connectInfo: ProviderConnectInfo) => void): Provider; +``` + +The event emits an object with a hexadecimal string `chainId` per the `eth_chainId` Ethereum RPC method, and other properties as determined by the Provider. + +#### disconnect + +The Provider emits `disconnect` when it becomes disconnected from all chains. + +```typescript +Provider.on('disconnect', listener: (error: ProviderRpcError) => void): Provider; +``` + +This event emits a [`ProviderRpcError`](#errors). The error `code` follows the table of [`CloseEvent` status codes](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes). + +#### chainChanged + +The Provider emits `chainChanged` when connecting to a new chain. + +```typescript +Provider.on('chainChanged', listener: (chainId: string) => void): Provider; +``` + +The event emits a hexadecimal string `chainId` per the `eth_chainId` Ethereum RPC method. + +#### accountsChanged + +The Provider emits `accountsChanged` if the accounts returned from the Provider (`eth_accounts`) change. + +```typescript +Provider.on('accountsChanged', listener: (accounts: string[]) => void): Provider; +``` + +The event emits with `accounts`, an array of account addresses, per the `eth_accounts` Ethereum RPC method. + +#### message + +The Provider emits `message` to communicate arbitrary messages to the consumer. +Messages may include JSON-RPC notifications, GraphQL subscriptions, and/or any other event as defined by the Provider. + +```typescript +interface ProviderMessage { + readonly type: string; + readonly data: unknown; +} + +Provider.on('message', listener: (message: ProviderMessage) => void): Provider; +``` + +##### Subscriptions + +[`eth_` subscription methods](https://geth.ethereum.org/docs/rpc/pubsub) and [`shh_` subscription methods](https://github.com/ethereum/go-ethereum/wiki/Whisper-v6-RPC-API#shh_subscribe) rely on this event to emit subscription updates. + +For e.g. `eth_subscribe` subscription updates, `ProviderMessage.type` will equal the string `'eth_subscription'`, and the subscription data will be the value of `ProviderMessage.data`. + +### Errors + +```typescript +interface ProviderRpcError extends Error { + message: string; + code: number; + data?: unknown; +} +``` + +## Appendix II: Examples + +These examples assume a web browser environment. + +```javascript +// Most Providers are available as window.ethereum on page load. +// This is only a convention, not a standard, and may not be the case in practice. +// Please consult the Provider implementation's documentation. +const ethereum = window.ethereum; + +// Example 1: Log chainId +ethereum + .request({ method: 'eth_chainId' }) + .then((chainId) => { + console.log(`hexadecimal string: ${chainId}`); + console.log(`decimal number: ${parseInt(chainId, 16)}`); + }) + .catch((error) => { + console.error(`Error fetching chainId: ${error.code}: ${error.message}`); + }); + +// Example 2: Log last block +ethereum + .request({ + method: 'eth_getBlockByNumber', + params: ['latest', true], + }) + .then((block) => { + console.log(`Block ${block.number}:`, block); + }) + .catch((error) => { + console.error( + `Error fetching last block: ${error.message}. + Code: ${error.code}. Data: ${error.data}` + ); + }); + +// Example 3: Log available accounts +ethereum + .request({ method: '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}` + ); + }); + +// Example 4: Log new blocks +ethereum + .request({ + method: 'eth_subscribe', + params: ['newHeads'], + }) + .then((subscriptionId) => { + ethereum.on('message', (message) => { + if (message.type === 'eth_subscription') { + const { data } = message; + if (data.subscription === subscriptionId) { + if ('result' in data && typeof data.result === 'object') { + const block = data.result; + console.log(`New block ${block.number}:`, block); + } else { + console.error(`Something went wrong: ${data.result}`); + } + } + } + }); + }) + .catch((error) => { + console.error( + `Error making newHeads subscription: ${error.message}. + Code: ${error.code}. Data: ${error.data}` + ); + }); + +// Example 5: Log when accounts change +const logAccounts = (accounts) => { + console.log(`Accounts:\n${accounts.join('\n')}`); +}; +ethereum.on('accountsChanged', logAccounts); +// to unsubscribe +ethereum.removeListener('accountsChanged', logAccounts); + +// Example 6: Log if connection ends +ethereum.on('disconnect', (code, reason) => { + console.log(`Ethereum Provider connection closed: ${reason}. Code: ${code}`); +}); +``` + +## Appendix III: Legacy Provider API + +This section documents the legacy Provider API, which is extensively used in production at the time of writing. +As it was never fully standardized, significant deviations occur in practice. +The authors recommend against implementing it except to support legacy Ethereum applications. + +### sendAsync (DEPRECATED) + +This method is superseded by [`request`](#request). + +`sendAsync` is like `request`, but with JSON-RPC objects and a callback. + +```typescript +Provider.sendAsync(request: Object, callback: Function): void; +``` + +Historically, the request and response object interfaces have followed the [Ethereum JSON-RPC specification](./eip-1474.md). + +### send (DEPRECATED) + +This method is superseded by [`request`](#request). + +```typescript +Provider.send(...args: unknown[]): unknown; +``` + +### Legacy Events + +#### close (DEPRECATED) + +This event is superseded by [`disconnect`](#disconnect). + +#### networkChanged (DEPRECATED) + +The event `networkChanged` is superseded by [`chainChanged`](#chainchanged). + +For details, see [EIP-155: Simple replay attack protection](./eip-155.md) and [EIP-695: Create eth_chainId method for JSON-RPC](./eip-695.md). + +#### notification (DEPRECATED) + +This event is superseded by [`message`](#message). + +Historically, this event has been emitted with e.g. `eth_subscribe` subscription updates of the form `{ subscription: string, result: unknown }`. diff --git a/EIPS/eip-1202.md b/EIPS/eip-1202.md new file mode 100644 index 0000000..9ab0f84 --- /dev/null +++ b/EIPS/eip-1202.md @@ -0,0 +1,160 @@ +--- +eip: 1202 +title: Voting Interface +description: A general interface for voting on-chain +author: Zainan Victor Zhou (@xinbenlv), Evan (@evbots), Yin Xu (@yingogobot) +discussions-to: https://ethereum-magicians.org/t/eip-1202-voting-interface/11484 +status: Draft +type: Standards Track +category: ERC +created: 2018-07-08 +--- + +## Abstract + +This EIP is an API for implementing voting with smart contract. This standard provides functionalities to voting as well as to view the vote result and set voting status. + +## Motivation + +Voting is one of the earliest example of EVM programming, and also a key to DAO/organizational governance process. We foresee many DAOs will ultimately need to leverage voting as one of the important part of their governance. By creating a voting standard for smart contract / token, we can have the following benefits + +### Benefits of having a standard + +1. Allow general UI and applications to be built on top of a standardized voting to allow more general user to participate, and encourage more DApp and DAO to think about their governance +2. Allow delegate voting / smart contract voting, automatic voting +3. Allow voting results to be recorded on-chain, in a standard way, and allow DAOs and DApps to honor the voting result programmatically. +4. Allow the compatibility with token standard such as [EIP-20](./eip-20.md) or other new standards([EIP-777](./eip-777.md)) and item standard such as [EIP-721](./eip-721.md) +5. Create massive potential for interoperability within Ethereum echo systems and other system. +6. Allow setting voting deadline, allow determine on single or multiple options. Allow requiring voting orders. (trade-off is interface complexity, we might need [EIP-20](./eip-20.md) approach and later a [EIP-777](./eip-777.md) for advanced voting) +7. Recording the voting with weights with token amount. +8. Possibly allow trust-worthy privacy-safe voting and anonymous voting (with either voter address being un-associated with the vote they cast, given a list of randomized/obfuscated voting options). + +9. Possibly allow result in reward by voting participation or voting result. + +### Non-Goal / Out of Scope + +1. **Delegation**: We intentionally leave delegation out of scope. A separate EIP could be proposed to address this particular use case. +2. **Eligibility or Weights**: Some of the implementing want to have weights or eligibility to vote to be configurable. Such as OpenZeppelin's implementation of GovernorBravo uses snapshot. Aslo weights calculation such as quadratic voting is not within the scope of this EIP. This EIP is intend to be flexible for +any current and new voting weights calculation. +3. **Proposal**: We intentionally leave Proposal out of scope. Proposals are going to be identified by `proposalId` but what information of the proposal includes, +whether they are on-chain or off-chain and whether they are exeutable, is leaved out from this proposal. A separate EIP could be proposed to address this particular use case. See one of such proposals [EIP-5247](./eip-5247.md) +4. **Signature Aggregations / Endorsement**: When implementing contracts want to allow user to submit their vote or approval of vote offline and have some other +account to generate the transaction, the signature aggregations or endorsements are not in scope of this EIP. A separate EIP could be proposed to address this particular use case. See one of such proposals here [EIP-5453](./eip-5453.md). + +### Use-cases + +1. Determine on issuing new token, issuing more token or issuing sub-token +2. Determine on creating new item under [EIP-721](./eip-721.md) +3. Determine on election on certain person or smart contract to be delegated leader for project or subproject +4. Determine on auditing result ownership allowing migration of smart contract proxy address + +## Specification + +1. Compliant contracts MUST implement the `IERC1202Core` below + +```solidity +interface IERC1202Core { + event VoteCast( + address indexed voter, + uint256 indexed proposalId, + uint8 support, + uint256 weight, + string reason, + bytes extraParams + ); + + function castVote( + uint256 proposalId, + uint8 support, + uint256 weight, + string calldata reasonUri, + bytes calldata extraParams + ) external payable returns; + + function castVoteFrom( + address from, + uint256 proposalId, + uint8 support, + uint256 weight, + string calldata reasonUri, + bytes calldata extraParams + ) external payable returns; + + function execute(uint256 proposalId, bytes memory extraParams) payable external; +} +``` + +2. Compliant contracts MAY implement the `IERC1202MultiVote` Interface. If the intention is for multi-options to be supported, e.g. for ranked-choices +or variant weights voting, Compliant contracts MUST implement `IERC1202MultiVote` Interface. + +```solidity +interface IERC1202MultiVote { + event MultiVoteCast( + address indexed voter, + uint256 indexed proposalId, + uint8[] support, + uint256[] weight, + string reason, + bytes extraParams + ); + + function castMultiVote( + uint256 proposalId, + uint8[] support, + uint256[] weight, + string calldata reasonUri, + bytes calldata extraParams + ) external payable; +} +``` + +### Getting Info: Voting Period, Eligibility, Weight + +```solidity +interface IERC1202Info { + function votingPeriodFor(uint256 proposalId) external view returns (uint256 startPointOfTime, uint256 endPointOfTime); + function eligibleVotingWeight(uint256 proposalId, address voter) external view returns (uint256); +} +``` + +## Rationale + +We made the following design decisions and here are the rationales. + +### Granularity and Anonymity + +We created a `view` function `ballotOf` primarily making it easier for people to check the vote from certain address. This has the following assumptions: + +- It's possible to check someone's vote directly given an address. If implementor don't want to make it so easily, they can simply reject all calls to this function. We want to make sure that we support both anonymous voting an non-anonymous voting. However since all calls to a smart contract is logged in block history, there is really no secrecy unless done with cryptography tricks. I am not cryptography-savvy enough to comment on the possibility. Please see "Second Feedback Questions 2018" for related topic. + +- It's assumes for each individual address, they can only vote for one decision. They can distribute their available voting power into more granular level. If implementor wants allow this, they ask the user to create another wallet address and grant the new address certain power. For example, a token based voting where voting weight is determined by the amount of token held by a voter, a voter who wants to distribute its voting power in two different option(option set) can transfer some of the tokens to the new account and cast the votes from both accounts. + +### Weights + +We assume there are `weight` of votes and can be checked by calling `eligibleVotingWeight(proposalId, address voter)`, and the weight distribution is either internally determined or determined by constructor. + +## Backwards Compatibility + +1. The `support` options are chosen to be `uint8` for the purpose to be backward compatible for GovernorBravo. It can be increased in the future + +## Security Considerations + +We expect the voting standard to be used in connection with other contracts such as token distributions, conducting actions in consensus or on behalf of an entity, multi-signature wallets, etc. + +The major security consideration is to ensure only using the standard interface for performing downstream actions or receiving upstream input (vote casting). We expect future audit tool to be based on standard interfaces. + +It's also important to note as discussed in this standard that for the sake of simplicity, this EIP is kept in the very basic form. It can be extended to support many different implementation variations. Such variations might contain different assumptions of the behavior and interpretation of actions. One example would be: What does it mean if someone votes multiple times through `vote`? + +- Would that mean the voter is increasing their weight, or +- vote multiple options in the meanwhile, or +- Does the latter vote override the previous vote? + +Because of the flexible nature of voting, we expect many subsequent standards need to be created as an extension of this EIP. We suggest any extension or implementations of this standard be thoroughly audited before included in large scale or high asset volume applications. + +The third consideration is non-triviality. Some voting applications assume **_anonymity_**, **_randomness_**, **_time-based deadline_**, **_ordering_**, etc, these requirements in Ethereum are known to be non-trivial to achieve. We suggest any applications or organizations rely on audited and time-proven shared libraries when these requirements need to be enforced in their applications. + +The fourth consideration is potential abuse. When voting is standardized and put on contract, it is possible to write another contract that rewards a voter to vote in a certain way. It creates potential issues of bribery and conflict of interest abuse that is previously hard to implement. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1203.md b/EIPS/eip-1203.md new file mode 100644 index 0000000..dc97178 --- /dev/null +++ b/EIPS/eip-1203.md @@ -0,0 +1,230 @@ +--- +eip: 1203 +title: ERC-1203 Multi-Class Token Standard (ERC-20 Extension) +author: Jeff Huang , Min Zu +discussions-to: https://github.com/ethereum/EIPs/issues/1203 +status: Stagnant +type: Standards Track +category: ERC +created: 2018-07-01 +--- + +## Simple Summary + +A standard interface for multi-class tokens (MCTs). + +## Abstract + +The following standard allows for the implementation of a standard API for MCTs within smart contracts. This standard provides basic functionality to track, transfer, and convert MCTs. + +## Motivation + +This standard is heavily inspired by ERC-20 Token Standard and ERC-721 Non-Fungible Token Standard. However, whereas these standards are chiefly concerned with representation of items/value in a single class, fungible or note, this proposed standard focus on that of a more complexed, multi-class system. It is fair to think of MCTs as a hybrid of fungible tokens (FT) and non-fungible tokens (NFTs), that is tokens are fungible within the same class but non-fungible with that from a different class. And conversions between classes may be optionally supported. + +MCTs are useful in representing various structures with heterogeneous components, such as: + +- **Abstract Concepts:** A company may have different classes of stocks (e.g. senior preferred, junior preferred, class A common, class B common) that together make up its outstanding equities. A shareholder's position of such company composites of zero or more shares in each class. + +- **Virtual Items:** A sandbox computer game may have many types of resources (e.g. rock, wood, berries, cows, meat, knife, etc.) that together make up that virtual world. A player's inventory has any combination and quantity of these resources + +- **Physical Items:** A supermarket may have many SKUs it has available for purchase (e.g. eggs, milk, beef jerky, beer, etc.). Things get added or removed from a shopper's cart as it moves down the aisle. + +It's sometimes possible, especially with regard to abstract concepts or virtual items, to convert from one class to another, at a specified conversion ratio. When it comes to physical items, such conversion essentially is the implementation of bartering. Though it might generally be easier to introduce a common intermediary class, i.e. money. + +## Specification + +```solidity +contract ERC20 { + function totalSupply() public view returns (uint256); + function balanceOf(address _owner) public view returns (uint256); + function transfer(address _to, uint256 _value) public returns (bool); + function approve(address _spender, uint256 _value) public returns (bool); + function allowance(address _owner, address _spender) public view returns (uint256); + function transferFrom(address _from, address _to, uint256 _value) public returns (bool); + + event Transfer(address indexed _from, address indexed _to, uint256 _value); + event Approval(address indexed _owner, address indexed _spender, uint256 _value); +} + +contract ERC1203 is ERC20 { + function totalSupply(uint256 _class) public view returns (uint256); + function balanceOf(address _owner, uint256 _class) public view returns (uint256); + function transfer(address _to, uint256 _class, uint256 _value) public returns (bool); + function approve(address _spender, uint256 _class, uint256 _value) public returns (bool); + function allowance(address _owner, address _spender, uint256 _class) public view returns (uint256); + function transferFrom(address _from, address _to, uint256 _class, uint256 _value) public returns (bool); + + function fullyDilutedTotalSupply() public view returns (uint256); + function fullyDilutedBalanceOf(address _owner) public view returns (uint256); + function fullyDilutedAllowance(address _owner, address _spender) public view returns (uint256); + function convert(uint256 _fromClass, uint256 _toClass, uint256 _value) public returns (bool); + + event Transfer(address indexed _from, address indexed _to, uint256 _class, uint256 _value); + event Approval(address indexed _owner, address indexed _spender, uint256 _class, uint256 _value); + event Convert(uint256 indexed _fromClass, uint256 indexed _toClass, uint256 _value); +} +``` + +### ERC-20 Methods and Events (fully compatible) + +Please see [ERC-20 Token Standard](./eip-20.md) for detailed specifications. Do note that these methods and events only work on the "default" class of an MCT. + +```solidity + function totalSupply() public view returns (uint256); + function balanceOf(address _owner) public view returns (uint256); + function transfer(address _to, uint256 _value) public returns (bool); + function approve(address _spender, uint256 _value) public returns (bool); + function allowance(address _owner, address _spender) public view returns (uint256); + function transferFrom(address _from, address _to, uint256 _value) public returns (bool); + + event Transfer(address indexed _from, address indexed _to, uint256 _value); + event Approval(address indexed _owner, address indexed _spender, uint256 _value); +``` + +### Tracking and Transferring + +**totalSupply** + +Returns the total number of tokens in the specified `_class` + +```solidity + function totalSupply(uint256 _class) public view returns (uint256); +``` + +**balanceOf** + +Returns the number of tokens of a specified `_class` that the `_owner` has + +```solidity + function balanceOf(address _owner, uint256 _class) public view returns (uint256); +``` + +**transfer** + +Transfer `_value` tokens of `_class` to address specified by `_to`, return `true` if successful + +```solidity + function transfer(address _to, uint256 _class, uint256 _value) public returns (bool); +``` + +**approve** + +Grant `_spender` the right to transfer `_value` tokens of `_class`, return `true` if successful + +```solidity + function approve(address _spender, uint256 _class, uint256 _value) public returns (bool); +``` + +**allowance** + +Return the number of tokens of `_class` that `_spender` is authorized to transfer on the behalf of `_owner` + +```solidity + function allowance(address _owner, address _spender, uint256 _class) public view returns (uint256); +``` + +**transferFrom** + +Transfer `_value` tokens of `_class` from address specified by `_from` to address specified by `_to` as previously approved, return `true` if successful + +```solidity + function transferFrom(address _from, address _to, uint256 _class, uint256 _value) public returns (bool); +``` + +**Transfer** + +Triggered when tokens are transferred or created, including zero value transfers + +```solidity + event Transfer(address indexed _from, address indexed _to, uint256 _class, uint256 _value); +``` + +**Approval** + +Triggered on successful `approve` + +```solidity + event Approval(address indexed _owner, address indexed _spender, uint256 _class, uint256 _value); +``` + +### Conversion and Dilution + +**fullyDilutedTotalSupply** + +Return the total token supply as if all converted to the lowest common denominator class + +```solidity + function fullyDilutedTotalSupply() public view returns (uint256); +``` + +**fullyDilutedBalanceOf** + +Return the total token owned by `_owner` as if all converted to the lowest common denominator class + +```solidity + function fullyDilutedBalanceOf(address _owner) public view returns (uint256); +``` + +**fullyDilutedAllowance** + +Return the total token `_spender` is authorized to transfer on behalf of `_owner` as if all converted to the lowest common denominator class + +```solidity + function fullyDilutedAllowance(address _owner, address _spender) public view returns (uint256); +``` + +**convert** + +Convert `_value` of `_fromClass` to `_toClass`, return `true` if successful + +```solidity + function convert(uint256 _fromClass, uint256 _toClass, uint256 _value) public returns (bool); +``` + +**Conversion** + +Triggered on successful `convert` + +```solidity + event Conversion(uint256 indexed _fromClass, uint256 indexed _toClass, uint256 _value); +``` + +## Rationale +This standard purposely extends ERC-20 Token Standard so that new MCTs following or existing ERC-20 tokens extending this standard are fully compatible with current wallets and exchanges. In addition, new methods and events are kept as closely to ERC-20 conventions as possible for ease of adoption. + +We have considered alternative implementations to support the multi-class structure, as discussed below, and we found current token standards incapable or inefficient in deal with such structures. + +**Using multiple ERC-20 tokens** + +It is certainly possible to create an ERC-20 token for each class, and a separate contract to coordinate potential conversions, but the short coming in this approach is clearly evident. The rationale behind this standard is to have a single contract to manage multiple classes of tokens. + +**Shoehorning ERC-721 token** + +Treating each token as unique, the non-fungible token standard offers maximum representational flexibility arguably at the expense of convenience. The main challenge of using ERC-721 to represent multi-class token is that separate logic is required to keep track of which tokens belongs to which class, a hacky and unnecessary endeavor. + +**Using ERC-1178 token** + +We came across ERC-1178 as we were putting final touches on our own proposal. The two ERCs look very similar on the surface but we believe there're a few key advantages this one has over ERC-1178. + +- ERC-1178 offers no backward compatibility whereas this proposal is an extension of ERC-20 and therefore fully compatible with all existing wallets and exchanges +- By the same token, existing ERC-20 contracts can extend themselves to adopt this standard and support additional classes without affecting their current behaviors +- This proposal introduces the concept of cross class conversion and dilution, making each token class integral part of a whole system rather than many silos + +## Backwards Compatibility +This EIP is fully compatible with the mandatory methods of ERC20 Token Standard so long as the implementation includes a "lowest common denominator" class, which may be class B common/gold coin/money in the abstract/virtual/physical examples above respectively. Where it is not possible to implement such class, then the implementation should specify a default class for tracking or transferring unless otherwise specified, e.g. US dollar is transferred unless other currency is explicitly specified. + +We find it contrived to require the optional methods of ERC20 Token Standard, `name()`, `symbol()`, and `decimals()`, but developers are certainly free to implement these as they wish. + +## Test Cases +The repository at [jeffishjeff/ERC-1203](https://github.com/jeffishjeff/ERC-1203) contains the [sample test cases](https://github.com/jeffishjeff/ERC-1203/blob/master/token.test.js). + +## Implementation +The repository at [jeffishjeff/ERC-1203](https://github.com/jeffishjeff/ERC-1203) contains the [sample implementation](https://github.com/jeffishjeff/ERC-1203/blob/master/token.sol). + +## References +- ERC-20 Token Standard. ./eip-20.md +- ERC-721 Non-Fungible Token Standard. ./eip-721.md +- ERC-1178 Multi-class Token Standard. ./eip-1178.md + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1207.md b/EIPS/eip-1207.md new file mode 100644 index 0000000..9b9fb0c --- /dev/null +++ b/EIPS/eip-1207.md @@ -0,0 +1,169 @@ +--- +eip: 1207 +title: DAuth Access Delegation Standard +author: Xiaoyu Wang (@wxygeek), Bicong Wang (@Wangbicong) +discussions-to: https://github.com/ethereum/EIPs/issues/1207 +status: Stagnant +type: Standards Track +category: ERC +created: 2018-07-10 +--- + +DAuth Access Delegation Standard +===== + +## Simple Summary +DAuth is a standard interface for accessing authorization delegation between smart contracts and users. + +## Abstract +The DAuth protocol defines a set of standard API allowing identity delegations between smart contracts without the user's private key. Identity delegations include accessing and operating a user's data and assets contained in the delegated contracts. + +## Motivation +The inspiration for designing DAuth comes from OAuth protocol that is extensively used in web applications. But unlike the centralized authorization of OAuth, DAuth works in a distributed manner, thus providing much more reliability and generality. + +## Specification +![Rationale](../assets/eip-1207/rationale.png) + +**Resource owner**: the authorizer + +**Resource contract**: the contract providing data and operators + +**API**: the resource contract APIs that the grantee contract can invoke + +**Client contract**: the grantee contract using authorization to access and operate the data + +**Grantee request**: the client contract calls the resource contract with the authorizer authorization + + +**AuthInfo** +``` js +struct AuthInfo { + string[] funcNames; + uint expireAt; +} +``` +Required - The struct contains user authorization information +* `funcNames`: a list of function names callable by the granted contract +* `expireAt`: the authorization expire timestamp in seconds + +**userAuth** +``` js +mapping(address => mapping(address => AuthInfo)) userAuth; +``` +Required - userAuth maps (authorizer address, grantee contract address) pair to the user’s authorization AuthInfo object + +**callableFuncNames** +``` js +string[] callableFuncNames; +``` +Required - All methods that are allowed other contracts to call +* The callable function MUST verify the grantee’s authorization + +**updateCallableFuncNames** +``` js +function updateCallableFuncNames(string _invokes) public returns (bool success); +``` +Optional - Update the callable function list for the client contract by the resource contract's administrator +* `_invokes`: the invoke methods that the client contract can call +* return: Whether the callableFuncNames is updated or not +* This method MUST return success or throw, no other outcomes can be possible + +**verify** +``` js +function verify(address _authorizer, string _invoke) internal returns (bool success); +``` +Required - check the invoke method authority for the client contract +* `_authorizer`: the user address that the client contract agents +* `_invoke`: the invoke method that the client contract wants to call +* return: Whether the grantee request is authorized or not +* This method MUST return success or throw, no other outcomes can be possible + +**grant** +``` js +function grant(address _grantee, string _invokes, uint _expireAt) public returns (bool success); +``` +Required - delegate a client contract to access the user's resource +* `_grantee`: the client contract address +* `_invokes`: the callable methods that the client contract can access. It is a string which contains all function names split by spaces +* `_expireAt`: the authorization expire timestamp in seconds +* return: Whether the grant is successful or not +* This method MUST return success or throw, no other outcomes can be possible +* A successful grant MUST fire the Grant event(defined below) + +**regrant** +``` js +function regrant(address _grantee, string _invokes, uint _expireAt) public returns (bool success); +``` +Optional - alter a client contract's delegation + +**revoke** +``` js +function revoke(address _grantee) public returns (bool success); +``` +Required - delete a client contract's delegation +* `_grantee`: the client contract address +* return: Whether the revoke is successful or not +* A successful revoke MUST fire the Revoke event(defined below). + +**Grant** +``` js +event Grant(address _authorizer, address _grantee, string _invokes, uint _expireAt); +``` +* This event MUST trigger when the authorizer grant a new authorization when `grant` or `regrant` processes successfully + +**Revoke** +``` js +event Revoke(address _authorizer, address _grantee); +``` +* This event MUST trigger when the authorizer revoke a specific authorization successfully + +**Callable Resource Contract Functions** + +All public or external functions that are allowed the grantee to call MUST use overload to implement two functions: The First one is the standard method that the user invokes directly, the second one is the grantee methods of the same function name with one more authorizer address parameter. + +Example: +``` js +function approve(address _spender, uint256 _value) public returns (bool success) { + return _approve(msg.sender, _spender, _value); +} + +function approve(address _spender, uint256 _value, address _authorizer) public returns (bool success) { + verify(_authorizer, "approve"); + + return _approve(_authorizer, _spender, _value); +} + +function _approve(address sender, address _spender, uint256 _value) internal returns (bool success) { + allowed[sender][_spender] = _value; + emit Approval(sender, _spender, _value); + return true; +} +``` + +## Rationale + +**Current Limitations** + +The current design of many smart contracts only considers the user invokes the smart contract functions by themselves using the private key. However, in some case, the user wants to delegate other client smart contracts to access and operate their data or assets in the resource smart contract. There isn’t a common protocol to provide a standard delegation approach. + +**Rationale** + +On the Ethereum platform, all storage is transparent and the `msg.sender` is reliable. Therefore, the DAuth don't need an `access_token` like OAuth. DAuth just recodes the users' authorization for the specific client smart contract's address. It is simple and reliable on the Ethereum platform. + +## Backwards Compatibility +This EIP introduces no backward compatibility issues. In the future, the new version protocol has to keep these interfaces. + +## Implementation +Following is the DAuth Interface implementation. Furthermore, the example implementations of EIP20 Interface and ERC-DAuth Interface are also provided. Developers can easily implement their own contracts with ERC-DAuth Interface and other EIP. + +* ERC-DAuth Interface implementation is available at: + + https://github.com/DIA-Network/ERC-DAuth/blob/master/ERC-DAuth-Interface.sol + +* Example implementation with EIP20 Interface and ERC-DAuth Interface is available at: + + https://github.com/DIA-Network/ERC-DAuth/blob/master/eip20-dauth-example/EIP20DAuth.sol + + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1227.md b/EIPS/eip-1227.md new file mode 100644 index 0000000..f731b13 --- /dev/null +++ b/EIPS/eip-1227.md @@ -0,0 +1,64 @@ +--- +eip: 1227 +title: Defuse Difficulty Bomb and Reset Block Reward +author: SmeargleUsedFly (@SmeargleUsedFly) +discussions-to: https://github.com/ethereum/EIPs/issues/1227 +status: Stagnant +type: Standards Track +category: Core +created: 2018-07-18 +requires: 649 +--- + +## Simple Summary +This EIP proposes to permanently disable the "difficulty bomb" and reset the block reward to pre-Byzantium levels. + +## Abstract +Starting with `FORK_BLKNUM` the client will calculate the difficulty without the additional exponential component. Furthermore, block rewards will be adjusted to a base of 5 ETH, uncle and nephew rewards will be adjusted accordingly. + +## Motivation +Due to the "difficulty bomb" (also known as the "ice age"), introduced in EIP [#2](./eip-2.md), an artificial exponential increase in difficulty until chain freeze, users may find it much more challenging to remain on the unforked chain after a hard-fork. This is a desirable effect of the ice age (in fact, its only stated purpose) in the case of a scheduled network upgrade, but is especially problematic when a hard-fork includes a controversial change. + +This situation has already been observed: during the Byzantium hard-fork users were given the "choice" of following the upgraded side of the chain or remaining on the original chain, the latter already experiencing block times greater than 30 seconds. In reality one will find that organizing a disperse and decentralized set of individuals to keep the original, soon-to-be-dead chain alive under such conditions impossible. This is exacerbated when a controversial change, such as EIP [#649](./eip-649.md), is merged in so close to the hard-fork date, as users cannot be organized to take an educated stance for or against the change on such short notice. + +Ultimately, the difficulty bomb serves but a single purpose: make it more difficult to keep the original chain alive after a hard-fork. This is unacceptable if the only way the community can make their voice heard is running/not running client software, and not through the EIP process, since they effectively have no choice and therefore no power. This EIP proposes to completely eliminate the difficulty bomb, returning some measure of power over Ethereum's governance process to the users, to the community. + +Given the controversy surrounding the directly relevant EIP [#649](./eip-649.md), the issuance should also be reset to pre-Byzantium levels. It may be reduced again at a later time via a new hard-fork, only this time users would actually have a meaningful choice in accepting the change or not. Note: the issuance reduction is not the focus of this proposal, and is optional; the defusing of the difficulty bomb is of primary concern. + +## Specification +#### Remove Exponential Component of Difficulty Adjustment +For the purposes of `calc_difficulty`, simply remove the exponential difficulty adjustment component, `epsilon`, i.e. the `int(2**((block.number // 100000) - 2))`. + +#### Reset Block, Uncle, and Nephew rewards +To ensure a constant Ether issuance, adjust the block reward to `new_block_reward`, where + + new_block_reward = 5_000_000_000_000_000_000 if block.number >= FORK_BLKNUM else block.reward + +(5E18 wei, or 5,000,000,000,000,000,000 wei, or 5 ETH). + +Analogue, if an uncle is included in a block for `block.number >= FORK_BLKNUM` such that `block.number - uncle.number = k`, the uncle reward is + + new_uncle_reward = (8 - k) * new_block_reward / 8 + +This is the existing pre-Byzantium formula for uncle rewards, simply adjusted with `new_block_reward`. + +The nephew reward for `block.number >= FORK_BLKNUM` is + + new_nephew_reward = new_block_reward / 32 + +This is the existing pre-Byzantium formula for nephew rewards, simply adjusted with `new_block_reward`. + +## Rationale +This will permanently, without further changes, disable the "ice age." It will also reset the block reward to pre-Byzantium levels. Both of these changes are specified similarly to EIP [#649](./eip-649.md), so they should require only minimal changes from client developers. + +## Backwards Compatibility +This EIP is not forward compatible and introduces backwards incompatibilities in the difficulty calculation, as well as the block, uncle and nephew reward structure. However, it may be controversial in nature among different sections of the userbase—the very problem this EIP is made to address. Therefore, it should not be included in a scheduled hardfork at a certain block number. It is suggested to implement this EIP in an isolated hard-fork before the second of the two Metropolis hard-forks. + +## Test Cases +Forthcoming. + +## Implementation +Forthcoming. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1234.md b/EIPS/eip-1234.md new file mode 100644 index 0000000..d689697 --- /dev/null +++ b/EIPS/eip-1234.md @@ -0,0 +1,61 @@ +--- +eip: 1234 +title: Constantinople Difficulty Bomb Delay and Block Reward Adjustment +author: Afri Schoedon (@5chdn) +discussions-to: https://ethereum-magicians.org/t/eip-1234-constantinople-difficulty-bomb-delay-and-block-reward-adjustment/833 +type: Standards Track +category: Core +status: Final +created: 2018-07-19 +--- + +## Simple Summary +The average block times are increasing due to the difficulty bomb (also known as the "_ice age_") slowly accelerating. This EIP proposes to delay the difficulty bomb for approximately 12 months and to reduce the block rewards with the Constantinople fork, the second part of the Metropolis fork. + +## Abstract +Starting with `CNSTNTNPL_FORK_BLKNUM` the client will calculate the difficulty based on a fake block number suggesting the client that the difficulty bomb is adjusting around 5 million blocks later than previously specified with the Homestead fork. Furthermore, block rewards will be adjusted to a base of 2 ETH, uncle and nephew rewards will be adjusted accordingly. + +## Motivation +The Casper development and switch to proof-of-stake is delayed, the Ethash proof-of-work should be feasible for miners and allow sealing new blocks every 15 seconds on average for another 12 months. With the delay of the ice age, there is a desire to not suddenly also increase miner rewards. The difficulty bomb has been known about for a long time and now it's going to stop from happening. In order to maintain stability of the system, a block reward reduction that offsets the ice age delay would leave the system in the same general state as before. Reducing the reward also decreases the likelihood of a miner driven chain split as Ethereum approaches proof-of-stake. + +## Specification +#### Relax Difficulty with Fake Block Number +For the purposes of `calc_difficulty`, simply replace the use of `block.number`, as used in the exponential ice age component, with the formula: + + fake_block_number = max(0, block.number - 5_000_000) if block.number >= CNSTNTNPL_FORK_BLKNUM else block.number + +#### Adjust Block, Uncle, and Nephew rewards +To ensure a constant Ether issuance, adjust the block reward to `new_block_reward`, where + + new_block_reward = 2_000_000_000_000_000_000 if block.number >= CNSTNTNPL_FORK_BLKNUM else block.reward + +(2E18 wei, or 2,000,000,000,000,000,000 wei, or 2 ETH). + +Analogue, if an uncle is included in a block for `block.number >= CNSTNTNPL_FORK_BLKNUM` such that `block.number - uncle.number = k`, the uncle reward is + + new_uncle_reward = (8 - k) * new_block_reward / 8 + +This is the existing pre-Constantinople formula for uncle rewards, simply adjusted with `new_block_reward`. + +The nephew reward for `block.number >= CNSTNTNPL_FORK_BLKNUM` is + + new_nephew_reward = new_block_reward / 32 + +This is the existing pre-Constantinople formula for nephew rewards, simply adjusted with `new_block_reward`. + +## Rationale +This will delay the ice age by 29 million seconds (approximately 12 months), so the chain would be back at 30 second block times in winter 2019. An alternate proposal was to add special rules to the difficulty calculation to effectively _pause_ the difficulty between different blocks. This would lead to similar results. + +This was previously discussed at All Core Devs Meeting [#42](https://github.com/ethereum/pm/blob/master/All%20Core%20Devs%20Meetings/Meeting%2042.md) and subsequent meetings; and accepted in the Constantinople Session [#1](https://github.com/ethereum/pm/issues/55). + +## Backwards Compatibility +This EIP is not forward compatible and introduces backwards incompatibilities in the difficulty calculation, as well as the block, uncle and nephew reward structure. Therefore, it should be included in a scheduled hardfork at a certain block number. It's suggested to include this EIP in the second Metropolis hard-fork, _Constantinople_. + +## Test Cases +Test cases shall be created once the specification is to be accepted by the developers or implemented by the clients. + +## Implementation +The implementation in it's logic does not differ from [EIP-649](./eip-649.md); an implementation for Parity-Ethereum is available in [parity-ethereum#9187](https://github.com/paritytech/parity-ethereum/pull/9187). + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1240.md b/EIPS/eip-1240.md new file mode 100644 index 0000000..e9c787c --- /dev/null +++ b/EIPS/eip-1240.md @@ -0,0 +1,40 @@ +--- +eip: 1240 +title: Remove Difficulty Bomb +author: Micah Zoltu (@MicahZoltu) +discussions-to: https://ethereum-magicians.org/t/difficulty-bomb-removal/832 +type: Standards Track +category: Core +status: Withdrawn +created: 2018-07-21 +--- + +## Simple Summary +The average block times are increasing due to the difficulty bomb (also known as the "_ice age_") slowly accelerating. This EIP proposes to remove the difficulty increase over time and replace it with a fixed difficulty targeting 15 second blocks. + +## Abstract +Starting with `FORK_BLOCK_NUMBER` the client will calculate the difficulty without considering the current block number. + +## Motivation +The difficulty bomb operates under the assumption that miners decide what code economic participants are running, rather than economic participants deciding for themselves. In reality, miners will mine whatever chain is most profitable and the most profitable chain is the one that economic participants use. If 99% of miners mine a chain that no economic participants use then that chain will have no value and the miners will cease mining of it in favor of some other chain that does have economic participants. Another way to put this is that miners will follow economic participants, not the other way around. + +## Specification +#### Remove Difficulty +For the purposes of `calc_difficulty`, if `block.number >= FORK_BLOCK_NUMBER` then change the epsilon component to `0` rather than having it be a function of block number. + +## Rationale +With the difficulty bomb removed, when Casper is released it will be up to economic participants to decide whether they want the features that Casper enables or not. If they do not want Casper, they are free to continue running unpatched clients and participating in the Ethereum network as it exists today. This freedom of choice is the cornerstone of DLTs and making it hard for people to make that choice (by creating an artificial pressure) does not work towards that goal of freedom of choice. If the development team is not confident that economic participants will want Casper, then they should re-evaluate their priorities rather than trying to force Casper onto users. + +Author Personal Note: I think we will see almost all economic participants in Ethereum switch to PoS/Sharding without any extra pressure beyond client defaults. + +## Backwards Compatibility +This EIP is not forward compatible and introduces backwards incompatibilities in the difficulty calculation. Therefore, it should be included in a scheduled hardfork at a certain block number. + +## Test Cases +Test cases shall be created once the specification is to be accepted by the developers or implemented by the clients. + +## Implementations +The yellow paper implements this change in https://github.com/ethereum/yellowpaper/pull/710 + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1261.md b/EIPS/eip-1261.md new file mode 100644 index 0000000..e2bf12a --- /dev/null +++ b/EIPS/eip-1261.md @@ -0,0 +1,390 @@ +--- +eip: 1261 +title: Membership Verification Token (MVT) +author: Chaitanya Potti (@chaitanyapotti), Partha Bhattacharya (@pb25193) +type: Standards Track +category: ERC +status: Stagnant +created: 2018-07-14 +requires: 165, 173 +discussions-to: https://github.com/ethereum/EIPs/issues/1261 +--- + +## Simple Summary + +A standard interface for Membership Verification Token(MVT). + +## Abstract + +The following standard allows for the implementation of a standard API for Membership Verification Token within smart contracts(called entities). This standard provides basic functionality to track membership of individuals in certain on-chain ‘organizations’. This allows for several use cases like automated compliance, and several forms of governance and membership structures. + +We considered use cases of MVTs being assigned to individuals which are non-transferable and revocable by the owner. MVTs can represent proof of recognition, proof of membership, proof of right-to-vote and several such otherwise abstract concepts on the blockchain. The following are some examples of those use cases, and it is possible to come up with several others: + +- Voting: Voting is inherently supposed to be a permissioned activity. So far, onchain voting systems are only able to carry out voting with coin balance based polls. This can now change and take various shapes and forms. +- Passport issuance, social benefit distribution, Travel permit issuance, Drivers licence issuance are all applications which can be abstracted into membership, that is belonging of an individual to a small set, recognized by some authority as having certain entitlements, without needing any individual specific information(right to welfare, freedom of movement, authorization to operate vehicles, immigration) +- Investor permissioning: Making regulatory compliance a simple on chain process. Tokenization of securities, that are streamlined to flow only to accredited addresses, tracing and certifying on chain addresses for AML purposes. +- Software licencing: Software companies like game developers can use the protocol to authorize certain hardware units(consoles) to download and use specific software(games) + +In general, an individual can have different memberships in their day to day life. The protocol allows for the creation of software that puts everything all at one place. Their identity can be verified instantly. Imagine a world where you don't need to carry a wallet full of identity cards (Passport, gym membership, SSN, Company ID etc) and organizations can easily keep track of all its members. Organizations can easily identify and disallow fake identities. + +Attributes are a huge part of ERC-1261 which help to store identifiable information regarding its members. Polls can make use of attributes to calculate the voterbase. +E.g: Users should belong to USA entity and not belong to Washington state attribute to be a part of a poll. + +There will exist a mapping table that maps attribute headers to an array of all possible attributes. This is done in order to subdivide entities into subgroups which are exclusive and exhaustive. For example, +header: blood group alphabet +Array: [ o, a, b, ab ] +header: blood group sign +Array: [ +, - ] + +NOT an example of exclusive exhaustive: +Header: video subscription +Array: [ Netflix, HBO, Amazon ] +Because a person is not necessitated to have EXACTLY one of the elements. He or she may have none or more than one. + +## Motivation + +A standard interface allows any user, applications to work with any MVT on Ethereum. We provide for simple ERC-1261 smart contracts. Additional applications are discussed below. + +This standard is inspired from the fact that voting on the blockchain is done with token balance weights. This has been greatly detrimental to the formation of flexible governance systems on the blockchain, despite the tremendous governance potential that blockchains offer. The idea was to create a permissioning system that allows organizations to vet people once into the organization on the blockchain, and then gain immense flexibility in the kind of governance that can be carried out. + +We have also reviewed other Membership EIPs including EIP-725/735 Claim Registry. A significant difference between #735 claims and #1261 MVTs is information ownership. In #735 the Claim Holder owns any claims made about themselves. The problem with this is that there is no way for a Claim Issuer to revoke or alter a claim once it has been issued. While #735 does specify a removeClaim method, a malicious implementation could simply ignore that method call, because they own the claim. + +Imagine that SafeEmploy™, a background checking company, issues a claim about Timmy. The claim states that Timmy has never been convicted of any felonies. Timmy makes some bad decisions, and now that claim is no longer true. SafeEmploy™ executes removeClaim, but Timmy's #735 contract just ignores it, because Timmy wants to stay employed (and is crypto-clever). #1261 MVTs do not have this problem. Ownership of a badge/claim is entirely determined by the contract issuing the badges, not the one receiving them. The issuer is free to remove or change those badges as they see fit. + +**Trade-off between trustlessness and usability:** +To truly understand the value of the protocol, it is important to understand the trade-off we are treading on. The MVT contract allows the creator to revoke the token, and essentially confiscate the membership of the member in question. To some, this might seem like an unacceptable flaw, however this is a design choice, and not a flaw. +The choice may seem to place a great amount of trust in the individuals who are managing the entity contract(entity owners). If the interests of the entity owner conflict with the interests of the members, the owner may resort to addition of fake addresses(to dominate consensus) or evicting members(to censor unfavourable decisions). At first glance this appears to be a major shortcomings, because the blockchain space is used to absolute removal of authority in most cases. Even the official definition of a dapp requires the absence of any party that manages the services provided by the application. However, the trust in entity owners is not misplaced, if it can be ensured that the interests of entity owners are aligned with the interests of members. +Another criticism of such a system would be that the standard edge of blockchain intermediation - “you cannot bribe the system if you don’t know who to bribe” - no longer holds. It is possible to bribe an entity owner into submission, and get them to censor or fake votes. There are several ways to respond to this argument. First of all, all activities, such as addition of members, and removal of members can be tracked on the blockchain and traces of such activity cannot be removed. It is not difficult to build analytics tools to detect malicious activity(adding 100 fake members suddenly who vote in the direction/sudden removal of a number of members voting in a certain direction). Secondly, the entity owners’ power is limited to the addition and removal of members. This means that they cannot tamper any votes. They can only alter the counting system to include fake voters or remove real voters. Any sensible auditor can identify the malicious/victim addresses and create an open source audit tool to find out the correct results. The biggest loser in this attack will be the entity owner, who has a reputation to lose. +Finally, one must understand why we are taking a step away from trustlessness in this trade-off. The answer is usability. Introducing a permissioning system expands the scope of products and services that can be delivered through the blockchain, while leveraging other aspects of the blockchain(cheap, immutable, no red-tape, secure). Consider the example of the driver licence issuing agency using the ERC-1300 standard. This is a service that simply cannot be deployed in a completely trustless environment. The introduction of permissioned systems expanded the scope of services on the blockchain to cover this particular service. Sure, they have the power to revoke a person’s licence for no reason. But will they? Who stands to lose the most, if the agency acts erratically? The agency itself. Now consider the alternative, the way licences(not necessarily only drivers licence, but say shareholder certificates and so on) are issued, the amount of time consumed, the complete lack of transparency. One could argue that if the legacy systems providing these services really wanted to carry out corruption and nepotism in the execution of these services, the present systems make it much easier to do so. Also, they are not transparent, meaning that there is no way to even detect if they act maliciously. +All that being said, we are very excited to share our proposal with the community and open up to suggestions in this space. + +## Specification + +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. + +**Every ERC-1261 compliant contract must implement the `ERC1261`, `ERC173` and `ERC165` interfaces** (subject to "caveats" below): + +```solidity +/// @title ERC-1261 MVT Standard +/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1261.md +/// The constructor should define the attribute set for this MVT. +/// Note: the ERC-165 identifier for this interface is 0x1d8362cf. +interface IERC1261 {/* is ERC173, ERC165 */ + /// @dev This emits when a token is assigned to a member. + event Assigned(address indexed _to, uint[] attributeIndexes); + + /// @dev This emits when a membership is revoked. + event Revoked(address indexed _to); + + /// @dev This emits when a user forfeits his membership + event Forfeited(address indexed _to); + + /// @dev This emits when a membership request is accepted + event ApprovedMembership(address indexed _to, uint[] attributeIndexes); + + /// @dev This emits when a membership is requested by an user + event RequestedMembership(address indexed _to); + + /// @dev This emits when data of a member is modified. + /// Doesn't emit when a new membership is created and data is assigned. + event ModifiedAttributes(address indexed _to, uint attributeIndex, uint attributeValueIndex); + + /// @notice Adds a new attribute (key, value) pair to the set of pre-existing attributes. + /// @dev Adds a new attribute at the end of the array of attributes and maps it to `values`. + /// Contract can set a max number of attributes and throw if limit is reached. + /// @param _name Name of the attribute which is to be added. + /// @param values List of values of the specified attribute. + function addAttributeSet(bytes32 _name, bytes32[] calldata values) external; + + /// @notice Modifies the attribute value of a specific attribute for a given `_to` address. + /// @dev Use appropriate checks for whether a user/admin can modify the data. + /// Best practice is to use onlyOwner modifier from ERC173. + /// @param _to The address whose attribute is being modified. + /// @param _attributeIndex The index of attribute which is being modified. + /// @param _modifiedValueIndex The index of the new value which is being assigned to the user attribute. + function modifyAttributeByIndex(address _to, uint _attributeIndex, uint _modifiedValueIndex) external; + + /// @notice Requests membership from any address. + /// @dev Throws if the `msg.sender` already has the token. + /// The individual `msg.sender` can request for a membership if some existing criteria are satisfied. + /// When a membership is requested, this function emits the RequestedMembership event. + /// dev can store the membership request and use `approveRequest` to assign membership later + /// dev can also oraclize the request to assign membership later + /// @param _attributeIndexes the attribute data associated with the member. + /// This is an array which contains indexes of attributes. + function requestMembership(uint[] calldata _attributeIndexes) external payable; + + /// @notice User can forfeit his membership. + /// @dev Throws if the `msg.sender` already doesn't have the token. + /// The individual `msg.sender` can revoke his/her membership. + /// When the token is revoked, this function emits the Revoked event. + function forfeitMembership() external payable; + + /// @notice Owner approves membership from any address. + /// @dev Throws if the `_user` doesn't have a pending request. + /// Throws if the `msg.sender` is not an owner. + /// Approves the pending request + /// Make oraclize callback call this function + /// When the token is assigned, this function emits the `ApprovedMembership` and `Assigned` events. + /// @param _user the user whose membership request will be approved. + function approveRequest(address _user) external; + + /// @notice Owner discards membership from any address. + /// @dev Throws if the `_user` doesn't have a pending request. + /// Throws if the `msg.sender` is not an owner. + /// Discards the pending request + /// Make oraclize callback call this function if criteria are not satisfied + /// @param _user the user whose membership request will be discarded. + function discardRequest(address _user) external; + + /// @notice Assigns membership of an MVT from owner address to another address. + /// @dev Throws if the member already has the token. + /// Throws if `_to` is the zero address. + /// Throws if the `msg.sender` is not an owner. + /// The entity assigns the membership to each individual. + /// When the token is assigned, this function emits the Assigned event. + /// @param _to The address to which the token is assigned. + /// @param _attributeIndexes The attribute data associated with the member. + /// This is an array which contains indexes of attributes. + function assignTo(address _to, uint[] calldata _attributeIndexes) external; + + /// @notice Only Owner can revoke the membership. + /// @dev This removes the membership of the user. + /// Throws if the `_from` is not an owner of the token. + /// Throws if the `msg.sender` is not an owner. + /// Throws if `_from` is the zero address. + /// When transaction is complete, this function emits the Revoked event. + /// @param _from The current owner of the MVT. + function revokeFrom(address _from) external; + + /// @notice Queries whether a member is a current member of the organization. + /// @dev MVT's assigned to the zero address are considered invalid, and this + /// function throws for queries about the zero address. + /// @param _to An address for whom to query the membership. + /// @return Whether the member owns the token. + function isCurrentMember(address _to) external view returns (bool); + + /// @notice Gets the value collection of an attribute. + /// @dev Returns the values of attributes as a bytes32 array. + /// @param _name Name of the attribute whose values are to be fetched + /// @return The values of attributes. + function getAttributeExhaustiveCollection(bytes32 _name) external view returns (bytes32[] memory); + + /// @notice Returns the list of all past and present members. + /// @dev Use this function along with isCurrentMember to find wasMemberOf() in Js. + /// It can be calculated as present in getAllMembers() and !isCurrentMember(). + /// @return List of addresses who have owned the token and currently own the token. + function getAllMembers() external view returns (address[]); + + /// @notice Returns the count of all current members. + /// @dev Use this function in polls as denominator to get percentage of members voted. + /// @return Count of current Members. + function getCurrentMemberCount() external view returns (uint); + + /// @notice Returns the list of all attribute names. + /// @dev Returns the names of attributes as a bytes32 array. + /// AttributeNames are stored in a bytes32 Array. + /// Possible values for each attributeName are stored in a mapping(attributeName => attributeValues). + /// AttributeName is bytes32 and attributeValues is bytes32[]. + /// Attributes of a particular user are stored in bytes32[]. + /// Which has a single attributeValue for each attributeName in an array. + /// Use web3.toAscii(data[0]).replace(/\u0000/g, "") to convert to string in JS. + /// @return The names of attributes. + function getAttributeNames() external view returns (bytes32[] memory); + + /// @notice Returns the attributes of `_to` address. + /// @dev Throws if `_to` is the zero address. + /// Use web3.toAscii(data[0]).replace(/\u0000/g, "") to convert to string in JS. + /// @param _to The address whose current attributes are to be returned. + /// @return The attributes associated with `_to` address. + function getAttributes(address _to) external view returns (bytes32[]); + + /// @notice Returns the `attribute` stored against `_to` address. + /// @dev Finds the index of the `attribute`. + /// Throws if the attribute is not present in the predefined attributes. + /// Returns the attributeValue for the specified `attribute`. + /// @param _to The address whose attribute is requested. + /// @param _attributeIndex The attribute Index which is required. + /// @return The attribute value at the specified name. + function getAttributeByIndex(address _to, uint _attributeIndex) external view returns (bytes32); +} + +interface ERC173 /* is ERC165 */ { + /// @dev This emits when ownership of a contract changes. + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); + + /// @notice Get the address of the owner + /// @return The address of the owner. + function owner() external view; + + /// @notice Set the address of the new owner of the contract + /// @param _newOwner The address of the new owner of the contract + function transferOwnership(address _newOwner) external; +} + +interface ERC165 { + /// @notice Query if a contract implements an interface + /// @param interfaceID The interface identifier, as specified in ERC-165 + /// @dev Interface identification is specified in ERC-165. This function + /// uses less than 30,000 gas. + /// @return `true` if the contract implements `interfaceID` and + /// `interfaceID` is not 0xffffffff, `false` otherwise + function supportsInterface(bytes4 interfaceID) external view returns (bool); +} +``` + +The **metadata extension** is OPTIONAL for ERC-1261 smart contracts (see "caveats", below). This allows your smart contract to be interrogated for its name and for details about the organization which your MV tokens represent. + +```solidity +/// @title ERC-1261 MVT Standard, optional metadata extension +/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1261.md +interface ERC1261Metadata /* is ERC1261 */ { + /// @notice A descriptive name for a collection of MVTs in this contract + function name() external view returns (string _name); + + /// @notice An abbreviated name for MVTs in this contract + function symbol() external view returns (string _symbol); +} +``` + +This is the "ERC1261 Metadata JSON Schema" referenced above. + +```json +{ + "title": "Organization Metadata", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Identifies the organization to which this MVT represents" + }, + "description": { + "type": "string", + "description": "Describes the organization to which this MVT represents" + } + } +} +``` + +### Caveats + +The 0.4.24 Solidity interface grammar is not expressive enough to document the ERC-1261 standard. A contract which complies with ERC-1261 MUST also abide by the following: + +- Solidity issue #3412: The above interfaces include explicit mutability guarantees for each function. Mutability guarantees are, in order weak to strong: `payable`, implicit nonpayable, `view`, and `pure`. Your implementation MUST meet the mutability guarantee in this interface and you MAY meet a stronger guarantee. For example, a `payable` function in this interface may be implemented as nonpayble (no state mutability specified) in your contract. We expect a later Solidity release will allow your stricter contract to inherit from this interface, but a workaround for version 0.4.24 is that you can edit this interface to add stricter mutability before inheriting from your contract. +- Solidity issue #3419: A contract that implements `ERC1261Metadata` SHALL also implement `ERC1261`. +- Solidity issue #2330: If a function is shown in this specification as `external` then a contract will be compliant if it uses `public` visibility. As a workaround for version 0.4.24, you can edit this interface to switch to `public` before inheriting from your contract. +- Solidity issues #3494, #3544: Use of `this.*.selector` is marked as a warning by Solidity, a future version of Solidity will not mark this as an error. + +_If a newer version of Solidity allows the caveats to be expressed in code, then this EIP MAY be updated and the caveats removed, such will be equivalent to the original specification._ + +## Rationale + +There are many potential uses of Ethereum smart contracts that depend on tracking membership. Examples of existing or planned MVT systems are Vault, a DAICO platform, and Stream, a security token framework. Future uses include the implementation of direct democracy, in-game memberships and badges, licence and travel document issuance, electronic voting machine trails, software licencing and many more. + +**MVT Word Choice:** + +Since the tokens are non transferable and revocable, they function like membership cards. Hence the word membership verification token. + +**Transfer Mechanism** + +MVTs can't be transferred. This is a design choice, and one of the features that distinguishes this protocol. +Any member can always ask the issuer to revoke the token from an existing address and assign to a new address. +One can think of the set of MVTs as identifying a user, and you cannot split the user into parts and have it be the same user, but you can transfer a user to a new private key. + +**Assign and Revoke mechanism** + +The assign and revoke functions' documentation only specify conditions when the transaction MUST throw. Your implementation MAY also throw in other situations. This allows implementations to achieve interesting results: + +- **Disallow additional memberships after a condition is met** — Sample contract available on GitHub +- **Blacklist certain address from receiving MV tokens** — Sample contract available on GitHub +- **Disallow additional memberships after a certain time is reached** — Sample contract available on GitHub +- **Charge a fee to user of a transaction** — require payment when calling `assign` and `revoke` so that condition checks from external sources can be made + +**ERC-173 Interface** + +We chose Standard Interface for Ownership (ERC-173) to manage the ownership of a ERC-1261 contract. + +A future EIP/ Zeppelin may create a multi-ownable implementation for ownership. We strongly support such an EIP and it would allow your ERC-1261 implementation to implement `ERC1261Metadata`, or other interfaces by delegating to a separate contract. + +**ERC-165 Interface** + +We chose Standard Interface Detection (ERC-165) to expose the interfaces that a ERC-1261 smart contract supports. + +A future EIP may create a global registry of interfaces for contracts. We strongly support such an EIP and it would allow your ERC-1261 implementation to implement `ERC1261Metadata`, or other interfaces by delegating to a separate contract. + +**Gas and Complexity** (regarding the enumeration extension) + +This specification contemplates implementations that manage a few and _arbitrarily large_ numbers of MVTs. If your application is able to grow then avoid using for/while loops in your code. These indicate your contract may be unable to scale and gas costs will rise over time without bound + +**Privacy** + +Personal information: The protocol does not put any personal information on to the blockchain, so there is no compromise of privacy in that respect. +Membership privacy: The protocol by design, makes it public which addresses are/aren’t members. Without making that information public, it would not be possible to independently audit governance activity or track admin(entity owner) activity. + +**Metadata Choices** (metadata extension) + +We have required `name` and `symbol` functions in the metadata extension. Every token EIP and draft we reviewed (ERC-20, ERC-223, ERC-677, ERC-777, ERC-827) included these functions. + +We remind implementation authors that the empty string is a valid response to `name` and `symbol` if you protest to the usage of this mechanism. We also remind everyone that any smart contract can use the same name and symbol as _your_ contract. How a client may determine which ERC-1261 smart contracts are well-known (canonical) is outside the scope of this standard. + +A mechanism is provided to associate MVTs with URIs. We expect that many implementations will take advantage of this to provide metadata for each MVT system. The URI MAY be mutable (i.e. it changes from time to time). We considered an MVT representing membership of a place, in this case metadata about the organization can naturally change. + +Metadata is returned as a string value. Currently this is only usable as calling from `web3`, not from other contracts. This is acceptable because we have not considered a use case where an on-blockchain application would query such information. + +_Alternatives considered: put all metadata for each asset on the blockchain (too expensive), use URL templates to query metadata parts (URL templates do not work with all URL schemes, especially P2P URLs), multiaddr network address (not mature enough)_ + +**Community Consensus** + +We have been very inclusive in this process and invite anyone with questions or contributions into our discussion. However, this standard is written only to support the identified use cases which are listed herein. + +## Backwards Compatibility + +We have adopted `name` and `symbol` semantics from the ERC-20 specification. + +Example MVT implementations as of July 2018: + +- Membership Verification Token(https://github.com/chaitanyapotti/MembershipVerificationToken) + +## Test Cases + +Membership Verification Token ERC-1261 Token includes test cases written using Truffle. + +## Implementations + +Membership Verification Token ERC1261 -- a reference implementation + +- MIT licensed, so you can freely use it for your projects +- Includes test cases +- Also available as a npm package - npm i membershipverificationtoken + +## References + +**Standards** + +1. ERC-20 Token Standard. ./eip-20.md +1. ERC-165 Standard Interface Detection. ./eip-165.md +1. ERC-725/735 Claim Registry ./eip-725.md +1. ERC-173 Owned Standard. ./eip-173.md +1. JSON Schema. https://json-schema.org/ +1. Multiaddr. https://github.com/multiformats/multiaddr +1. RFC 2119 Key words for use in RFCs to Indicate Requirement Levels. https://www.ietf.org/rfc/rfc2119.txt + +**Issues** + +1. The Original ERC-1261 Issue. https://github.com/ethereum/eips/issues/1261 +1. Solidity Issue \#2330 -- Interface Functions are Axternal. https://github.com/ethereum/solidity/issues/2330 +1. Solidity Issue \#3412 -- Implement Interface: Allow Stricter Mutability. https://github.com/ethereum/solidity/issues/3412 +1. Solidity Issue \#3419 -- Interfaces Can't Inherit. https://github.com/ethereum/solidity/issues/3419 + +**Discussions** + +1. Gitter #EIPs (announcement of first live discussion). https://gitter.im/ethereum/EIPs?at=5b5a1733d2f0934551d37642 +1. ERC-1261 (announcement of first live discussion). https://github.com/ethereum/eips/issues/1261 + +**MVT Implementations and Other Projects** + +1. Membership Verification Token ERC-1261 Token. https://github.com/chaitanyapotti/MembershipVerificationToken + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1271.md b/EIPS/eip-1271.md new file mode 100644 index 0000000..55da665 --- /dev/null +++ b/EIPS/eip-1271.md @@ -0,0 +1,162 @@ +--- +eip: 1271 +title: Standard Signature Validation Method for Contracts +description: Standard way to verify a signature when the account is a smart contract +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: Final +type: Standards Track +category: ERC +created: 2018-07-25 +--- + +## Abstract +Externally Owned Accounts (EOA) can sign messages with their associated private keys, but currently contracts cannot. We propose a standard way for any contracts to verify whether a signature on a behalf of a given contract is valid. This is possible via the implementation of a `isValidSignature(hash, signature)` function on the signing contract, which can be called to validate a signature. + +## Motivation + +There are and will be many contracts that want to utilize signed messages for validation of rights-to-move assets or other purposes. In order for these contracts to be able to support non Externally Owned Accounts (i.e., contract owners), we need a standard mechanism by which a contract can indicate whether a given signature is valid or not on its behalf. + +One example of an application that requires signatures to be provided would be decentralized exchanges with off-chain orderbook, where buy/sell orders are signed messages. 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 a signature. When it comes to contracts however, regular signatures are not possible since contracts do not possess a private key, hence this proposal. + +## Specification + +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(bytes32,bytes)") + bytes4 constant internal MAGICVALUE = 0x1626ba7e; + + /** + * @dev Should return whether the signature provided is valid for the provided hash + * @param _hash Hash of the data to be signed + * @param _signature Signature byte array associated with _hash + * + * MUST return the bytes4 magic value 0x1626ba7e 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( + bytes32 _hash, + 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. + +This function should be implemented by contracts which desire to sign messages (e.g. smart contract wallets, DAOs, multisignature wallets, etc.) Applications wanting to support contract signatures should call this method if the signer is a contract. + + +## Rationale +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 signing contract. 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 hash signed from the signature. A bytes32 hash is used instead of the unhashed message for simplicy, since contracts could expect a certain hashing function that is not standard, such as with [EIP-712](./eip-712.md). + +`isValidSignature()` should not be able to modify states in order to prevent `GasToken` minting or similar attack vectors. Again, this is to simplify the implementation surface of the function for better standardization and to allow off-chain contract queries. + +The specific return value is expected to be returned instead of a boolean in order to have stricter and simpler verification of a signature. + +## Backwards Compatibility + +This EIP is backward compatible with previous work on signature validation since this method is specific to contract based signatures and not EOA signatures. + +## Reference Implementation + +Example implementation of a signing contract: + +```solidity + + /** + * @notice Verifies that the signer is the owner of the signing contract. + */ + function isValidSignature( + bytes32 _hash, + bytes calldata _signature + ) external override view returns (bytes4) { + // Validate signatures + if (recoverSigner(_hash, _signature) == owner) { + return 0x1626ba7e; + } else { + return 0xffffffff; + } + } + + /** + * @notice Recover the signer of hash, assuming it's an EOA account + * @dev Only for EthSign signatures + * @param _hash Hash of message that was signed + * @param _signature Signature encoded as (bytes32 r, bytes32 s, uint8 v) + */ + function recoverSigner( + bytes32 _hash, + bytes memory _signature + ) internal pure returns (address signer) { + require(_signature.length == 65, "SignatureValidator#recoverSigner: invalid signature length"); + + // Variables are not scoped in Solidity. + uint8 v = uint8(_signature[64]); + bytes32 r = _signature.readBytes32(0); + bytes32 s = _signature.readBytes32(32); + + // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature + // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines + // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most + // signatures from current libraries generate a unique signature with an s-value in the lower half order. + // + // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value + // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or + // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept + // these malleable signatures as well. + // + // Source OpenZeppelin + // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/cryptography/ECDSA.sol + + if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { + revert("SignatureValidator#recoverSigner: invalid signature 's' value"); + } + + if (v != 27 && v != 28) { + revert("SignatureValidator#recoverSigner: invalid signature 'v' value"); + } + + // Recover ECDSA signer + signer = ecrecover(_hash, v, r, s); + + // Prevent signer from being 0x0 + require( + signer != address(0x0), + "SignatureValidator#recoverSigner: INVALID_SIGNER" + ); + + return signer; + } +``` + +Example implementation of a contract calling the isValidSignature() function on an external signing contract ; + +```solidity + function callERC1271isValidSignature( + address _addr, + bytes32 _hash, + bytes calldata _signature + ) external view { + bytes4 result = IERC1271Wallet(_addr).isValidSignature(_hash, _signature); + require(result == 0x1626ba7e, "INVALID_SIGNATURE"); + } +``` + +## Security Considerations +Since there are no gas-limit expected for calling the isValidSignature() function, it is possible that some implementation will consume a large amount of gas. It is therefore important to not hardcode an amount of gas sent when calling this method on an external contract as it could prevent the validation of certain signatures. + +Note also that each contract implementing this method is responsible to ensure that the signature passed is indeed valid, otherwise catastrophic outcomes are to be expected. + + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1276.md b/EIPS/eip-1276.md new file mode 100644 index 0000000..f13ba7e --- /dev/null +++ b/EIPS/eip-1276.md @@ -0,0 +1,63 @@ +--- +eip: 1276 +title: Eliminate Difficulty Bomb and Adjust Block Reward on Constantinople Shift +author: EOS Classic (@eosclassicteam) +discussions-to: https://ethereum-magicians.org/t/eip-1276-eliminate-difficulty-bomb-and-adjust-block-reward-on-constantinople-shift/908 +type: Standards Track +category: Core +status: Stagnant +created: 2018-07-31 +--- + +## Simple Summary +The average block times are increasing due to the factor of difficulty logic well known as difficulty bomb. This EIP proposes to eliminate the difficulty bomb forever and to reduce the block rewards with the Constantinople fork, the second part of the Metropolis fork. + +## Abstract +Starting with `CNSTNTNPL_FORK_BLKNUM` the client will calculate the difficulty without considering the current block number. Furthermore, block rewards will be adjusted to a base of 2 ETH, uncle and nephew rewards will be adjusted accordingly. + +## Motivation +Block time has been played a most important role on blockchain ecosystem, and it is being adjusted by the logic of mining difficulty calculation that is already implemented on the node client as a part of proof-of-work consensus. Last year, average block time rapidly increased due to the wrong design of difficulty logic that is meant to be changed on the part of Casper upgrade, however, implementation of casper has been delayed therefore it was inevitable to delay the difficulty bomb in order to prevent the significant delay of processing transactions on ethereum network. + +Despite of the successful hardfork to delay the activation of difficulty bomb, activation of the difficulty bomb is expected to happen again on the upcoming period before implementing casper protocol on ethereum network. Therefore, completely removing the difficulty bomb is the most proper way to response the block time increase instead of delaying it again. + +Also decreasing the block mining reward along with difficulty bomb removal is expected to help the growth of the stable ethereum ecosystem, right now ethereum dominates 92% of the total hashrate share of ethash based chains, and this corresponds to a tremendous level of energy consumption. As this energy consumption has a correlated environmental cost the network participants have an ethical obligation to ensure this cost is not higher than necessary. At this time, the most direct way to reduce this cost is to lower the block reward in order to limit the appeal of ETH mining. Unchecked growth in hashrate is also counterproductive from a security standpoint. Reducing the reward also decreases the likelihood of a miner driven chain split as Ethereum approaches proof-of-stake. + +## Specification +#### Remove Exponential Component of Difficulty Adjustment +For the purposes of `calc_difficulty`, simply remove the exponential difficulty adjustment component, `epsilon`, i.e. the `int(2**((block.number // 100000) - 2))`. + +#### Adjust Block, Uncle, and Nephew rewards +To ensure a constant Ether issuance, adjust the block reward to `new_block_reward`, where + + new_block_reward = 2_000_000_000_000_000_000 if block.number >= CNSTNTNPL_FORK_BLKNUM else block.reward + +(2E18 wei, or 2,000,000,000,000,000,000 wei, or 2 ETH). + +Analogue, if an uncle is included in a block for `block.number >= CNSTNTNPL_FORK_BLKNUM` such that `block.number - uncle.number = k`, the uncle reward is + + new_uncle_reward = (8 - k) * new_block_reward / 8 + +This is the existing pre-Constantinople formula for uncle rewards, simply adjusted with `new_block_reward`. + +The nephew reward for `block.number >= CNSTNTNPL_FORK_BLKNUM` is + + new_nephew_reward = new_block_reward / 32 + +This is the existing pre-Constantinople formula for nephew rewards, simply adjusted with `new_block_reward`. + +## Rationale +This will completely remove the difficulty bomb on difficulty adjustment algorithm without delaying the difficulty bomb again, therefore it is possible to prevent network delay on the beginning of 2019. + +This EIP-1276 opposes directly the intent of [EIP-1234](./eip-1234.md) which should be also considered in discussions. + +## Backwards Compatibility +This EIP is not forward compatible and introduces backwards incompatibilities in the difficulty calculation, as well as the block, uncle and nephew reward structure. Therefore, it should be included in a scheduled hardfork at a certain block number. It's suggested to include this EIP in the second Metropolis hard-fork, _Constantinople_. + +## Test Cases +Test cases shall be created once the specification is to be accepted by the developers or implemented by the clients. + +## Implementation +The implementation shall be created once the specification is to be accepted by the developers or implemented by the clients. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1283.md b/EIPS/eip-1283.md new file mode 100644 index 0000000..cdc8d22 --- /dev/null +++ b/EIPS/eip-1283.md @@ -0,0 +1,335 @@ +--- +eip: 1283 +title: Net gas metering for SSTORE without dirty maps +author: Wei Tang (@sorpaas) +discussions-to: https://github.com/sorpaas/EIPs/issues/1 +status: Final +type: Standards Track +category: Core +created: 2018-08-01 +--- + +## Abstract + +This EIP proposes net gas metering changes for `SSTORE` opcode, enabling +new usages for contract storage, and reducing excessive gas costs +where it doesn't match how most implementation works. + +This acts as an alternative for EIP-1087, where it tries to be +friendlier to implementations that use different optimization +strategies for storage change caches. + +## Motivation + +This EIP proposes a way for gas metering on SSTORE (as an alternative +for EIP-1087 and EIP-1153), using information that is more universally +available to most implementations, and require as little change in +implementation structures as possible. + +* *Storage slot's original value*. +* *Storage slot's current value*. +* Refund counter. + +Usages that benefits from this EIP's gas reduction scheme includes: + +* Subsequent storage write operations within the same call frame. This + includes reentry locks, same-contract multi-send, etc. +* Exchange storage information between sub call frame and parent call + frame, where this information does not need to be persistent outside + of a transaction. This includes sub-frame error codes and message + passing, etc. + +## Specification + +Definitions of terms are as below: + +* *Storage slot's original value*: This is the value of the storage if + a reversion happens on the *current transaction*. +* *Storage slot's current value*: This is the value of the storage + before SSTORE operation happens. +* *Storage slot's new value*: This is the value of the storage after + SSTORE operation happens. + +Replace `SSTORE` opcode gas cost calculation (including refunds) with +the following logic: + +* If *current value* equals *new value* (this is a no-op), 200 gas is + deducted. +* If *current value* does not equal *new value* + * If *original value* equals *current value* (this storage slot has + not been changed by the current execution context) + * If *original value* is 0, 20000 gas is deducted. + * Otherwise, 5000 gas is deducted. If *new value* is 0, add 15000 + gas to refund counter. + * If *original value* does not equal *current value* (this storage + slot is dirty), 200 gas is deducted. Apply both of the following + clauses. + * If *original value* is not 0 + * If *current value* is 0 (also means that *new value* is not + 0), remove 15000 gas from refund counter. We can prove that + refund counter will never go below 0. + * If *new value* is 0 (also means that *current value* is not + 0), add 15000 gas to refund counter. + * If *original value* equals *new value* (this storage slot is + reset) + * If *original value* is 0, add 19800 gas to refund counter. + * Otherwise, add 4800 gas to refund counter. + +Refund counter works as before -- it is limited to half of the gas +consumed. On a transaction level, refund counter will never go below +zero. However, there are some important notes depending on the +implementation details: + +* If an implementation uses "transaction level" refund counter (refund + is checkpointed at each call frame), then the refund counter + continues to be unsigned. +* If an implementation uses "execution-frame level" refund counter + (a new refund counter is created at each call frame, and then merged + back to parent when the call frame finishes), then the refund + counter needs to be changed to signed -- at internal calls, a child + refund can go below zero. + +## Explanation + +The new gas cost scheme for `SSTORE` is divided into three different +types: + +* **No-op**: the virtual machine does not need to do anything. This is + the case if *current value* equals *new value*. +* **Fresh**: this storage slot has not been changed, or has been reset + to its original value. This is the case if *current value* does not + equal *new value*, and *original value* equals *current value*. +* **Dirty**: this storage slot has already been changed. This is the + case if *current value* does not equal *new value*, and *original + value* does not equal *current value*. + +We can see that the above three types cover all possible variations of +*original value*, *current value*, and *new value*. + +**No-op** is a trivial operation. Below we only consider cases for +**Fresh** and **Dirty**. + +All initial (not-**No-op**) `SSTORE` on a particular storage slot starts +with **Fresh**. After that, it will become **Dirty** if the value has +been changed. When going from **Fresh** to **Dirty**, we charge the +gas cost the same as current scheme. A **Dirty** storage slot can be +reset back to **Fresh** via a `SSTORE` opcode. This will trigger a +refund. + +When a storage slot remains at **Dirty**, we charge 200 gas. In this +case, we would also need to keep track of `R_SCLEAR` refunds -- if we +already issued the refund but it no longer applies (*current value* is +0), then removes this refund from the refund counter. If we didn't +issue the refund but it applies now (*new value* is 0), then adds this +refund to the refund counter. It is not possible where a refund is not +issued but we remove the refund in the above case, because all storage +slot starts with **Fresh** state. + +### State Transition + +Below is a graph ([by +@Arachnid](https://github.com/ethereum/EIPs/pull/1283#issuecomment-410229053)) +showing possible state transition of gas costs. We ignore **No-op** +state because that is trivial: + +![State Transition](../assets/eip-1283/state.png) + +Below is table version of the above diagram. Vertical shows the *new +value* being set, and horizontal shows the state of *original value* +and *current value*. + +When *original value* is 0: + +| | A (`current=orig=0`) | B (`current!=orig`) | +|----|----------------------|--------------------------| +| ~0 | B; 20k gas | B; 200 gas | +| 0 | A; 200 gas | A; 200 gas, 19.8k refund | + +When *original value* is not 0: + +| | X (`current=orig!=0`) | Y (`current!=orig`) | Z (`current=0`) | +|-------------|-----------------------|-------------------------|---------------------------| +| `orig` | X; 200 gas | X; 200 gas, 4.8k refund | X; 200 gas, -10.2k refund | +| `~orig, ~0` | Y; 5k gas | Y; 200 gas | Y; 200 gas, -15k refund | +| 0 | Z; 5k gas, 15k refund | Z; 200 gas, 15k refund | Z; 200 gas | + +## Rationale + +This EIP mostly achieves what a transient storage tries to do +(EIP-1087 and EIP-1153), but without the complexity of introducing the +concept of "dirty maps", or an extra storage struct. + +* We don't suffer from the optimization limitation of + EIP-1087. EIP-1087 requires keeping a dirty map for storage changes, + and implicitly makes the assumption that a transaction's storage + changes are committed to the storage trie at the end of a + transaction. This works well for some implementations, but not for + others. After EIP-658, an efficient storage cache implementation + would probably use an in-memory trie (without RLP encoding/decoding) + or other immutable data structures to keep track of storage changes, + and only commit changes at the end of a block. For them, it is + possible to know a storage's original value and current value, but + it is not possible to iterate over all storage changes without + incurring additional memory or processing costs. +* It never costs more gas compared with the current scheme. +* It covers all usages for a transient storage. Clients that are easy + to implement EIP-1087 will also be easy to implement this + specification. Some other clients might require a little bit extra + refactoring on this. Nonetheless, no extra memory or processing cost + is needed on runtime. + +Regarding `SSTORE` gas cost and refunds, see Appendix for proofs of +properties that this EIP satisfies. + +* For *absolute gas used* (that is, actual *gas used* minus *refund*), + this EIP is equivalent to EIP-1087 for all cases. +* For one particular case, where a storage slot is changed, reset to + its original value, and then changed again, EIP-1283 would move more + gases to refund counter compared with EIP-1087. + +Examine examples provided in EIP-1087's Motivation: + +* If a contract with empty storage sets slot 0 to 1, then back to 0, + it will be charged `20000 + 200 - 19800 = 400` gas. +* A contract with empty storage that increments slot 0 5 times will be + charged `20000 + 5 * 200 = 21000` gas. +* A balance transfer from account A to account B followed by a + transfer from B to C, with all accounts having nonzero starting and + ending balances, it will cost `5000 * 3 + 200 - 4800 = 10400` gas. + +## Backwards Compatibility + +This EIP requires a hard fork to implement. No gas cost increase is +anticipated, and many contracts will see gas reduction. + +## Test Cases + +Below we provide 17 test cases. 15 of them covering consecutive two +`SSTORE` operations are based on work [by +@chfast](https://github.com/ethereum/tests/issues/483). Two additional +case with three `SSTORE` operations is used to test the case when a +slot is reset and then set again. + +| Code | Used Gas | Refund | Original | 1st | 2nd | 3rd | +|------------------------------------|----------|--------|----------|-----|-----|-----| +| `0x60006000556000600055` | 412 | 0 | 0 | 0 | 0 | | +| `0x60006000556001600055` | 20212 | 0 | 0 | 0 | 1 | | +| `0x60016000556000600055` | 20212 | 19800 | 0 | 1 | 0 | | +| `0x60016000556002600055` | 20212 | 0 | 0 | 1 | 2 | | +| `0x60016000556001600055` | 20212 | 0 | 0 | 1 | 1 | | +| `0x60006000556000600055` | 5212 | 15000 | 1 | 0 | 0 | | +| `0x60006000556001600055` | 5212 | 4800 | 1 | 0 | 1 | | +| `0x60006000556002600055` | 5212 | 0 | 1 | 0 | 2 | | +| `0x60026000556000600055` | 5212 | 15000 | 1 | 2 | 0 | | +| `0x60026000556003600055` | 5212 | 0 | 1 | 2 | 3 | | +| `0x60026000556001600055` | 5212 | 4800 | 1 | 2 | 1 | | +| `0x60026000556002600055` | 5212 | 0 | 1 | 2 | 2 | | +| `0x60016000556000600055` | 5212 | 15000 | 1 | 1 | 0 | | +| `0x60016000556002600055` | 5212 | 0 | 1 | 1 | 2 | | +| `0x60016000556001600055` | 412 | 0 | 1 | 1 | 1 | | +| `0x600160005560006000556001600055` | 40218 | 19800 | 0 | 1 | 0 | 1 | +| `0x600060005560016000556000600055` | 10218 | 19800 | 1 | 0 | 1 | 0 | + +## Appendix: Proof + +Because the *storage slot's original value* is defined as the value +when a reversion happens on the *current transaction*, it's easy to +see that call frames won't interfere SSTORE gas calculation. So +although the below proof is discussed without call frames, it applies +to all situations with call frames. We will discuss the case +separately for *original value* being zero and not zero, and use +*induction* to prove some properties of SSTORE gas cost. + +*Final value* is the value of a particular storage slot at the end of +a transaction. *Absolute gas used* is the absolute value of *gas used* +minus *refund*. We use `N` to represent the total number of SSTORE +operations on a storage slot. For states discussed below, refer to +*State Transition* in *Explanation* section. + +### Original Value Being Zero + +When *original value* is 0, we want to prove that: + +* **Case I**: If the *final value* ends up still being 0, we want to charge `200 * + N` gases, because no disk write is needed. +* **Case II**: If the *final value* ends up being a non-zero value, we want to + charge `20000 + 200 * (N-1)` gas, because it requires writing this + slot to disk. + +#### Base Case + +We always start at state A. The first SSTORE can: + +* Go to state A: 200 gas is deducted. We satisfy *Case I* because + `200 * N == 200 * 1`. +* Go to state B: 20000 gas is deducted. We satisfy *Case II* because + `20000 + 200 * (N-1) == 20000 + 200 * 0`. + +#### Inductive Step + +* From A to A. The previous gas cost is `200 * (N-1)`. The current + gas cost is `200 + 200 * (N-1)`. It satisfy *Case I*. +* From A to B. The previous gas cost is `200 * (N-1)`. The current + gas cost is `20000 + 200 * (N-1)`. It satisfy *Case II*. +* From B to B. The previous gas cost is `20000 + 200 * (N-2)`. The + current gas cost is `200 + 20000 + 200 * (N-2)`. It satisfy + *Case II*. +* From B to A. The previous gas cost is `20000 + 200 * (N-2)`. The + current gas cost is `200 - 19800 + 20000 + 200 * (N-2)`. It satisfy + *Case I*. + +### Original Value Not Being Zero + +When *original value* is not 0, we want to prove that: + +* **Case I**: If the *final value* ends up unchanged, we want to + charge `200 * N` gases, because no disk write is needed. +* **Case II**: If the *final value* ends up being zero, we want to + charge `5000 - 15000 + 200 * (N-1)` gas. Note that `15000` is the + refund in actual definition. +* **Case III**: If the *final value* ends up being a changed non-zero + value, we want to charge `5000 + 200 * (N-1)` gas. + +#### Base Case + +We always start at state X. The first SSTORE can: + +* Go to state X: 200 gas is deducted. We satisfy *Case I* because + `200 * N == 200 * 1`. +* Go to state Y: 5000 gas is deducted. We satisfy *Case III* because + `5000 + 200 * (N-1) == 5000 + 200 * 0`. +* Go to state Z: The absolute gas used is `5000 - 15000` where 15000 + is the refund. We satisfy *Case II* because `5000 - 15000 + 200 * + (N-1) == 5000 - 15000 + 200 * 0`. + +#### Inductive Step + +* From X to X. The previous gas cost is `200 * (N-1)`. The current gas + cost is `200 + 200 * (N-1)`. It satisfy *Case I*. +* From X to Y. The previous gas cost is `200 * (N-1)`. The current gas + cost is `5000 + 200 * (N-1)`. It satisfy *Case III*. +* From X to Z. The previous gas cost is `200 * (N-1)`. The current + absolute gas cost is `5000 - 15000 + 200 * (N-1)`. It satisfy *Case + II*. +* From Y to X. The previous gas cost is `5000 + 200 * (N-2)`. The + absolute current gas cost is `200 - 4800 + 5000 + 200 * (N-2)`. It + satisfy *Case I*. +* From Y to Y. The previous gas cost is `5000 + 200 * (N-2)`. The + current gas cost is `200 + 5000 + 200 * (N-2)`. It satisfy *Case + III*. +* From Y to Z. The previous gas cost is `5000 + 200 * (N-2)`. The + current absolute gas cost is `200 - 15000 + 5000 + 200 * (N-2)`. It + satisfy *Case II*. +* From Z to X. The previous gas cost is `5000 - 15000 + 200 * + (N-2)`. The current absolute gas cost is `200 + 10200 + 5000 - + 15000 + 200 * (N-2)`. It satisfy *Case I*. +* From Z to Y. The previous gas cost is `5000 - 15000 + 200 * + (N-2)`. The current absolute gas cost is `200 + 15000 + 5000 - + 15000 + 200 * (N-2)`. It satisfy *Case III*. +* From Z to Z. The previous gas cost is `5000 - 15000 + 200 * + (N-2)`. The current absolute gas cost is `200 + 5000 - 15000 + 200 * + (N-2)`. It satisfy *Case II*. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1285.md b/EIPS/eip-1285.md new file mode 100644 index 0000000..7d6d205 --- /dev/null +++ b/EIPS/eip-1285.md @@ -0,0 +1,42 @@ +--- +eip: 1285 +title: Increase Gcallstipend gas in the CALL opcode +author: Ben Kaufman , Adam Levi +discussions-to: https://ethereum-magicians.org/t/eip-1285-increase-gcallstipend-gas-in-the-call-opcode/941 +status: Stagnant +type: Standards Track +category: Core +created: 2018-08-01 +--- + +## Simple Summary +Increase the ``Gcallstipend`` fee parameter in the ``CALL`` opcode from ``2,300`` to ``3,500`` gas units. + +## Abstract +Currently, the ``CALL`` opcode forwards a stipend of ``2,300`` gas units for a non zero value ``CALL`` operations where a contract is called. This stipend is given to the contract to allow execution of its ``fallback`` function. The stipend given is intentionally small in order to prevent the called contract from spending the call gas or performing an attack (like re-entrancy). +While the stipend is small it should still give the sufficient gas required for some cheap opcodes like ``LOG``, but it's not enough for some more complex and modern logics to be implemented. +This EIP proposes to increase the given stipend from ``2,300`` to ``3,500`` to increase the usability of the ``fallback`` function. + + +## Motivation +The main motivation behind this EIP is to allow simple fallback functions to be implemented for contracts following the ``"Proxy"`` pattern. Simply explained, a ``"Proxy Contract"`` is a contract which use ``DELEGATECALL`` in its ``fallback`` function to behave according to the logic of another contract and serve as an independent instance for the logic of the contract it points to. +This pattern is very useful for saving gas per deployment (as Proxy contracts are very lean) and it opens the ability to experiment with upgradability of contracts. +On average, the ``DELEGATECALL`` functionality of a proxy contract costs about ``1,000`` gas units. +When a contract transfers ETH to a proxy contract, the proxy logic will consume about ``1,000`` gas units before the ``fallback`` function of the logic contract will be executed. This leaves merely about 1,300 gas units for the execution of the logic. This is a severe limitation as it is not enough for an average ``LOG`` operation (it might be enough for a ``LOG`` with one parameter). +By slightly increasing the gas units given in the stipend we allow proxy contracts have proper ``fallback`` logic without increasing the attack surface of the calling contract. + +## Specification +Increase the ``Gcallstipend`` fee parameter in the ``CALL`` opcode from ``2,300`` to ``3,500`` gas unit. +The actual change to the Ethereum clients would be to change the ``CallStipend`` they store as a constant. +For an implementation example you can find a Geth client implementation linked [here](https://github.com/ben-kaufman/go-ethereum/tree/eip-1285). The actual change to the code can be found [here](https://github.com/ben-kaufman/go-ethereum/blob/eip-1285/params/protocol_params.go#L41). + +## Rationale +The rational for increasing the ``Gcallstipend`` gas parameter by ``1,200`` gas units comes from the cost of performing ``DELEGATECALL`` and ``SLOAD`` with a small margin for some small additional operations. All while still keeping the stipend relatively small and insufficient for accessing the storage or changing the state. + +## Backwards Compatibility +This EIP requires a backwards incompatible change for the ``Gcallstipend`` gas parameter in the ``CALL`` opcode. + + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1295.md b/EIPS/eip-1295.md new file mode 100644 index 0000000..4eb2074 --- /dev/null +++ b/EIPS/eip-1295.md @@ -0,0 +1,89 @@ +--- +eip: 1295 +title: Modify Ethereum PoW Incentive Structure and Delay Difficulty Bomb +author: Brian Venturo (@atlanticcrypto) +discussions-to: https://github.com/atlanticcrypto/Discussion/issues/1 +status: Stagnant +type: Standards Track +category: Core +created: 2018-08-05 +--- + +## Simple Summary +Network security and overall ecosystem maturity warrants the continued incentivization of Proof of Work participation but may allow for a reduction in ancillary ETH issuance and the delay of the Difficulty Bomb. This EIP proposes a reduction of Uncle and removal of Nephew rewards while delaying the Difficulty Bomb with the Constantinople hard fork. + +## Abstract +Starting with CNSTNTNPL_FORK_BLKNUM the client will calculate the difficulty based on a fake block number suggesting the client that the difficulty bomb is adjusting around 6 million blocks later than previously specified with the Homestead fork. + +Furthermore, Uncle rewards will be adjusted and Nephew rewards will be removed to eliminate excess ancillary ETH issuance. The current ETH block reward of 3 ETH will remain constant. + + +## Motivation +Network scalability and security are at the forefront of risks to the Ethereum protocol. With great strides being made towards on and off chain scalability, the existence of an artificial throughput limiting device in the protocol is not warranted. Removing the risk of reducing throughput through the initialization of the Difficulty Bomb is "low-hanging-fruit" to ensure continued operation at a minimum of current throughput through the next major hard fork (scheduled for late 2019). + +The security layer of the Ethereum network is and should remain robust. Incentives for continued operation of the security of the growing ecosystem are paramount. + +At the same time, the ancillary issuance benefits of the Ethereum protocol can be adjusted to reduce the overall issuance profile. Aggressively adjusting Uncle and removing Nephew rewards will reduce the inflationary aspect of ETH issuance, while keeping the current block reward constant at 3 ETH will ensure top line incentives stay in place. + +## Specification +#### Relax Difficulty with Fake Block Number +For the purposes of `calc_difficulty`, simply replace the use of `block.number`, as used in the exponential ice age component, with the formula: + + fake_block_number = max(0, block.number - 6_000_000) if block.number >= CNSTNTNPL_FORK_BLKNUM else block.number + +#### Adjust Uncle and Nephew rewards +If an uncle is included in a block for `block.number >= CNSTNTNPL_FORK_BLKNUM` such that `block.number - uncle.number = k`, the uncle reward is + + new_uncle_reward = (3 - k) * new_block_reward / 8 + +This is the existing pre-Constantinople formula for uncle rewards, adjusted to reward 2 levels of Uncles at a reduced rate. + +The nephew reward for `block.number >= CNSTNTNPL_FORK_BLKNUM` is + + new_nephew_reward = 0 + +This is a removal of all rewards for Nephew blocks. + +## Rationale + +The security layer of the Ethereum network is and should remain robust. Incentives for continued operation of the growing ecosystem’s security are paramount. + +At the same time, the ancillary issuance benefits of the Ethereum protocol can be adjusted to reduce the overall issuance profile. Aggressively adjusting Uncle and removing Nephew rewards will reduce the inflationary aspect of ETH issuance, while keeping the current block reward constant at 3 ETH will ensure top line incentives stay in place. + +The Difficulty Bomb was instituted as a type of planned obsolescence to force the implementation of network upgrades with regular frequency. With the focus on scalability for the protocol, delaying a mechanism whereby the throughput of the network can be limited artificially, due to any circumstance, is a logical step to ensure continued minimum network operation at the current throughput level. We believe the existence of the Difficulty Bomb has allowed for the inclusion of protocol upgrades in forced hard-forks, and will continue to do so. + +Through August 4th, the 2018 year to date reward issued to Uncle blocks totaled over 635,000 ETH. The average reward per Uncle totals 2.27 ETH. The year to date average Uncle rate is 22.49%. Using the year to date metrics, the ongoing average ETH paid as an Uncle reward per block is 0.51 ETH plus the Uncle inclusion reward of 0.021 ETH (0.09375 ETH * .2249), total Uncle related reward per block being over 0.53 ETH. With total year to date block rewards totaling approximately 3,730,000 ETH, the network has paid an additional 17pct in Uncle rewards. This is issuance that can be reduced while still maintaining the overall integrity and incentivization of network security. + +Reducing the issuance of ETH in rewarding Uncle blocks (forked blocks caused by latency in propagation, a multi-faceted problem) should directly incentivize the investment in technologies and efficiencies to reduce block propagation latency which may lead to a reduction in Network wide Uncle rates, reducing ancillary issuance further. + +Reducing the Uncle rewards from the current specification to the proposed will yield two levels of ancillary ETH issuance for Uncles: + +Level 1 Uncle -> 0.75 ETH + +Level 2 Uncle -> 0.375 ETH + +These levels are sufficient to continue incentivizing decentralized participation, while also providing an immediate economic incentive for the upgrade of the Ethereum node network and its tangential infrastructure. + +We believe that the ETH network has been subsidizing transaction inclusion through the robust Uncle Reward structure since inception. We also believe that a removal of the set subsidy will create a dynamic response mechanism whereby Miners and transaction senders will minimize total costs of transaction inclusion. This dynamic response structure may limit unnecessary layer 1 transaction throughput while providing incentives for layer 2 scaling solutions. + +The Nephew reward structure should be entirely eliminated. + +Due to current market conditions, and the likelihood of a further USD denominated price decline (50%), we believe that any top line reduction to security incentives will put the Ethereum network at undue risk. Unlike the time of the Byzantium hard fork, current USD denominated economics for securing the Ethereum network threaten the participation of the most decentralized Miner community (at home miners), which we believe make up the largest proportion of the overall network hashrate. We believe eliminating this portion of the community will increase centralization and the probability of an organized network attack. + +For a technology so new and with so much potential, we find it extremely irresponsible to increase the likelihood of a network attack by reducing ETH Issuance past this proposal’s level. + +With a reduction to the Uncle and removal of the Nephew reward, ancillary ETH issuance should drop (under normal market conditions, i.e. 22.49% uncle rate) by over 75pct and total ETH issuance from the successful sealing and mining of valid blocks should drop over 10pct. + +Paired with the diffusal of the Difficulty Bomb, this proposal strikes a balance between ensuring status quo network throughput, reducing overall ETH issuance, and continuing top line incentives for investment in infrastructure and efficiencies to continue operating the Ethereum network’s security layer. + +## Backwards Compatibility +This EIP is not forward compatible and introduces backwards incompatibilities in the difficulty calculation, as well as the block, uncle and nephew reward structure. Therefore, it should be included in a scheduled hardfork at a certain block number. It's suggested to include this EIP in the second Metropolis hard-fork, Constantinople. + +## Test Cases +Test cases shall be created once the specification is to be accepted by the developers or implemented by the clients. + +## Implementation +Forthcoming. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1319.md b/EIPS/eip-1319.md new file mode 100644 index 0000000..92ab556 --- /dev/null +++ b/EIPS/eip-1319.md @@ -0,0 +1,176 @@ +--- +eip: 1319 +title: Smart Contract Package Registry Interface +author: Piper Merriam , Christopher Gewecke , g. nicholas d'andrea , Nick Gheorghita (@njgheorghita) +type: Standards Track +category: ERC +status: Stagnant +created: 2018-08-13 +discussions-to: https://github.com/ethereum/EIPs/issues/1319 +--- + +## Simple Summary +A standard interface for smart contract package registries. + +## Abstract +This EIP specifies an interface for publishing to and retrieving assets from smart contract package registries. It is a companion EIP to [1123](./eip-1123.md) which defines a standard for smart contract package manifests. + +## Motivation +The goal is to establish a framework that allows smart contract publishers to design and deploy code registries with arbitrary business logic while exposing a set of common endpoints that tooling can use to retrieve assets for contract consumers. + +A clear standard would help the existing EthPM Package Registry evolve from a centralized, single-project community resource into a decentralized multi-registry system whose constituents are bound together by the proposed interface. In turn, these registries could be ENS name-spaced, enabling installation conventions familiar to users of `npm` and other package managers. + +**Examples** +```shell +$ ethpm install packages.zeppelin.eth/Ownership +``` + +```javascript +const SimpleToken = await web3.packaging + .registry('packages.ethpm.eth') + .getPackage('simple-token') + .getVersion('^1.1.5'); +``` + +## Specification +The specification describes a small read/write API whose components are mandatory. It allows registries to manage versioned releases using the conventions of [semver](https://semver.org/) without imposing this as a requirement. It assumes registries will share the following structure and conventions: + ++ a **registry** is a deployed contract which manages a collection of **packages**. ++ a **package** is a collection of **releases** ++ a **package** is identified by a unique string name and unique bytes32 **packageId** within a given **registry** ++ a **release** is identified by a `bytes32` **releaseId** which must be unique for a given package name and release version string pair. ++ a **releaseId** maps to a set of data that includes a **manifestURI** string which describes the location of an [EIP 1123 package manifest](./eip-1123.md). This manifest contains data about the release including the location of its component code assets. ++ a **manifestURI** is a URI as defined by [RFC3986](https://tools.ietf.org/html/rfc3986) which can be used to retrieve the contents of the package manifest. In addition to validation against RFC3986, each **manifestURI** must also contain a hash of the content as specified in the [EIP-1123](./eip-1123.md). + +### Examples + +**Package Names / Release Versions** + +```shell +"simple-token" # package name +"1.0.1" # version string +``` + +**Release IDs** + +Implementations are free to choose any scheme for generating a **releaseId**. A common approach would be to hash the strings together as below: + +```solidity +// Hashes package name and a release version string +function generateReleaseId(string packageName, string version) + public + view + returns (bytes32 releaseId) + { + return keccak256(abi.encodePacked(packageName, version)); + } +``` +Implementations **must** expose this id generation logic as part of their public `read` API so +tooling can easily map a string based release query to the registry's unique identifier for that release. + +**Manifest URIs** + +Any IPFS or Swarm URI meets the definition of **manifestURI**. + +Another example is content on GitHub addressed by its SHA-1 hash. The Base64 encoded content at this hash can be obtained by running: +```shell +$ curl https://api.github.com/repos/:owner/:repo/git/blobs/:file_sha + +# Example +$ curl https://api.github.com/repos/rstallman/hello/git/blobs/ce013625030ba8dba906f756967f9e9ca394464a +``` + +The string "hello" can have its GitHub SHA-1 hash independently verified by comparing it to the output of: +```shell +$ printf "blob 6\000hello\n" | sha1sum +> ce013625030ba8dba906f756967f9e9ca394464a +``` + +### Write API Specification +The write API consists of a single method, `release`. It passes the registry the package name, a +version identifier for the release, and a URI specifying the location of a manifest which +details the contents of the release. +```solidity +function release(string packageName, string version, string manifestURI) public + returns (bytes32 releaseId); +``` + +### Events + +#### VersionRelease +MUST be triggered when `release` is successfully called. + +```solidity +event VersionRelease(string packageName, string version, string manifestURI) +``` + +### Read API Specification + +The read API consists of a set of methods that allows tooling to extract all consumable data from a registry. + +```solidity +// Retrieves a slice of the list of all unique package identifiers in a registry. +// `offset` and `limit` enable paginated responses / retrieval of the complete set. (See note below) +function getAllPackageIds(uint offset, uint limit) public view + returns ( + bytes32[] packageIds, + uint pointer + ); + +// Retrieves the unique string `name` associated with a package's id. +function getPackageName(bytes32 packageId) public view returns (string packageName); + +// Retrieves the registry's unique identifier for an existing release of a package. +function getReleaseId(string packageName, string version) public view returns (bytes32 releaseId); + +// Retrieves a slice of the list of all release ids for a package. +// `offset` and `limit` enable paginated responses / retrieval of the complete set. (See note below) +function getAllReleaseIds(string packageName, uint offset, uint limit) public view + returns ( + bytes32[] releaseIds, + uint pointer + ); + +// Retrieves package name, release version and URI location data for a release id. +function getReleaseData(bytes32 releaseId) public view + returns ( + string packageName, + string version, + string manifestURI + ); + +// Retrieves the release id a registry *would* generate for a package name and version pair +// when executing a release. +function generateReleaseId(string packageName, string version) + public + view + returns (bytes32 releaseId); + +// Returns the total number of unique packages in a registry. +function numPackageIds() public view returns (uint totalCount); + +// Returns the total number of unique releases belonging to the given packageName in a registry. +function numReleaseIds(string packageName) public view returns (uint totalCount); +``` +**Pagination** + +`getAllPackageIds` and `getAllReleaseIds` support paginated requests because it's possible that the return values for these methods could become quite large. The methods should return a `pointer` that points to the next available item in a list of all items such that a caller can use it to pick up from where the previous request left off. (See [here](https://mixmax.com/blog/api-paging-built-the-right-way) for a discussion of the merits and demerits of various pagination strategies.) The `limit` parameter defines the maximum number of items a registry should return per request. + +## Rationale +The proposal hopes to accomplish the following: + ++ Define the smallest set of inputs necessary to allow registries to map package names to a set of +release versions while allowing them to use any versioning schema they choose. ++ Provide the minimum set of getter methods needed to retrieve package data from a registry so that registry aggregators can read all of their data. ++ Define a standard query that synthesizes a release identifier from a package name and version pair so that tooling can resolve specific package version requests without needing to query a registry about all of a package's releases. + +Registries may offer more complex `read` APIs that manage requests for packages within a semver range or at `latest` etc. This EIP is agnostic about how tooling or registries might implement these. It recommends that registries implement [EIP-165](./eip-165.md) and avail themselves of resources to publish more complex interfaces such as [EIP-926](./eip-926.md). + +## Backwards Compatibility +No existing standard exists for package registries. The package registry currently deployed by EthPM would not comply with the standard since it implements only one of the method signatures described in the specification. + +## Implementation +A reference implementation of this proposal is in active development at the EthPM organization on GitHub [here](https://github.com/ethpm/escape-truffle). + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1328.md b/EIPS/eip-1328.md new file mode 100644 index 0000000..ddc1527 --- /dev/null +++ b/EIPS/eip-1328.md @@ -0,0 +1,72 @@ +--- +eip: 1328 +title: WalletConnect URI Format +description: Define URI format for initiating connections between applications and wallets +author: ligi (@ligi), Pedro Gomes (@pedrouid) +discussions-to: https://ethereum-magicians.org/t/wallet-connect-eip/850 +status: Review +type: Standards Track +category: ERC +created: 2018-08-15 +--- + +## Abstract + +This standard defines how the data to connect some application and a wallet can be encoded with a URI. This URI can then be shown either as a QR code or as a link. + +## Specification + +### Syntax + +WalletConnect request URI with the following parameters: + + request = "wc" ":" topic [ "@" version ][ "?" parameters ] + topic = STRING + version = 1*DIGIT + parameters = parameter *( "&" parameter ) + parameter = key "=" value + key = STRING + value = STRING + +### Semantics + +Required parameters are dependent on the WalletConnect protocol version: + +For WalletConnect v1.0 protocol (`version`=`1`) the parameters are: + +- `key` - symmetric key used for encryption +- `bridge` - url of the bridge server for relaying messages + +For WalletConnect v2.0 protocol (`version`=`2`) the parameters are: + +- `symKey` - symmetric key used for encrypting messages over relay +- `methods` - jsonrpc methods supported for pairing topic +- `relay-protocol` - transport protocol for relaying messages +- `relay-data` - (optional) transport data for relaying messages + + +### Example + +``` +# 1.0 +wc:8a5e5bdc-a0e4-4702-ba63-8f1a5655744f@1?bridge=https%3A%2F%2Fbridge.walletconnect.org&key=41791102999c339c844880b23950704cc43aa840f3739e365323cda4dfa89e7a + +# 2.0 +wc:7f6e504bfad60b485450578e05678ed3e8e8c4751d3c6160be17160d63ec90f9@2?relay-protocol=irn&symKey=587d5484ce2a2a6ee3ba1962fdd7e8588e06200c46823bd18fbd67def96ad303&methods=[wc_sessionPropose],[wc_authRequest,wc_authBatchRequest]" +``` + +## Rationale + +This proposal moves away from the JSON format used in the alpha version of the WalletConnect protocol because it suffered from very inefficient parsing of the intent of the QR code, thereby making it easier to create better QR code parsers APIs for wallets to implement. Also by using a URI instead of JSON inside the QR-Code the Android Intent system can be leveraged. + +## Backwards Compatibility + +Versioning is required as part of the syntax for this URI specification to allow the WalletConnect protocol to evolve and allow backwards-compatibility whenever a new version is introduced. + +## Security Considerations + +URIs should be shared between user devices or applications and no sensitive data is shared within the URI that could compromise the communication or would allow control of the user's private keys. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1337.md b/EIPS/eip-1337.md new file mode 100644 index 0000000..665756b --- /dev/null +++ b/EIPS/eip-1337.md @@ -0,0 +1,247 @@ +--- +eip: 1337 +title: Subscriptions on the blockchain +author: Kevin Owocki , Andrew Redden , Scott Burke , Kevin Seagraves , Luka Kacil , Štefan Šimec , Piotr Kosiński (@kosecki123), ankit raj , John Griffin , Nathan Creswell +discussions-to: https://ethereum-magicians.org/t/eip-1337-subscriptions-on-the-blockchain/4422 +type: Standards Track +status: Stagnant +category: ERC +created: 2018-08-01 +requires: 20, 165 +--- + +## Simple Summary +Monthly subscriptions are a key monetization channel for legacy web, and arguably they are the most healthy monetization channel for businesses on the legacy web (especially when compared to ad/surveillance) based models. They are arguably more healthy than a token based economic system (depending upon the vesting model of the ICO) because + +##### For a user: + * you don't have to read a complex whitepaper to use a dapps utility (as opposed to utility tokens) +* you don't have to understand the founder's vesting schedules +* you can cancel anytime + +##### For a Service Provider: +* since you know your subscriber numbers, churn numbers, conversion rate, you get consistent cash flow, and accurate projections +* you get to focus on making your customers happy +* enables you to remove speculators from your ecosystem + +For these reasons, we think it's imperative to create a standard way to do 'subscriptions' on Ethereum. + +## Abstract +To enable replay-able transactions users sign a concatenated bytes hash that is composed of the input data needed to execute the transaction. This data is stored off chain by the recipient of the payment and is transmitted to the customers smart contract for execution alongside a provided signature. + +## Motivation +Recurring payments are the bedrock of SaSS and countless other businesses, a robust specification for defining this interaction will enable a broad spectrum of revenue generation and business models. + +## Specification +#### Enum Contract + +EIP-1337 Contracts should be compiled with a contract that references all the enumerations that are required for operation + +```SOLIDITY +/// @title Enum - Collection of enums +/// Original concept from Richard Meissner - Gnosis safe contracts +contract Enum { + enum Operation { + Call, + DelegateCall, + Create, + ERC20, + ERC20Approve + } + enum SubscriptionStatus { + ACTIVE, + PAUSED, + CANCELLED, + EXPIRED + } + + enum Period { + INIT, + DAY, + WEEK, + MONTH + } +} +``` + +#### EIP-165 + +EIP-1337 compliant contracts support EIP-165 announcing what interfaces they support + +```SOLIDITY +interface ERC165 { + /** + * @notice Query if a contract implements an interface + * @param interfaceID The interface identifier, as specified in ERC-165 + * @dev Interface identification is specified in ERC-165. This function + * uses less than 30,000 gas. + * @return `true` if the contract implements `interfaceID` and + * `interfaceID` is not 0xffffffff, `false` otherwise + **/ + function supportsInterface(bytes4 interfaceID) external view returns (bool); +} +``` + +#### Public View Functions + +###### isValidSubscription +```SOLIDITY + +/** @dev Checks if the subscription is valid. + * @param bytes subscriptionHash is the identifier of the customer's subscription with its relevant details. + * @return success is the result of whether the subscription is valid or not. + **/ + +function isValidSubscription( + uint256 subscriptionHash + ) + public + view + returns ( + bool success + ) +``` +###### getSubscriptionStatus +```SOLIDITY + +/** @dev returns the value of the subscription + * @param bytes subscriptionHash is the identifier of the customer's subscription with its relevant details. + * @return status is the enumerated status of the current subscription, 0 expired, 1 active, 2 paused, 3 cancelled + **/ +function getSubscriptionStatus( + uint256 subscriptionHash + ) + public + view + returns ( + uint256 status, + uint256 nextWithdraw + ) +``` + +###### getSubscriptionHash + +```SOLIDITY +/** @dev returns the hash of cocatenated inputs to the address of the contract holding the logic., + * the owner would sign this hash and then provide it to the party for execution at a later date, + * this could be viewed like a cheque, with the exception that unless you specifically + * capture the hash on chain a valid signature will be executable at a later date, capturing the hash lets you modify the status to cancel or expire it. + * @param address recipient the address of the person who is getting the funds. + * @param uint256 value the value of the transaction + * @param bytes data the data the user is agreeing to + * @param uint256 txGas the cost of executing one of these transactions in gas(probably safe to pad this) + * @param uint256 dataGas the cost of executing the data portion of the transaction(delegate calls etc) + * @param uint 256 gasPrice the agreed upon gas cost of Execution of this subscription(cost incurment is up to implementation, ie, sender or receiver) + * @param address gasToken address of the token in which gas will be compensated by, address(0) is ETH, only works in the case of an enscrow implementation) + * @param bytes meta dynamic bytes array with 4 slots, 2 required, 2 optional // address refundAddress / uint256 period / uint256 offChainID / uint256 expiration (uinx timestamp) + * @return bytes32, return the hash input arguments concatenated to the address of the contract that holds the logic. + **/ +function getSubscriptionHash( + address recipient, + uint256 value, + bytes data, + Enum.Operation operation, + uint256 txGas, + uint256 dataGas, + uint256 gasPrice, + address gasToken, + bytes meta + ) + public + view + returns ( + bytes32 subscriptionHash + ) +``` + + +###### getModifyStatusHash + +```SOLIDITY +/** @dev returns the hash of concatenated inputs that the owners user would sign with their public keys + * @param address recipient the address of the person who is getting the funds. + * @param uint256 value the value of the transaction + * @return bytes32 returns the hash of concatenated inputs with the address of the contract holding the subscription hash + **/ +function getModifyStatusHash( + bytes32 subscriptionHash + Enum.SubscriptionStatus status + ) + public + view + returns ( + bytes32 modifyStatusHash + ) +``` +#### Public Functions + +###### modifyStatus +```SOLIDITY + +/** @dev modifys the current subscription status + * @param uint256 subscriptionHash is the identifier of the customer's subscription with its relevant details. + * @param Enum.SubscriptionStatus status the new status of the subscription + * @param bytes signatures of the requested method being called + * @return success is the result of the subscription being paused + **/ +function modifyStatus( + uint256 subscriptionHash, + Enum.SubscriptionStatus status, + bytes signatures + ) + public + returns ( + bool success + ) +``` + +###### executeSubscription +```SOLIDITY + +/** @dev returns the hash of cocatenated inputs to the address of the contract holding the logic., + * the owner would sign this hash and then provide it to the party for execution at a later date, + * this could be viewed like a cheque, with the exception that unless you specifically + * capture the hash on chain a valid signature will be executable at a later date, capturing the hash lets you modify the status to cancel or expire it. + * @param address recipient the address of the person who is getting the funds. + * @param uint256 value the value of the transaction + * @param bytes data the data the user is agreeing to + * @param uint256 txGas the cost of executing one of these transactions in gas(probably safe to pad this) + * @param uint256 dataGas the cost of executing the data portion of the transaction(delegate calls etc) + * @param uint 256 gasPrice the agreed upon gas cost of Execution of this subscription(cost incurment is up to implementation, ie, sender or receiver) + * @param address gasToken address of the token in which gas will be compensated by, address(0) is ETH, only works in the case of an enscrow implementation) + * @param bytes meta dynamic bytes array with 4 slots, 2 required, 2 optional // address refundAddress / uint256 period / uint256 offChainID / uint256 expiration (uinx timestamp) + * @param bytes signatures signatures concatenated that have signed the inputs as proof of valid execution + * @return bool success something to note that a failed execution will still pay the issuer of the transaction for their gas costs. + **/ +function executeSubscription( + address to, + uint256 value, + bytes data, + Enum.Operation operation, + uint256 txGas, + uint256 dataGas, + uint256 gasPrice, + address gasToken, + bytes meta, + bytes signatures + ) + public + returns ( + bool success + ) +``` + +## Rationale +Merchants who accept credit-cards do so by storing a token that is retrieved from a third party processor(stripe, paypal, etc), this token is used to grant access to pull payment from the cx's credit card provider and move funds to the merchant account. +Having users sign input data acts in a similliar fashion and enables that merchant to store the signature of the concatenated bytes hash and input data used to generate the hash and pass them off to the contract holding the subscription logic, thus enabling a workflow that is similliar to what exists in the present day legacy web. + +## Backwards Compatibility +N/A + +## Test Cases +TBD + +## Implementation +TBD + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1344.md b/EIPS/eip-1344.md new file mode 100644 index 0000000..02e46ae --- /dev/null +++ b/EIPS/eip-1344.md @@ -0,0 +1,51 @@ +--- +eip: 1344 +title: ChainID opcode +author: Richard Meissner (@rmeissner), Bryant Eisenbach (@fubuloubu) +discussions-to: https://ethereum-magicians.org/t/add-chain-id-opcode-for-replay-protection-when-handling-signed-messages-in-contracts/1131 +category: Core +type: Standards Track +status: Final +created: 2018-08-22 +requires: 155 +--- + +## Abstract +This EIP adds an opcode that returns the current chain's EIP-155 unique identifier. + +## Motivation +[EIP-155](./eip-155.md) proposes to use the chain ID to prevent replay attacks between different chains. It would be a great benefit to have the same possibility inside smart contracts when handling signatures, especially for Layer 2 signature schemes using [EIP-712](./eip-712.md). + +## Specification +Adds a new opcode `CHAINID` at 0x46, which uses 0 stack arguments. It pushes the current chain ID onto the stack. Chain ID is a 256-bit value. The operation costs `G_base` to execute. + +The value of the current chain ID is obtained from the chain ID configuration, which should match the EIP-155 unique identifier a client will accept from incoming transactions. Please note that per EIP-155, it is not *required* that a transaction have an EIP-155 unique identifier, but in that scenario this opcode will still return the configured chain ID and not a default. + +## Rationale +The current approach proposed by EIP-712 is to specify the chain ID at compile time. Using this approach will result in problems after a hardfork, as well as human error that may lead to loss of funds or replay attacks on signed messages. +By adding the proposed opcode it will be possible to access the current chain ID and validate signatures based on that. + +Currently, there is no specification for how chain ID is set for a particular network, relying on choices made manually by the client implementers and the chain community. There is a potential scenario where, during a "contentious split" over a divisive issue, a community using a particular value of chain ID will make a decision to split into two such chains. When this scenario occurs, it will be unsafe to maintain chain ID to the same value on both chains, as chain ID is used for replay protection for in-protocol transactions (per EIP-155), as well as for L2 and "meta-transaction" use cases (per EIP-712 as enabled by this proposal). There are two potential resolutions in this scenario under the current process: 1) one chain decides to modify their value of chain ID (while the other keeps it), or 2) both chains decide to modify their value of chain ID. + +In order to mitigate this situation, users of the proposed `CHAINID` opcode **must** ensure that their application can handle a potential update to the value of chain ID during their usage of their application in case this does occur, if required for the continued use of the application. A Trustless Oracle that logs the timestamp when a change is made to chain ID can be implemented either as an application-level feature inside the application contract system, or referenced as a globally standard contract. Failure to provide a mitigation for this scenario could lead to a sudden loss of legitimacy of previously signed off-chain messages, which could be an issue during settlement periods and other longer-term verification events for these types of messages. Not all applications of this opcode may need mitigations to handle this scenario, but developers should provide reasoning on a case-by-case basis. + +One example of a scenario where it would not make sense to leverage a global oracle is with the Plasma L2 paradigm. In the Plasma paradigm, an operator or group of operators submit blocks from the L2 network to the base chain (in this case Ethereum) summarizing transactions that have occurred on that chain. The submission of these blocks may not perfectly align with major events on the mainchain, such as a split causing an update of chain ID, which may cause a significant insecurity in the protocol if chain ID is utilized in signing messages. If the operators are not allowed to control the update of chain ID they will not be able to perfectly synchronize the update with their block submissions, and certain past transactions may be rejected because they do not align with the update. This is one example of the unintended consequences of trying to specify too much of the behavior of chain ID during a contentious split, and why having a simple opcode for access is most optimal, versus a more complicated precompile or contract. + +This proposed opcode would be the simplest possible way to implement this functionality, and allows developers the flexibility to implement their own global or local handling of chain ID changes, if required. + +## Backwards Compatibility +This EIP is fully backwards compatible with all chains which implement EIP-155 chain ID domain separator for transaction signing. + +## References +This was previously suggested as part of [EIP-901](https://github.com/ethereum/EIPs/issues/901). + +## Test Cases +Test Cases added to [ethereum/tests](https://github.com/ethereum/tests/pull/627) + +## Implementation +A reference implementation for the Trinity Python client is [here](https://github.com/ethereum/py-evm/pull/1803). + +An example implementation of a trustless chain ID oracle was implemented [here](https://github.com/fubuloubu/chain-id-oracle/blob/master/ChainIdOracle.vy). + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1352.md b/EIPS/eip-1352.md new file mode 100644 index 0000000..55c1a52 --- /dev/null +++ b/EIPS/eip-1352.md @@ -0,0 +1,40 @@ +--- +eip: 1352 +title: Specify restricted address range for precompiles/system contracts +author: Alex Beregszaszi (@axic) +discussions-to: https://ethereum-magicians.org/t/eip-1352-specify-restricted-address-range-for-precompiles-system-contracts/1151 +status: Stagnant +type: Standards Track +category: Core +created: 2018-07-27 +--- + +## Simple Summary +Specify an Ethereum address range occupied by precompiles and future system contracts. Regular accounts and contracts cannot obtain such an address. + +## Abstract +The address range between 0x0000000000000000000000000000000000000000 and 0x000000000000000000000000000000000000ffff is reserved for precompiles and system contracts. + +## Motivation +This will simplify certain future features where unless this is implemented, several exceptions must be specified. + +## Specification +The address range between 0x0000000000000000000000000000000000000000 and 0x000000000000000000000000000000000000ffff is reserved for precompiles and system contracts. + +Due to the extremely low probability (and lack of adequate testing possibilities) no explicit checks should be added to ensure that external transaction signing or +the invoking of the `CREATE` instruction can result in a precompile address. + +## Rationale +N/A + +## Backwards Compatibility +No contracts on the main network have been created at the specified addresses. As a result it should pose no backwards compatibility problems. + +## Test Cases +N/A + +## Implementation +N/A + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1355.md b/EIPS/eip-1355.md new file mode 100644 index 0000000..dba48d4 --- /dev/null +++ b/EIPS/eip-1355.md @@ -0,0 +1,52 @@ +--- +eip: 1355 +title: Ethash 1a +author: Paweł Bylica (@chfast), Jean M. Cyr (@jean-m-cyr) +discussions-to: https://ethereum-magicians.org/t/eip-1355-ethash-1a/1167 +status: Withdrawn +type: Standards Track +category: Core +created: 2018-08-26 +--- + +## Motivation + +Provide minimal set of changes to Ethash algorithm to hinder and delay the adoption of ASIC based mining. + +## Specification + +1. Define hash function `fnv1a()` as + ```python + def fnv1a(v1, v2): + return ((v1 ^ v2) * FNV1A_PRIME) % 2**32 + ``` + where `FNV1A_PRIME` is 16777499 or 16777639. +2. Change the hash function that determines the DAG item index in Ethash algorithm from `fnv()` to new `fnv1a()`. + In [Main Loop](https://github.com/ethereum/wiki/wiki/Ethash#main-loop) change + ```python + p = fnv(i ^ s[0], mix[i % w]) % (n // mixhashes) * mixhashes + ``` + to + ```python + p = fnv1a(i ^ s[0], mix[i % w]) % (n // mixhashes) * mixhashes + ``` + +## Rationale + +The usual argument for decentralization and network security. + +Unless programmable, an ASIC is hardwired to perform sequential operations in a given order. fnv1a changes the order in which an exclusive-or and a multiply are applied, effectively disabling the current wave of ASICS. A second objective is minimize ethash changes to be the least disruptive, to facilitate rapid development, and to lower the analysis and test requirements. Minimizing changes to ethash reduces risk associated with updating all affected network components, and also reduces the risk of detuning existing GPUs. It's expected that this specific change would have no effect on existing GPU performance. + +Changing fnv to fnv1a has no cryptographic implications. It is merely an efficient hash function with good dispersion characteristics used to scramble DAG indexing. We remain focused on risk mitigation by reducing the need for rigorous cryptographic analysis. + + +### FNV Primes + +The 16777639 satisfies all requirements from [Wikipedia](https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV_prime). + +The 16777499 is preferred by FNV authors but not used in the reference FNV implementation because of historical reasons. +See [A few remarks on FNV primes](http://www.isthe.com/chongo/tech/comp/fnv/index.html#fnv-prime). + +## Copyright + +This work is licensed under a [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-nc-sa/4.0/). diff --git a/EIPS/eip-1363.md b/EIPS/eip-1363.md new file mode 100644 index 0000000..d4984c3 --- /dev/null +++ b/EIPS/eip-1363.md @@ -0,0 +1,208 @@ +--- +eip: 1363 +title: Payable Token +author: Vittorio Minacori (@vittominacori) +discussions-to: https://github.com/ethereum/eips/issues/1363 +status: Final +type: Standards Track +category: ERC +created: 2020-01-31 +requires: 20, 165 +--- + +## Simple Summary +Defines a token interface for [ERC-20](./eip-20.md) tokens that supports executing recipient code after `transfer` or `transferFrom`, or spender code after `approve`. + +## Abstract +Standard functions a token contract and contracts working with tokens can implement to make a token Payable. + +`transferAndCall` and `transferFromAndCall` will call an `onTransferReceived` on a `ERC1363Receiver` contract. + +`approveAndCall` will call an `onApprovalReceived` on a `ERC1363Spender` contract. + +## Motivation +There is no way to execute code after a [ERC-20](./eip-20.md) transfer or approval (i.e. making a payment), so to make an action it is required to send another transaction and pay GAS twice. + +This proposal wants to make token payments easier and working without the use of any other listener. It allows to make a callback after a transfer or approval in a single transaction. + +There are many proposed uses of Ethereum smart contracts that can accept [ERC-20](./eip-20.md) payments. + +Examples could be +* to create a token payable crowdsale +* selling services for tokens +* paying invoices +* making subscriptions + +For these reasons it was named as **"Payable Token"**. + +Anyway you can use it for specific utilities or for any other purposes who require the execution of a callback after a transfer or approval received. + +This proposal has been inspired by the [ERC-721](./eip-721.md) `onERC721Received` and `ERC721TokenReceiver` behaviours. + +## Specification +Implementing contracts **MUST** implement the [ERC-1363](./eip-1363.md) interface as well as the [ERC-20](./eip-20.md) and [ERC-165](./eip-165.md) interfaces. + +```solidity +pragma solidity ^0.8.0; + +interface ERC1363 /* is ERC20, ERC165 */ { + /* + * Note: the ERC-165 identifier for this interface is 0xb0202a11. + * 0xb0202a11 === + * bytes4(keccak256('transferAndCall(address,uint256)')) ^ + * bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^ + * bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^ + * bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^ + * bytes4(keccak256('approveAndCall(address,uint256)')) ^ + * bytes4(keccak256('approveAndCall(address,uint256,bytes)')) + */ + + /** + * @notice Transfer tokens from `msg.sender` to another address and then call `onTransferReceived` on receiver + * @param to address The address which you want to transfer to + * @param value uint256 The amount of tokens to be transferred + * @return true unless throwing + */ + function transferAndCall(address to, uint256 value) external returns (bool); + + /** + * @notice Transfer tokens from `msg.sender` to another address and then call `onTransferReceived` on receiver + * @param to address The address which you want to transfer to + * @param value uint256 The amount of tokens to be transferred + * @param data bytes Additional data with no specified format, sent in call to `to` + * @return true unless throwing + */ + function transferAndCall(address to, uint256 value, bytes memory data) external returns (bool); + + /** + * @notice Transfer tokens from one address to another and then call `onTransferReceived` on receiver + * @param from address The address which you want to send tokens from + * @param to address The address which you want to transfer to + * @param value uint256 The amount of tokens to be transferred + * @return true unless throwing + */ + function transferFromAndCall(address from, address to, uint256 value) external returns (bool); + + + /** + * @notice Transfer tokens from one address to another and then call `onTransferReceived` on receiver + * @param from address The address which you want to send tokens from + * @param to address The address which you want to transfer to + * @param value uint256 The amount of tokens to be transferred + * @param data bytes Additional data with no specified format, sent in call to `to` + * @return true unless throwing + */ + function transferFromAndCall(address from, address to, uint256 value, bytes memory data) external returns (bool); + + /** + * @notice Approve the passed address to spend the specified amount of tokens on behalf of msg.sender + * and then call `onApprovalReceived` on spender. + * @param spender address The address which will spend the funds + * @param value uint256 The amount of tokens to be spent + */ + function approveAndCall(address spender, uint256 value) external returns (bool); + + /** + * @notice Approve the passed address to spend the specified amount of tokens on behalf of msg.sender + * and then call `onApprovalReceived` on spender. + * @param spender address The address which will spend the funds + * @param value uint256 The amount of tokens to be spent + * @param data bytes Additional data with no specified format, sent in call to `spender` + */ + function approveAndCall(address spender, uint256 value, bytes memory data) external returns (bool); +} + +interface ERC20 { + function totalSupply() external view returns (uint256); + function balanceOf(address account) external view returns (uint256); + function transfer(address recipient, uint256 amount) external returns (bool); + function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); + function allowance(address owner, address spender) external view returns (uint256); + function approve(address spender, uint256 amount) external returns (bool); + event Transfer(address indexed from, address indexed to, uint256 value); + event Approval(address indexed owner, address indexed spender, uint256 value); +} + +interface ERC165 { + function supportsInterface(bytes4 interfaceId) external view returns (bool); +} +``` + +A contract that wants to accept token payments via `transferAndCall` or `transferFromAndCall` **MUST** implement the following interface: + +```solidity +/** + * @title ERC1363Receiver interface + * @dev Interface for any contract that wants to support `transferAndCall` or `transferFromAndCall` + * from ERC1363 token contracts. + */ +interface ERC1363Receiver { + /* + * Note: the ERC-165 identifier for this interface is 0x88a7ca5c. + * 0x88a7ca5c === bytes4(keccak256("onTransferReceived(address,address,uint256,bytes)")) + */ + + /** + * @notice Handle the receipt of ERC1363 tokens + * @dev Any ERC1363 smart contract calls this function on the recipient + * after a `transfer` or a `transferFrom`. This function MAY throw to revert and reject the + * transfer. Return of other than the magic value MUST result in the + * transaction being reverted. + * Note: the token contract address is always the message sender. + * @param operator address The address which called `transferAndCall` or `transferFromAndCall` function + * @param from address The address which are token transferred from + * @param value uint256 The amount of tokens transferred + * @param data bytes Additional data with no specified format + * @return `bytes4(keccak256("onTransferReceived(address,address,uint256,bytes)"))` + * unless throwing + */ + function onTransferReceived(address operator, address from, uint256 value, bytes memory data) external returns (bytes4); +} +``` + +A contract that wants to accept token payments via `approveAndCall` **MUST** implement the following interface: + +```solidity +/** + * @title ERC1363Spender interface + * @dev Interface for any contract that wants to support `approveAndCall` + * from ERC1363 token contracts. + */ +interface ERC1363Spender { + /* + * Note: the ERC-165 identifier for this interface is 0x7b04a2d0. + * 0x7b04a2d0 === bytes4(keccak256("onApprovalReceived(address,uint256,bytes)")) + */ + + /** + * @notice Handle the approval of ERC1363 tokens + * @dev Any ERC1363 smart contract calls this function on the recipient + * after an `approve`. This function MAY throw to revert and reject the + * approval. Return of other than the magic value MUST result in the + * transaction being reverted. + * Note: the token contract address is always the message sender. + * @param owner address The address which called `approveAndCall` function + * @param value uint256 The amount of tokens to be spent + * @param data bytes Additional data with no specified format + * @return `bytes4(keccak256("onApprovalReceived(address,uint256,bytes)"))` + * unless throwing + */ + function onApprovalReceived(address owner, uint256 value, bytes memory data) external returns (bytes4); +} +``` + +## Rationale +The choice to use `transferAndCall`, `transferFromAndCall` and `approveAndCall` derives from the [ERC-20](./eip-20.md) naming. They want to highlight that they have the same behaviours of `transfer`, `transferFrom` and `approve` with the addition of a callback on receiver or spender. + +## Backwards Compatibility +This proposal has been inspired also by [ERC-223](https://github.com/ethereum/EIPs/issues/223) and [ERC-677](https://github.com/ethereum/EIPs/issues/677) but it uses the [ERC-721](./eip-721.md) approach, so it doesn't override the [ERC-20](./eip-20.md) `transfer` and `transferFrom` methods and defines the interfaces IDs to be implemented maintaining the [ERC-20](./eip-20.md) backwards compatibility. + +## Security Considerations +The `approveAndCall` and `transferFromAndCall` methods can be affected by the same issue of the standard [ERC-20](./eip-20.md) `approve` and `transferFrom` method. + +Changing an allowance with the `approveAndCall` methods brings the risk that someone may use both the old and the new allowance by unfortunate transaction ordering. + +One possible solution to mitigate this race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards ([EIP-20#issuecomment-263524729](https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729)). + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-137.md b/EIPS/eip-137.md new file mode 100644 index 0000000..b35e9ef --- /dev/null +++ b/EIPS/eip-137.md @@ -0,0 +1,386 @@ +--- +eip: 137 +title: Ethereum Domain Name Service - Specification +author: Nick Johnson +status: Final +type: Standards Track +category: ERC +created: 2016-04-04 +--- + +# Abstract + +This draft EIP describes the details of the Ethereum Name Service, a proposed protocol and ABI definition that provides flexible resolution of short, human-readable names to service and resource identifiers. This permits users and developers to refer to human-readable and easy to remember names, and permits those names to be updated as necessary when the underlying resource (contract, content-addressed data, etc) changes. + +The goal of domain names is to provide stable, human-readable identifiers that can be used to specify network resources. In this way, users can enter a memorable string, such as 'vitalik.wallet' or 'www.mysite.swarm', and be directed to the appropriate resource. The mapping between names and resources may change over time, so a user may change wallets, a website may change hosts, or a swarm document may be updated to a new version, without the domain name changing. Further, a domain need not specify a single resource; different record types allow the same domain to reference different resources. For instance, a browser may resolve 'mysite.swarm' to the IP address of its server by fetching its A (address) record, while a mail client may resolve the same address to a mail server by fetching its MX (mail exchanger) record. +# Motivation + +Existing [specifications](https://github.com/ethereum/wiki/wiki/Registrar-ABI) and [implementations](https://ethereum.gitbooks.io/frontier-guide/content/registrar_services.html) for name resolution in Ethereum provide basic functionality, but suffer several shortcomings that will significantly limit their long-term usefulness: +- A single global namespace for all names with a single 'centralised' resolver. +- Limited or no support for delegation and sub-names/sub-domains. +- Only one record type, and no support for associating multiple copies of a record with a domain. +- Due to a single global implementation, no support for multiple different name allocation systems. +- Conflation of responsibilities: Name resolution, registration, and whois information. + +Use-cases that these features would permit include: +- Support for subnames/sub-domains - eg, live.mysite.tld and forum.mysite.tld. +- Multiple services under a single name, such as a DApp hosted in Swarm, a Whisper address, and a mail server. +- Support for DNS record types, allowing blockchain hosting of 'legacy' names. This would permit an Ethereum client such as Mist to resolve the address of a traditional website, or the mail server for an email address, from a blockchain name. +- DNS gateways, exposing ENS domains via the Domain Name Service, providing easier means for legacy clients to resolve and connect to blockchain services. + +The first two use-cases, in particular, can be observed everywhere on the present-day internet under DNS, and we believe them to be fundamental features of a name service that will continue to be useful as the Ethereum platform develops and matures. + +The normative parts of this document does not specify an implementation of the proposed system; its purpose is to document a protocol that different resolver implementations can adhere to in order to facilitate consistent name resolution. An appendix provides sample implementations of resolver contracts and libraries, which should be treated as illustrative examples only. + +Likewise, this document does not attempt to specify how domains should be registered or updated, or how systems can find the owner responsible for a given domain. Registration is the responsibility of registrars, and is a governance matter that will necessarily vary between top-level domains. + +Updating of domain records can also be handled separately from resolution. Some systems, such as swarm, may require a well defined interface for updating domains, in which event we anticipate the development of a standard for this. +# Specification +## Overview + +The ENS system comprises three main parts: +- The ENS registry +- Resolvers +- Registrars + +The registry is a single contract that provides a mapping from any registered name to the resolver responsible for it, and permits the owner of a name to set the resolver address, and to create subdomains, potentially with different owners to the parent domain. + +Resolvers are responsible for performing resource lookups for a name - for instance, returning a contract address, a content hash, or IP address(es) as appropriate. The resolver specification, defined here and extended in other EIPs, defines what methods a resolver may implement to support resolving different types of records. + +Registrars are responsible for allocating domain names to users of the system, and are the only entities capable of updating the ENS; the owner of a node in the ENS registry is its registrar. Registrars may be contracts or externally owned accounts, though it is expected that the root and top-level registrars, at a minimum, will be implemented as contracts. + +Resolving a name in ENS is a two-step process. First, the ENS registry is called with the name to resolve, after hashing it using the procedure described below. If the record exists, the registry returns the address of its resolver. Then, the resolver is called, using the method appropriate to the resource being requested. The resolver then returns the desired result. + +For example, suppose you wish to find the address of the token contract associated with 'beercoin.eth'. First, get the resolver: + +```javascript +var node = namehash("beercoin.eth"); +var resolver = ens.resolver(node); +``` + +Then, ask the resolver for the address for the contract: + +```javascript +var address = resolver.addr(node); +``` + +Because the `namehash` procedure depends only on the name itself, this can be precomputed and inserted into a contract, removing the need for string manipulation, and permitting O(1) lookup of ENS records regardless of the number of components in the raw name. +## Name Syntax + +ENS names must conform to the following syntax: + +
<domain> ::= <label> | <domain> "." <label>
+<label> ::= any valid string label per [UTS46](https://unicode.org/reports/tr46/)
+
+ +In short, names consist of a series of dot-separated labels. Each label must be a valid normalised label as described in [UTS46](https://unicode.org/reports/tr46/) with the options `transitional=false` and `useSTD3AsciiRules=true`. For Javascript implementations, a [library](https://www.npmjs.com/package/idna-uts46) is available that normalises and checks names. + +Note that while upper and lower case letters are allowed in names, the UTS46 normalisation process case-folds labels before hashing them, so two names with different case but identical spelling will produce the same namehash. + +Labels and domains may be of any length, but for compatibility with legacy DNS, it is recommended that labels be restricted to no more than 64 characters each, and complete ENS names to no more than 255 characters. For the same reason, it is recommended that labels do not start or end with hyphens, or start with digits. + +## namehash algorithm + +Before being used in ENS, names are hashed using the 'namehash' algorithm. This algorithm recursively hashes components of the name, producing a unique, fixed-length string for any valid input domain. The output of namehash is referred to as a 'node'. + +Pseudocode for the namehash algorithm is as follows: + +``` +def namehash(name): + if name == '': + return '\0' * 32 + else: + label, _, remainder = name.partition('.') + return sha3(namehash(remainder) + sha3(label)) +``` + +Informally, the name is split into labels, each label is hashed. Then, starting with the last component, the previous output is concatenated with the label hash and hashed again. The first component is concatenated with 32 '0' bytes. Thus, 'mysite.swarm' is processed as follows: + +``` +node = '\0' * 32 +node = sha3(node + sha3('swarm')) +node = sha3(node + sha3('mysite')) +``` + +Implementations should conform to the following test vectors for namehash: + + namehash('') = 0x0000000000000000000000000000000000000000000000000000000000000000 + namehash('eth') = 0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae + namehash('foo.eth') = 0xde9b09fd7c5f901e23a3f19fecc54828e9c848539801e86591bd9801b019f84f + +## Registry specification + +The ENS registry contract exposes the following functions: + +```solidity +function owner(bytes32 node) constant returns (address); +``` + +Returns the owner (registrar) of the specified node. + +```solidity +function resolver(bytes32 node) constant returns (address); +``` + +Returns the resolver for the specified node. + +```solidity +function ttl(bytes32 node) constant returns (uint64); +``` + +Returns the time-to-live (TTL) of the node; that is, the maximum duration for which a node's information may be cached. + +```solidity +function setOwner(bytes32 node, address owner); +``` + +Transfers ownership of a node to another registrar. This function may only be called by the current owner of `node`. A successful call to this function logs the event `Transfer(bytes32 indexed, address)`. + +```solidity +function setSubnodeOwner(bytes32 node, bytes32 label, address owner); +``` + +Creates a new node, `sha3(node, label)` and sets its owner to `owner`, or updates the node with a new owner if it already exists. This function may only be called by the current owner of `node`. A successful call to this function logs the event `NewOwner(bytes32 indexed, bytes32 indexed, address)`. + +```solidity +function setResolver(bytes32 node, address resolver); +``` + +Sets the resolver address for `node`. This function may only be called by the owner of `node`. A successful call to this function logs the event `NewResolver(bytes32 indexed, address)`. + +```solidity +function setTTL(bytes32 node, uint64 ttl); +``` + +Sets the TTL for a node. A node's TTL applies to the 'owner' and 'resolver' records in the registry, as well as to any information returned by the associated resolver. +## Resolver specification + +Resolvers may implement any subset of the record types specified here. Where a record types specification requires a resolver to provide multiple functions, the resolver MUST implement either all or none of them. Resolvers MUST specify a fallback function that throws. + +Resolvers have one mandatory function: + +```solidity +function supportsInterface(bytes4 interfaceID) constant returns (bool) +``` + +The `supportsInterface` function is documented in [EIP-165](./eip-165.md), and returns true if the resolver implements the interface specified by the provided 4 byte identifier. An interface identifier consists of the XOR of the function signature hashes of the functions provided by that interface; in the degenerate case of single-function interfaces, it is simply equal to the signature hash of that function. If a resolver returns `true` for `supportsInterface()`, it must implement the functions specified in that interface. + +`supportsInterface` must always return true for `0x01ffc9a7`, which is the interface ID of `supportsInterface` itself. + + Currently standardised resolver interfaces are specified in the table below. + +The following interfaces are defined: + +| Interface name | Interface hash | Specification | +| --- | --- | --- | +| `addr` | 0x3b3b57de | [Contract address](#addr) | +| `name` | 0x691f3431 | #181 | +| `ABI` | 0x2203ab56 | #205 | +| `pubkey` | 0xc8690233 | #619 | + +EIPs may define new interfaces to be added to this registry. + +###
Contract Address Interface + +Resolvers wishing to support contract address resources must provide the following function: + +```solidity +function addr(bytes32 node) constant returns (address); +``` + +If the resolver supports `addr` lookups but the requested node does not have an addr record, the resolver MUST return the zero address. + +Clients resolving the `addr` record MUST check for a zero return value, and treat this in the same manner as a name that does not have a resolver specified - that is, refuse to send funds to or interact with the address. Failure to do this can result in users accidentally sending funds to the 0 address. + +Changes to an address MUST trigger the following event: + +```solidity +event AddrChanged(bytes32 indexed node, address a); +``` +# Appendix A: Registry Implementation + +```solidity +contract ENS { + struct Record { + address owner; + address resolver; + uint64 ttl; + } + + mapping(bytes32=>Record) records; + + event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner); + event Transfer(bytes32 indexed node, address owner); + event NewResolver(bytes32 indexed node, address resolver); + + modifier only_owner(bytes32 node) { + if(records[node].owner != msg.sender) throw; + _ + } + + function ENS(address owner) { + records[0].owner = owner; + } + + function owner(bytes32 node) constant returns (address) { + return records[node].owner; + } + + function resolver(bytes32 node) constant returns (address) { + return records[node].resolver; + } + + function ttl(bytes32 node) constant returns (uint64) { + return records[node].ttl; + } + + function setOwner(bytes32 node, address owner) only_owner(node) { + Transfer(node, owner); + records[node].owner = owner; + } + + function setSubnodeOwner(bytes32 node, bytes32 label, address owner) only_owner(node) { + var subnode = sha3(node, label); + NewOwner(node, label, owner); + records[subnode].owner = owner; + } + + function setResolver(bytes32 node, address resolver) only_owner(node) { + NewResolver(node, resolver); + records[node].resolver = resolver; + } + + function setTTL(bytes32 node, uint64 ttl) only_owner(node) { + NewTTL(node, ttl); + records[node].ttl = ttl; + } +} +``` +# Appendix B: Sample Resolver Implementations +### Built-in resolver + +The simplest possible resolver is a contract that acts as its own name resolver by implementing the contract address resource profile: + +```solidity +contract DoSomethingUseful { + // Other code + + function addr(bytes32 node) constant returns (address) { + return this; + } + + function supportsInterface(bytes4 interfaceID) constant returns (bool) { + return interfaceID == 0x3b3b57de || interfaceID == 0x01ffc9a7; + } + + function() { + throw; + } +} +``` + +Such a contract can be inserted directly into the ENS registry, eliminating the need for a separate resolver contract in simple use-cases. However, the requirement to 'throw' on unknown function calls may interfere with normal operation of some types of contract. + +### Standalone resolver + +A basic resolver that implements the contract address profile, and allows only its owner to update records: + +```solidity +contract Resolver { + event AddrChanged(bytes32 indexed node, address a); + + address owner; + mapping(bytes32=>address) addresses; + + modifier only_owner() { + if(msg.sender != owner) throw; + _ + } + + function Resolver() { + owner = msg.sender; + } + + function addr(bytes32 node) constant returns(address) { + return addresses[node]; + } + + function setAddr(bytes32 node, address addr) only_owner { + addresses[node] = addr; + AddrChanged(node, addr); + } + + function supportsInterface(bytes4 interfaceID) constant returns (bool) { + return interfaceID == 0x3b3b57de || interfaceID == 0x01ffc9a7; + } + + function() { + throw; + } +} +``` + +After deploying this contract, use it by updating the ENS registry to reference this contract for a name, then calling `setAddr()` with the same node to set the contract address it will resolve to. +### Public resolver + +Similar to the resolver above, this contract only supports the contract address profile, but uses the ENS registry to determine who should be allowed to update entries: + +```solidity +contract PublicResolver { + event AddrChanged(bytes32 indexed node, address a); + event ContentChanged(bytes32 indexed node, bytes32 hash); + + ENS ens; + mapping(bytes32=>address) addresses; + + modifier only_owner(bytes32 node) { + if(ens.owner(node) != msg.sender) throw; + _ + } + + function PublicResolver(address ensAddr) { + ens = ENS(ensAddr); + } + + function addr(bytes32 node) constant returns (address ret) { + ret = addresses[node]; + } + + function setAddr(bytes32 node, address addr) only_owner(node) { + addresses[node] = addr; + AddrChanged(node, addr); + } + + function supportsInterface(bytes4 interfaceID) constant returns (bool) { + return interfaceID == 0x3b3b57de || interfaceID == 0x01ffc9a7; + } + + function() { + throw; + } +} +``` +# Appendix C: Sample Registrar Implementation + +This registrar allows users to register names at no cost if they are the first to request them. + +```solidity +contract FIFSRegistrar { + ENS ens; + bytes32 rootNode; + + function FIFSRegistrar(address ensAddr, bytes32 node) { + ens = ENS(ensAddr); + rootNode = node; + } + + function register(bytes32 subnode, address owner) { + var node = sha3(rootNode, subnode); + var currentOwner = ens.owner(node); + if(currentOwner != 0 && currentOwner != msg.sender) + throw; + + ens.setSubnodeOwner(rootNode, subnode, owner); + } +} +``` diff --git a/EIPS/eip-1380.md b/EIPS/eip-1380.md new file mode 100644 index 0000000..6be8374 --- /dev/null +++ b/EIPS/eip-1380.md @@ -0,0 +1,57 @@ +--- +eip: 1380 +title: Reduced gas cost for call to self +author: Alex Beregszaszi (@axic), Jacques Wagener (@jacqueswww) +discussions-to: https://ethereum-magicians.org/t/eip-1380-reduced-gas-cost-for-call-to-self/1242 +status: Stagnant +type: Standards Track +category: Core +created: 2018-08-31 +requires: 150 +--- + +## Abstract +Reduce the gas cost for call instructions, when the goal is to run a new instance of the currently loaded contract. + +## Motivation +The current gas cost of 700 for all call types (`CALL`, `DELEGATECALL`, `CALLCODE` and `STATICCALL`) does not take into account that a call to a contract itself +does not need to perform additional I/O operations, because the current contract code has already been loaded into memory. + +Reducing the call-to-self gas cost would greatly benefit smart contract languages, such as Solidity and Vyper, who would then be able to utilise `CALL` instead +of `JUMP` opcodes for internal function calls. While languages can already utilise `CALL` for internal function calls, they are discouraged to do so due to the +gas costs associated with it. + +Using `JUMP` comes at a considerable cost in complexity to the implementation of a smart contract language and/or compiler. The context (including stack and memory) +must be swapped in and out of the calling functions context. A desired feature is having *pure* functions, which do not modify the state of memory, and realising +them through `JUMP` requires a bigger effort from the compiler as opposed to being able to use `CALL`s. + +Using call-to-self provides the guarantee that when making an internal call the function can rely on a clear reset state of memory or context, benefiting both +contract writers and contract consumers against potentially undetetected edge cases were memory could poison the context of the internal function. + +Because of the `JUMP` usage for internal functions a smart contract languages are also at risk of reaching the stack depth limit considerbly faster, if nested +function calls with many in and/or outputs are required. + +Reducing the gas cost, and thereby incentivising of using call-to-self instead of `JUMP`s for the internal function implementation will also benefit static +analyzers and tracers. + +## Specification +If `block.number >= FORK_BLKNUM`, then decrease the cost of `CALL`, `DELEGATECALL`, `CALLCODE` and `STATICCALL` from 700 to 40, +if and only if, the destination address of the call equals to the address of the caller. + +## Rationale +EIP150 has increased the cost of these instructions from 40 to 700 to more fairly charge for loading new contracts from disk, e.g. to reflect the I/O charge more closely. +By assuming that 660 is the cost of loading a contract from disk, one can assume that the original 40 gas is a fair cost of creating a new VM instance of an already loaded contract code. + +## Backwards Compatibility +This should pose no risk to backwards compatibility. Currently existing contracts should not notice the difference, just see cheaper execution. +With EIP150 contract (and language) developers had a lesson that relying on strict gas costs is not feasible as costs may change. +The impact of this EIP is even less that of EIP150 because the costs are changing downwards and not upwards. + +## Test Cases +TBA + +## Implementation +TBA + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1386.md b/EIPS/eip-1386.md new file mode 100644 index 0000000..a832170 --- /dev/null +++ b/EIPS/eip-1386.md @@ -0,0 +1,88 @@ +--- +eip: 1386 +title: Attestation management contract +author: Weiwu Zhang , James Sangalli +discussions-to: https://github.com/ethereum/EIPs/issues/1386 +status: Stagnant +type: Standards Track +category: ERC +created: 2018-09-08 +--- + +### Introduction + +Very often, we will need to use Attestations like "Alice lives in Australia" on the blockchain; that is issued by a valid issuer off chain for privacy reasons and is revokable inside a smart contract. + +An issuer can create a smart contract where he revokes multiple attestations in one go by building a bloom filter of all the hashes of the revoked attestations. + +An issuer can also put the validation method in their smart contract that can be called by other smart contracts who need to validate attestations issued by them. This allows each attestor to update their attestation format separately. + +### Purpose + +This ERC provides an interface for attestation issuers to manage their attestation signing keys and the attestations that are issued off chain for actions such as revocation and validation. + +In our draft implementation we include functions to hold cryptographic attestations, change the issuing contracts of attestations, revoke attestations and verify the authenticity of a cryptographic attestation. + +### Example use cases + +Let's say that our friend, Alice, wants to buy a bottle of wine to consume with her friends. She wants to do the order online and have it delivered to her home address whilst paying for it with Ether. + +Alice has a cryptographic attestation from her local road and maritime services who attests to her age, date of birth, country of residence and ability to drive. + +Alice is able to split up this attestation (see merkle tree attestations ERC [here](https://github.com/alpha-wallet/blockchain-attestation/blob/master/ethereum/lib/MerkleTreeAttestation.sol)) and provides only the leaf that states she is over the age of 21. + +Alice goes to buy the wine through the wine vendors smart contract and feeds in the merkle tree attestation proving that she is above 21 and can thus buy the wine, whilst attaching the appropriate amount of ether to complete the purchase. + +The issuer smart contract is able to validate her attestation, check that the issuer contract is valid and capable of performing such an attestation to her age. In this case it would have to be from someone like a driver's licence authority, as attestations to age from a school ID are not of a high enough capacity. + +The wine vendors smart contract validates the attestation, checks the payment amount is correct and credits Alice with the wine tokens she needs to complete the sale and deliver the wine. + +When the wine vendor shows up to her apartment with the wine, there is no need to prove her age again. + +### Draft interface +```solidity +/* each attestation issuer should provide their own verify() for the + * attestations they issued. There are two reasons for this. First, we + * need to leave room for new attestation methods other than the + * Merkle Tree format we are recommending. Second, the validity of the + * attestation may depend on the context that only the attestor + * knows. For example, a ticket as an attestation issued on a + * successful redemption of an American Express credit */ + +contract Issuer { + struct Attestation + { + bytes32[] merklePath; + bool valid; + uint8 v; + bytes32 r; + bytes32 s; + address attestor; + address recipient; + bytes32 salt; + bytes32 key; + bytes32 val; + }` + /* Verify the authenticity of an attestation */ + function verify(Attestation attestation); + function addattestorKey(address newAttestor, string capacity, uint expiry); + + /* this should call the revoke first */ + function replaceKey(address attestorToReplace, string capacity, uint expiry, address newAttestor); + + /* this revokes a single key */ + function removeKey(address attestor); + + /* if the key exists with such capacity and isn't revoked or expired */ + function validateKey(address attestor, string capacity) returns (bool); + + /* revoke an attestation by replace the bloom filter, this helps preserve privacy */ + function revokeAttestations(Bloomfilter b); + +} +``` + +Please click [here](https://github.com/alpha-wallet/blockchain-attestation/blob/master/ethereum/example-james-squire/james-squire.sol) to see a draft implementation of this interface + +### Related ERC's +#1388 #1387 diff --git a/EIPS/eip-1387.md b/EIPS/eip-1387.md new file mode 100644 index 0000000..1061ffe --- /dev/null +++ b/EIPS/eip-1387.md @@ -0,0 +1,49 @@ +--- +eip: 1387 +title: Merkle Tree Attestations with Privacy enabled +author: Weiwu Zhang , James Sangalli +discussions-to: https://github.com/ethereum/EIPs/issues/1387 +status: Stagnant +type: Standards Track +category: ERC +created: 2018-09-08 +--- + +### Introduction + +It's often needed that an Ethereum smart contract must verify a claim (I live in Australia) attested by a valid attester. + +For example, an ICO contract might require that the participant, Alice, lives in Australia before she participates. Alice's claim of residency could come from a local Justice of the Peace who could attest that "Alice is a resident of Australia in NSW". + +Unlike previous attempts, we assume that the attestation is signed and issued off the blockchain in a Merkle Tree format. Only a part of the Merkle tree is revealed by Alice at each use. Therefore we avoid the privacy problem often associated with issuing attestations on chain. We also assume that Alice has multiple signed Merkle Trees for the same factual claim to avoid her transactions being linkable. + +## Purpose +This ERC provides an interface and reference implementation for smart contracts that need users to provide an attestation and validate it. + +### Draft implementation +```solidity +contract MerkleTreeAttestationInterface { + struct Attestation + { + bytes32[] merklePath; + bool valid; + uint8 v; + bytes32 r; + bytes32 s; + address attester; + address recipient; + bytes32 salt; + bytes32 key; + bytes32 val; + } + + function validate(Attestation attestation) public returns(bool); +} + +``` +### Relevant implementation examples +[Here](https://github.com/alpha-wallet/blockchain-attestation/blob/master/ethereum/lib/MerkleTreeAttestation.sol) is an example implementation of the MerkleTreeAttestationInterface +[Here](https://github.com/alpha-wallet/blockchain-attestation/blob/master/ethereum/example-james-squire/james-squire.sol) is an example service which would use such a merkle tree attestation + +### Related ERC's +#1388 #1386 diff --git a/EIPS/eip-1388.md b/EIPS/eip-1388.md new file mode 100644 index 0000000..ed05a64 --- /dev/null +++ b/EIPS/eip-1388.md @@ -0,0 +1,87 @@ +--- +eip: 1388 +title: Attestation Issuers Management List +author: Weiwu Zhang , James Sangalli +discussions-to: https://github.com/ethereum/EIPs/issues/1388 +status: Stagnant +type: Standards Track +category: ERC +created: 2018-09-08 +--- + +### Introduction + +In smart contracts, we will need methods to handle cryptographic attestations to a users identifier or abilities. Let's say we have a real estate agent, KiwiRealtors, that provides an "expression of interest" function though a smart contract and requires the users to provide an attestation that they are a resident of New Zealand or Australia, as a legal requirement. This has actually happened in the New Zealand property market and it is the perfect example of a need to handle such attestations. + +However, it is not practical for a smart contract to explicitly trust an attestation issuer. There are multiple issuers who can provide an attestation to a person's residency - a local Justice of the Peace, the land title office, local police, passport authority etc. We envision a model where the effort to manage the list of qualified issuers is practically outsourced to a list. + +Anyone can publish a list of issuers. Only the most trusted and carefully maintained lists gets popular use. + +### Purpose +This ERC provides a smart contract interface for anyone to manage a list of attestation issuers. A smart contract would explicitly trust a list, and therefore all attestations issued by the issuers on the list. + +### Draft implementation +```solidity + /* The purpose of this contract is to manage the list of attestation + * issuer contracts and their capacity to fulfill requirements + */ + contract ManagedListERC + { + /* a manager is the steward of a list. Only he/she/it can change the + * list by removing/adding attestation issuers to the list. + + * An issuer in the list is represented by their contract + * addresses, not by the attestation signing keys managed by such a + * contract. + */ + struct List + { + string name; + string description; // short description of what the list entails + string capacity; // serves as a filter for the attestation signing keys + /* if a smart contract specifies a list, only attestation issued + * by issuers on that list is accepted. Furthermore, if that + * list has a non-empty capacity, only attestations signed by a + * signing key with that capacity is accepted. */ + + address[] issuerContracts; // all these addresses are contracts, no signing capacity + uint expiry; + } + + // find which list the sender is managing, then add an issuer to it + function addIssuer(address issuerContractAddress) public; + + //return false if the list identified by the sender doesn't have this issuer in the list + function removeIssuer(address issuerContractAddress, List listToRemoveIssuerFrom) public returns(bool); + + /* called by services, e.g. Kiwi Properties or James Squire */ + /* loop through all issuer's contract and execute validateKey() on + * every one of them in the hope of getting a hit, return the + * contract address of the first hit. Note that there is an attack + * method for one issuer to claim to own the key of another which + * is mitigated by later design. */ + //loop through the issuers array, calling validate on the signingKeyOfAttestation + function getIssuerCorrespondingToAttestationKey(bytes32 list_id, address signingKeyOfAttestation) public returns (address); + + /* for simplicity we use sender's address as the list ID, + * accepting these consequences: a) if one user wish to maintain + * several lists with different capacity, he or she must use a + * different sender address for each. b) if the user replaced the + * sender's key, either because he or she suspects the key is + * compromised or that it is lost and reset through special means, + * then the list is still identified by the first sender's + * address. + */ + + function createList(List list) public; + + /* replace list manager's key with the new key */ + function replaceListIndex(List list, address manager) public returns(bool); + + } +``` + +Click [here](https://github.com/alpha-wallet/blockchain-attestation/blob/master/ethereum/trustlist/ManagedList.sol) to see an example implementation of this ERC + +### Related ERC's +#1387 #1386 diff --git a/EIPS/eip-140.md b/EIPS/eip-140.md new file mode 100644 index 0000000..e907550 --- /dev/null +++ b/EIPS/eip-140.md @@ -0,0 +1,58 @@ +--- +eip: 140 +title: REVERT instruction +author: Alex Beregszaszi (@axic), Nikolai Mushegian +type: Standards Track +category: Core +status: Final +created: 2017-02-06 +--- + +## Simple Summary + +The `REVERT` instruction provides a way to stop execution and revert state changes, without consuming all provided gas and with the ability to return a reason. + +## Abstract + +The `REVERT` instruction will stop execution, roll back all state changes done so far and provide a pointer to a memory section, which can be interpreted as an error code or message. While doing so, it will not consume all the remaining gas. + +## Motivation + +Currently this is not possible. There are two practical ways to revert a transaction from within a contract: running out of gas or executing an invalid instruction. Both of these options will consume all remaining gas. Additionally, reverting an EVM execution means that all changes, including LOGs, are lost and there is no way to convey a reason for aborting an EVM execution. + +## Specification + +On blocks with `block.number >= BYZANTIUM_FORK_BLKNUM`, the `REVERT` instruction is introduced at `0xfd`. It expects two stack items, the top item is the `memory_offset` followed by `memory_length`. It does not produce any stack elements because it stops execution. + +The semantics of `REVERT` with respect to memory and memory cost are identical to those of `RETURN`. The sequence of bytes given by `memory_offset` and `memory_length` is called "error message" in the following. + +The effect of `REVERT` is that execution is aborted, considered as failed, and state changes are rolled back. The error message will be available to the caller in the returndata buffer and will also be copied to the output area, i.e. it is handled in the same way as the regular return data is handled. + +The cost of the `REVERT` instruction equals to that of the `RETURN` instruction, i.e. the rollback itself does not consume all gas, the contract only has to pay for memory. + +In case there is not enough gas left to cover the cost of `REVERT` or there is a stack underflow, the effect of the `REVERT` instruction will equal to that of a regular out of gas exception, i.e. it will consume all gas. + +In the same way as all other failures, the calling opcode returns `0` on the stack following a `REVERT` opcode in the callee. + +In case `REVERT` is used in the context of a `CREATE` or `CREATE2` call, no code is deployed, `0` is put on the stack and the error message is available in the returndata buffer. + +The content of the optionally provided memory section is not defined by this EIP, but is a candidate for another Informational EIP. + +## Backwards Compatibility + +This change has no effect on contracts created in the past unless they contain `0xfd` as an instruction. + +## Test Cases + +``` +6c726576657274656420646174616000557f726576657274206d657373616765000000000000000000000000000000000000600052600e6000fd +``` + +should: +- return `0x726576657274206d657373616765` as `REVERT` data, +- the storage at key `0x0` should be left as unset and +- use 20024 gas in total. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-141.md b/EIPS/eip-141.md new file mode 100644 index 0000000..429e38a --- /dev/null +++ b/EIPS/eip-141.md @@ -0,0 +1,29 @@ +--- +eip: 141 +title: Designated invalid EVM instruction +author: Alex Beregszaszi (@axic) +type: Standards Track +category: Core +status: Final +created: 2017-02-09 +--- + +## Abstract + +An instruction is designated to remain as an invalid instruction. + +## Motivation + +The invalid instruction can be used as a distinct reason to abort execution. + +## Specification + +The opcode `0xfe` is the `INVALID` instruction. It can be used to abort the execution (i.e. duplicates as an `ABORT` instruction). + +## Backwards Compatibility + +This instruction was never used and therefore has no effect on past contracts. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1417.md b/EIPS/eip-1417.md new file mode 100644 index 0000000..1bf50ed --- /dev/null +++ b/EIPS/eip-1417.md @@ -0,0 +1,283 @@ +--- +eip: 1417 +title: Poll Standard +author: Chaitanya Potti (@chaitanyapotti), Partha Bhattacharya (@pb25193) +type: Standards Track +category: ERC +status: Stagnant +created: 2018-09-16 +requires: 165, 1261 +discussions-to: https://github.com/ethereum/EIPs/issues/1417 +--- + +## Note to Readers + +1. We have created a couple of implementations of polls for varied use cases. + Please refer to them [here](https://github.com/chaitanyapotti/Voting) + +## Simple Summary + +A standard interface for Polls to be used with EIP-1261 (MVT). + +## Abstract + +The following standard allows for the implementation of a standard API for polls to be used with MVTs (refer [EIP-1261](./eip-1261.md)). The standard provides basic functionality to vote, unvote, tally votes, get voter turnout, and a lot more. The poll standard attempts to modularize blockchain voting by breaking down a poll into 4 crucial building blocks: voterbase qualification, vote weight calculation, vote consequences, and vote tallying. By creating a common interface for polls that have different kinds of building blocks, the poll standard makes it possible to make interactive front end applications which can seamlessly get data from a poll contract in order to bring transparency into consensus and decision making on the blockchain. + +We considered the usage of polls with MVTs because MVTs serve as a permissioning mechanism. The manual permissioning of polls allows for vote weightage functions to take up several shapes and forms. Hence the voterbase function applies several logical checks on the vote sender to confirm that they are member(see EIP 1261) of a certain entity or combination of entities. For the specification of the nature of voting, we define the vote weight function. The vote weight function decides how much of vote share each voter will receive and this can be based on several criteria, some of which are listed below in this article. There are certain kinds of polls that enforce certain consequences on the voter, for example a poll may require a voter to lock in a certain amount of tokens, or require the voter to pay a small fee. These on-chain consequences can be coded into the consequence module of the poll standard. Finally, the last module is where the votes are added. A ballot for each candidate is updated whenever relevant, depending on the vote value, and the corresponding NoV count(number of voters). This module is common for most polls, and is the most straightforward. Polls may be time bound, ie. having a finish time, after which no votes are recorded, or be unbound, such that there is no finish time. The following are some examples of specific polls which leverage the flexibility of the poll standard, and it is possible to come up with several others: + +- Plurality Voting: The simplest form of voting is when you want all eligible voters to have one vote per person. This is the simplest to code, as the vote weight is 1, and there is no vote consequence. The only relevant module here is the voterbase, which can be categorized by one or more MVT contracts. +- Token proportional voting: This kind of a poll is actually possible without the use of a voterbase function, because the vote weight function having token proportionality automatically rules out addresses which don't hold the appropriate ERC - 20/ ERC - 777 token. However the voterbase function may be leveraged to further permission the system and give voting rights only to a fixed subset of token holders. +- Capped Token Proportional Voting: This is a modified version of the previous example, where each voter is given proportional vote share only until a certain limit of token ownership. After exceeding that limit, holding more coins does not add more vote share. This format leverages the voterbase module effectively, disallowing people from spreading their coins across multiple addresses by allowing the admin to control which addresses can vote. +- Delegated Voting: Certain polls may allow voters to delegate their votes to other voters. This is known as delegated voting or liquid democracy. For such a poll, a complicated vote weight function is needed, and a data structure concerning the voterbase is also required. A consequence of voting here would be that a user cannot delegate, and a consequence of delegating is that a user cannot vote. Sample implementation of polls contains an example of this vote scheme. +- Karma Based Voting: A certain form of poll may be based on weightage from digital respect. This digital respect would be like a simple upvote from one member of voterbase to another. A mapping of mappings along with an appropriate vote weight function can serve this purpose. Sample implementation has an example. +- Quadratic voting: A system where each vote is associated with a fee, and the fee is proportional to the square of the vote weight that the voter wants. This can be designed by applying a vote weight based on the transaction message, and then charging a fee in the vote consequence module. + +The poll standard is intended to be a smart contract standard that makes poll deployment flexible, transparent and accessible. + +## Motivation + +A standard interface allows any user or applications to work with any Poll contract on Ethereum. We provide for simple ERC-1417 smart contracts. Additional applications are discussed below. + +This standard is inspired by the lack of governance tools in the blockchain space. Whenever there is a consensus collection exercise, someone goes ahead and deploys some kind of poll, and there is no standard software for accessing the data on the poll. For an end user who is not a developer, this is a real problem. The poll, which might be fully transparent, appears to be completely opaque to a common user who does not understand blockchain. In order for developers to build applications for interacting with and accessing poll data, and for poll deployers to have ready application level support, there must be a standardization of poll interfaces. + +This realization happened while conducting market research on DAICOs. The first ever DAICO, Abyss, had far from optimal user experience, and abysmal transparency. Since then, we have been working on a poll standard. During the process, we came across EIP 1202, the voting standard, and found that the discussion there had already diverged from our thoughts to an extent that it made sense to publish a separate proposal altogether. Some of the benefits brought by the poll standard - EIP 1417 aims to offer some additional benefits. + +1. Modularization: EIP 1417 modularizes the code present in the poll standard into 4 major building blocks based on functionality. These are: voterbase logic, vote weight calculation, vote consequence processing, and tallying module. This makes it easy for developers to change parts of a poll without disrupting other parts, and also helps people understand better, code written in the same format by other people. + +2. Permissioning: Permissioning is an important aspect of polls, and is missing in most poll proposals so far, on the blockchain. For some reason, most blockchain based polls seem to consider token holding as the only way to permission a poll. However this hampers flexibility, and hence our poll standard is leveraging EIP 1261 in order to clear the permissioning hurdle. Not only does it allow for more creative poll structures in terms of vote weightage, but even improves the flexibility in permissioning by allowing developers to combine several entities and read attributes from entities. + +3. Flexibility: The vote weight module of the poll standard can be used effectively to design various kinds of poll contracts which function differently and are suited to different environments. Some examples are quadratic voting, karma voting, delegated voting, token based voting, and one person one vote systems. These schemes are possible due to the separation of voterbase creation and vote weight calculation. + +4. NoV Counts: Several weighted polls have struggled to provide proper transparency because they only show the final result without enough granularity. This is because they do not store the number of voters that have voted for each proposal, and only store the total accrued vote for each option. EIP 1417 solves this by additionally recording number of voters(NoV) in each proposal. This NoV count is redundant in the case of one person one vote, but elsewhere, it is helpful in figuring out concentration of power. This ensures that malicious parties can be traced to a larger extent. + +5. Event Logging: The poll standard logs an event during a successful vote, unsuccessful vote, and a successful unvote. This is being done so that in the event of a malicious admin removing real members or adding fake members, communities can build tools in order to perform advanced audits and simulate results in the absence of the malicious attack. Such advanced features are completely absent in most polls, and hence, it is hard to investigate such polls. + +6. Pollscan.io: The Electus foundation is working on a web based application for accessing and interacting with poll data on the blockchain, it will be deployed on the domain name www.pollscan.io in the coming months. + +All that being said, we are very excited to share our proposal with the community and open up to suggestions in this space. + +### Benefits + +1. Building applications (pollscan.io) on top of a standardized voting interface enables transparency and encourage more DAO/DAICO's to act responsibly in terms of governance +2. Create Action contracts which take actions programmatically based on the result of a poll +3. Allow the compatibility with token standard such as [ERC-20](./eip-20.md) or (./eip-777.md)) and membership standard such as [EIP-1261](./eip-1261.md) +4. Flexibility allows for various voting schemes including but not limited to modern schemes such as PLCR Voting + +### Use-cases: + +Polls are useful in any context of collective decision making, which include but aren't limited to: + +1. Governing public resources, like ponds, playgrounds, streets etc +2. Maintaining fiscal policy in a transparent consensus driven manner +3. Governing crowdfunded projects - refer DAICO, Vitalik Buterin +4. Implementation of Futarchy +5. Decision making in political parties, and municipal corporations +6. Governing expenditure of a cryptocurrency community + +## Specification + +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. + +**Every ERC-1417 compliant contract must implement the `ERC1417` and `ERC165` interfaces** (subject to "caveats" below): + +```solidity +/// @title ERC-1417 Poll Standard +/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1417.md +/// Note: the ERC-165 identifier for this interface is 0x4fad898b. +interface IPoll { + /// @dev This emits when a person tries to vote without permissions. Useful for auditing purposes. + /// E.g.: To prevent an admin to revoke permissions; calculate the result had they not been removed. + /// @param _from User who tried to vote + /// @param _to the index of the proposal he voted to + /// @param voteWeight the weight of his vote + event TriedToVote(address indexed _from, uint8 indexed _to, uint voteWeight); + + /// @dev This emits when a person votes successfully + /// @param _from User who successfully voted + /// @param _to the index of the proposal he voted to + /// @param voteWeight the weight of his vote + event CastVote(address indexed _from, uint8 indexed _to, uint voteWeight); + + /// @dev This emits when a person revokes his vote + /// @param _from User who successfully unvoted + /// @param _to the index of the proposal he unvoted + /// @param voteWeight the weight of his vote + event RevokedVote(address indexed _from, uint8 indexed _to, uint voteWeight); + + /// @notice Handles the vote logic + /// @dev updates the appropriate data structures regarding the vote. + /// stores the proposalId against the user to allow for unvote + /// @param _proposalId the index of the proposal in the proposals array + function vote(uint8 _proposalId) external; + + /// @notice Handles the unvote logic + /// @dev updates the appropriate data structures regarding the unvote + function revokeVote() external; + + /// @notice gets the proposal names + /// @dev limit the proposal count to 32 (for practical reasons), loop and generate the proposal list + /// @return the list of names of proposals + function getProposals() external view returns (bytes32[]); + + /// @notice returns a boolean specifying whether the user can vote + /// @dev implement logic to enable checks to determine whether the user can vote + /// if using eip-1261, use protocol addresses and interface (IERC1261) to enable checking with attributes + /// @param _to the person who can vote/not + /// @return a boolean as to whether the user can vote + function canVote(address _to) external view returns (bool); + + /// @notice gets the vote weight of the proposalId + /// @dev returns the current cumulative vote weight of a proposal + /// @param _proposalId the index of the proposal in the proposals array + /// @return the cumulative vote weight of the specified proposal + function getVoteTally(uint _proposalId) external view returns (uint); + + /// @notice gets the no. of voters who voted for the proposal + /// @dev use a struct to keep a track of voteWeights and voterCount + /// @param _proposalId the index of the proposal in the proposals array + /// @return the voter count of the people who voted for the specified proposal + function getVoterCount(uint _proposalId) external view returns (uint); + + /// @notice calculates the vote weight associated with the person `_to` + /// @dev use appropriate logic to determine the vote weight of the individual + /// For sample implementations, refer to end of the eip + /// @param _to the person whose vote weight is being calculated + /// @return the vote weight of the individual + function calculateVoteWeight(address _to) external view returns (uint); + + /// @notice gets the leading proposal at the current time + /// @dev calculate the leading proposal at the current time + /// For practical reasons, limit proposal count to 32. + /// @return the index of the proposal which is leading + function winningProposal() external view returns (uint8); + + /// @notice gets the name of the poll e.g.: "Admin Election for Autumn 2018" + /// @dev Set the name in the constructor of the poll + /// @return the name of the poll + function getName() external view returns (bytes32); + + /// @notice gets the type of the Poll e.g.: Token (XYZ) weighted poll + /// @dev Set the poll type in the constructor of the poll + /// @return the type of the poll + function getPollType() external view returns (bytes32); + + /// @notice gets the logic to be used in a poll's `canVote` function + /// e.g.: "XYZ Token | US & China(attributes in erc-1261) | Developers(attributes in erc-1261)" + /// @dev Set the Voterbase logic in the constructor of the poll + /// @return the voterbase logic + function getVoterBaseLogic() external view returns (bytes32); + + /// @notice gets the start time for the poll + /// @dev Set the start time in the constructor of the poll as Unix Standard Time + /// @return start time as Unix Standard Time + function getStartTime() external view returns (uint); + + /// @notice gets the end time for the poll + /// @dev Set the end time in the constructor of the poll as Unix Time or specify duration in constructor + /// @return end time as Unix Standard Time + function getEndTime() external view returns (uint); + + /// @notice returns the list of entity addresses (eip-1261) used for perimissioning purposes. + /// @dev addresses list can be used along with IERC1261 interface to define the logic inside `canVote()` function + /// @return the list of addresses of entities + function getProtocolAddresses() external view returns (address[]); + + /// @notice gets the vote weight against all proposals + /// @dev limit the proposal count to 32 (for practical reasons), loop and generate the vote tally list + /// @return the list of vote weights against all proposals + function getVoteTallies() external view returns (uint[]); + + /// @notice gets the no. of people who voted against all proposals + /// @dev limit the proposal count to 32 (for practical reasons), loop and generate the vote count list + /// @return the list of voter count against all proposals + function getVoterCounts() external view returns (uint[]); + + /// @notice For single proposal polls, returns the total voterbase count. + /// For multi proposal polls, returns the total vote weight against all proposals + /// this is used to calculate the percentages for each proposal + /// @dev limit the proposal count to 32 (for practical reasons), loop and generate the voter base denominator + /// @return an integer which specifies the above mentioned amount + function getVoterBaseDenominator() external view returns (uint); +} +``` + +### Caveats + +The 0.4.24 Solidity interface grammar is not expressive enough to document the ERC-1417 standard. A contract which complies with ERC-1417 MUST also abide by the following: + +- Solidity issue #3412: The above interfaces include explicit mutability guarantees for each function. Mutability guarantees are, in order weak to strong: `payable`, implicit nonpayable, `view`, and `pure`. Your implementation MUST meet the mutability guarantee in this interface and you MAY meet a stronger guarantee. For example, a `payable` function in this interface may be implemented as nonpayble (no state mutability specified) in your contract. We expect a later Solidity release will allow your stricter contract to inherit from this interface, but a workaround for version 0.4.24 is that you can edit this interface to add stricter mutability before inheriting from your contract. +- Solidity issue #2330: If a function is shown in this specification as `external` then a contract will be compliant if it uses `public` visibility. As a workaround for version 0.4.24, you can edit this interface to switch to `public` before inheriting from your contract. + +_If a newer version of Solidity allows the caveats to be expressed in code, then this EIP MAY be updated and the caveats removed, such will be equivalent to the original specification._ + +## Rationale + +As the poll standard is built with the intention of creating a system that allows for more transparency and accessibility of governance data, the design choices in the poll standard are driven by this motivator. In this section we go over some of the major design choices, and why these choices were made: + +1. Event logging: The logic behind maintaining event logs in the cases of: + + - Cast Vote + - Unvote + - Failed Vote + is to ensure that in the event of a manipulated voterbase, simple off chain checks can be performed to audit the integrity of the poll result. + +2. No poll finish trigger: There was a consideration of adding functions in the poll which execute after completion of the poll to carry out some pre-decided logic. However this was deemed to be unnecessary - because such an action can be deployed in a separate contract which simply reads the result of a given poll, and against the spirit of modularity, because no actions can be created after the poll has been deployed. Also, such functions would not be able to combine the results of polls, and definitely would not fit into polls that do not have an end time. + +3. Allow for unbound polls: The poll standard, unlike other voting standard proposals, does not force polls to have an end time. This becomes relevant in some cases where the purpose of a poll is to have a live register of ongoing consensus. Some other use cases come into picture when you want to deploy a set of action contracts which read from the poll, and want to be able to execute the action contract whenever a poll reaches a certain threshold, rather than waiting for the end of the poll. + +4. Modularization: There have been opinions in the Ethereum community that there cannot exist a voting standard, because voting contracts can be of various types, and have several shapes and forms. However we disagree, and make the case that modularization is the solution. While different polls may need different logic, they all need consistent end points. All polls need to give out results along with headcounts, all polls should have event logs, all polls should be examinable with frontend tools, and so on. The poll standard is not a statement saying “all polls should be token based” or any such specific system. However the poll standard is a statement saying that all polls should have a common access and modification protocol - this will enable more apps to include governance without having to go through the trouble of making customers start using command line. + +Having explained our rationale, we are looking forward to hearing from the community some thoughts on how this can be made more useful or powerful. + +**Gas and Complexity** (regarding the enumeration for proposal count) + +This specification contemplates implementations that contain a sample of 32 proposals (max up to blockgaslimit). If your application is able to grow and needs more than 32 proposals, then avoid using for/while loops in your code. These indicate your contract may be unable to scale and gas costs will rise over time without bound + +**Privacy** + +Personal information: The standard does not put any personal information on to the blockchain, so there is no compromise of privacy in that respect. + +**Community Consensus** + +We have been very inclusive in this process and invite anyone with questions or contributions into our discussion. However, this standard is written only to support the identified use cases which are listed herein. + +## Test Cases + +Voting Standard includes test cases written using Truffle. + +## Implementations + +Voting Standard -- a reference implementation + +- MIT licensed, so you can freely use it for your projects +- Includes test cases +- Also available as a npm package - npm i electusvoting + +## References + +**Standards** + +- [EIP-20: ERC-20 Token Standard (a.k.a. ERC-20)](./eip-20.md) +- [EIP-165: Standard Interface Detection](./eip-165.md) +- [EIP-721: Non-Fungible Token Standard(a.k.a. ERC-721)](./eip-721.md) +- [ERC-1261 MV Token Standard](./eip-1261.md) +- [RFC 2119 Key words for use in RFCs to Indicate Requirement Levels](https://www.ietf.org/rfc/rfc2119.txt) + +**Issues** + +1. The Original ERC-1417 Issue. https://github.com/ethereum/eips/issues/1417 +1. Solidity Issue \#2330 -- Interface Functions are Axternal. https://github.com/ethereum/solidity/issues/2330 +1. Solidity Issue \#3412 -- Implement Interface: Allow Stricter Mutability. https://github.com/ethereum/solidity/issues/3412 +1. Solidity Issue \#3419 -- Interfaces Can't Inherit. https://github.com/ethereum/solidity/issues/3419 + +**Discussions** + +1. ERC-1417 (announcement of first live discussion). https://github.com/ethereum/eips/issues/1417 + +**Voting Implementations and Other Projects** + +- [Voting Implementations](https://github.com/chaitanyapotti/Voting) + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1418.md b/EIPS/eip-1418.md new file mode 100644 index 0000000..90047bb --- /dev/null +++ b/EIPS/eip-1418.md @@ -0,0 +1,195 @@ +--- +eip: 1418 +title: Blockchain Storage Rent Payment +description: At each block, deduct value from every account based on the quantity of storage used by that account. +author: William Entriken (@fulldecent) +discussions-to: https://ethereum-magicians.org/t/eip-1418-storage-rent/10737 +status: Draft +type: Standards Track +category: Core +created: 2018-09-16 +requires: 1559 +--- + +## Abstract + +At each block, deduct an amount of value ("rent") from every account based on the quantity of storage used by that account. + +## Motivation + +Ethereum is a public utility and we are underpricing the long-term costs of storage. Storage cost can be approximately modeled as bytes × time. + +## Specification + +**Updated transaction type** + +A new transaction type is introduced. Whereas [EIP-1559](./eip-1559.md) introduced warm access for contract state, this new type introduces warm access for contract code. + +**New state variables (per account)** + +* **σ[a]_rent** -- an amount of value, in Wei, this is a signed value +* **σ[a]_storageWords** -- number of words in storage + +**New constants** + +* **`RENT_WORD_COST`** -- The rent cost, in Wei, paid for each word-block +* **`RENT_ACCOUNT_COST`** -- The rent cost, in Wei, paid for each account-block +* **`FORK_BLOCK`** – When implementation starts + +**New opcodes** + +* **`RENTBALANCE(address)`** -- G_BALANCE -- Similar to `BALANCE` + * This returns the logical `σ[a]_rent` value which is defined to reduce each block. It is possible for the implementation to calculate this value using the recommended implementation variables, rather than storing and updating `σ[a]_rent` every block for every account. +* **`SENDRENT(address, amount)`** -- G_BASE -- Convert value to rent and send to account + 1. `σ[account]_rent` += amount + 2. `σ[msg.sender]_balance` -= amount + +**Updated opcodes** + +A new subroutine, paying for rent, is established as such: + +```pseudocode +PAYRENT(account) + blocks_to_pay = NUMBER - σ[account]_rentLastPaid + cost_per_block = RENT_ACCOUNT_COST + RENT_WORD_COST * (⌈∥σ[account]_code∥ / 32⌉ + * σ[a]_storageWords) + rent_to_pay = blocks_to_pay * cost_per_block + σ[account]_rent -= rent_to_pay + if σ[account]_rent < 0 + σ[account]_value += σ[account]_rent + σ[account]_rent = 0 + end + if σ[account]_value < 0 + σ[account]_rent = σ[account]_value + σ[account]_value = 0 + end + σ[account]_rentLastPaid = NUMBER + σ[account]_rentEvictBlock = NUMBER + ⌊σ[account]_rent / cost_per_block⌋ +END PAYRENT +``` + +* **`SSTORE(account, key, value)`** + * Perform PAYRENT(account) + * If `account` is evicted (i.e. `NUMBER` > `σ[account]_rentEvictBlock`) then transaction fails unless using the new transaction type and sufficient proofs are included to validate the old storage root and calculate the new root. + * Do normal SSTORE operation + * If the old value was zero for this [account, key] and the new value is non-zero, then `σ[account]_storageWords++` + * If the old value was non-zero for this [account, key] and the new value is zero, then `σ[account]_storageWords--`, and if the result is negative then set to zero +* **`SLOAD(account, key)`** + * If `account` is evicted (i.e. `NUMBER` > `σ[account]_rentEvictBlock`) then transaction fails unless using the new transaction type and sufficient proofs are included to validate the existing storage root and the existing storage value. + * Do normal SLOAD operation. +* **`CALL (and derivatives)`** + * If the target block is evicted (i.e. `NUMBER` > `σ[account]_rentEvictBlock`) then transaction fails unless using the new transaction type and sufficient proof is included to validate the existing code. + * Do normal CALL operation +* **`CREATE`** + * Set σ[account]_rentLastPaid = NUMBER + * Do normal CREATE operation + * `σ[account]_storageWord = 0` + * Note: it is possible there is a pre-existing rent balance here + +**New built-in contract** + +* `PAYRENT(address, amount)` -- Calls `PAYRENT` opcode + * This is a convenience for humans to send Ether from their accounts and turn it into rent. Note that simple accounts (CODESIZE == 0) cannot call arbitrary opcodes, they can only call CREATE or CALL. + * The gas cost of PAYRENT will be 10,000 or lower if possible. + +**Calculating `σ[account]_storageWord` for existing accounts** + +DRAFT... + +It is not an acceptable upgrade if on the fork block it is necessary for only archive nodes to participate which know the full storage amount for each account. + +An acceptable upgrade will be if the required `σ[account]_storageWord` can be calculated (or estimated) incrementally based on new transaction activity. + +DRAFT: I think it is possible to make such an acceptable upgrade using an unbiased estimator + +* add one bit of storage per `SSTORE` for legacy accounts on the first access of a given key +* add log(n) bits for each trie level +* assume that storage keys are a random variable + +To think more about... + +**No changes to current opcode gas costs.** + +## Rationale + +**No call** + +A contract will not know or react to the receipt of rent. This is okay. Workaround: if a contract really needed to know who provided rent payments then it could create a function in its ABI to attribute these payments. It is already possible to send payments to a contract without attribution by using `SELFDESTRUCT`. Other blockchains like TRON allow to transfer value to a contract without performing a call. + +**Eviction responsibility / lazy evaluation** + +The specification gives responsibility for eviction to the consensus clients. This is the most predictable behavior because it happens exactly when it should. Also there need not be any incentive mechanism (refund gas, bounty) for outside participants (off-chain) to monitor accounts and request removal. + +It is possible that an arbitrary number of accounts will be evicted in one block. That doesn't matter. Client implementations do not need to track which accounts are evicted, consensus is achieved just by agreeing on the conditions under which an account would be evicted. + +**No converting rent to value** + +Ether converted to rent cannot be converted back. Anybody that works in accounting and knows about gifts cards should tell you this is a good idea. It makes reasoning about the system much easier. + +**Accounts pay rent** + +Yes, they pay rent. It costs resources to account for their balances so we charge them rent. + +**Why do you need a separate rent account?** + +Because anybody/everybody can contribute to the rent account. If you depend on a contract, you should contribute to its rent. + +But the contract can spend all of its value. + +By maintaining a separate rent and value balance, this allows people to contribute to the rent while being confident that this is allowing the contract to stay around. + +NOTE: cloning. With this EIP, it may become feasible to allow storage cloning. Yes really. Because the new clone will be paying rent. See other EIP, I think made by Augur team. + +### Economics & constants + +An `SSTORE` executed in 2015 cost 20,000 gas and has survived about 6 million blocks. The gas price has been around 1 ~ 50 Gwei. So basically 4,000 Wei per block per word so far. Maybe storing an account is 10 times more intensive than storing a word. But actually `G_transaction` is 21,000 and `G_sstore` is 20,000 so these are similar and they can both create new accounts / words. + +How about: + +* `RENT_WORD_COST` -- 4,000 Wei +* `RENT_ACCOUNT_COST` -- 4,000 Wei +* `FORK_BLOCK` – when implementation starts + +The rent is priced in cold, hard Ether. It is not negotiated by clients, it is not dynamic. + +A future EIP may change this pricing to be dynamic. For example to notarize a block, notaries may be required to prove they have the current storage dataset (excluding evictions). Additionally, they may also prove they have the dataset plus evictions to earn an additional fee. The relative storage of the evicted accounts, and the other accounts versus the value of the additional fee may be used as a feedback mechanism to set a market price for storage. + +FYI, there are about 15B words in the Ethereum Mainnet dataset and about 100M total Ether mined. This means if all Ether was spent on storage at current proposed prices it would be 400 terabyte-years of storage. I'm not sure if it is helpful to look at it that way. + +## Backwards Compatibility + +EIP-1559 already introduces a mechanism for nodes to participate without recording the full network state and for clients to warm cache with storage data in their type 2 transactions. + +Users will need to be educated. + +Many smart contracts allow anybody to use an arbitrary amount of storage in them. This can limit the usefulness of deploying this proposal on an existing chain. + +**Recommended implementation variables (per account)** + +* **σ[a]_rentLastPaid** -- a block number that is set when: + * Value is transferred into an account (`CREATE`, `CALL`, `SELFDESTRUCT`) + * Code is set for an account (`CREATE`) + * An account's storage is updated (`SSTORE`) + * This begins with a logical value of `FORK_BLOCK` for all accounts + +* **σ[a]_rentEvictBlock** -- the block number when this account will be evicted + +**Storage note** + +For every account that is evicted, clients may choose to delete that storage from disk. A future EIP may make an incentive to keep this extra data for a fee. A future EIP may create a mechanism for clients to exchange information about these storage states. + +## Security Considerations + +Many smart contracts allow anybody to use an arbitrary amount of storage in them. + +## Copyright + +Copyright and related rights waived via CC0. + + diff --git a/EIPS/eip-1438.md b/EIPS/eip-1438.md new file mode 100644 index 0000000..5477245 --- /dev/null +++ b/EIPS/eip-1438.md @@ -0,0 +1,142 @@ +--- +eip: 1438 +title: dApp Components (avatar) & Universal Wallet +author: Jet Lim (@Nitro888) +discussions-to: https://ethresear.ch/t/avatar-system-and-universal-wallet-for-ethereum-address/3473 +status: Stagnant +type: Standards Track +category: ERC +created: 2018-09-21 +--- + +## Simple Summary +Contracts are open source based. And most developers use the public contracts at the start of the project to modify or simply include them. This is project-oriented centralized development and I think it is a waste of resources. Therefore, we propose to make dApp or contracts component-ready for use in other services. + +## Abstract +There have been suggestions for modified tokens based on erc20, but since many tokens have already been built on erc20, it is necessary to increase the utilization of already developed erc20 tokens. Therefore, we propose a universal wallet that can use erc20 tokens universally. We also propose a component dApp that allows you to create and save your avatar (& social badge system), and use it immediately in other services. All of the dApps suggested in this document are based on decentralized development and use that anyone can create and participate in. + +## Motivation +While many projects are under development in an open source way, they are simply adding and deploy with open sources to their projects. This means that you are developing a centralized service that uses your own dApp-generated information on your own. In order to improve the block chain ecosystem, all resources created by dApp and placed in the public block chain must be reusable in another dApp. This means that you can enhance your service by exchanging the generated information with other dApp. Likewise, ERC20 Tokens require Universal Wallet standards to be easy to use for direct transactions. + +### Seeds for improvement of the blockchain ecosystem. +- Synergy - With other dApps and resources. +- Enhanced interface - For ERC20 tokens. +- Easy & Decentralized - Everyone should be able to add to their services easily, without censorship. + + +#### The following avatar store, badge system, and universal wallet are kind of examples about component dApp. +![intro](/assets/eip-1438/intro.png) + +## Specification +### 1. Avatar +#### 1.1. Avatar Shop +- The avatar store is created after ERC20 currency is set. +- You can customize asset category & viewer script. + +#### 1.2. Upload asset & user data +The avatar's information & assets are stored in the event log part of the block chain. +- Assets are SVG format. (compressed with gzip) +- avatar information data is json (compressed with msgpack) + +![avatar](/assets/eip-1438/avatar.png) +** The avatar assets from [Avataaars](https://github.com/fangpenlin/avataaars) developed by [Fang-Pen Lin](https://twitter.com/fangpenlin), the original avatar is designed by [Pablo Stanley](https://twitter.com/pablostanley). + +### 2. Universal Wallet +![wallet](/assets/eip-1438/wallet.png) +#### 2.1. ERC20 interface +``` js +contract ERC20Interface { + function totalSupply() public constant returns (uint); + function balanceOf(address tokenOwner) public constant returns (uint balance); + function allowance(address tokenOwner, address spender) public constant returns (uint remaining); + function transfer(address to, uint tokens) public returns (bool success); + function approve(address spender, uint tokens) public returns (bool success); + function transferFrom(address from, address to, uint tokens) public returns (bool success); + + event Transfer(address indexed from, address indexed to, uint tokens); + event Approval(address indexed tokenOwner, address indexed spender, uint tokens); +} +``` + +#### 2.2. Fixed ERC20 contract for receive approval and execute function in one call +``` js +function approveAndCall(address spender, uint tokens, bytes data) public returns (bool success) { + allowed[msg.sender][spender] = tokens; + emit Approval(msg.sender, spender, tokens); + ApproveAndCallFallBack(spender).receiveApproval(msg.sender, tokens, this, data); + return true; +} +``` + +#### 2.3. And ApproveAndCallFallBack contract for Fixed ERC20. +However, many ERC20 tokens are not prepared. +``` js +contract ApproveAndCallFallBack { + function receiveApproval(address from, uint256 tokens, address token, bytes data) public; +} +``` +#### 2.4. Universal Wallet +We propose a Universal Wallet to solve this problem. + +``` js +contract UniversalWallet is _Base { + + constructor(bytes _msgPack) _Base(_msgPack) public {} + function () public payable {} + + //------------------------------------------------------- + // erc20 interface + //------------------------------------------------------- + function balanceOf(address _erc20) public constant returns (uint balance) { + if(_erc20==address(0)) + return address(this).balance; + return _ERC20Interface(_erc20).balanceOf(this); + } + function transfer(address _erc20, address _to, uint _tokens) onlyOwner public returns (bool success) { + require(balanceOf(_erc20)>=_tokens); + if(_erc20==address(0)) + _to.transfer(_tokens); + else + return _ERC20Interface(_erc20).transfer(_to,_tokens); + return true; + } + function approve(address _erc20, address _spender, uint _tokens) onlyOwner public returns (bool success) { + require(_erc20 != address(0)); + return _ERC20Interface(_erc20).approve(_spender,_tokens); + } + + //------------------------------------------------------- + // pay interface + //------------------------------------------------------- + function pay(address _store, uint _tokens, uint256[] _options) onlyOwner public { + address erc20 = _ApproveAndCallFallBack(_store).erc20(); + address spender = _ApproveAndCallFallBack(_store).spender(); + if(erc20 == address(0)) { + transfer(erc20,spender,_tokens); + _ApproveAndCallFallBack(_store).receiveApproval(_options); + } else { + _ERC20Interface(erc20).approve(spender,_tokens); + _ApproveAndCallFallBack(_store).receiveApproval(_options); + } + } + function pay(address _store, uint _tokens, bytes _msgPack) onlyOwner public { + address erc20 = _ApproveAndCallFallBack(_store).erc20(); + address spender = _ApproveAndCallFallBack(_store).spender(); + if(erc20 == address(0)) { + transfer(erc20,spender,_tokens); + _ApproveAndCallFallBack(_store).receiveApproval(_msgPack); + } else { + _ERC20Interface(erc20).approve(spender,_tokens); + _ApproveAndCallFallBack(_store).receiveApproval(_msgPack); + } + } +} +``` + +## Test Cases +- https://www.nitro888.com +- https://github.com/Nitro888/nitro888.github.io +- https://github.com/Nitro888/dApp-Alliance + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1444.md b/EIPS/eip-1444.md new file mode 100644 index 0000000..4ee0142 --- /dev/null +++ b/EIPS/eip-1444.md @@ -0,0 +1,322 @@ +--- +eip: 1444 +title: Localized Messaging with Signal-to-Text +author: Brooklyn Zelenka (@expede), Jennifer Cooper (@jenncoop) +discussions-to: https://ethereum-magicians.org/t/eip-1444-localized-messaging-with-signal-to-text/ +status: Stagnant +type: Standards Track +category: ERC +created: 2018-09-23 +--- + +## Simple Summary + +A method of converting machine codes to human-readable text in any language and phrasing. + +## Abstract + +An on-chain system for providing user feedback by converting machine-efficient codes into human-readable strings in any language or phrasing. The system does not impose a list of languages, but rather lets users create, share, and use the localizated text of their choice. + +## Motivation + +There are many cases where an end user needs feedback or instruction from a smart contact. Directly exposing numeric codes does not make for good UX or DX. If Ethereum is to be a truly global system usable by experts and lay persons alike, systems to provide feedback on what happened during a transaction are needed in as many languages as possible. + +Returning a hard-coded string (typically in English) only serves a small segment of the global population. This standard proposes a method to allow users to create, register, share, and use a decentralized collection of translations, enabling richer messaging that is more culturally and linguistically diverse. + +There are several machine efficient ways of representing intent, status, state transition, and other semantic signals including booleans, enums and [ERC-1066 codes](./eip-1066.md). By providing human-readable messages for these signals, the developer experience is enhanced by returning easier to consume information with more context (ex. `revert`). End user experience is enhanced by providing text that can be propagated up to the UI. + +## Specification + +### Contract Architecture + +Two types of contract: `LocalizationPreferences`, and `Localization`s. + +The `LocalizationPreferences` contract functions as a proxy for `tx.origin`. + +```diagram + +--------------+ + | | + +------> | Localization | + | | | + | +--------------+ + | + | ++-----------+ +-------------------------+ | +--------------+ +| | | | <------+ | | +| Requestor | <------> | LocalizationPreferences | <-------------> | Localization | +| | | | <------+ | | ++-----------+ +-------------------------+ | +--------------+ + | + | + | +--------------+ + | | | + +------> | Localization | + | | + +--------------+ +``` + +### `Localization` + +A contract that holds a simple mapping of codes to their text representations. + +```solidity +interface Localization { + function textFor(bytes32 _code) external view returns (string _text); +} +``` + +#### `textFor` + +Fetches the localized text representation. + +```solidity +function textFor(bytes32 _code) external view returns (string _text); +``` + +### `LocalizationPreferences` + +A proxy contract that allows users to set their preferred `Localization`. Text lookup is delegated to the user's preferred contract. + +A fallback `Localization` with all keys filled MUST be available. If the user-specified `Localization` has not explicitly set a loalization (ie. `textFor` returns `""`), the `LocalizationPreferences` MUST redelegate to the fallback `Localization`. + +```solidity +interface LocalizationPreferences { + function set(Localization _localization) external returns (bool); + function textFor(bytes32 _code) external view returns (bool _wasFound, string _text); +} +``` + +#### `set` + +Registers a user's preferred `Localization`. The registering user SHOULD be considered `tx.origin`. + +```solidity +function set(Localization _localization) external; +``` + +#### `textFor` + +Retrieve text for a code found at the user's preferred `Localization` contract. + +The first return value (`bool _wasFound`) represents if the text is available from that `Localization`, or if a fallback was used. If the fallback was used in this context, the `textFor`'s first return value MUST be set to `false`, and is `true` otherwise. + +```solidity +function textFor(bytes32 _code) external view returns (bool _wasFound, string _text); +``` + +### String Format + +All strings MUST be encoded as [UTF-8](https://www.ietf.org/rfc/rfc3629.txt). + +```solidity +"Špeĉiäl chârãçtérs are permitted" +"As are non-Latin characters: アルミ缶の上にあるみかん。" +"Emoji are legal: 🙈🙉🙊🎉" +"Feel free to be creative: (ノ◕ヮ◕)ノ*:・゚✧" +``` + +### Templates + +Template strings are allowed, and MUST follow the [ANSI C `printf`](https://pubs.opengroup.org/onlinepubs/009696799/utilities/printf.html) conventions. + +```solidity +"Satoshi's true identity is %s" +``` + +Text with 2 or more arguments SHOULD use the POSIX parameter field extension. + +```solidity +"Knock knock. Who's there? %1$s. %1$s who? %2$s!" +``` + +## Rationale + +### `bytes32` Keys + +`bytes32` is very efficient since it is the EVM's base word size. Given the enormous number of elements (card(A) > 1.1579 × 1077), it can embed nearly any practical signal, enum, or state. In cases where an application's key is longer than `bytes32`, hashing that long key can map that value into the correct width. + +Designs that use datatypes with small widths than `bytes32` (such as `bytes1` in [ERC-1066](./eip-1066.md)) can be directly embedded into the larger width. This is a trivial one-to-one mapping of the smaller set into the the larger one. + +### Local vs Globals and Singletons + +This spec has opted to not _force_ a single global registry, and rather allow any contract and use case deploy their own system. This allows for more flexibility, and does not restrict the community for opting to use singleton `LocalizationPreference` contracts for common use cases, share `Localization`s between different proxys, delegate translations between `Localization`s, and so on. + +There are many practical uses of agreed upon singletons. For instance, translating codes that aim to be fairly universal and integrated directly into the broader ecosystem (wallets, frameworks, debuggers, and the like) will want to have a single `LocalizationPreference`. + +Rather the dispersing several `LocalizationPreference`s for different use cases and codes, one could imagine a global "registry of registries". While this approach allows for a unified lookups of all translations in all use cases, it is antithetical to the spirit of decentralization and freedom. Such a system also increases the lookup complexity, places an onus on getting the code right the first time (or adding the overhead of an upgradable contract), and need to account for use case conflicts with a "unified" or centralized numbering system. Further, lookups should be lightweight (especially in cases like looking up revert text). + +For these reasons, this spec chooses the more decentralized, lightweight, free approach, at the cost of on-chain discoverability. A registry could still be compiled, but would be difficult to enforce, and is out of scope of this spec. + +### Off Chain Storage + +A very viable alternative is to store text off chain, with a pointer to the translations on-chain, and emit or return a `bytes32` code for another party to do the lookup. It is difficult to guarantee that off-chain resources will be available, and requires coordination from some other system like a web server to do the code-to-text matching. This is also not compatible with `revert` messages. + +### ASCII vs UTF-8 vs UTF-16 + +UTF-8 is the most widely used encoding at time of writing. It contains a direct embedding of ASCII, while providing characters for most natural languages, emoji, and special characters. + +Please see the [UTF-8 Everywhere Manifesto](https://utf8everywhere.org/) for more information. + +### When No Text is Found + +Returning a blank string to the requestor fully defeats the purpose of a localization system. The two options for handling missing text are: + +1. A generic "text not found" message in the preferred language +2. The actual message, in a different language + +#### Generic Option + +This designed opted to not use generic fallback text. It does not provide any useful information to the user other than to potentially contact the `Localization` maintainer (if one even exists and updating is even possible). + +#### Fallback Option + +The design outlined in this proposal is to providing text in a commonly used language (ex. English or Mandarin). First, this is the language that will be routed to if the user has yet to set a preference. Second, there is a good chance that a user may have _some_ proficiency with the language, or at least be able to use an automated translation service. + +Knowing that the text fell back via `textFor`s first return field boolean is _much_ simpler than attempting language detection after the fact. This information is useful for certain UI cases. for example where there may be a desire to explain why localization fell back. + +### Decentralized Text Crowdsourcing + +In order for Ethereum to gain mass adoption, users must be able to interact with it in the language, phrasing, and level of detail that they are most comfortable with. Rather than imposing a fixed set of translations as in a traditional, centralized application, this EIP provides a way for anyone to create, curate, and use translations. This empowers the crowd to supply culturally and linguistically diverse messaging, leading to broader and more distributed access to information. + +### `printf`-style Format Strings + +C-style `printf` templates have been the de facto standard for some time. They have wide compatibility across most languages (either in standard or third-party libraries). This makes it much easier for the consuming program to interpolate strings with low developer overhead. + +#### Parameter Fields + +The POSIX parameter field extension is important since languages do not share a common word order. Parameter fields enable the reuse and rearrangement of arguments in different localizations. + +```solidity +("%1$s is an element with the atomic number %2$d!", "Mercury", 80); +// => "Mercury is an element with the atomic number 80!" +``` + +#### Simplified Localizations + +Localization text does not require use of all parameters, and may simply ignore values. This can be useful for not exposing more technical information to users that would otherwise find it confusing. + +```ruby +#!/usr/bin/env ruby + +sprintf("%1$s é um elemento", "Mercurio", 80) +# => "Mercurio é um elemento" +``` + +```clojure +#!/usr/bin/env clojure + +(format "Element #%2$s" "Mercury" 80) +;; => Element #80 +``` + +### Interpolation Strategy + +Please note that it is highly advisable to return the template string _as is_, with arguments as multiple return values or fields in an `event`, leaving the actual interpolation to be done off chain. + + +```solidity +event AtomMessage { + bytes32 templateCode; + bytes32 atomCode; + uint256 atomicNumber; +} +``` + +```javascript +#!/usr/bin/env node + +var printf = require('printf'); + +const { returnValues: { templateCode, atomCode, atomicNumber } } = eventResponse; + +const template = await AppText.textFor(templateCode); +// => "%1$s ist ein Element mit der Ordnungszahl %2$d!" + +const atomName = await PeriodicTableText.textFor(atomCode); +// => "Merkur" + +printf(template, atomName, 80); +// => "Merkur ist ein Element mit der Ordnungszahl 80!" +``` + +### Unspecified Behaviour + +This spec does not specify: + +* Public or private access to the default `Localization` +* Who may set text + * Deployer + * `onlyOwner` + * Anyone + * Whitelisted users + * and so on +* When text is set + * `constructor` + * Any time + * Write to empty slots, but not overwrite existing text + * and so on + +These are intentionally left open. There are many cases for each of these, and restricting any is fully beyond the scope of this proposal. + +## Implementation + +```solidity +pragma solidity ^0.4.25; + +contract Localization { + mapping(bytes32 => string) private dictionary_; + + constructor() public {} + + // Currently overwrites anything + function set(bytes32 _code, string _message) external { + dictionary_[_code] = _message; + } + + function textFor(bytes32 _code) external view returns (string _message) { + return dictionary_[_code]; + } +} + +contract LocalizationPreference { + mapping(address => Localization) private registry_; + Localization public defaultLocalization; + + bytes32 private empty_ = keccak256(abi.encodePacked("")); + + constructor(Localization _defaultLocalization) public { + defaultLocalization = _defaultLocalization; + } + + function set(Localization _localization) external returns (bool) { + registry_[tx.origin] = _localization; + return true; + } + + function get(bytes32 _code) external view returns (bool, string) { + return get(_code, tx.origin); + } + + // Primarily for testing + function get(bytes32 _code, address _who) public view returns (bool, string) { + string memory text = getLocalizationFor(_who).textFor(_code); + + if (keccak256(abi.encodePacked(text)) != empty_) { + return (true, text); + } else { + return (false, defaultLocalization.textFor(_code)); + } + } + + function getLocalizationFor(address _who) internal view returns (Localization) { + if (Localization(registry_[_who]) == Localization(0)) { + return Localization(defaultLocalization); + } else { + return Localization(registry_[tx.origin]); + } + } +} +``` + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-145.md b/EIPS/eip-145.md new file mode 100644 index 0000000..f2bfed5 --- /dev/null +++ b/EIPS/eip-145.md @@ -0,0 +1,377 @@ +--- +eip: 145 +title: Bitwise shifting instructions in EVM +author: Alex Beregszaszi (@axic), Paweł Bylica (@chfast) +type: Standards Track +category: Core +status: Final +created: 2017-02-13 +--- + +## Simple Summary + +To provide native bitwise shifting with cost on par with other arithmetic operations. + +## Abstract + +Native bitwise shifting instructions are introduced, which are more efficient processing wise on the host and are cheaper to use by a contract. + +## Motivation + +EVM is lacking bitwise shifting operators, but supports other logical and arithmetic operators. Shift operations can be implemented via arithmetic operators, but that has a higher cost and requires more processing time from the host. Implementing `SHL` and `SHR` using arithmetic cost each 35 gas, while the proposed instructions take 3 gas. + +## Specification + +The following instructions are introduced: + +### `0x1b`: `SHL` (shift left) + +The `SHL` instruction (shift left) pops 2 values from the stack, first `arg1` and then `arg2`, and pushes on the stack `arg2` shifted to the left by `arg1` number of bits. The result is equal to + +``` +(arg2 * 2^arg1) mod 2^256 +``` + +Notes: +- The value (`arg2`) is interpreted as an unsigned number. +- The shift amount (`arg1`) is interpreted as an unsigned number. +- If the shift amount (`arg1`) is greater or equal 256 the result is 0. +- This is equivalent to `PUSH1 2 EXP MUL`. + +### `0x1c`: `SHR` (logical shift right) + +The `SHR` instruction (logical shift right) pops 2 values from the stack, first `arg1` and then `arg2`, and pushes on the stack `arg2` shifted to the right by `arg1` number of bits with zero fill. The result is equal to + +``` +floor(arg2 / 2^arg1) +``` + +Notes: +- The value (`arg2`) is interpreted as an unsigned number. +- The shift amount (`arg1`) is interpreted as an unsigned number. +- If the shift amount (`arg1`) is greater or equal 256 the result is 0. +- This is equivalent to `PUSH1 2 EXP DIV`. + +### `0x1d`: `SAR` (arithmetic shift right) + +The `SAR` instruction (arithmetic shift right) pops 2 values from the stack, first `arg1` and then `arg2`, and pushes on the stack `arg2` shifted to the right by `arg1` number of bits with sign extension. The result is equal to + +``` +floor(arg2 / 2^arg1) +``` + +Notes: +- The value (`arg2`) is interpreted as a signed number. +- The shift amount (`arg1`) is interpreted as an unsigned number. +- If the shift amount (`arg1`) is greater or equal 256 the result is 0 if `arg2` is non-negative or -1 if `arg2` is negative. +- This is **not** equivalent to `PUSH1 2 EXP SDIV`, since it rounds differently. See `SDIV(-1, 2) == 0`, while `SAR(-1, 1) == -1`. + +The cost of the shift instructions is set at `verylow` tier (3 gas). + +## Rationale + +Instruction operands were chosen to fit the more natural use case of shifting a value already on the stack. This means the operand order is swapped compared to most arithmetic insturctions. + +## Backwards Compatibility + +The newly introduced instructions have no effect on bytecode created in the past. + +## Test Cases + +### `SHL` (shift left) + +1. ``` + PUSH 0x0000000000000000000000000000000000000000000000000000000000000001 + PUSH 0x00 + SHL + --- + 0x0000000000000000000000000000000000000000000000000000000000000001 + ``` +2. ``` + PUSH 0x0000000000000000000000000000000000000000000000000000000000000001 + PUSH 0x01 + SHL + --- + 0x0000000000000000000000000000000000000000000000000000000000000002 + ``` +3. ``` + PUSH 0x0000000000000000000000000000000000000000000000000000000000000001 + PUSH 0xff + SHL + --- + 0x8000000000000000000000000000000000000000000000000000000000000000 + ``` +4. ``` + PUSH 0x0000000000000000000000000000000000000000000000000000000000000001 + PUSH 0x0100 + SHL + --- + 0x0000000000000000000000000000000000000000000000000000000000000000 + ``` +5. ``` + PUSH 0x0000000000000000000000000000000000000000000000000000000000000001 + PUSH 0x0101 + SHL + --- + 0x0000000000000000000000000000000000000000000000000000000000000000 + ``` +6. ``` + PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + PUSH 0x00 + SHL + --- + 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + ``` +7. ``` + PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + PUSH 0x01 + SHL + --- + 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe + ``` +8. ``` + PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + PUSH 0xff + SHL + --- + 0x8000000000000000000000000000000000000000000000000000000000000000 + ``` +9. ``` + PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + PUSH 0x0100 + SHL + --- + 0x0000000000000000000000000000000000000000000000000000000000000000 + ``` +10. ``` + PUSH 0x0000000000000000000000000000000000000000000000000000000000000000 + PUSH 0x01 + SHL + --- + 0x0000000000000000000000000000000000000000000000000000000000000000 + ``` +11. ``` + PUSH 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + PUSH 0x01 + SHL + --- + 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe + ``` + + +### `SHR` (logical shift right) + +1. ``` + PUSH 0x0000000000000000000000000000000000000000000000000000000000000001 + PUSH 0x00 + SHR + --- + 0x0000000000000000000000000000000000000000000000000000000000000001 + ``` +2. ``` + PUSH 0x0000000000000000000000000000000000000000000000000000000000000001 + PUSH 0x01 + SHR + --- + 0x0000000000000000000000000000000000000000000000000000000000000000 + ``` +3. ``` + PUSH 0x8000000000000000000000000000000000000000000000000000000000000000 + PUSH 0x01 + SHR + --- + 0x4000000000000000000000000000000000000000000000000000000000000000 + ``` +4. ``` + PUSH 0x8000000000000000000000000000000000000000000000000000000000000000 + PUSH 0xff + SHR + --- + 0x0000000000000000000000000000000000000000000000000000000000000001 + ``` +5. ``` + PUSH 0x8000000000000000000000000000000000000000000000000000000000000000 + PUSH 0x0100 + SHR + --- + 0x0000000000000000000000000000000000000000000000000000000000000000 + ``` +6. ``` + PUSH 0x8000000000000000000000000000000000000000000000000000000000000000 + PUSH 0x0101 + SHR + --- + 0x0000000000000000000000000000000000000000000000000000000000000000 + ``` +7. ``` + PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + PUSH 0x00 + SHR + --- + 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + ``` +8. ``` + PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + PUSH 0x01 + SHR + --- + 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + ``` +9. ``` + PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + PUSH 0xff + SHR + --- + 0x0000000000000000000000000000000000000000000000000000000000000001 + ``` +10. ``` + PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + PUSH 0x0100 + SHR + --- + 0x0000000000000000000000000000000000000000000000000000000000000000 + ``` +11. ``` + PUSH 0x0000000000000000000000000000000000000000000000000000000000000000 + PUSH 0x01 + SHR + --- + 0x0000000000000000000000000000000000000000000000000000000000000000 + ``` + +### `SAR` (arithmetic shift right) + +1. ``` + PUSH 0x0000000000000000000000000000000000000000000000000000000000000001 + PUSH 0x00 + SAR + --- + 0x0000000000000000000000000000000000000000000000000000000000000001 + ``` +2. ``` + PUSH 0x0000000000000000000000000000000000000000000000000000000000000001 + PUSH 0x01 + SAR + --- + 0x0000000000000000000000000000000000000000000000000000000000000000 + ``` +3. ``` + PUSH 0x8000000000000000000000000000000000000000000000000000000000000000 + PUSH 0x01 + SAR + --- + 0xc000000000000000000000000000000000000000000000000000000000000000 + ``` +4. ``` + PUSH 0x8000000000000000000000000000000000000000000000000000000000000000 + PUSH 0xff + SAR + --- + 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + ``` +5. ``` + PUSH 0x8000000000000000000000000000000000000000000000000000000000000000 + PUSH 0x0100 + SAR + --- + 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + ``` +6. ``` + PUSH 0x8000000000000000000000000000000000000000000000000000000000000000 + PUSH 0x0101 + SAR + --- + 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + ``` +7. ``` + PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + PUSH 0x00 + SAR + --- + 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + ``` +8. ``` + PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + PUSH 0x01 + SAR + --- + 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + ``` +9. ``` + PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + PUSH 0xff + SAR + --- + 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + ``` +10. ``` + PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + PUSH 0x0100 + SAR + --- + 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + ``` +11. ``` + PUSH 0x0000000000000000000000000000000000000000000000000000000000000000 + PUSH 0x01 + SAR + --- + 0x0000000000000000000000000000000000000000000000000000000000000000 + ``` +12. ``` + PUSH 0x4000000000000000000000000000000000000000000000000000000000000000 + PUSH 0xfe + SAR + --- + 0x0000000000000000000000000000000000000000000000000000000000000001 + ``` +13. ``` + PUSH 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + PUSH 0xf8 + SAR + --- + 0x000000000000000000000000000000000000000000000000000000000000007f + ``` +14. ``` + PUSH 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + PUSH 0xfe + SAR + --- + 0x0000000000000000000000000000000000000000000000000000000000000001 + ``` +15. ``` + PUSH 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + PUSH 0xff + SAR + --- + 0x0000000000000000000000000000000000000000000000000000000000000000 + ``` +16. ``` + PUSH 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + PUSH 0x0100 + SAR + --- + 0x0000000000000000000000000000000000000000000000000000000000000000 + ``` + + +## Implementation + +Client support: +- cpp-ethereum: https://github.com/ethereum/cpp-ethereum/pull/4054 + +Compiler support: +- Solidity/LLL: https://github.com/ethereum/solidity/pull/2541 + +## Tests + +Sources: +- https://github.com/ethereum/tests/tree/develop/src/GeneralStateTestsFiller/stShift + +Filled Tests: +- https://github.com/ethereum/tests/tree/develop/GeneralStateTests/stShift +- https://github.com/ethereum/tests/tree/develop/BlockchainTests/GeneralStateTests/stShift + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1450.md b/EIPS/eip-1450.md new file mode 100644 index 0000000..4e6e0da --- /dev/null +++ b/EIPS/eip-1450.md @@ -0,0 +1,326 @@ +--- +eip: 1450 +title: ERC-1450 A compatible security token for issuing and trading SEC-compliant securities +author: John Shiple (@johnshiple), Howard Marks , David Zhang +discussions-to: https://ethereum-magicians.org/t/erc-proposal-ldgrtoken-a-compatible-security-token-for-issuing-and-trading-sec-compliant-securities/1468 +status: Stagnant +type: Standards Track +category: ERC +created: 2018-09-25 +--- + +# ERC-1450 - A compatible security token for issuing and trading SEC-compliant securities + +## Simple Summary +`ERC-1450` is an `ERC-20` compatible token that enables issuing tokens representing securities that are required to comply with one or more of the following [Securities Act Regulations: Regulation Crowdfunding, Regulation D, and Regulation A](https://www.sec.gov/smallbusiness/exemptofferings). + +## Abstract +`ERC-1450` facilitates the recording of ownership and transfer of securities sold in compliance with the [Securities Act Regulations CF, D and A](https://www.sec.gov/smallbusiness/exemptofferings). The issuance and trading of securities is subject to the Securities Exchange Commission (SEC) and specific U.S. state blue sky laws and regulations. + +`ERC-1450` manages securities ownership during issuance and trading. The Issuer is the only role that should create a `ERC-1450` and assign the RTA. The RTA is the only role that is allowed to execute `ERC-1450`’s `mint`, `burnFrom`, and `transferFrom` functions. No role is allowed to execute `ERC-1450`’s `transfer` function. + +## Motivation +With the advent of the [JOBS Act](https://www.sec.gov/spotlight/jobs-act.shtml) in 2012 and the launch of Regulation Crowdfunding and the amendments to Regulation A and Regulation D in 2016, there has been an expansion in the exemptions available to Issuers and Investors to sell and purchase securities that have not been "registered" with the SEC under the Securities Act of 1933. + +There are currently no token standards that expressly facilitate conformity to securities law and related regulations. ERC-20 tokens do not support the regulated roles of Funding Portal, Broker Dealer, RTA, and Investor and do not support the [Bank Secrecy Act/USA Patriot Act KYC and AML requirements](https://www.occ.treas.gov/topics/compliance-bsa/bsa/index-bsa.html). Other improvements (notably [EIP-1404 (Simple Restricted Token Standard)](https://github.com/ethereum/EIPs/issues/1404) have tried to tackle KYC and AML regulatory requirement. This approach is novel because the RTA is solely responsible for performing KYC and AML and should be solely responsible for `transferFrom`, `mint`, and `burnFrom`. + +## Specification +`ERC-1450` extends `ERC-20`. + +### `ERC-1450` +`ERC-1450` requires that only the Issuer can create a token representing the security that only the RTA manages. Instantiating the `ERC-1450` requires the `Owned` and `IssuerControlled` modifiers, and only the Issuer should execute the `ERC-1450` constructor for a compliant token. `ERC-1450` extends the general `Ownable` modifier to describe a specific subset of owners that automate and decentralize compliance through the contract modifiers `Owned` and `IssuerControlled` and the function modifiers `onlyOwner` and `onlyIssuerTransferAgent`. The `Owned` contract modifier instantiates the `onlyOwner` modifier for functions. The `IssuerControlled` modifier instantiates the `onlyIssuerTransferAgent` modifier for functions. + +`ERC-1450` must prevent anyone from executing the `transfer`, `allowance`, and `approve` functions and/or implement these functions to always fail. `ERC-1450` updates the `transferFrom`, `mint`, and `burnFrom` functions. `transferFrom`, `mint`, and `burnFrom` may only be executed by the RTA and are restricted with the `onlyIssuerTransferAgent` modifier. Additionally, `ERC-1450` defines the functions `transferOwnership`, `setTransferAgent`, `setPhysicalAddressOfOperation`, and `isTransferAgent`. Only the issuer may call the `transferOwnership`, `setTransferAgent`, and `setPhysicalAddressOfOperation` functions. Anyone may call the `isTransferAgent` function. + +### Issuers and RTAs +For compliance reasons, the `ERC-1450` constructor must specify the issuer (the `owner`), the RTA (`transferAgent`), the security’s `name`, and the security’s `symbol`. + +#### Issuer Owned +`ERC-1450` must specify the `owner` in its constructor, apply the `Owned` modifier, and instantiate the `onlyOwner` modifier to enable specific functions to permit only the Issuer’s `owner` address to execute them. `ERC-1450` also defines the function `transferOwnership` which transfers ownership of the Issuer to the new `owner`’s address and can only be called by the `owner`. `transferOwnership` triggers the `OwnershipTransferred` event. + +#### Issuer Controlled +`IssuerControlled` maintains the Issuer’s ownership of their securities by owning the contract and enables the Issuer to set and update the RTA for the Issuer’s securities. `ERC-1450`‘s constructor must have an `IssuerControlled` modifier with the issuer specified in its `ERC-1450` constructor. `IssuerControlled` instantiates the `onlyIssuerTransferAgent` modifier for `ERC-1450` to enable specific functions (`setPhysicalAddressOfOperation` and `setTransferAgent`) to permit only the Issuer to execute these functions. + +#### Register Transfer Agent Controlled +`ERC-1450` defines the `setTransferAgent` function (to change the RTA) and `setPhysicalAddressOfOperation` function (to change the Issuer’s address) and must restrict execution to the Issuer’s owner with the `onlyOwner` modifier. `setTransferAgent` must emit the `TransferAgentUpdated` event. `setPhysicalAddressOfOperation` must emit the `PhysicalAddressOfOperationUpdated` event. + +`ERC-1450` must specify the `transferAgent` in its constructor and instantiate the `onlyIssuerTransferAgent` modifier to enable specific functions (`transferFrom`, `mint`, and `burnFrom`) to permit only the Issuer’s `transferAgent` address to execute them. `ERC-1450` also defines the public function `isTransferAgent` to lookup and identify the Issuer’s RTA. + +#### Securities +`ERC-1450` updates the `transferFrom`, `mint`, and `burnFrom` functions by applying the `onlyIssuerTransferAgent` to enable the issuance, re-issuance, and trading of securities. + +### ERC-20 Extension +`ERC-20` tokens provide the following functionality: + +```solidity +contract ERC20 { + function totalSupply() public view returns (uint256); + function balanceOf(address who) public view returns (uint256); + function transfer(address to, uint256 value) public returns (bool); + function allowance(address owner, address spender) public view returns (uint256); + function transferFrom(address from, address to, uint256 value) public returns (bool); + function approve(address spender, uint256 value) public returns (bool); + event Approval(address indexed owner, address indexed spender, uint256 value); + event Transfer(address indexed from, address indexed to, uint256 value); +} +``` + +`ERC-20` is extended as follows: + +```solidity +/** + * ERC-1450 is an ERC-20 compatible token that facilitates compliance with one or more of Securities Act Regulations CF, D and A. + * + * Implementations of the ERC-1450 standard must define the following optional ERC-20 + * fields: + * + * name - The name of the security + * symbol - The symbol of the security + * + * Implementations of the ERC-1450 standard must specify the following constructor + * arguments: + * + * _owner - the address of the owner + * _transferAgent - the address of the transfer agent + * _name - the name of the security + * _symbol - the symbol of the security + * + * Implementations of the ERC-1450 standard must implement the following contract + * modifiers: + * + * Owned - Only the address of the security’s issuer is permitted to execute the + * token’s constructor. This modifier also sets up the onlyOwner function modifier. + * IssuerControlled - This modifier sets up the onlyIssuerTransferAgent function modifier. + * + * Implementations of the ERC-1450 standard must implement the following function + * modifiers: + * + * onlyOwner - Only the address of the security’s issuer is permitted to execute the + * functions transferOwnership, setTransferAgent, and setPhysicalAddressOfOperation. + * onlyIssuerTransferAgent - Only the address of the issuer’s Registered Transfer + * Agent is permitted to execute the functions transferFrom, mint, and burnFrom. + * + * Implementations of the ERC-1450 standard must implement the following required ERC-20 + * event to always fail: + * + * Approval - Should never be called as the functions that emit this event must be + * implemented to always fail. + * + * Implementations of the ERC-1450 standard must implement the following required + * ERC-20 functions to always fail: + * + * transfer - Not a legal, regulated call for transferring securities because + * the token holder initiates the token transfer. The function must be implemented to + * always fail. + * allowance - Not a legal, regulated call for transferring securities because + * the token holder may not allow third parties to initiate token transfers. The + * function must be implemented to always fail. + * approve - Not a legal, regulated call for transferring securities because + * the token holder may not allow third parties to initiate token transfers. The + * function must be implemented to always fail. + * + * Implementations of the ERC-1450 standard must implement the following optional + * ERC-20 function: + * decimals - Must return '0' because securities are indivisible entities. + * + * Implementations of the ERC-1450 standard must implement the following functions: + * + * mint - Only the address of the issuer's Registered Transfer Agent may create new + * securities. + * burnFrom - Only the address of the issuer’s Registered Transfer Agent may burn or + * destroy securities. + */ + +Contract ERC-1450 is Owned, IssuerControlled { + + /** + * The constructor must implement a modifier (Owned) that creates the onlyOwner modifier + * to allow only the address of the issuer (the owner) to execute the transferOwnership, + * setTransferAgent, and setPhysicalAddressOfOperation functions. The construct must also + * implement a modifier (TransferAgentControlled) that creates the onlyIssuerTransferAgent + * modifier to allow only the address of the issuer’s Registered Transfer Agent to execute + * the functions transferFrom, mint, and burnFrom). + */ + constructor(address _owner, address _transferAgent, string _name, string _symbol) + Owned(_issuer) TransferAgentControlled(_transferAgent) public; + + /** + * Specify that only the owner (issuer) may execute a function. + * + * onlyOwner requires the msg.sender to be the owner’s address. + */ + modifier onlyOwner(); + + /** + * Specify that only the issuer’s transferAgent may execute a function. + * + * onlyIssuerTransferAgent requires the msg.sender to be the transferAgent’s address. + */ + modifier onlyIssuerTransferAgent(); + + /** + * Transfer ownership of a security from one issuer to another issuer. + * + * transferOwnership must implement the onlyOwner modifier to only allow the + * address of the issuer’s owner to transfer ownership. + * transferOwnership requires the _newOwner address to be the address of the new + * issuer. + */ + function transferOwnership(address _newOwner) public onlyOwner; + + /** + * Triggered after transferOwnership is executed. + */ + event OwnershipTransferred() + + /** + * Sets the transfer agent for the security. + * + * setTransferAgent must implement the onlyOwner modifier to only allow the + * address of the issuer’s specify the security’s transfer agent. + * setTransferAgent requires the _newTransferAgent address to be the address of the + * new transfer agent. + */ + function setTransferAgent(address _newTransferAgent) public onlyOwner; + + /** + * Triggered after setTransferAgent is executed. + */ + event TransferAgentUpdated(address indexed previousTransferAgent, address indexed + newTransferAgent); + + /** + * Sets the issuers physical address of operation. + * + * setPhysicalAddressOfOperation must implement the onlyOwner modifier to only allow + * the address of the issuer’s owner to transfer ownership. + * setPhysicalAddressOfOperation requires the _newPhysicalAddressOfOperation address + * to be the new address of the issuer. + */ + function setPhysicalAddressOfOperation(string _newPhysicalAddressOfOperation) public + onlyOwner; + + /** + * Triggered after setPhysicalAddressOfOperation is executed. + */ + event PhysicalAddressOfOperationUpdated(string previousPhysicalAddressOfOperation, + string newPhysicalAddressOfOperation); + + /** + * Look up the security’s transfer agent. + * + * isTransferAgent is a public function. + * isTransferAgent requires the _lookup address to determine if that address + * is the security’s transfer agent. + */ + function isTransferAgent(address _lookup) public view returns (bool); + + /** + * transfer is not a legal, regulated call and must be implemented to always fail. + */ + transfer(address to, uint tokens) public returns (bool success); + + /** + * Approval does not have to be implemented. This event should never be triggered as + * the functions that emit this even are not legal, regulated calls. + */ + event Approval(address indexed tokenOwner, address indexed spender, uint tokens); + + /** + * allowance is not a legal, regulated call and must be implemented to always fail. + */ + allowance(address tokenOwner, address spender) public constant returns (uint remaining); + + /** + * approve is not a legal, regulated call and must be implemented to always fail. + */ + approve(address spender, uint tokens) public returns (bool success); + + /** + * Transfer securities. + * + * transferFrom must implement the onlyIssuerTransferAgent modifier to only allow the + * address of the issuer’s Registered Transfer Agent to transfer `ERC-1450`s. + * transferFrom requires the _from address to have _value tokens. + * transferFrom requires that the _to address must not be 0 because securities must + * not destroyed in this manner. + */ + function transferFrom(address _from, address _to, uint256 _value) public + onlyIssuerTransferAgent returns (bool); + + /** + * Create new securities. + * + * mint must implement the onlyIssuerTransferAgent modifier to only allow the address + * of the issuer’s Registered Transfer Agent to mint `ERC-1450` tokens. + * mint requires that the _to address must not be 0 because securities must + * not destroyed in this manner. + * mint must add _value tokens to the _to address and increase the totalSupply by + * _value. + * mint must emit the Transfer event. + */ + function mint(address _to, uint256 _value) public onlyIssuerTransferAgent returns + (bool); + + /** + * Burn or destroy securities. + * + * burnFrom must implement the onlyIssuerTransferAgent modifier to only allow the + * address of the issuer’s Registered Transfer Agent to burn `ERC-1450`s. + * burnFrom requires the _from address to have _value tokens. + * burnFrom must subtract _value tokens from the _from address and decrease the + * totalSupply by _value. + * burnFrom must emit the Transfer event. + */ + function burnFrom(address _who, uint256 _value) public onlyIssuerTransferAgent returns + (bool); +} +``` + +### Securities Exchange Commission Requirements +The SEC has very strict requirements as to the specific roles that are allowed to perform specific actions. Specifically, only the RTA may `mint` and `transferFrom` securities. + +Implementers must maintain off-chain services and databases that record and track the Investor’s name, physical address, Ethereum address, and security ownership amount. The implementers and the SEC must be able to access the Investor’s private information on an as needed basis. Issuers and the RTA must be able to produce a current list of all Investors, including the names, addresses, and security ownership levels for every security at any given moment. Issuers and the RTA must be able to re-issue securities to Investors for a variety of regulated reasons. + +Private Investor information must never be publicly exposed on a public blockchain. + +### Managing Investor Information +Special care and attention must be taken to ensure that the personally identifiable information of Investors is never exposed or revealed to the public. + +### Issuers who lost access to their address or private keys +There is no recourse if the Issuer loses access to their address to an existing instance of their securities. Special care and efforts must be made by the Issuer to secure and safely store their address and associated private key. The Issuer can reassign ownership to another Issuer but not in the case where the Issuer loses their private key. + +If the Issuer loses access, the Issuer’s securities must be rebuilt using off-chain services. The Issuer must create (and secure) a new address. The RTA can read the existing Issuer securities, and the RTA can `mint` Investor securities accordingly under a new `ERC-1450` smart contract. + +### Registered Transfer Agents who lost access to their address or private keys +If the RTA loses access, the RTA can create a new Ethereum address, and the Issuer can execute the `setTransferAgent` function to reassign the RTA. + +### Handling Investors (security owners) who lost access to their addresses or private keys +Investors may “lose” their credentials for a number of reasons: they simply “lost” their credentials, they were hacked or the victim of fraud, they committed securities-related fraud, or a life event (like death) occurred. Because the RTA manages the Issuer’s securities, the RTA may authorize ownership related changes of securities (as long as they are properly notarized and verified). + +If an Investor (or, say, the Investor’s heir) loses their credentials, the Investor must go through a notarized process to notify the RTA of the situation and supply a new Investor address. From there, the RTA can `mint` the “lost” securities to the new Investor address and `burnFrom` the old Investor address (because the RTA knows all Investors’ addresses). + +## Rationale +The are currently no token standards that facilitate compliance with SEC regulations. The closest token is [ERC-884 (Delaware General Corporations Law (DGCL) compatible share token)](./eip-884.md) which states that SEC requirements are out of scope. [EIP-1404 (Simple Restricted Token Standard)](https://github.com/ethereum/EIPs/issues/1404) does not go far enough to address SEC requirements around re-issuing securities to Investors. + +## Backwards Compatibility +`ERC-1450` maintains compatibility with ERC-20 tokens with the following stipulations: +* `function allowance(address tokenOwner, address spender) public constant returns (uint remaining);` + * Must be implemented to always fail because allowance is not a legal, regulated call for a security. +* `function transfer(address to, uint tokens) public returns (bool success);` + * As the token holder initiates the transfer, must be implemented to always fail because transfer is not a legal, regulated call for a security. +* `function approve(address spender, uint tokens) public returns (bool success);` + * Must be implemented to always fail because approve is not a legal, regulated call for a security +* `function transferFrom(address from, address to, uint tokens) public returns (bool success);` + * Must be implemented so that only the Issuer’s RTA can perform this action +* `event Approval(address indexed tokenOwner, address indexed spender, uint tokens);` + * Does not have to be implemented. Approval should never be called as the functions that emit this event must be implemented to always fail + +## Test Cases +Test cases are available at [https://github.com/StartEngine/ldgr_smart_contracts/tree/master/test](https://github.com/StartEngine/ldgr_smart_contracts/tree/master/test). + +## Implementations +A reference implementation is available at [https://github.com/StartEngine/ldgr_smart_contracts](https://github.com/StartEngine/ldgr_smart_contracts). + +## Copyright Waiver +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1459.md b/EIPS/eip-1459.md new file mode 100644 index 0000000..b59df0e --- /dev/null +++ b/EIPS/eip-1459.md @@ -0,0 +1,172 @@ +--- +eip: 1459 +title: Node Discovery via DNS +description: Scheme for authenticated updateable Ethereum node lists via DNS. +author: Felix Lange (@fjl), Péter Szilágyi (@karalabe) +discussions-to: https://github.com/ethereum/devp2p/issues/50 +status: Draft +type: Standards Track +category: Networking +created: 2018-09-26 +requires: 778 +--- + +## Abstract + +This document describes a scheme for authenticated, updateable Ethereum node +lists retrievable via DNS. + +## Motivation + +Many Ethereum clients contain hard-coded bootstrap node lists. Updating those +lists requires a software update. The current lists are small, giving the client +little choice of initial entry point into the Ethereum network. We would like to +maintain larger node lists containing hundreds of nodes, and update them +regularly. + +The scheme described here is a replacement for client bootstrap node lists with +equivalent security and many additional benefits. Large lists populated by +traversing the node discovery DHT can serve as a fallback option for nodes which +can't join the DHT due to restrictive network policy. DNS-based node lists may +also be useful to Ethereum peering providers because their customers can +configure the client to use the provider's list. + +## Specification + +A 'node list' is a list of 'node records' [as defined by EIP-778](./eip-778.md) +of arbitrary length. Lists +may refer to other lists using links. The entire list is signed using a +secp256k1 private key. The corresponding public key must be known to the client +in order to verify the list. + +To refer to a DNS node list, clients use a URL with 'enrtree' scheme. The URL +contains the DNS name on which the list can be found as well as the public key +that signed the list. The public key is contained in the username part of the +URL and is the base32 encoding (RFC-4648) of the compressed 32-byte binary public key. + +Example: + + enrtree://AM5FCQLWIZX2QFPNJAP7VUERCCRNGRHWZG3YYHIUV7BVDQ5FDPRT2@nodes.example.org + +This URL refers to a node list at the DNS name 'nodes.example.org' and is signed +by the public key +`0x049f88229042fef9200246f49f94d9b77c4e954721442714e85850cb6d9e5daf2d880ea0e53cb3ac1a75f9923c2726a4f941f7d326781baa6380754a360de5c2b6` + +### DNS Record Structure + +The nodes in a list are encoded as a merkle tree for distribution via the DNS +protocol. Entries of the merkle tree are contained in DNS TXT records. The root +of the tree is a TXT record with the following content: + + enrtree-root:v1 e= l= seq= sig= + +where + +- `enr-root` and `link-root` refer to the root hashes of subtrees containing + nodes and links subtrees. +- `sequence-number` is the tree's update sequence number, a decimal integer. +- `signature` is a 65-byte secp256k1 EC signature over the keccak256 hash of the + record content, excluding the `sig=` part, encoded as URL-safe base64 (RFC-4648). + +Further TXT records on subdomains map hashes to one of three entry types. The +subdomain name of any entry is the base32 encoding of the (abbreviated) +keccak256 hash of its text content. + +- `enrtree-branch:,,...,` is an intermediate tree entry containing + hashes of subtree entries. +- `enrtree://@` is a leaf pointing to a different list located at + another fully qualified domain name. Note that this format matches the URL + encoding. This type of entry may only appear in the subtree pointed to by + `link-root`. +- `enr:` is a leaf containing a node record. The node record is + encoded as a URL-safe base64 string. Note that this type of entry matches the + canonical ENR text encoding. It may only appear in the `enr-root` subtree. + +No particular ordering or structure is defined for the tree. Whenever the tree +is updated, its sequence number should increase. The content of any TXT record +should be small enough to fit into the 512 byte limit imposed on UDP DNS +packets. This limits the number of hashes that can be placed into an +`enrtree-branch` entry. + +Example in zone file format: + + ; name ttl class type content + @ 60 IN TXT enrtree-root:v1 e=JWXYDBPXYWG6FX3GMDIBFA6CJ4 l=C7HRFPF3BLGF3YR4DY5KX3SMBE seq=1 sig=o908WmNp7LibOfPsr4btQwatZJ5URBr2ZAuxvK4UWHlsB9sUOTJQaGAlLPVAhM__XJesCHxLISo94z5Z2a463gA + C7HRFPF3BLGF3YR4DY5KX3SMBE 86900 IN TXT enrtree://AM5FCQLWIZX2QFPNJAP7VUERCCRNGRHWZG3YYHIUV7BVDQ5FDPRT2@morenodes.example.org + JWXYDBPXYWG6FX3GMDIBFA6CJ4 86900 IN TXT enrtree-branch:2XS2367YHAXJFGLZHVAWLQD4ZY,H4FHT4B454P6UXFD7JCYQ5PWDY,MHTDO6TMUBRIA2XWG5LUDACK24 + 2XS2367YHAXJFGLZHVAWLQD4ZY 86900 IN TXT enr:-HW4QOFzoVLaFJnNhbgMoDXPnOvcdVuj7pDpqRvh6BRDO68aVi5ZcjB3vzQRZH2IcLBGHzo8uUN3snqmgTiE56CH3AMBgmlkgnY0iXNlY3AyNTZrMaECC2_24YYkYHEgdzxlSNKQEnHhuNAbNlMlWJxrJxbAFvA + H4FHT4B454P6UXFD7JCYQ5PWDY 86900 IN TXT enr:-HW4QAggRauloj2SDLtIHN1XBkvhFZ1vtf1raYQp9TBW2RD5EEawDzbtSmlXUfnaHcvwOizhVYLtr7e6vw7NAf6mTuoCgmlkgnY0iXNlY3AyNTZrMaECjrXI8TLNXU0f8cthpAMxEshUyQlK-AM0PW2wfrnacNI + MHTDO6TMUBRIA2XWG5LUDACK24 86900 IN TXT enr:-HW4QLAYqmrwllBEnzWWs7I5Ev2IAs7x_dZlbYdRdMUx5EyKHDXp7AV5CkuPGUPdvbv1_Ms1CPfhcGCvSElSosZmyoqAgmlkgnY0iXNlY3AyNTZrMaECriawHKWdDRk2xeZkrOXBQ0dfMFLHY4eENZwdufn1S1o + +### Client Protocol + +To find nodes at a given DNS name, say "mynodes.org": + +1. Resolve the TXT record of the name and check whether it contains a valid + "enrtree-root=v1" entry. Let's say the `enr-root` hash contained in the entry + is "CFZUWDU7JNQR4VTCZVOJZ5ROV4". +2. Verify the signature on the root against the known public key and check + whether the sequence number is larger than or equal to any previous number + seen for that name. +3. Resolve the TXT record of the hash subdomain, e.g. + "CFZUWDU7JNQR4VTCZVOJZ5ROV4.mynodes.org" and verify whether the content + matches the hash. +4. The next step depends on the entry type found: + - for `enrtree-branch`: parse the list of hashes and continue resolving them (step 3). + - for `enr`: decode, verify the node record and import it to local node storage. + +During traversal, the client must track hashes and domains which are already +resolved to avoid going into an infinite loop. It's in the client's best +interest to traverse the tree in random order. + +Client implementations should avoid downloading the entire tree at once during +normal operation. It's much better to request entries via DNS when-needed, i.e. +at the time when the client is looking for peers. + +## Rationale + +### Why DNS? + +We have chosen DNS as the distribution medium because it is always available, +even under restrictive network conditions. The protocol provides low latency and +answers to DNS queries can be cached by intermediate resolvers. No custom server +software is needed. Node lists can be deployed to any DNS provider such as +CloudFlare DNS, dnsimple, Amazon Route 53 using their respective client +libraries. + +### Why is this a merkle tree? + +Being a merkle tree, any node list can be authenticated by a single signature on +the root. Hash subdomains protect the integrity of the list. At worst +intermediate resolvers can block access to the list or disallow updates to it, +but cannot corrupt its content. The sequence number prevents replacing the root +with an older version. + +Synchronizing updates on the client side can be done incrementally, which +matters for large lists. Individual entries of the tree are small enough to fit +into a single UDP packet, ensuring compatibility with environments where only +basic UDP DNS is available. The tree format also works well with caching +resolvers: only the root of the tree needs a short TTL. Intermediate entries and +leaves can be cached for days. + +### Why does the link subtree exist? + +Links between lists enable federation and web-of-trust functionality. The +operator of a large list can delegate maintenance to other list providers. If +two node lists link to each other, users can use either list and get nodes from +both. + +The link subtree is separate from the tree containing ENRs. This is done to +enable client implementations to sync these trees independently. A client +wanting to get as many nodes as possible will sync the link tree first and add +all linked names to the sync horizon. + +## Security Considerations + +Discovery via DNS is less secure than via DHT, because it relies on a trusted +party to publish the records regularly. The actor could easily eclipse +bootstrapping nodes by only publishing node records that it controls. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1462.md b/EIPS/eip-1462.md new file mode 100644 index 0000000..9bb9020 --- /dev/null +++ b/EIPS/eip-1462.md @@ -0,0 +1,117 @@ +--- +eip: 1462 +title: Base Security Token +author: Maxim Kupriianov , Julian Svirsky +discussions-to: https://ethereum-magicians.org/t/erc-1462-base-security-token/1501 +status: Stagnant +type: Standards Track +category: ERC +created: 2018-10-01 +requires: 20, 1066 +--- + +## Simple Summary + +An extension to ERC-20 standard token that provides compliance with securities regulations and legal enforceability. + +## Abstract + +This EIP defines a minimal set of additions to the default token standard such as [ERC-20](./eip-20.md), that allows for compliance with domestic and international legal requirements. Such requirements include KYC (Know Your Customer) and AML (Anti Money Laundering) regulations, and the ability to lock tokens for an account, and restrict them from transfer due to a legal dispute. Also the ability to attach additional legal documentation, in order to set up a dual-binding relationship between the token and off-chain legal entities. + +The scope of this standard is being kept as narrow as possible to avoid restricting potential use-cases of this base security token. Any additional functionality and limitations not defined in this standard may be enforced on per-project basis. + +## Motivation + +There are several security token standards that have been proposed recently. Examples include [ERC-1400](https://github.com/ethereum/EIPs/issues/1411), also [ERC-1450](https://eips.ethereum.org/EIPS/eip-1450). We have concerns about each of them, mostly because the scope of each of these EIPs contains many project-specific or market-specific details. Since many EIPs are coming from the respective backing companies, they capture many niche requirements that are excessive for a general case. + +For instance, ERC-1411 uses dependency on [ERC-1410](https://github.com/ethereum/eips/issues/1410) but it falls out of the "security tokens" scope. Also its dependency on [ERC-777](./eip-777.md) will block the adoption for a quite period of time before ERC-777 is finalized, but the integration guidelines for existing ERC-20 workflows are not described in that EIP, yet. Another attempt to make a much simpler base standard [ERC-1404](https://github.com/ethereum/EIPs/issues/1404) is missing a few important points, specifically it doesn't provide enough granularity to distinguish between different ERC-20 transfer functions such as `transfer` and `transferFrom`. It also doesn't provide a way to bind legal documentation to the issued tokens. + +What we propose in this EIP is a simple and very modular solution for creating a base security token for the widest possible scope of applications, so it can be used by other issuers to build upon. The issuers should be able to add more restrictions and policies to the token, using the functions and implementation proposed below, but they must not be limited in any way while using this ERC. + +## Specification + +The ERC-20 token provides the following basic features: + +```solidity +contract ERC20 { + function totalSupply() public view returns (uint256); + function balanceOf(address who) public view returns (uint256); + function transfer(address to, uint256 value) public returns (bool); + function allowance(address owner, address spender) public view returns (uint256); + function transferFrom(address from, address to, uint256 value) public returns (bool); + function approve(address spender, uint256 value) public returns (bool); + event Approval(address indexed owner, address indexed spender, uint256 value); + event Transfer(address indexed from, address indexed to, uint256 value); +} +``` + +This will be extended as follows: + +```solidity +interface BaseSecurityToken /* is ERC-20 */ { + // Checking functions + function checkTransferAllowed (address from, address to, uint256 value) public view returns (byte); + function checkTransferFromAllowed (address from, address to, uint256 value) public view returns (byte); + function checkMintAllowed (address to, uint256 value) public view returns (byte); + function checkBurnAllowed (address from, uint256 value) public view returns (byte); + + // Documentation functions + function attachDocument(bytes32 _name, string _uri, bytes32 _contentHash) external; + function lookupDocument(bytes32 _name) external view returns (string, bytes32); +} +``` + +### Transfer Checking Functions + +We introduce four new functions that should be used to check that the actions are allowed for the provided inputs. The implementation details of each function are left for the token issuer, it is the issuer's responsibility to add all necessary checks that will validate an operation in accordance with KYC/AML policies and legal requirements set for a specific token asset. + +Each function must return a status code from the common set of Ethereum status codes (ESC), according to [ERC-1066](./eip-1066.md). Localization of these codes is out of the scope of this proposal and may be optionally solved by adopting [ERC-1444](./eip-1444.md) on the application level. If the operation is allowed by a checking function, the return status code must be `0x11` (Allowed) or an issuer-specific code with equivalent but more precise meaning. If the operation is not allowed by a checking function, the status must be `0x10` (Disallowed) or an issuer-specific code with equivalent but more precise meaning. Upon an internal error, the function must return the most relevant code from the general code table or an issuer-specific equivalent, example: `0xF0` (Off-Chain Failure). + +**For [ERC-20](./eip-20.md) based tokens,** +* It is required that transfer function must be overridden with logic that checks the corresponding checkTransferAllowed return status code. +* It is required that `transferFrom` function must be overridden with logic that checks the corresponding `checkTransferFromAllowed` return status code. +* It is required that `approve` function must be overridden with logic that checks the corresponding `checkTransferFromAllowed` return status code. +* Other functions such as `mint` and `burn` must be overridden, if they exist in the token implementation, they should check `checkMintAllowed` and `checkBurnAllowed` status codes accordingly. + +**For [ERC-777](./eip-777.md) based tokens,** +* It is required that `send` function must be overridden with logic that checks the corresponding return status codes: + - `checkTransferAllowed` return status code, if transfer happens on behalf of the tokens owner; + - `checkTransferFromAllowed` return status code, if transfer happens on behalf of an operator (i.e. delegated transfer). +* It is required that `burn` function must be overridden with logic that checks the corresponding `checkBurnAllowed` return status code. +* Other functions, such as `mint` must be overridden, if they exist in the token implementation, e.g. if the security token is mintable. `mint` function must call `checkMintAllowed` ad check it return status code. + +For both cases, + +* It is required for guaranteed compatibility with ERC-20 and ERC-777 wallets that each checking function returns `0x11` (Allowed) if not overridden with the issuer's custom logic. +* It is required that all overridden checking functions must revert if the action is not allowed or an error occurred, according to the returned status code. + +Inside checker functions the logic is allowed to use any feature available on-chain: perform calls to registry contracts with whitelists/blacklists, use built-in checking logic that is defined on the same contract, or even run off-chain queries through an oracle. + +### Documentation Functions + +We also introduce two new functions that should be used for document management purposes. Function `attachDocument` adds a reference pointing to an off-chain document, with specified name, URI and contents hash. The hashing algorithm is not specified within this standard, but the resulting hash must not be longer than 32 bytes. Function `lookupDocument` gets the referenced document by its name. + +* It is not required to use documentation functions, they are optional and provided as a part of a legal framework. +* It is required that if `attachDocument` function has been used, the document reference must have a unique name, overwriting the references under same name is not allowed. All implementations must check if the reference under the given name is already existing. + +## Rationale + +This EIP targets both ERC-20 and ERC-777 based tokens, although the most emphasis is given to ERC-20 due to its widespread adoption. However, this extension is designed to be compatible with the forthcoming ERC-777 standard, as well. + +All checking functions are named with prefixes `check` since they return check status code, not booleans, because that is important to facilitate the debugging and tracing process. It is responsibility of the issuer to implement the logic that will handle the return codes appropriately. Some handlers will simply throw errors, other handlers would log information for future process mining. More rationale for status codes can be seen in [ERC-1066](./eip-1066.md). + +We require two different transfer validation functions: `checkTransferAllowed` and `checkTransferFromAllowed` since the corresponding `transfer` and `transferFrom` are usually called in different contexts. Some token standards such as [ERC-1450](./eip-1450.md) explicitly disallow use of `transfer`, while allowing only `transferFrom`. There might be also different complex scenarios, where `transfer` and `transferFrom` should be treated differently. ERC-777 is relying on its own `send` for transferring tokens, so it is reasonable to switch between checker functions based on its call context. We decided to omit the `checkApprove` function since it would be used in exactly the same context as `checkTransferFromAllowed`. In many cases it is required not only regulate securities transfers, but also restrict burn and `mint` operations, and additional checker functions have been added for that. + +The documentation functions that we propose here are a must-have tool to create dual-bindings with off-chain legal documents, a great example of this can be seen in [Neufund's Employee Incentive Options Plan](https://medium.com/@ZoeAdamovicz/37376fd0384a) legal framework that implements full legal enforceability: the smart contract refers to printed ESOP Terms & Conditions Document, which itself refers back to smart contract. This is becoming a widely adopted practice even in cases where there are no legal requirements to reference the documents within the security token. However they're almost always required, and it's a good way to attach useful documentation of various types. + +## Backwards Compatibility + +This EIP is fully backwards compatible as its implementation extends the functionality of ERC-20 and ERC-777 tokens. + +## Implementation + +* https://github.com/AtlantPlatform/BaseSecurityToken + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1470.md b/EIPS/eip-1470.md new file mode 100644 index 0000000..2ac9031 --- /dev/null +++ b/EIPS/eip-1470.md @@ -0,0 +1,104 @@ +--- +eip: 1470 +title: Smart Contract Weakness Classification (SWC) +author: Gerhard Wagner (@thec00n) +discussions-to: https://github.com/ethereum/EIPs/issues/1469 +status: Stagnant +type: Informational +created: 2018-09-18 +--- + +## Simple Summary + +This EIP proposes a classification scheme for security weaknesses in Ethereum smart contracts. + +## Abstract + +The SWC is a smart contract specific software weakness classification scheme for developers, tool vendors and security practitioners. The SWC is loosely aligned to the terminologies and structure used in the [Common Weakness Enumeration - CWE](https://cwe.mitre.org) scheme while overlaying a wide range of weakness variants that are specific to smart contracts. + +The goals of the SWC scheme are as follows: + +- Provide a straightforward way to classify weaknesses in smart contract systems. +- Provide a straightforward way to identify the weakness(es) that lead to a vulnerability in a smart contract system. +- Define a common language for describing weaknesses in smart contract systems' architecture, design and code. +- Train and increase the performance of smart contract security analysis tools. + + +## Motivation + +In the software security industry, it is a widely accepted practice to use a common terminology and to classify security related bugs and errors with a standardized scheme. While this has not stopped vulnerabilities from appearing in software, it has helped communities focusing on web applications, network protocols, IOT devices and various other fields to educate users and developers to understand the nature of security related issues in their software. It has also allowed the security community to quickly understand vulnerabilities that occur in production systems to perform root cause analysis or triage findings from various security analysis sources. In recent years various organizations and companies also published vulnerability data to find the most widespread security issues based on collected vulnerability data. Two examples that are widely used and referred to are the [SANS TOP 25 Most Dangerous Software Errors](https://www.sans.org/top25-software-errors) and the [OWASP TOP 10](https://www.owasp.org/index.php/Top_10-2017_Top_10). None of those publications would have been possible without a common classification scheme. + +At present no such weakness classification scheme exists for weaknesses specific to Ethereum Smart Contracts. Common language and awareness of security weaknesses is mostly derived from academic papers, best practice guides and published articles. Findings from audit reports and security tool analysis add to the wide range of terminologies that is used to describe the discovered weaknesses. It is often time consuming to understand the technical root cause and the risk associated to findings from different sources even for security experts. + +## Rationale + +While recognizing the current gap, the SWC does not aim to reinvent the wheel in regards to classification of security weaknesses. It rather proposes to build on top of what has worked well in other parts of the software security community - specifically the Common Weakness Enumeration (CWE), a list of software vulnerability types that stands out in terms of adoption and breadth of coverage. While CWE does not describe any weaknesses specific to smart contracts, it does describe related weaknesses at higher abstraction layers. This EIP proposes to create smart contract specific variants while linking back to the larger spectrum of software errors and mistakes listed in the CWE that different platforms and technologies have in common. + +## Specification + +Before discussing the SWC specification it is important to describe the terminology used: + +- Weakness: A software error or mistake that in the right conditions can by itself or coupled with other weaknesses lead to a vulnerability. +- Vulnerability: A weakness or multiple weaknesses which directly or indirectly lead to an undesirable state in a smart contract system. +- Variant: A specific weakness that is described in a very low detail specific to Ethereum smart contracts. Each variant is assigned an unique SWC ID. +- Relationships: CWE has a wide range of _Base_ and _Class_ types that group weaknesses on higher abstraction layers. The CWE uses _Relationships_ to link SWC smart contract weakness variants to existing _Base_ or _Class_ CWE types. _Relationships_ are used to provide context on how SWCs are linked to the wider group of software security weaknesses and to be able to generate useful visualisations and insights through issue data sets. In its current revision it is proposed to link a SWC to its closest parent in the CWE. +- SWC ID: A numeric identifier linked to a variant (e.g. SWC-101). +- Test Case: A test case constitutes a micro-sample or real-world smart contract that demonstrates concrete instances of one or multiple SWC variants. Test cases serve as the basis for meaningful weakness classification and are useful to security analysis tool developers. + +The SWC in its most basic form links a numeric identifier to a weakness variant. For example the identifier _SWC-101_ is linked to the _Integer Overflow and Underflow_ variant. While a list with the weakness title and a unique id is useful by itself, it would also be ambiguous without further details. Therefore the SWC recommends to add a definition and test cases to any weakness variant. + +**SWC definition** + +A SWC definition is formatted in markdown to allow good readability and tools to process them easily. It consists of the following attributes. + +- Title: A name for the weakness that points to the technical root cause. +- Relationships: Links a CWE _Base_ or _Class_ type to its CWE variant. The _Integer Overflow and Underflow_ variant for example is linked to [CWE-682 - Incorrect Calculation](https://cwe.mitre.org/data/definitions/682.html). +- Description: Describes the nature and potential impact of the weakness on the contract system. +- Remediation: Describes ways on how to fix the weakness. +- References: Links to external references that contain relevant additional information on the weakness. + +**Test cases** + +Test cases include crafted as well as real-world samples of vulnerable smart contracts. A single test case consists of three components: + +1. Source code of a smart contract sample; e.g. Solidity, Vyper, etc. +2. Compiled asset from an EVM compiler in machine readable format; e.g. JSON or ethPM. +3. Test result configuration that describes which and how many instances of a weakness variant can be found in a given sample. The YAML schema for the proposed test case configuration is listed below. + +```YAML +title: SWC config +type: object +required: +- description +- issues +properties: + description: + type: string + issues: + title: Issues + type: array + items: + title: Issue + type: object + required: + - id + - count + properties: + id: + type: string + count: + type: number + locations: + items: + bytecode_offsets: + type: object + line_numbers: + type: object +``` + +## Implementation + +The Smart Contract Weakness Classification registry located in this [GitHub repository](https://github.com/SmartContractSecurity/SWC-registry) uses the SWC scheme proposed in this EIP. A GitHub Pages rendered version is also available [here](https://smartcontractsecurity.github.io/SWC-registry/). + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1474.md b/EIPS/eip-1474.md new file mode 100644 index 0000000..b3834dc --- /dev/null +++ b/EIPS/eip-1474.md @@ -0,0 +1,2254 @@ +--- +eip: 1474 +title: Remote procedure call specification +author: Paul Bouchon , Erik Marks (@rekmarks) +discussions-to: https://ethereum-magicians.org/t/eip-remote-procedure-call-specification/1537 +status: Stagnant +type: Standards Track +category: Interface +created: 2018-10-02 +--- + +## Simple Summary + +This proposal defines a standard set of remote procedure call methods that an Ethereum node should implement. + +## Abstract + +Nodes created by the current generation of Ethereum clients expose inconsistent and incompatible remote procedure call (RPC) methods because no formal Ethereum RPC specification exists. This proposal standardizes such a specification to provide developers with a predictable Ethereum RPC interface regardless of underlying node implementation. + +## Specification + +### Concepts + +#### RFC-2119 + +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). + +#### JSON-RPC + +Communication with Ethereum nodes is accomplished using [JSON-RPC](https://www.jsonrpc.org/specification), a stateless, lightweight [remote procedure call](https://en.wikipedia.org/wiki/Remote_procedure_call) protocol that uses [JSON](http://www.json.org/) as its data format. Ethereum RPC methods **MUST** be called using [JSON-RPC request objects](https://www.jsonrpc.org/specification#request_object) and **MUST** respond with [JSON-RPC response objects](https://www.jsonrpc.org/specification#response_object). + +#### Error codes + +If an Ethereum RPC method encounters an error, the `error` member included on the response object **MUST** be an object containing a `code` member and descriptive `message` member. The following list contains all possible error codes and associated messages: + +|Code|Message|Meaning|Category| +|-|-|-|-| +|-32700|Parse error|Invalid JSON|standard| +|-32600|Invalid request|JSON is not a valid request object|standard| +|-32601|Method not found|Method does not exist|standard| +|-32602|Invalid params|Invalid method parameters|standard| +|-32603|Internal error|Internal JSON-RPC error|standard| +|-32000|Invalid input|Missing or invalid parameters|non-standard| +|-32001|Resource not found|Requested resource not found|non-standard| +|-32002|Resource unavailable|Requested resource not available|non-standard| +|-32003|Transaction rejected|Transaction creation failed|non-standard| +|-32004|Method not supported|Method is not implemented|non-standard| +|-32005|Limit exceeded|Request exceeds defined limit|non-standard| +|-32006|JSON-RPC version not supported|Version of JSON-RPC protocol is not supported|non-standard| + +Example error response: + +```sh +{ + "id": 1337 + "jsonrpc": "2.0", + "error": { + "code": -32003, + "message": "Transaction rejected" + } +} +``` + +#### Value encoding + +Specific types of values passed to and returned from Ethereum RPC methods require special encoding: + +##### `Quantity` + +- A `Quantity` value **MUST** be hex-encoded. +- A `Quantity` value **MUST** be "0x"-prefixed. +- A `Quantity` value **MUST** be expressed using the fewest possible hex digits per byte. +- A `Quantity` value **MUST** express zero as "0x0". + +Examples `Quantity` values: + +|Value|Valid|Reason| +|-|-|-| +|0x|`invalid`|empty not a valid quantity| +|0x0|`valid`|interpreted as a quantity of zero| +|0x00|`invalid`|leading zeroes not allowed| +|0x41|`valid`|interpreted as a quantity of 65| +|0x400|`valid`|interpreted as a quantity of 1024| +|0x0400|`invalid`|leading zeroes not allowed| +|ff|`invalid`|values must be prefixed| + +##### `Block Identifier` + +The RPC methods below take a default block identifier as a parameter. +- `eth_getBalance` +- `eth_getStorageAt` +- `eth_getTransactionCount` +- `eth_getCode` +- `eth_call` +- `eth_getProof` + +Since there is no way to clearly distinguish between a `Data` parameter and a `Quantity` parameter, [EIP-1898](./eip-1898.md) provides a format to specify a block either using the block hash or block number. The block identifier is a JSON `object` with the following fields: + +|Property|Type|Description| +|-|-|-| +|`[blockNumber]`|{[`Quantity`](#quantity)}|The block in the canonical chain with this number| +|OR `[blockHash]`|{[`Data`](#data)}|The block uniquely identified by this hash. The `blockNumber` and `blockHash` properties are mutually exclusive; exactly one of them must be set.| +|`requireCanonical`|{`boolean`}|(optional) Whether or not to throw an error if the block is not in the canonical chain as described below. Only allowed in conjunction with the `blockHash` tag. Defaults to `false`.| + +If the block is not found, the callee SHOULD raise a JSON-RPC error (the recommended error code is `-32001: Resource not found`. If the tag is `blockHash` and `requireCanonical` is `true`, the callee SHOULD additionally raise a JSON-RPC error if the block is not in the canonical chain (the recommended error code is `-32000: Invalid input` and in any case should be different than the error code for the block not found case so that the caller can distinguish the cases). The block-not-found check SHOULD take precedence over the block-is-canonical check, so that if the block is not found the callee raises block-not-found rather than block-not-canonical. + +##### `Data` + +- A `Data` value **MUST** be hex-encoded. +- A `Data` value **MUST** be "0x"-prefixed. +- A `Data` value **MUST** be expressed using two hex digits per byte. + +Examples `Data` values: + +|Value|Valid|Reason| +|-|-|-| +|0x|`valid`|interpreted as empty data| +|0x0|`invalid`|each byte must be represented using two hex digits| +|0x00|`valid`|interpreted as a single zero byte| +|0x41|`true`|interpreted as a data value of 65| +|0x004200|`true`|interpreted as a data value of 16896| +|0xf0f0f|`false`|bytes require two hex digits| +|004200|`false`|values must be prefixed| + +##### Proposing changes + +New Ethereum RPC methods and changes to existing methods **MUST** be proposed via the traditional EIP process. This allows for community consensus around new method implementations and proposed method modifications. RPC method proposals **MUST** reach "draft" status before being added to this proposal and the official Ethereum RPC specification defined herein. + +### Methods + +#### web3_clientVersion + +##### Description + +Returns the version of the current client + +##### Parameters + +_(none)_ + +##### Returns + +{`string`} - client version + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "web3_clientVersion", + "params": [], +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": "Mist/v0.9.3/darwin/go1.4.1" +} +``` +--- + +#### web3_sha3 + +##### Description + +Hashes data using the Keccak-256 algorithm + +##### Parameters + +|#|Type|Description| +|-|-|-| +|1|{[`Data`](#data)}|data to hash| + +##### Returns + +{[`Data`](#data)} - Keccak-256 hash of the given data + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "web3_sha3", + "params": ["0x68656c6c6f20776f726c64"] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": "0xc94770007dda54cF92009BFF0dE90c06F603a09f" +} +``` +--- + +#### net_listening + +##### Description + +Determines if this client is listening for new network connections + +##### Parameters + +_(none)_ + +##### Returns + +{`boolean`} - `true` if listening is active or `false` if listening is not active + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "net_listening", + "params": [] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": true +} +``` +--- + +#### net_peerCount + +##### Description + +Returns the number of peers currently connected to this client + +##### Parameters + +_(none)_ + +##### Returns + +{[`Quantity`](#quantity)} - number of connected peers + +##### Example +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "net_peerCount", + "params": [] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": "0x2" +} +``` +--- + +#### net_version + +##### Description + +Returns the chain ID associated with the current network + +##### Parameters + +_(none)_ + +##### Returns + +{`string`} - chain ID associated with the current network + +Common chain IDs: + +- `"1"` - Ethereum mainnet +- `"3"` - Ropsten testnet +- `"4"` - Rinkeby testnet +- `"42"` - Kovan testnet + +**Note:** See EIP-155 for a [complete list](./eip-155.md#list-of-chain-ids) of possible chain IDs. + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337 + "jsonrpc": "2.0", + "method": "net_version", + "params": [], +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": "3" +} +``` +--- + +#### eth_accounts + +##### Description + +Returns a list of addresses owned by this client + +##### Parameters + +_(none)_ + +##### Returns + +{[`Data[]`](#data)} - array of addresses + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "eth_accounts", + "params": [] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": ["0xc94770007dda54cF92009BFF0dE90c06F603a09f"] +} +``` +--- + +#### eth_blockNumber + +##### Description + +Returns the number of the most recent block seen by this client + +##### Parameters + +_(none)_ + +##### Returns + +{[`Quantity`](#quantity)} - number of the latest block + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "eth_blockNumber", + "params": [] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": "0xc94" +} +``` +--- + +#### eth_call + +##### Description + +Executes a new message call immediately without submitting a transaction to the network + +##### Parameters + +|#|Type|Description| +|-|-|-| +|1|{`object`}|@property {[`Data`](#data)} `[from]` - transaction sender
@property {[`Data`](#data)} `to` - transaction recipient or `null` if deploying a contract
@property {[`Quantity`](#quantity)} `[gas]` - gas provided for transaction execution
@property {[`Quantity`](#quantity)} `[gasPrice]` - price in wei of each gas used
@property {[`Quantity`](#quantity)} `[value]` - value in wei sent with this transaction
@property {[`Data`](#data)} `[data]` - contract code or a hashed method call with encoded args| +|2|{[`Quantity`](#quantity)\|`string`\|[`Block Identifier`](#block-identifier)}|block number, or one of `"latest"`, `"earliest"` or `"pending"`, or a block identifier as described in [`Block Identifier`](#block-identifier)| + +##### Returns + +{[`Data`](#data)} - return value of executed contract + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "eth_call", + "params": [{ + "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675", + "from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", + "gas": "0x76c0", + "gasPrice": "0x9184e72a000", + "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", + "value": "0x9184e72a" + }] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": "0x" +} +``` +--- + +#### eth_coinbase + +##### Description + +Returns the coinbase address for this client + +##### Parameters + +_(none)_ + +##### Returns + +{[`Data`](#data)} - coinbase address + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "eth_coinbase", + "params": [] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": "0xc94770007dda54cF92009BFF0dE90c06F603a09f" +} +``` +--- + +#### eth_estimateGas + +##### Description + +Estimates the gas necessary to complete a transaction without submitting it to the network + +**Note:** The resulting gas estimation may be significantly more than the amount of gas actually used by the transaction. This is due to a variety of reasons including EVM mechanics and node performance. + +##### Parameters + +|#|Type|Description| +|-|-|-| +|1|{`object`}|@property {[`Data`](#data)} `[from]` - transaction sender
@property {[`Data`](#data)} `[to]` - transaction recipient
@property {[`Quantity`](#quantity)} `[gas]` - gas provided for transaction execution
@property {[`Quantity`](#quantity)} `[gasPrice]` - price in wei of each gas used
@property {[`Quantity`](#quantity)} `[value]` - value in wei sent with this transaction
@property {[`Data`](#data)} `[data]` - contract code or a hashed method call with encoded args| +|2|{[`Quantity`](#quantity)\|`string`}|block number, or one of `"latest"`, `"earliest"` or `"pending"`| + +##### Returns + +{[`Quantity`](#quantity)} - amount of gas required by transaction + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "eth_estimateGas", + "params": [{ + "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675", + "from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", + "gas": "0x76c0", + "gasPrice": "0x9184e72a000", + "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", + "value": "0x9184e72a" + }] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": "0x5208" +} +``` +--- + +#### eth_gasPrice + +##### Description + +Returns the current price of gas expressed in wei + +##### Parameters + +_(none)_ + +##### Returns + +{[`Quantity`](#quantity)} - current gas price in wei + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "eth_gasPrice", + "params": [] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": "0x09184e72a000" +} +``` +--- + +#### eth_getBalance + +##### Description + +Returns the balance of an address in wei + +##### Parameters + +|#|Type|Description| +|-|-|-| +|1|{[`Data`](#data)}|address to query for balance| +|2|{[`Quantity`](#quantity)\|`string`\|[`Block Identifier`](#block-identifier)}|block number, or one of `"latest"`, `"earliest"` or `"pending"`, or a block identifier as described in [`Block Identifier`](#block-identifier)| + +##### Returns + +{[`Quantity`](#quantity)} - balance of the provided account in wei + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "eth_getBalance", + "params": ["0xc94770007dda54cF92009BFF0dE90c06F603a09f", "latest"] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": "0x0234c8a3397aab58" +} +``` +--- + +#### eth_getBlockByHash + +##### Description + +Returns information about a block specified by hash + +##### Parameters + +|#|Type|Description| +|-|-|-| +|1|{[`Data`](#data)}|hash of a block| +|2|{`boolean`}|`true` will pull full transaction objects, `false` will pull transaction hashes| + +##### Returns + +{`null|object`} - `null` if no block is found, otherwise a block object with the following members: + +- {[`Data`](#data)} `extraData` - "extra data" field of this block +- {[`Data`](#data)} `hash` - block hash or `null` if pending +- {[`Data`](#data)} `logsBloom` - logs bloom filter or `null` if pending +- {[`Data`](#data)} `miner` - address that received this block's mining rewards +- {[`Data`](#data)} `nonce` - proof-of-work hash or `null` if pending +- {[`Data`](#data)} `parentHash` - parent block hash +- {[`Data`](#data)} `receiptsRoot` -root of the this block's receipts trie +- {[`Data`](#data)} `sha3Uncles` - SHA3 of the uncles data in this block +- {[`Data`](#data)} `stateRoot` - root of this block's final state trie +- {[`Data`](#data)} `transactionsRoot` - root of this block's transaction trie +- {[`Quantity`](#quantity)} `difficulty` - difficulty for this block +- {[`Quantity`](#quantity)} `gasLimit` - maximum gas allowed in this block +- {[`Quantity`](#quantity)} `gasUsed` - total used gas by all transactions in this block +- {[`Quantity`](#quantity)} `number` - block number or `null` if pending +- {[`Quantity`](#quantity)} `size` - size of this block in bytes +- {[`Quantity`](#quantity)} `timestamp` - unix timestamp of when this block was collated +- {[`Quantity`](#quantity)} `totalDifficulty` - total difficulty of the chain until this block +- {`Array`} `transactions` - list of transaction objects or hashes +- {`Array`} `uncles` - list of uncle hashes + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "eth_getBlockByHash", + "params":["0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331", true] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": { + "difficulty": "0x027f07", + "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000", + "gasLimit": "0x9f759", + "gasUsed": "0x9f759", + "hash": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331", + "logsBloom": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331", + "miner": "0x4e65fda2159562a496f9f3522f89122a3088497a", + "nonce": "0xe04d296d2460cfb8472af2c5fd05b5a214109c25688d3704aed5484f9a7792f2", + "number": "0x1b4", + "parentHash": "0x9646252be9520f6e71339a8df9c55e4d7619deeb018d2a3f2d21fc165dde5eb5", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "size": "0x027f07", + "stateRoot": "0xd5855eb08b3387c0af375e9cdb6acfc05eb8f519e419b874b6ff2ffda7ed1dff", + "timestamp": "0x54e34e8e" + "totalDifficulty": "0x027f07", + "transactions": [] + "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncles": ["0x1606e5...", "0xd5145a9..."] + } +} +``` +--- + +#### eth_getBlockByNumber + +##### Description + +Returns information about a block specified by number + +##### Parameters + +|#|Type|Description| +|-|-|-| +|1|{[`Quantity`](#quantity)\|`string`}|block number, or one of `"latest"`, `"earliest"` or `"pending"`| +|2|{`boolean`}|`true` will pull full transaction objects, `false` will pull transaction hashes| + +##### Returns + +{`null|object`} - `null` if no block is found, otherwise a block object with the following members: + +- {[`Data`](#data)} `extraData` - "extra data" field of this block +- {[`Data`](#data)} `hash` - block hash or `null` if pending +- {[`Data`](#data)} `logsBloom` - logs bloom filter or `null` if pending +- {[`Data`](#data)} `miner` - address that received this block's mining rewards +- {[`Data`](#data)} `nonce` - proof-of-work hash or `null` if pending +- {[`Data`](#data)} `parentHash` - parent block hash +- {[`Data`](#data)} `receiptsRoot` -root of the this block's receipts trie +- {[`Data`](#data)} `sha3Uncles` - SHA3 of the uncles data in this block +- {[`Data`](#data)} `stateRoot` - root of this block's final state trie +- {[`Data`](#data)} `transactionsRoot` - root of this block's transaction trie +- {[`Quantity`](#quantity)} `difficulty` - difficulty for this block +- {[`Quantity`](#quantity)} `gasLimit` - maximum gas allowed in this block +- {[`Quantity`](#quantity)} `gasUsed` - total used gas by all transactions in this block +- {[`Quantity`](#quantity)} `number` - block number or `null` if pending +- {[`Quantity`](#quantity)} `size` - size of this block in bytes +- {[`Quantity`](#quantity)} `timestamp` - unix timestamp of when this block was collated +- {[`Quantity`](#quantity)} `totalDifficulty` - total difficulty of the chain until this block +- {`Array`} `transactions` - list of transaction objects or hashes +- {`Array`} `uncles` - list of uncle hashes + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "eth_getBlockByNumber", + "params":["0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331", true] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": { + "difficulty": "0x027f07", + "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000", + "gasLimit": "0x9f759", + "gasUsed": "0x9f759", + "hash": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331", + "logsBloom": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331", + "miner": "0x4e65fda2159562a496f9f3522f89122a3088497a", + "nonce": "0xe04d296d2460cfb8472af2c5fd05b5a214109c25688d3704aed5484f9a7792f2", + "number": "0x1b4", + "parentHash": "0x9646252be9520f6e71339a8df9c55e4d7619deeb018d2a3f2d21fc165dde5eb5", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "size": "0x027f07", + "stateRoot": "0xd5855eb08b3387c0af375e9cdb6acfc05eb8f519e419b874b6ff2ffda7ed1dff", + "timestamp": "0x54e34e8e" + "totalDifficulty": "0x027f07", + "transactions": [] + "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncles": ["0x1606e5...", "0xd5145a9..."] + } +} +``` +--- + +#### eth_getBlockTransactionCountByHash + +##### Description + +Returns the number of transactions in a block specified by block hash + +##### Parameters + +|#|Type|Description| +|-|-|-| +|1|{[`Data`](#data)}|hash of a block| + +##### Returns + +{[`Quantity`](#quantity)} - number of transactions in the specified block + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "eth_getBlockTransactionCountByHash", + "params": ["0xc94770007dda54cF92009BFF0dE90c06F603a09f"] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": "0xc" +} +``` +--- + +#### eth_getBlockTransactionCountByNumber + +##### Description + +Returns the number of transactions in a block specified by block number + +##### Parameters + +|#|Type|Description| +|-|-|-| +|1|{[`Quantity`](#quantity)\|`string`}|block number, or one of `"latest"`, `"earliest"` or `"pending"`| + +##### Returns + +{[`Quantity`](#quantity)} - number of transactions in the specified block + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "eth_getBlockTransactionCountByNumber", + "params": ["0xe8"] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": "0xa" +} +``` +--- + +#### eth_getCode + +##### Description + +Returns the contract code stored at a given address + +##### Parameters + +|#|Type|Description| +|-|-|-| +|1|{[`Data`](#data)}|address to query for code| +|2|{[`Quantity`](#quantity)\|`string`\|[`Block Identifier`](#block-identifier)}|block number, or one of `"latest"`, `"earliest"` or `"pending"`, or a block identifier as described in [`Block Identifier`](#block-identifier)| + +##### Returns + +{[`Data`](#data)} - code from the specified address + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "eth_getCode", + "params": ["0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", "0x2"] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": "0x600160008035811a818181146012578301005b601b6001356025565b8060005260206000f25b600060078202905091905056" +} +``` +--- + +#### eth_getFilterChanges + +##### Description + +Returns a list of all logs based on filter ID since the last log retrieval + +##### Parameters + +|#|Type|Description| +|-|-|-| +|1|{[`Quantity`](#quantity)}|ID of the filter| + +##### Returns + +{`Array`} - array of log objects with the following members: + +- {[`Data`](#data)} `address` - address from which this log originated +- {[`Data`](#data)} `blockHash` - hash of block containing this log or `null` if pending +- {[`Data`](#data)} `data` - contains the non-indexed arguments of the log +- {[`Data`](#data)} `transactionHash` - hash of the transaction that created this log or `null` if pending +- {[`Quantity`](#quantity)} `blockNumber` - number of block containing this log or `null` if pending +- {[`Quantity`](#quantity)} `logIndex` - index of this log within its block or `null` if pending +- {[`Quantity`](#quantity)} `transactionIndex` - index of the transaction that created this log or `null` if pending +- {[`Data[]`](#data)} `topics` - list of order-dependent topics +- {`boolean`} `removed` - `true` if this filter has been destroyed and is invalid + +**Note:** The return value of `eth_getFilterChanges` when retrieving logs from `eth_newBlockFilter` and `eth_newPendingTransactionFilter` filters will be an array of hashes, not an array of Log objects. + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "eth_getFilterChanges", + "params": ["0x16"] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": [{ + "address": "0x16c5785ac562ff41e2dcfdf829c5a142f1fccd7d", + "blockHash": "0x8216c5785ac562ff41e2dcfdf5785ac562ff41e2dcfdf829c5a142f1fccd7d", + "blockNumber":"0x1b4", + "data":"0x0000000000000000000000000000000000000000000000000000000000000000", + "logIndex": "0x1", + "topics": [], + "transactionHash": "0xdf829c5a142f1fccd7d8216c5785ac562ff41e2dcfdf5785ac562ff41e2dcf", + "transactionIndex": "0x0" + }] +} +``` +--- + +#### eth_getFilterLogs + +##### Description + +Returns a list of all logs based on filter ID + +##### Parameters + +|#|Type|Description| +|-|-|-| +|1|{[`Quantity`](#quantity)}|ID of the filter| + +##### Returns + +{`Array`} - array of log objects with the following members: + +- {[`Data`](#data)} address - address from which this log originated +- {[`Data`](#data)} blockHash - hash of block containing this log or `null` if pending +- {[`Data`](#data)} data - contains the non-indexed arguments of the log +- {[`Data`](#data)} transactionHash - hash of the transaction that created this log or `null` if pending +- {[`Quantity`](#quantity)} blockNumber - number of block containing this log or `null` if pending +- {[`Quantity`](#quantity)} logIndex - index of this log within its block or `null` if pending +- {[`Quantity`](#quantity)} transactionIndex - index of the transaction that created this log or `null` if pending +- {`Array`} topics - list of order-dependent topics +- {`boolean`} removed - `true` if this filter has been destroyed and is invalid + +**Note:** The return value of `eth_getFilterLogs` when retrieving logs from `eth_newBlockFilter` and `eth_newPendingTransactionFilter` filters will be an array of hashes, not an array of Log objects. + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "eth_getFilterLogs", + "params": ["0x16"] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": [{ + "address": "0x16c5785ac562ff41e2dcfdf829c5a142f1fccd7d", + "blockHash": "0x8216c5785ac562ff41e2dcfdf5785ac562ff41e2dcfdf829c5a142f1fccd7d", + "blockNumber":"0x1b4", + "data":"0x0000000000000000000000000000000000000000000000000000000000000000", + "logIndex": "0x1", + "topics": [], + "transactionHash": "0xdf829c5a142f1fccd7d8216c5785ac562ff41e2dcfdf5785ac562ff41e2dcf", + "transactionIndex": "0x0" + }] +} +``` +--- + +#### eth_getLogs + +##### Description + +Returns a list of all logs based on a filter object + +##### Parameters + +|#|Type|Description| +|-|-|-| +|1|{`object`}|@property {[`Quantity`](#quantity)\|`string`} `[fromBlock]` - block number, or one of `"latest"`, `"earliest"` or `"pending"`
@property {[`Quantity`](#quantity)\|`string`} `[toBlock]` - block number, or one of `"latest"`, `"earliest"` or `"pending"`
@property {[`Data`](#data)\|[`Data[]`](#data)} `[address]` - contract address or a list of addresses from which logs should originate
@property {[`Data[]`](#data)} `[topics]` - list of order-dependent topics
@property {[`Data`](#data)} `[blockhash]` - restrict logs to a block by hash| + +**Note:** If `blockhash` is passed, neither `fromBlock` nor `toBlock` are allowed or respected. + +##### Returns + +{`Array`} - array of log objects with the following members: + +- {[`Data`](#data)} `address` - address from which this log originated +- {[`Data`](#data)} `blockHash` - hash of block containing this log or `null` if pending +- {[`Data`](#data)} `data` - contains the non-indexed arguments of the log +- {[`Data`](#data)} `transactionHash` - hash of the transaction that created this log or `null` if pending +- {[`Quantity`](#quantity)} `blockNumber` - number of block containing this log or `null` if pending +- {[`Quantity`](#quantity)} `logIndex` - index of this log within its block or `null` if pending +- {[`Quantity`](#quantity)} `transactionIndex` - index of the transaction that created this log or `null` if pending +- {[`Data`](#data)} `topics` - list of order-dependent topics +- {`boolean`} `removed` - `true` if this filter has been destroyed and is invalid + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "eth_getLogs", + "params": [{ + "topics":["0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b"] + }] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": [{ + "address": "0x16c5785ac562ff41e2dcfdf829c5a142f1fccd7d", + "blockHash": "0x8216c5785ac562ff41e2dcfdf5785ac562ff41e2dcfdf829c5a142f1fccd7d", + "blockNumber":"0x1b4", + "data":"0x0000000000000000000000000000000000000000000000000000000000000000", + "logIndex": "0x1", + "topics": [], + "transactionHash": "0xdf829c5a142f1fccd7d8216c5785ac562ff41e2dcfdf5785ac562ff41e2dcf", + "transactionIndex": "0x0" + }] +} +``` +--- + +#### eth_getStorageAt + +##### Description + +Returns the value from a storage position at an address + +##### Parameters + +|#|Type|Description| +|-|-|-| +|1|{[`Data`](#data)}|address of stored data| +|2|{[`Quantity`](#quantity)}|index into stored data| +|3|{[`Quantity`](#quantity)\|`string`\|[`Block Identifier`](#block-identifier)}|block number, or one of `"latest"`, `"earliest"` or `"pending"`, or a block identifier as described in [`Block Identifier`](#block-identifier)| + +##### Returns + +{[`Data`](#data)} - value stored at the given address and data index + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "eth_getStorageAt", + "params": ["0x295a70b2de5e3953354a6a8344e616ed314d7251", "0x0", "latest"] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": "0x00000000000000000000000000000000000000000000000000000000000004d2" +} +``` +--- + +#### eth_getTransactionByBlockHashAndIndex + +##### Description + +Returns information about a transaction specified by block hash and transaction index + +##### Parameters + +|#|Type|Description| +|-|-|-| +|1|{[`Data`](#data)}|hash of a block| +|2|{[`Quantity`](#quantity)}|index of a transaction in the specified block| + +##### Returns + +{`null|object`} - `null` if no transaction is found, otherwise a transaction object with the following members: + +- {[`Data`](#data)} `r` - ECDSA signature r +- {[`Data`](#data)} `s` - ECDSA signature s +- {[`Data`](#data)} `blockHash` - hash of block containing this transaction or `null` if pending +- {[`Data`](#data)} `from` - transaction sender +- {[`Data`](#data)} `hash` - hash of this transaction +- {[`Data`](#data)} `input` - contract code or a hashed method call +- {[`Data`](#data)} `to` - transaction recipient or `null` if deploying a contract +- {[`Quantity`](#quantity)} `v` - ECDSA recovery ID +- {[`Quantity`](#quantity)} `blockNumber` - number of block containing this transaction or `null` if pending +- {[`Quantity`](#quantity)} `gas` - gas provided for transaction execution +- {[`Quantity`](#quantity)} `gasPrice` - price in wei of each gas used +- {[`Quantity`](#quantity)} `nonce` - unique number identifying this transaction +- {[`Quantity`](#quantity)} `transactionIndex` - index of this transaction in the block or `null` if pending +- {[`Quantity`](#quantity)} `value` - value in wei sent with this transaction + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "eth_getTransactionByBlockHashAndIndex", + "params":["0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331", "0x0"] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": { + "blockHash": "0x1d59ff54b1eb26b013ce3cb5fc9dab3705b415a67127a003c3e61eb445bb8df2", + "blockNumber": "0x5daf3b", + "from": "0xa7d9ddbe1f17865597fbd27ec712455208b6b76d", + "gas": "0xc350", + "gasPrice": "0x4a817c800", + "hash": "0x88df016429689c079f3b2f6ad39fa052532c56795b733da78a91ebe6a713944b", + "input": "0x68656c6c6f21", + "nonce": "0x15", + "r": "0x1b5e176d927f8e9ab405058b2d2457392da3e20f328b16ddabcebc33eaac5fea", + "s": "0x4ba69724e8f69de52f0125ad8b3c5c2cef33019bac3249e2c0a2192766d1721c", + "to": "0xf02c1c8e6114b1dbe8937a39260b5b0a374432bb", + "transactionIndex": "0x41", + "v": "0x25", + "value": "0xf3dbb76162000" + } +} +``` +--- + +#### eth_getTransactionByBlockNumberAndIndex + +##### Description + +Returns information about a transaction specified by block number and transaction index + +##### Parameters + +|#|Type|Description| +|-|-|-| +|1|{[`Quantity`](#quantity)\|`string`}|block number, or one of `"latest"`, `"earliest"` or `"pending"`| +|2|{[`Quantity`](#quantity)}|index of a transaction in the specified block| + +##### Returns + +{`null|object`} - `null` if no transaction is found, otherwise a transaction object with the following members: + +- {[`Data`](#data)} `r` - ECDSA signature r +- {[`Data`](#data)} `s` - ECDSA signature s +- {[`Data`](#data)} `blockHash` - hash of block containing this transaction or `null` if pending +- {[`Data`](#data)} `from` - transaction sender +- {[`Data`](#data)} `hash` - hash of this transaction +- {[`Data`](#data)} `input` - contract code or a hashed method call +- {[`Data`](#data)} `to` - transaction recipient or `null` if deploying a contract +- {[`Quantity`](#quantity)} `v` - ECDSA recovery ID +- {[`Quantity`](#quantity)} `blockNumber` - number of block containing this transaction or `null` if pending +- {[`Quantity`](#quantity)} `gas` - gas provided for transaction execution +- {[`Quantity`](#quantity)} `gasPrice` - price in wei of each gas used +- {[`Quantity`](#quantity)} `nonce` - unique number identifying this transaction +- {[`Quantity`](#quantity)} `transactionIndex` - index of this transaction in the block or `null` if pending +- {[`Quantity`](#quantity)} `value` - value in wei sent with this transaction + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "eth_getTransactionByBlockNumberAndIndex", + "params":["0x29c", "0x0"] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": { + "blockHash": "0x1d59ff54b1eb26b013ce3cb5fc9dab3705b415a67127a003c3e61eb445bb8df2", + "blockNumber": "0x5daf3b", + "from": "0xa7d9ddbe1f17865597fbd27ec712455208b6b76d", + "gas": "0xc350", + "gasPrice": "0x4a817c800", + "hash": "0x88df016429689c079f3b2f6ad39fa052532c56795b733da78a91ebe6a713944b", + "input": "0x68656c6c6f21", + "nonce": "0x15", + "r": "0x1b5e176d927f8e9ab405058b2d2457392da3e20f328b16ddabcebc33eaac5fea", + "s": "0x4ba69724e8f69de52f0125ad8b3c5c2cef33019bac3249e2c0a2192766d1721c", + "to": "0xf02c1c8e6114b1dbe8937a39260b5b0a374432bb", + "transactionIndex": "0x41", + "v": "0x25", + "value": "0xf3dbb76162000" + } +} +``` +--- + +#### eth_getTransactionByHash + +##### Description + +Returns information about a transaction specified by hash + +##### Parameters + +|#|Type|Description| +|-|-|-| +|1|{[`Data`](#data)}|hash of a transaction| + +##### Returns + +{`null|object`} - `null` if no transaction is found, otherwise a transaction object with the following members: + +- {[`Data`](#data)} `r` - ECDSA signature r +- {[`Data`](#data)} `s` - ECDSA signature s +- {[`Data`](#data)} `blockHash` - hash of block containing this transaction or `null` if pending +- {[`Data`](#data)} `from` - transaction sender +- {[`Data`](#data)} `hash` - hash of this transaction +- {[`Data`](#data)} `input` - contract code or a hashed method call +- {[`Data`](#data)} `to` - transaction recipient or `null` if deploying a contract +- {[`Quantity`](#quantity)} `v` - ECDSA recovery ID +- {[`Quantity`](#quantity)} `blockNumber` - number of block containing this transaction or `null` if pending +- {[`Quantity`](#quantity)} `gas` - gas provided for transaction execution +- {[`Quantity`](#quantity)} `gasPrice` - price in wei of each gas used +- {[`Quantity`](#quantity)} `nonce` - unique number identifying this transaction +- {[`Quantity`](#quantity)} `transactionIndex` - index of this transaction in the block or `null` if pending +- {[`Quantity`](#quantity)} `value` - value in wei sent with this transaction + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "eth_getTransactionByHash", + "params": ["0x88df016429689c079f3b2f6ad39fa052532c56795b733da78a91ebe6a713944b"] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": { + "blockHash": "0x1d59ff54b1eb26b013ce3cb5fc9dab3705b415a67127a003c3e61eb445bb8df2", + "blockNumber": "0x5daf3b", + "from": "0xa7d9ddbe1f17865597fbd27ec712455208b6b76d", + "gas": "0xc350", + "gasPrice": "0x4a817c800", + "hash": "0x88df016429689c079f3b2f6ad39fa052532c56795b733da78a91ebe6a713944b", + "input": "0x68656c6c6f21", + "nonce": "0x15", + "r": "0x1b5e176d927f8e9ab405058b2d2457392da3e20f328b16ddabcebc33eaac5fea", + "s": "0x4ba69724e8f69de52f0125ad8b3c5c2cef33019bac3249e2c0a2192766d1721c", + "to": "0xf02c1c8e6114b1dbe8937a39260b5b0a374432bb", + "transactionIndex": "0x41", + "v": "0x25", + "value": "0xf3dbb76162000" + } +} +``` +--- + +#### eth_getTransactionCount + +##### Description + +Returns the number of transactions sent from an address + +##### Parameters + +|#|Type|Description| +|-|-|-| +|1|{[`Data`](#data)}|address to query for sent transactions| +|2|{[`Quantity`](#quantity)\|`string`\|[`Block Identifier`](#block-identifier)}|block number, or one of `"latest"`, `"earliest"` or `"pending"`, or a block identifier as described in [`Block Identifier`](#block-identifier)| + +##### Returns + +{[`Quantity`](#quantity)} - number of transactions sent from the specified address + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "eth_getTransactionCount", + "params": ["0xc94770007dda54cF92009BFF0dE90c06F603a09f", "latest"] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": "0x1" +} +``` +--- + +#### eth_getTransactionReceipt + +##### Description + +Returns the receipt of a transaction specified by hash + +**Note:** Transaction receipts are unavailable for pending transactions. + +##### Parameters + +|#|Type|Description| +|-|-|-| +|1|{[`Data`](#data)}|hash of a transaction| + +##### Returns + +{`null|object`} - `null` if no transaction is found, otherwise a transaction receipt object with the following members: + +- {[`Data`](#data)} `blockHash` - hash of block containing this transaction +- {[`Data`](#data)} `contractAddress` - address of new contract or `null` if no contract was created +- {[`Data`](#data)} `from` - transaction sender +- {[`Data`](#data)} `logsBloom` - logs bloom filter +- {[`Data`](#data)} `to` - transaction recipient or `null` if deploying a contract +- {[`Data`](#data)} `transactionHash` - hash of this transaction +- {[`Quantity`](#quantity)} `blockNumber` - number of block containing this transaction +- {[`Quantity`](#quantity)} `cumulativeGasUsed` - gas used by this and all preceding transactions in this block +- {[`Quantity`](#quantity)} `gasUsed` - gas used by this transaction +- {[`Quantity`](#quantity)} `status` - `1` if this transaction was successful or `0` if it failed +- {[`Quantity`](#quantity)} `transactionIndex` - index of this transaction in the block +- {`Array`} `logs` - list of log objects generated by this transaction + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "eth_getTransactionReceipt", + "params": ["0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238"] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": { + "blockHash": '0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b', + "blockNumber": '0xb', + "contractAddress": '0xb60e8dd61c5d32be8058bb8eb970870f07233155', + "cumulativeGasUsed": '0x33bc', + "gasUsed": '0x4dc', + "logs": [], + "logsBloom": "0x00...0", + "status": "0x1", + "transactionHash": '0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238', + "transactionIndex": '0x1' + } +} +``` +--- + +#### eth_getUncleByBlockHashAndIndex + +##### Description + +Returns information about an uncle specified by block hash and uncle index position + +##### Parameters + +|#|Type|Description| +|-|-|-| +|1|{[`Data`](#data)}|hash of a block| +|2|{[`Quantity`](#quantity)}|index of uncle| + +##### Returns + +{`null|object`} - `null` if no block or uncle is found, otherwise an uncle object with the following members: + +- {[`Data`](#data)} `extraData` - "extra data" field of this block +- {[`Data`](#data)} `hash` - block hash or `null` if pending +- {[`Data`](#data)} `logsBloom` - logs bloom filter or `null` if pending +- {[`Data`](#data)} `miner` - address that received this block's mining rewards +- {[`Data`](#data)} `nonce` - proof-of-work hash or `null` if pending +- {[`Data`](#data)} `parentHash` - parent block hash +- {[`Data`](#data)} `receiptsRoot` -root of the this block's receipts trie +- {[`Data`](#data)} `sha3Uncles` - SHA3 of the uncles data in this block +- {[`Data`](#data)} `stateRoot` - root of this block's final state trie +- {[`Data`](#data)} `transactionsRoot` - root of this block's transaction trie +- {[`Quantity`](#quantity)} `difficulty` - difficulty for this block +- {[`Quantity`](#quantity)} `gasLimit` - maximum gas allowed in this block +- {[`Quantity`](#quantity)} `gasUsed` - total used gas by all transactions in this block +- {[`Quantity`](#quantity)} `number` - block number or `null` if pending +- {[`Quantity`](#quantity)} `size` - size of this block in bytes +- {[`Quantity`](#quantity)} `timestamp` - unix timestamp of when this block was collated +- {[`Quantity`](#quantity)} `totalDifficulty` - total difficulty of the chain until this block +- {`Array`} `uncles` - list of uncle hashes + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "eth_getUncleByBlockHashAndIndex", + "params": ["0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b", "0x0"] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": { + "difficulty": "0x027f07", + "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000", + "gasLimit": "0x9f759", + "gasUsed": "0x9f759", + "hash": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331", + "logsBloom": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331", + "miner": "0x4e65fda2159562a496f9f3522f89122a3088497a", + "nonce": "0xe04d296d2460cfb8472af2c5fd05b5a214109c25688d3704aed5484f9a7792f2", + "number": "0x1b4", + "parentHash": "0x9646252be9520f6e71339a8df9c55e4d7619deeb018d2a3f2d21fc165dde5eb5", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "size": "0x027f07", + "stateRoot": "0xd5855eb08b3387c0af375e9cdb6acfc05eb8f519e419b874b6ff2ffda7ed1dff", + "timestamp": "0x54e34e8e" + "totalDifficulty": "0x027f07", + "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncles": [] + } +} +``` +--- + +#### eth_getUncleByBlockNumberAndIndex + +##### Description + +Returns information about an uncle specified by block number and uncle index position + +##### Parameters + +|#|Type|Description| +|-|-|-| +|1|{[`Quantity`](#quantity)\|`string`}|block number, or one of `"latest"`, `"earliest"` or `"pending"`| +|2|{[`Quantity`](#quantity)}|index of uncle| + +##### Returns + +{`null|object`} - `null` if no block or uncle is found, otherwise an uncle object with the following members: + +- {[`Data`](#data)} `extraData` - "extra data" field of this block +- {[`Data`](#data)} `hash` - block hash or `null` if pending +- {[`Data`](#data)} `logsBloom` - logs bloom filter or `null` if pending +- {[`Data`](#data)} `miner` - address that received this block's mining rewards +- {[`Data`](#data)} `nonce` - proof-of-work hash or `null` if pending +- {[`Data`](#data)} `parentHash` - parent block hash +- {[`Data`](#data)} `receiptsRoot` -root of the this block's receipts trie +- {[`Data`](#data)} `sha3Uncles` - SHA3 of the uncles data in this block +- {[`Data`](#data)} `stateRoot` - root of this block's final state trie +- {[`Data`](#data)} `transactionsRoot` - root of this block's transaction trie +- {[`Quantity`](#quantity)} `difficulty` - difficulty for this block +- {[`Quantity`](#quantity)} `gasLimit` - maximum gas allowed in this block +- {[`Quantity`](#quantity)} `gasUsed` - total used gas by all transactions in this block +- {[`Quantity`](#quantity)} `number` - block number or `null` if pending +- {[`Quantity`](#quantity)} `size` - size of this block in bytes +- {[`Quantity`](#quantity)} `timestamp` - unix timestamp of when this block was collated +- {[`Quantity`](#quantity)} `totalDifficulty` - total difficulty of the chain until this block +- {`Array`} `uncles` - list of uncle hashes + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "eth_getUncleByBlockNumberAndIndex", + "params": ["0x29c", "0x0"] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": { + "difficulty": "0x027f07", + "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000", + "gasLimit": "0x9f759", + "gasUsed": "0x9f759", + "hash": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331", + "logsBloom": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331", + "miner": "0x4e65fda2159562a496f9f3522f89122a3088497a", + "nonce": "0xe04d296d2460cfb8472af2c5fd05b5a214109c25688d3704aed5484f9a7792f2", + "number": "0x1b4", + "parentHash": "0x9646252be9520f6e71339a8df9c55e4d7619deeb018d2a3f2d21fc165dde5eb5", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "size": "0x027f07", + "stateRoot": "0xd5855eb08b3387c0af375e9cdb6acfc05eb8f519e419b874b6ff2ffda7ed1dff", + "timestamp": "0x54e34e8e" + "totalDifficulty": "0x027f07", + "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncles": [] + } +} +``` +--- + +#### eth_getUncleCountByBlockHash + +##### Description + +Returns the number of uncles in a block specified by block hash + +##### Parameters + +|#|Type|Description| +|-|-|-| +|1|{[`Data`](#data)}|hash of a block| + +##### Returns + +{[`Quantity`](#quantity)} - number of uncles in the specified block + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "eth_getUncleCountByBlockHash", + "params": ["0xc94770007dda54cF92009BFF0dE90c06F603a09f"] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": "0xc" +} +``` +--- + +#### eth_getUncleCountByBlockNumber + +##### Description + +Returns the number of uncles in a block specified by block number + +##### Parameters + +|#|Type|Description| +|-|-|-| +|1|{[`Quantity`](#quantity)\|`string`}|block number, or one of `"latest"`, `"earliest"` or `"pending"`| + +##### Returns + +{[`Quantity`](#quantity)} - number of uncles in the specified block + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "eth_getUncleCountByBlockNumber", + "params": ["0xe8"] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": "0x1" +} +``` +--- + +#### eth_getWork + +##### Description + +Returns a list containing relevant information for proof-of-work + +##### Parameters + +_none_ + +##### Returns + +{[`Data[]`](#data)} - array with the following items: + +1. {[`Data`](#data)} - current block header pow-hash +1. {[`Data`](#data)} - seed hash used for the DAG +1. {[`Data`](#data)} - boundary condition ("target"), 2^256 / difficulty + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "eth_getWork", + "params": [] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": [ + "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + "0x5EED00000000000000000000000000005EED0000000000000000000000000000", + "0xd1ff1c01710000000000000000000000d1ff1c01710000000000000000000000" + ] +} +``` +--- + +#### eth_hashrate + +##### Description + +Returns the number of hashes-per-second this node is mining at + +##### Parameters + +_(none)_ + +##### Returns + +{[`Quantity`](#quantity)} - number of hashes-per-second + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "eth_hashrate", + "params": [] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": "0x38a" +} +``` +--- + +#### eth_mining + +##### Description + +Determines if this client is mining new blocks + +##### Parameters + +_(none)_ + +##### Returns + +{`boolean`} - `true` if this client is mining or `false` if it is not mining + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "eth_mining", + "params": [] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": true +} +``` +--- + +#### eth_newBlockFilter + +##### Description + +Creates a filter to listen for new blocks that can be used with `eth_getFilterChanges` + +##### Parameters + +_none_ + +##### Returns + +{[`Quantity`](#quantity)} - ID of the newly-created filter that can be used with `eth_getFilterChanges` + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337 + "jsonrpc": "2.0", + "method": "eth_newBlockFilter", + "params": [] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": "0x1" +} +``` +--- + +#### eth_newFilter + +##### Description + +Creates a filter to listen for specific state changes that can then be used with `eth_getFilterChanges` + +##### Parameters + +|#|Type|Description| +|-|-|-| +|1|{`object`}|@property {[`Quantity`](#quantity)\|`string`} `[fromBlock]` - block number, or one of `"latest"`, `"earliest"` or `"pending"`
@property {[`Quantity`](#quantity)\|`string`} `[toBlock]` - block number, or one of `"latest"`, `"earliest"` or `"pending"`
@property {[`Data`](#data)\|[`Data[]`](#data)} `[address]` - contract address or a list of addresses from which logs should originate
@property {[`Data[]`](#data)} `[topics]` - list of order-dependent topics| + +**Note:** Topics are order-dependent. A transaction with a log with topics `[A, B]` will be matched by the following topic filters: +- `[]` - "anything" +- `[A]` - "A in first position (and anything after)" +- `[null, B]` - "anything in first position AND B in second position (and anything after)" +- `[A, B]` - "A in first position AND B in second position (and anything after)" +- `[[A, B], [A, B]]` - "(A OR B) in first position AND (A OR B) in second position (and anything after)" + +##### Returns + +{[`Quantity`](#quantity)} - ID of the newly-created filter that can be used with `eth_getFilterChanges` + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337 + "jsonrpc": "2.0", + "method": "eth_newFilter", + "params": [{ + "topics": ["0x0000000000000000000000000000000000000000000000000000000012341234"] + }] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": "0x1" +} +``` +--- + +#### eth_newPendingTransactionFilter + +##### Description + +Creates a filter to listen for new pending transactions that can be used with `eth_getFilterChanges` + +##### Parameters + +_none_ + +##### Returns + +{[`Quantity`](#quantity)} - ID of the newly-created filter that can be used with `eth_getFilterChanges` + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337 + "jsonrpc": "2.0", + "method": "eth_newPendingTransactionFilter", + "params": [] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": "0x1" +} +``` +--- + +#### eth_protocolVersion + +##### Description + +Returns the current Ethereum protocol version + +##### Parameters + +_(none)_ + +##### Returns + +{`string`} - current Ethereum protocol version + +##### Example +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "eth_protocolVersion", + "params": [] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": "54" +} +``` +--- + +#### eth_sendRawTransaction + +##### Description + +Sends and already-signed transaction to the network + +##### Parameters + +|#|Type|Description| +|-|-|-| +|1|{[`Data`](#data)}|signed transaction data| + +##### Returns + +{[`Data`](#data)} - transaction hash, or the zero hash if the transaction is not yet available + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "eth_sendRawTransaction", + "params": ["0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331" +} +``` +--- + +#### eth_sendTransaction + +##### Description + +Creates, signs, and sends a new transaction to the network + +##### Parameters + +|#|Type|Description| +|-|-|-| +|1|{`object`}|@property {[`Data`](#data)} `from` - transaction sender
@property {[`Data`](#data)} `[to]` - transaction recipient
@property {[`Quantity`](#quantity)} `[gas="0x15f90"]` - gas provided for transaction execution
@property {[`Quantity`](#quantity)} `[gasPrice]` - price in wei of each gas used
@property {[`Quantity`](#quantity)} `[value]` - value in wei sent with this transaction
@property {[`Data`](#data)} `[data]` - contract code or a hashed method call with encoded args
@property {[`Quantity`](#quantity)} `[nonce]` - unique number identifying this transaction| + +##### Returns + +{[`Data`](#data)} - transaction hash, or the zero hash if the transaction is not yet available + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "eth_sendTransaction", + "params": [{ + "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675", + "from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", + "gas": "0x76c0", + "gasPrice": "0x9184e72a000", + "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", + "value": "0x9184e72a" + }] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331" +} +``` +--- + +#### eth_sign + +##### Description + +Calculates an Ethereum-specific signature in the form of `keccak256("\x19Ethereum Signed Message:\n" + len(message) + message))` + +##### Parameters + +|#|Type|Description| +|-|-|-| +|1|{[`Data`](#data)}|address to use for signing| +|2|{[`Data`](#data)}|data to sign| + +##### Returns + +{[`Data`](#data)} - signature hash of the provided data + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "eth_sign", + "params": ["0x9b2055d370f73ec7d8a03e965129118dc8f5bf83", "0xdeadbeaf"] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": "0xa3f20717a250c2b0b729b7e5becbff67fdaef7e0699da4de7ca5895b02a170a12d887fd3b17bfdce3481f10bea41f45ba9f709d39ce8325427b57afcfc994cee1b" +} +``` +--- + +#### eth_signTransaction + +##### Description + +Signs a transaction that can be submitted to the network at a later time using with `eth_sendRawTransaction` + +##### Parameters + +|#|Type|Description| +|-|-|-| +|1|{`object`}|@property {[`Data`](#data)} `from` - transaction sender
@property {[`Data`](#data)} `[to]` - transaction recipient
@property {[`Quantity`](#quantity)} `[gas="0x15f90"]` - gas provided for transaction execution
@property {[`Quantity`](#quantity)} `[gasPrice]` - price in wei of each gas used
@property {[`Quantity`](#quantity)} `[value]` - value in wei sent with this transaction
@property {[`Data`](#data)} `[data]` - contract code or a hashed method call with encoded args
@property {[`Quantity`](#quantity)} `[nonce]` - unique number identifying this transaction| + +##### Returns + +{[`Data`](#data)} - signature hash of the transaction object + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "eth_signTransaction", + "params": [{ + "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675", + "from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", + "gas": "0x76c0", + "gasPrice": "0x9184e72a000", + "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", + "value": "0x9184e72a" + }] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": "0xa3f20717a250c2b0b729b7e5becbff67fdaef7e0699da4de7ca5895b02a170a12d887fd3b17bfdce3481f10bea41f45ba9f709d39ce8325427b57afcfc994cee1b" +} +``` +--- + +#### eth_signTypedData + +##### Description + +Calculates an Ethereum-specific signature in the form of `keccak256("\x19Ethereum Signed Message:\n" + len(message) + message))` + +##### Parameters + +|#|Type|Description| +|-|-|-| +|1|{[`Data`](#data)}|address to use for signing| +|2|{[`Data`](#data)}|message to sign containing type information, a domain separator, and data| + +**Note:** Client developers should refer to EIP-712 for complete semantics around [encoding and signing data](./eip-712.md#specification). Dapp developers should refer to EIP-712 for the expected structure of [RPC method input parameters](./eip-712.md#parameters). + +##### Returns + +{[`Data`](#data)} - signature hash of the provided message + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337 + "jsonrpc": "2.0", + "method": "eth_signTypedData", + "params": ["0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", { + "types": { + "EIP712Domain": [{ + "name": "name", + "type": "string" + }, { + "name": "version", + "type": "string" + }, { + "name": "chainId", + "type": "uint256" + }, { + "name": "verifyingContract", + "type": "address" + }], + "Person": [{ + "name": "name", + "type": "string" + }, { + "name": "wallet", + "type": "address" + }], + "Mail": [{ + "name": "from", + "type": "Person" + }, { + "name": "to", + "type": "Person" + }, { + "name": "contents", + "type": "string" + }] + }, + "primaryType": "Mail", + "domain": { + "name": "Ether Mail", + "version": "1", + "chainId": 1, + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" + }, + "message": { + "from": { + "name": "Cow", + "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" + }, + "to": { + "name": "Bob", + "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" + }, + "contents": "Hello, Bob!" + } + }] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": "0x4355c47d63924e8a72e509b65029052eb6c299d53a04e167c5775fd466751c9d07299936d304c153f6443dfa05f40ff007d72911b6f72307f996231605b915621c" +} +``` +--- + +#### eth_submitHashrate + +##### Description + +Submit a mining hashrate + +##### Parameters + +|#|Type|Description| +|-|-|-| +|1|{[`Data`](#data)}|hash rate| +|2|{[`Data`](#data)}|random ID identifying this node| + +##### Returns + +{`boolean`} - `true` if submitting went through successfully, `false` otherwise + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "eth_submitHashrate", + "params": [ + "0x0000000000000000000000000000000000000000000000000000000000500000", + "0x59daa26581d0acd1fce254fb7e85952f4c09d0915afd33d3886cd914bc7d283c" + ] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": true +} +``` +--- + +#### eth_submitWork + +##### Description + +Submit a proof-of-work solution + +##### Parameters + +|#|Type|Description| +|-|-|-| +|1|{[`Data`](#data)}|nonce found| +|2|{[`Data`](#data)}|header's pow-hash| +|3|{[`Data`](#data)}|mix digest| + +##### Returns + +{`boolean`} - `true` if the provided solution is valid, `false` otherwise + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "eth_submitWork", + "params": [ + "0x0000000000000001", + "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + "0xD1GE5700000000000000000000000000D1GE5700000000000000000000000000" + ] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": true +} +``` +--- + +#### eth_syncing + +##### Description + +Returns information about the status of this client's network synchronization + +##### Parameters + +_(none)_ + +##### Returns + +{`boolean|object`} - `false` if this client is not syncing with the network, otherwise an object with the following members: + +- {[`Quantity`](#quantity)} `currentBlock` - number of the most-recent block synced +- {[`Quantity`](#quantity)} `highestBlock` - number of latest block on the network +- {[`Quantity`](#quantity)} `startingBlock` - block number at which syncing started + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "eth_syncing", + "params": [] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": { + "currentBlock": '0x386', + "highestBlock": '0x454', + "startingBlock": '0x384' + } +} +``` +--- + +#### eth_uninstallFilter + +##### Description + +Destroys a filter based on filter ID + +**Note:** This should only be called if a filter and its notifications are no longer needed. This will also be called automatically on a filter if its notifications are not retrieved using `eth_getFilterChanges` for a period of time. + +##### Parameters + +|#|Type|Description| +|-|-|-| +|1|{[`Quantity`](#quantity)}|ID of the filter to destroy| + +##### Returns + +{`boolean`} - `true` if the filter is found and successfully destroyed or `false` if it is not + +##### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1337, + "jsonrpc": "2.0", + "method": "eth_uninstallFilter", + "params": ["0xb"] +}' + +# Response +{ + "id": 1337, + "jsonrpc": "2.0", + "result": true +} +``` +--- + +## Rationale + +Much of Ethereum's effectiveness as an enterprise-grade application platform depends on its ability to provide a reliable and predictable developer experience. Nodes created by the current generation of Ethereum clients expose RPC endpoints with differing method signatures; this forces applications to work around method inconsistencies to maintain compatibility with various Ethereum RPC implementations. + +Both Ethereum client developers and downstream dapp developers lack a formal Ethereum RPC specification. This proposal standardizes such a specification in a way that's versionable and modifiable through the traditional EIP process. + +## Backwards compatibility + +This proposal impacts Ethereum client developers by requiring that any exposed RPC interface adheres to this specification. This proposal impacts dapp developers by requiring that any RPC calls currently used in applications are made according to this specification. + +## Implementation + +The current generation of Ethereum clients includes several implementations that attempt to expose this RPC specification: + +|Client Name|Language|Homepage| +|-|-|-| +|Geth|Go|[geth.ethereum.org](https://geth.ethereum.org)| +|Parity|Rust|[parity.io/ethereum](https://parity.io/ethereum)| +|Aleth|C++|[cpp-ethereum.org](https://cpp-ethereum.org)| + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1482.md b/EIPS/eip-1482.md new file mode 100644 index 0000000..90b8bfa --- /dev/null +++ b/EIPS/eip-1482.md @@ -0,0 +1,58 @@ +--- +eip: 1482 +title: Define a maximum block timestamp drift +author: Maurelian (@Maurelian) +discussions-to: https://ethereum-magicians.org/t/define-a-maximum-block-timestamp-drift/1556 +status: Stagnant +type: Standards Track +category: Core +created: 2018-10-09 +--- + +## Simple Summary + +Include an explicit definition of the acceptable timestamp drift in the protocol specification. + +## Abstract + +On the basis that both Geth and Parity implement the same timestamp validation requirements, this should be written into the reference specification. + +## Motivation + +There is a lack of clarity about how accurate timestamps in the block header must be. The yellow paper describes the timestamp as + +> A scalar value equal to the reasonable output of Unix’s time() at this block’s inception + +This causes [confusion](https://ethereum.stackexchange.com/questions/5924/how-do-ethereum-mining-nodes-maintain-a-time-consistent-with-the-network/5926#5926) about the safe use of the `TIMESTAMP` opcode (solidity's `block.timestamp` or `now`) in smart contract development. + +Differing interpretations of 'reasonable' may create a risk of consenus failures. + + +## Specification + +The yellow paper should define a timestamp as: + +> A scalar value equal to the output of Unix’s time() at this block’s inception. For the purpose of block validation, it must be greater than the previous block's timestamp, and no more than 15 seconds greater than system time. + + +## Rationale + +Both [Geth](https://github.com/ethereum/go-ethereum/blob/4e474c74dc2ac1d26b339c32064d0bac98775e77/consensus/ethash/consensus.go#L45) and [Parity](https://github.com/paritytech/parity-ethereum/blob/73db5dda8c0109bb6bc1392624875078f973be14/ethcore/src/verification/verification.rs#L296-L307) reject blocks with timestamp more than 15 seconds in the future. This establishes a defacto standard, which should be made explicit in the reference specification. + + +## Backwards Compatibility + +It may be necessary to relax this requirement for blocks which were mined early in the main chain's history, if they would be considered invalid. + +## Test Cases + +These would be important to have. + + + + +## 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. +_ +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1484.md b/EIPS/eip-1484.md new file mode 100644 index 0000000..3966021 --- /dev/null +++ b/EIPS/eip-1484.md @@ -0,0 +1,544 @@ +--- +eip: 1484 +title: Digital Identity Aggregator +author: Anurag Angara , Andy Chorlian , Shane Hampton , Noah Zinsmeister +discussions-to: https://github.com/ethereum/EIPs/issues/1495 +status: Stagnant +type: Standards Track +category: ERC +created: 2018-10-12 +requires: 191 +--- + +## Simple Summary +A protocol for aggregating digital identity information that's broadly interoperable with existing, proposed, and hypothetical future digital identity standards. + +## Abstract +This EIP proposes an identity management and aggregation framework on the Ethereum blockchain. It allows entities to claim an `Identity` via a singular `Identity Registry` smart contract, associate it with Ethereum addresses in a variety of meaningful ways, and use it to interact with smart contracts. This enables arbitrarily complex identity-related functionality. Notably (among other features) ERC-1484 `Identities`: are self-sovereign, can natively support [ERC-725](./eip-725.md) and [ERC-1056](./eip-1056.md) identities, are [DID compliant](https://github.com/NoahZinsmeister/ERC-1484/blob/master/best-practices/DID-Method.md), and can be fully powered by [meta-transactions](https://github.com/NoahZinsmeister/ERC-1484/tree/master/contracts/examples/Providers/MetaTransactions). + +## Motivation +Emerging identity standards and related frameworks proposed by the Ethereum community (including ERCs/EIPs [725](./eip-725.md), [735](https://github.com/ethereum/EIPs/issues/735), [780](https://github.com/ethereum/EIPs/issues/780), [1056](./eip-1056.md), etc.) define and instrumentalize digital identity in a variety of ways. As existing approaches mature, new standards emerge, and isolated, non-standard approaches to identity develop, coordinating on identity will become increasingly burdensome for blockchain users and developers, and involve the unnecessary duplication of work. + +The proliferation of on-chain identity solutions can be traced back to the fact that each codifies a notion of identity and links it to specific aspects of Ethereum (claims protocols, per-identity smart contracts, signature verification schemes, etc.). This proposal eschews that approach, instead introducing a protocol layer in between the Ethereum network and individual identity applications. This solves identity management and interoperability challenges by enabling any identity-driven application to leverage an un-opinionated identity management protocol. + +## Definitions +- `Identity Registry`: A single smart contract which is the hub for all `Identities`. The primary responsibility of the `Registry` is to define and enforce the rules of a global namespace for `Identities`, which are individually denominated by Ethereum Identification Numbers (EINs). + +- `Identity`: A data structure containing all the core information relevant to an identity, namely: a `Recovery Address`, an `Associated Addresses` set, a `Providers` set, and a `Resolvers` set. `Identities` are denominated by EINs (incrementing `uint` identifiers starting at 1), which are unique but otherwise uninformative. Each `Identity` is a Solidity struct: + +```solidity +struct Identity { + address recoveryAddress; + AddressSet.Set associatedAddresses; + AddressSet.Set providers; + AddressSet.Set resolvers; +} +``` + +- `Associated Address`: An Ethereum address publicly associated with an `Identity`. In order for an address to become an `Associated Address`, an `Identity` must either transact from or produce an appropriately signed message from the candidate address and an existing `Associated Address`, indicating intent to associate. An `Associated Address` can be removed from an `Identity` by transacting/producing a signature indicating intent to disassociate. A given address may only be an `Associated Address` for one `Identity` at any given time. + +- `Provider`: An Ethereum address (typically but not by definition a smart contract) authorized to act on behalf of `Identities` who have authorized them to do so. This includes but is not limited to managing the `Associated Address`, `Provider`, and `Resolver` sets for an `Identity`. `Providers` exist to facilitate user adoption by making it easier to manage `Identities`. + +- `Resolver`: A smart contract containing arbitrary information pertaining to `Identities`. A resolver may implement an identity standard, such as ERC-725, or may consist of a smart contract leveraging or declaring identifying information about `Identities`. These could be simple attestation structures or more sophisticated financial dApps, social media dApps, etc. Each `Resolver` added to an `Identity` makes the `Identity` more informative. + +- `Recovery Address`: An Ethereum address (either an account or smart contract) that can be used to recover lost `Identities` as outlined in the [Recovery](#recovery) section. + +- `Destruction`: In the event of irrecoverable loss of control of an `Identity`, `Destruction` is a contingency measure to permanently disable the `Identity`. It removes all `Associated Addresses`, `Providers`, and optionally `Resolvers` while preserving the `Identity`. Evidence of the existence of the `Identity` persists, while control over the `Identity` is nullified. + +## Specification +A digital identity in this proposal can be viewed as an omnibus account, containing more information about an identity than any individual identity application could. This omnibus identity is resolvable to an unlimited number of sub-identities called `Resolvers`. This allows an atomic entity, the `Identity`, to be resolvable to abstract data structures, the `Resolvers`. `Resolvers` recognize `Identities` by any of their `Associated Addresses`, or by their `EIN`. + +The protocol revolves around claiming an `Identity` and managing `Associated Addresses`, `Providers` and `Resolvers`. Identities can delegate much or all of this responsibility to one or more `Providers`, or perform it directly from an `Associated Address`. `Associated Addresses`/`Providers` may add and remove `Resolvers` and `Providers` indiscriminately. `Associated Addresses` may only be added or removed with the appropriate permission. + +### Identity Registry +The `Identity Registry` contains functionality to create new `Identities` and for existing `Identities` to manage their `Associated Addresses`, `Providers`, and `Resolvers`. It is important to note that this registry fundamentally requires transactions for every aspect of building out an `Identity`. However, recognizing the importance of accessibility to dApps and identity applications, we empower `Providers` to build out `Identities` on the behalf of users, without requiring users to pay gas costs. An example of this pattern, often referred to as a meta transactions, can be [seen in the reference implementation](https://github.com/NoahZinsmeister/ERC-1484/tree/master/contracts/examples/Providers/MetaTransactions). + +Due to the fact that multiple addresses can be associated with a given identity (though not the reverse), `Identities` are denominated by `EIN`. This `uint` identifier can be encoded in QR format or mapped to more user-friendly formats, such as a `string`, in registries at the `Provider` or `Resolver` level. + +### Address Management +The address management function consists of trustlessly connecting multiple user-owned `Associated Addresses` to an `Identity`. It does not give special status to any particular `Associated Address`, rather leaving this (optional) specification to identity applications built on top of the protocol - for instance, `management`, `action`, `claim` and `encryption` keys denominated in the ERC-725 standard, or `identifiers` and `delegates` as denominated in ERC-1056. This allows a user to access common identity data from multiple wallets while still: + +- retaining the ability to interact with contracts outside of their identity +- taking advantage of address-specific permissions established at the application layer of a user's identity. + +Trustlessness in the address management function is achieved through a robust permissioning scheme. To add an `Associated Address` to an `Identity`, implicit permission from a transaction sender or explicit permission from a signature is required from 1) an address already within the registry and 2) an address to be claimed. Importantly, the transaction need not come from any particular address, as long as permission is established, which allows not only users but third parties (companies, governments, etc.) to bear the overhead of managing identities. To prevent a compromised `Associated Address` from unilaterally removing other `Associated Addresses`, it's only possible to remove an `Associated Address` by transacting or producing a signature from it. + +All signatures required in ERC-1484 are designed per the [ERC-191](./eip-191.md) v0 specification. To avoid replay attacks, all signatures must include a timestamp within a rolling lagged window of the current `block.timestamp`. For more information, see this [best practices document](https://github.com/NoahZinsmeister/ERC-1484/blob/master/best-practices/VerifyingSignatures.md) in the reference implementation. + +### Provider Management +While the protocol allows users to directly call identity management functions, it also aims to be more robust and future-proof by allowing `Providers`, typically smart contracts, to perform identity management functions on a user's behalf. A `Provider` set by an `Identity` can perform address management and resolver management functions by passing a user's `EIN` in function calls. + +### Resolver Management +A `Resolver` is any smart contract that encodes information which resolves to an `Identity`. We remain agnostic about the specific information that can be encoded in a resolver and the functionality that this enables. The existence of `Resolvers` is primarily what makes this ERC an identity *protocol* rather than an identity *application*. `Resolvers` resolve abstract data in smart contracts to an atomic entity, the `Identity`. + +### Recovery +If users lose control over an `Associated Address`, the `Recovery Address` provides a fallback mechanism. Upon `Identity` creation, a `Recovery Address` is passed as a parameter by the creator. Recovery functionality is triggered in three scenarios: + +**1. Changing Recovery Address**: If a recovery key is lost, an `Associated Address`/`Provider` can [triggerRecoveryAddressChange](#triggerrecoveryaddresschange)/[triggerRecoveryAddressChangeFor](#triggerrecoveryaddresschangefor). To prevent malicious behavior from someone who has gained control of an `Associated Address` or `Provider` and is changing the `Recovery Address` to one under their control, this action triggers a 14 day challenge period during which the old `Recovery Address` may reject the change by [triggering recovery](#triggerrecovery). If the `Recovery Address` does not reject the change within 14 days, the `Recovery Address` is changed. + +**2. Recovery**: Recovery occurs when a user recognizes that an `Associated Address` or the `Recovery Address` belonging to the user is lost or stolen. In this instance the `Recovery Address` must call [triggerRecovery](#triggerrecovery). This removes all `Associated Addresses` and `Providers` from the corresponding `Identity` and replaces them with an address passed in the function call. The `Identity` and associated `Resolvers` maintain integrity. The user is now responsible for adding the appropriate un-compromised addresses back to their `Identity`. + +*Importantly, the `Recovery Address` can be a user-controlled wallet or another address, such as a multisig wallet or smart contract. This allows for arbitrarily sophisticated recovery logic! This includes the potential for recovery to be fully compliant with standards such as [DID](https://decentralized.id/).* + +**3. Destruction** +The Recovery scheme offers considerable power to a `Recovery Address`; accordingly, `Destruction` is a nuclear option to combat malicious control over an `Identity` when a `Recovery Address` is compromised. If a malicious actor compromises a user's `Recovery Address` and triggers recovery, any address removed in the `Recovery` process can call [triggerDestruction](#triggerdestruction) within 14 days to permanently disable the `Identity`. The user would then need to create a new `Identity`, and would be responsible for engaging in recovery schemes for any identity applications built in the `Resolver` or `Provider` layers. + +#### Alternative Recovery Considerations +We considered many possible alternatives when devising the Recovery process outlined above. We ultimately selected the scheme that was most un-opinionated, modular, and consistent with the philosophy behind the `Associated Address`, `Provider`, and `Resolver` components. Still, we feel that it is important to highlight some of the other recovery options we considered, to provide a rationale as to how we settled on what we did. + +**High Level Concerns** +Fundamentally, a Recovery scheme needs to be resilient to a compromised address taking control of a user's `Identity`. A secondary concern is preventing a compromised address from maliciously destroying a user's identity due to off-chain utility, which is not an optimal scenario, but is strictly better than if they've gained control. + +**Alternative 1: Nuclear Option** +This approach would allow any `Associated Address` to destroy an `Identity` whenever another `Associated Address` is compromised. While this may seem severe, we strongly considered it because this ERC is an identity *protocol*, not an identity *application*. This means that though a user's compromised `Identity` is destroyed, they should still have recourse to whatever restoration mechanisms are available in each of their actual identities at the `Resolver` and/or `Provider` level. We ultimately dismissed this approach for two main reasons: + +- It is not robust in cases where a user has only one `Associated Address` +- It would increase the frequency of recovery requests to identity applications due to its unforgiving nature. + +**Alternative 2: Unilateral Address Removal via Providers** +This would allow `Associated Addresses`/`Providers` to remove `Associated Addresses` without a signature from said address. This implementation would allow `Providers` to include arbitrarily sophisticated schemes for removing a rogue address - for instance, multi-sig requirements, centralized off-chain verification, user controlled master addresses, deferral to a jurisdictional contract, and more. To prevent a compromised `Associated Address` from simply setting a malicious `Provider` to remove un-compromised addresses, it would have required a waiting period between when a `Provider` is set and when they would be able to remove an `Associated Address`. We dismissed this approach because we felt it placed too high of a burden on `Providers`. If a `Provider` offered a sophisticated range of functionality to a user, but post-deployment a threat was found in the Recovery logic of the provider, `Provider`-specific infrastructure would need to be rebuilt. We also considered including a flag that would allow a user to decide whether or not a `Provider` may remove `Associated Addresses` unilaterally. Ultimately, we concluded that only allowing removal of `Associated Addresses` via the `Recovery Address` enables equally sophisticated recovery logic while separating the functionality from `Providers`, leaving less room for users to relinquish control to potentially flawed implementations. + +## Rationale +We find that at a protocol layer, identities should not rely on specific claim or attestation structures, but should instead be a part of a trustless framework upon which arbitrarily sophisticated claim and attestation structures may be built. + +The main criticism of existing identity solutions is that they're overly restrictive. We aim to limit requirements, keep identities modular and future-proof, and remain un-opinionated regarding any functionality a particular identity component may have. This proposal gives users the option to interact on the blockchain using an robust `Identity` rather than just an address. + +## Implementation +**The reference implementation for ERC-1484 may be found in [NoahZinsmeister/ERC-1484](https://github.com/NoahZinsmeister/ERC-1484).** + +#### identityExists + +Returns a `bool` indicating whether or not an `Identity` denominated by the passed `EIN` exists. + +```solidity +function identityExists(uint ein) public view returns (bool); +``` + +#### hasIdentity + +Returns a `bool` indicating whether or not the passed `_address` is associated with an `Identity`. + +```solidity +function hasIdentity(address _address) public view returns (bool); +``` + +#### getEIN + +Returns the `EIN` associated with the passed `_address`. Throws if the address is not associated with an `EIN`. + +```solidity +function getEIN(address _address) public view returns (uint ein); +``` + +#### isAssociatedAddressFor + +Returns a `bool` indicating whether or not the passed `_address` is associated with the passed `EIN`. + +```solidity +function isAssociatedAddressFor(uint ein, address _address) public view returns (bool); +``` + +#### isProviderFor + +Returns a `bool` indicating whether or not the passed `provider` has been set by the passed `EIN`. + +```solidity +function isProviderFor(uint ein, address provider) public view returns (bool); +``` + +#### isResolverFor + +Returns a `bool` indicating whether or not the passed `resolver` has been set by the passed `EIN`. + +```solidity +function isResolverFor(uint ein, address resolver) public view returns (bool); +``` + +#### getIdentity + +Returns the `recoveryAddress`, `associatedAddresses`, `providers` and `resolvers` of the passed `EIN`. + +```solidity +function getIdentity(uint ein) public view + returns ( + address recoveryAddress, + address[] memory associatedAddresses, address[] memory providers, address[] memory resolvers + ); +``` + +#### createIdentity + +Creates an `Identity`, setting the `msg.sender` as the sole `Associated Address`. Returns the `EIN` of the new `Identity`. + +```solidity +function createIdentity(address recoveryAddress, address[] memory providers, address[] memory resolvers) + public returns (uint ein); +``` + +Triggers event: [IdentityCreated](#identitycreated) + +#### createIdentityDelegated + +Performs the same logic as `createIdentity`, but can be called by any address. This function requires a signature from the `associatedAddress` to ensure their consent. + +```solidity +function createIdentityDelegated( + address recoveryAddress, address associatedAddress, address[] memory providers, address[] memory resolvers, + uint8 v, bytes32 r, bytes32 s, uint timestamp +) + public returns (uint ein); +``` + +Triggers event: [IdentityCreated](#identitycreated) + +#### addAssociatedAddress + +Adds the `addressToAdd` to the `EIN` of the `approvingAddress`. The `msg.sender` must be either of the `approvingAddress` or the `addressToAdd`, and the signature must be from the other one. + +```solidity +function addAssociatedAddress( + address approvingAddress, address addressToAdd, uint8 v, bytes32 r, bytes32 s, uint timestamp +) + public +``` + +Triggers event: [AssociatedAddressAdded](#associatedaddressadded) + +#### addAssociatedAddressDelegated + +Adds the `addressToAdd` to the `EIN` of the `approvingAddress`. Requires signatures from both the `approvingAddress` and the `addressToAdd`. + +```solidity +function addAssociatedAddressDelegated( + address approvingAddress, address addressToAdd, + uint8[2] memory v, bytes32[2] memory r, bytes32[2] memory s, uint[2] memory timestamp +) + public +``` + +Triggers event: [AssociatedAddressAdded](#associatedaddressadded) + +#### removeAssociatedAddress + +Removes the `msg.sender` as an `Associated Address` from its `EIN`. + +```solidity +function removeAssociatedAddress() public; +``` + +Triggers event: [AssociatedAddressRemoved](#associatedaddressremoved) + + +#### removeAssociatedAddressDelegated + +Removes the `addressToRemove` from its associated `EIN`. Requires a signature from the `addressToRemove`. + +```solidity +function removeAssociatedAddressDelegated(address addressToRemove, uint8 v, bytes32 r, bytes32 s, uint timestamp) + public; +``` + +Triggers event: [AssociatedAddressRemoved](#associatedaddressremoved) + +#### addProviders + +Adds an array of `Providers` to the `Identity` of the `msg.sender`. + +```solidity +function addProviders(address[] memory providers) public; +``` + +Triggers event: [ProviderAdded](#provideradded) + +#### addProvidersFor + +Performs the same logic as `addProviders`, but must be called by a `Provider`. + +```solidity +function addProvidersFor(uint ein, address[] memory providers) public; +``` + +Triggers event: [ProviderAdded](#provideradded) + +#### removeProviders + +Removes an array of `Providers` from the `Identity` of the `msg.sender`. + +```solidity +function removeProviders(address[] memory providers) public; +``` + +Triggers event: [ProviderRemoved](#providerremoved) + + +#### removeProvidersFor + +Performs the same logic as `removeProviders`, but is called by a `Provider`. + +```solidity +function removeProvidersFor(uint ein, address[] memory providers) public; +``` + +Triggers event: [ProviderRemoved](#providerremoved) + + +#### addResolvers + +Adds an array of `Resolvers` to the `EIN` of the `msg.sender`. + +```solidity +function addResolvers(address[] memory resolvers) public; +``` + +Triggers event: [ResolverAdded](#resolveradded) + +#### addResolversFor + +Performs the same logic as `addResolvers`, but must be called by a `Provider`. + +```solidity +function addResolversFor(uint ein, address[] memory resolvers) public; +``` + +Triggers event: [ResolverAdded](#resolveradded) + +#### removeResolvers + +Removes an array of `Resolvers` from the `EIN` of the `msg.sender`. + +```solidity +function removeResolvers(address[] memory resolvers) public; +``` + +Triggers event: [ResolverRemoved](#resolverremoved) + +#### removeResolversFor + +Performs the same logic as `removeResolvers`, but must be called by a `Provider`. + +```solidity +function removeResolversFor(uint ein, address[] memory resolvers) public; +``` + +Triggers event: [ResolverRemoved](#resolverremoved) + +#### triggerRecoveryAddressChange + +Initiates a change in the current `recoveryAddress` for the `EIN` of the `msg.sender`. + +```solidity +function triggerRecoveryAddressChange(address newRecoveryAddress) public; +``` + +Triggers event: [RecoveryAddressChangeTriggered](#recoveryaddresschangetriggered) + +#### triggerRecoveryAddressChangeFor + +Initiates a change in the current `recoveryAddress` for a given `EIN`. + +```solidity +function triggerRecoveryAddressChangeFor(uint ein, address newRecoveryAddress) public; +``` + +Triggers event: [RecoveryAddressChangeTriggered](#recoveryaddresschangetriggered) + +#### triggerRecovery + +Triggers `EIN` recovery from the current `recoveryAddress`, or the old `recoveryAddress` if changed within the last 2 weeks. + +```solidity +function triggerRecovery(uint ein, address newAssociatedAddress, uint8 v, bytes32 r, bytes32 s, uint timestamp) public; +``` + +Triggers event: [RecoveryTriggered](#recoverytriggered) + +#### triggerDestruction + +Triggers destruction of an `EIN`. This renders the `Identity` permanently unusable. + +```solidity +function triggerDestruction(uint ein, address[] memory firstChunk, address[] memory lastChunk, bool clearResolvers) + public; +``` + +Triggers event: [IdentityDestroyed](#identitydestroyed) + +### Events + +#### IdentityCreated + +MUST be triggered when an `Identity` is created. + +```solidity +event IdentityCreated( + address indexed initiator, uint indexed ein, + address recoveryAddress, address associatedAddress, address[] providers, address[] resolvers, bool delegated +); +``` + +#### AssociatedAddressAdded + +MUST be triggered when an address is added to an `Identity`. + +```solidity +event AssociatedAddressAdded( + address indexed initiator, uint indexed ein, address approvingAddress, address addedAddress, bool delegated +); +``` + +#### AssociatedAddressRemoved + +MUST be triggered when an address is removed from an `Identity`. + +```solidity +event AssociatedAddressRemoved(address indexed initiator, uint indexed ein, address removedAddress, bool delegated); +``` + +#### ProviderAdded + +MUST be triggered when a provider is added to an `Identity`. + +```solidity +event ProviderAdded(address indexed initiator, uint indexed ein, address provider, bool delegated); +``` + +#### ProviderRemoved + +MUST be triggered when a provider is removed. + +```solidity +event ProviderRemoved(address indexed initiator, uint indexed ein, address provider, bool delegated); +``` + +#### ResolverAdded + +MUST be triggered when a resolver is added. + +```solidity +event ResolverAdded(address indexed initiator, uint indexed ein, address resolvers, bool delegated); +``` + +#### ResolverRemoved + +MUST be triggered when a resolver is removed. + +```solidity +event ResolverRemoved(address indexed initiator, uint indexed ein, address resolvers, bool delegated); +``` + +#### RecoveryAddressChangeTriggered + +MUST be triggered when a recovery address change is triggered. + +```solidity +event RecoveryAddressChangeTriggered( + address indexed initiator, uint indexed ein, + address oldRecoveryAddress, address newRecoveryAddress, bool delegated +); +``` + +#### RecoveryTriggered + +MUST be triggered when recovery is triggered. + +```solidity +event RecoveryTriggered( + address indexed initiator, uint indexed ein, address[] oldAssociatedAddresses, address newAssociatedAddress +); +``` + +#### IdentityDestroyed + +MUST be triggered when an `Identity` is destroyed. + +```solidity +event IdentityDestroyed(address indexed initiator, uint indexed ein, address recoveryAddress, bool resolversReset); +``` + +### Solidity Interface +```solidity +interface IdentityRegistryInterface { + function isSigned(address _address, bytes32 messageHash, uint8 v, bytes32 r, bytes32 s) + external pure returns (bool); + + // Identity View Functions ///////////////////////////////////////////////////////////////////////////////////////// + function identityExists(uint ein) external view returns (bool); + function hasIdentity(address _address) external view returns (bool); + function getEIN(address _address) external view returns (uint ein); + function isAssociatedAddressFor(uint ein, address _address) external view returns (bool); + function isProviderFor(uint ein, address provider) external view returns (bool); + function isResolverFor(uint ein, address resolver) external view returns (bool); + function getIdentity(uint ein) external view returns ( + address recoveryAddress, + address[] memory associatedAddresses, address[] memory providers, address[] memory resolvers + ); + + // Identity Management Functions /////////////////////////////////////////////////////////////////////////////////// + function createIdentity(address recoveryAddress, address[] calldata providers, address[] calldata resolvers) + external returns (uint ein); + function createIdentityDelegated( + address recoveryAddress, address associatedAddress, address[] calldata providers, address[] calldata resolvers, + uint8 v, bytes32 r, bytes32 s, uint timestamp + ) external returns (uint ein); + function addAssociatedAddress( + address approvingAddress, address addressToAdd, uint8 v, bytes32 r, bytes32 s, uint timestamp + ) external; + function addAssociatedAddressDelegated( + address approvingAddress, address addressToAdd, + uint8[2] calldata v, bytes32[2] calldata r, bytes32[2] calldata s, uint[2] calldata timestamp + ) external; + function removeAssociatedAddress() external; + function removeAssociatedAddressDelegated(address addressToRemove, uint8 v, bytes32 r, bytes32 s, uint timestamp) + external; + function addProviders(address[] calldata providers) external; + function addProvidersFor(uint ein, address[] calldata providers) external; + function removeProviders(address[] calldata providers) external; + function removeProvidersFor(uint ein, address[] calldata providers) external; + function addResolvers(address[] calldata resolvers) external; + function addResolversFor(uint ein, address[] calldata resolvers) external; + function removeResolvers(address[] calldata resolvers) external; + function removeResolversFor(uint ein, address[] calldata resolvers) external; + + // Recovery Management Functions /////////////////////////////////////////////////////////////////////////////////// + function triggerRecoveryAddressChange(address newRecoveryAddress) external; + function triggerRecoveryAddressChangeFor(uint ein, address newRecoveryAddress) external; + function triggerRecovery(uint ein, address newAssociatedAddress, uint8 v, bytes32 r, bytes32 s, uint timestamp) + external; + function triggerDestruction( + uint ein, address[] calldata firstChunk, address[] calldata lastChunk, bool resetResolvers + ) external; + + // Events ////////////////////////////////////////////////////////////////////////////////////////////////////////// + event IdentityCreated( + address indexed initiator, uint indexed ein, + address recoveryAddress, address associatedAddress, address[] providers, address[] resolvers, bool delegated + ); + event AssociatedAddressAdded( + address indexed initiator, uint indexed ein, address approvingAddress, address addedAddress + ); + event AssociatedAddressRemoved(address indexed initiator, uint indexed ein, address removedAddress); + event ProviderAdded(address indexed initiator, uint indexed ein, address provider, bool delegated); + event ProviderRemoved(address indexed initiator, uint indexed ein, address provider, bool delegated); + event ResolverAdded(address indexed initiator, uint indexed ein, address resolvers); + event ResolverRemoved(address indexed initiator, uint indexed ein, address resolvers); + event RecoveryAddressChangeTriggered( + address indexed initiator, uint indexed ein, address oldRecoveryAddress, address newRecoveryAddress + ); + event RecoveryTriggered( + address indexed initiator, uint indexed ein, address[] oldAssociatedAddresses, address newAssociatedAddress + ); + event IdentityDestroyed(address indexed initiator, uint indexed ein, address recoveryAddress, bool resolversReset); +} +``` + +## Backwards Compatibility +`Identities` established under this standard consist of existing Ethereum addresses; accordingly, there are no backwards compatibility issues. Deployed, non-upgradeable smart contracts that wish to become `Resolvers` for `Identities` will need to write wrapper contracts that resolve addresses to `EIN`-denominated `Identities`. + +## Additional References +- [ERC-1484 Reference Implementation](https://github.com/NoahZinsmeister/ERC-1484) +- [ERC-191 Signatures](./eip-191.md) +- [ERC-725 Identities](./eip-725.md) +- [ERC-1056 Identities](./eip-1056.md) + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1485.md b/EIPS/eip-1485.md new file mode 100644 index 0000000..724623b --- /dev/null +++ b/EIPS/eip-1485.md @@ -0,0 +1,200 @@ +--- +eip: 1485 +title: TEthashV1 +author: trustfarm , trustfarm +discussions-to: https://ethereum-magicians.org/t/anti-eth-asic-mining-eip-1488-pr/1807 +status: Stagnant +type: Standards Track +category: Core +created: 2018-11-01 +--- + +## Simple Summary +This EIP modifies ethash in order to break ASIC miners specialized for the current ethash mining algorithm. + +## Abstract +This EIP pursue "obsolete current ASIC miners" by modifying PoW algorithm in a very low risk manner and update to latest hash algorithm from deprecated FNV Hash algorithms. + +Following TEthashV1 algorithm suggests safe transition of PoW algorithms and secure the FNV Algorithm in MIX Parts. + +## Motivation +Provide original Ethash proof of work verification with minimal set of changes by updating FNV0 algorithm + +## Specification + +#### 1. Reference materials on ETHASH FNV0 + +#### Where FNV Applied on ETHASH + +- In [ETHASH](https://github.com/ethereum/wiki/wiki/Ethash) , FNV Hash is used on + * 1) On data aggregation function, MIX parts. + + * Ethash Algorithm + + ``` + Header + Nonce + | + Keccak + | + **[MIX 0]** --> **[DAG Page]** + | | + Mixing <--| + ... + | + **[Mix 63]** + | + |-----> Mix64 [Process] ---> Mix Digest [32B] + ``` + + * FNV used in DAG Generation + and Mixing for random access or DAG Page. + +#### 2. Current applied Ethash FNV hash implementation is deprecated now. + +[FNV-0_hash (deprecated)](https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV-0_hash_(deprecated)) + + It is a simple way of hashing algorithm + + ``` + hash = 0 + for each byte_of_data to be hashed + hash = hash × FNV_prime + hash = hash XOR octet_of_data + return hash + ``` + + When analysed FNV-0 , there's very weak [avalanche effect](https://simple.wikipedia.org/wiki/Avalanche_effect), when hash input changes on 1~2bits. refer [FNV-Analysis reference section](https://github.com/tao-foundation/FNV-Analysis#how-to-test-and-analysis-reference-test-code) + + We need to research and apply newer FNV hash or short message hash algorithm. + +#### 3. FNV1A hash algorithm description + +Previous proposed algorithm based on FNV1 [EIP-1355](./eip-1355.md) + +There's a implementation that looks like "Missing Offset Bias" at **FNV1A**. + +Quotation of [original algorithm FNV1A](https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV-1a_hash) +``` +use hash offset +FNV-1a hash +The FNV-1a hash differs from the FNV-1 hash by only the order in which the multiply and XOR is performed:[8][10] + + hash = FNV_offset_basis + for each byte_of_data to be hashed + hash = hash XOR byte_of_data + hash = hash × FNV_prime + return hash +``` + +FNV_offset_basis and computation order change of xor and multiplication Makes one more xor and multiply computation, but more secure hash effects than FNV0. +and make dispersion boundary condition (0, even number, ..) by using of Prime Number. + +#### 4. Real Implementation for FNV1A + +Consider real computation resources, in TEthashV1 uses hash byte_of_data to 4bytes aligned data. + +In TETHashV1, Adapts fully follow the FNV1A implementation. + + - TETHASHV1 FNV1A implementation + +Following are reference implementation of FNV1A adapted in TETHashV1. + +```cpp +// Reference Pseudo c/cpp implementation + +#define FNV_PRIME 0x01000193U +#define FNV_OFFSET_BASIS 0x811c9dc5U + +#define fnv1a(x, y) ((((FNV_OFFSET_BASIS^(x))*FNV_PRIME) ^ (y)) * FNV_PRIME) +#define fnv1a_reduce(a,b,c,d) (fnv1a(fnv1a(fnv1a(a, b), c), d)) +``` + +Another Byte aligned implementation of FNV1A , call to FNV1c + +```cpp +#define FNV_PRIME 0x01000193U +#define FNV_OFFSET_BASIS 0x811c9dc5U + +#define fnv1i(x) ( (( (( (( \ + ( ((FNV_OFFSET_BASIS)^( ((x)>>24)&0x000000ff )) * FNV_PRIME) \ + ^ (((x)>>16 )&0x000000ff)) * FNV_PRIME) \ + ^ (((x)>>8 )&0x000000ff)) * FNV_PRIME) \ + ^ (((x) )&0x000000ff)) * FNV_PRIME) \ + ) +#define fnv1c(x, y) ((fnv1i(x) ^ (y)) * FNV_PRIME) +``` + +#### 5. [FNV-Analysis](https://github.com/tao-foundation/FNV-Analysis) +FNV Mix Algorithm Analysis for TEthashV1 + +#### How to test and analysis reference test code. + +You can compile it with simple in terminal. +No additional library needs, + +```sh +gcc -o fnvtest fnvcltest.c +``` + +And You can execute it +``` +fnvtest + +F(00,00)::VEC(0, 0, ffffffff, 0):: FNV :00000000, DF=00000000(00) DS(00000000), FNV1 :00000000, DF=00000000(00) DS(00000000), FNV1a:117697cd, DF=117697cd(17) DS(117697cd), FNV1c:1210d00f, DF=127f8dbf(20) DS(11a1725f), F___RC=efe1b9c4, DF:efe1b9c4(19) , F1__RC=deb68dfe, DF:deb68dfe(22) , F1A_RC=99bad28b, DF:99bad28b(17) , F1C_RC=e29fa497, DF:e29fa497(18) +F(00,01)::VEC(0, 1, ffffffff, 0):: FNV :00000001, DF=00000001(01) DS(00000001), FNV1 :01000193, DF=01000193(06) DS(01000193), FNV1a:1076963a, DF=010001f7(09) DS(01000193), FNV1c:1110ce7c, DF=03001e73(11) DS(01000193), F___RC=fefffe6d, DF:111e47a9(14) , F1__RC=d9fd8597, DF:074b0869(12) , F1A_RC=72c287e0, DF:eb78556b(19) , F1C_RC=6b6991ef, DF:89f63578(17) +F(00,02)::VEC(0, 2, ffffffff, 0):: FNV :00000002, DF=00000003(02) DS(00000001), FNV1 :02000326, DF=030002b5(08) DS(01000193), FNV1a:0f7694a7, DF=1f00029d(11) DS(01000193), FNV1c:1410d335, DF=05001d49(09) DS(030004b9), F___RC=d8fd8404, DF:26027a69(13) , F1__RC=9b16d24c, DF:42eb57db(19) , F1A_RC=c17f0ecb, DF:b3bd892b(18) , F1C_RC=a5be8e78, DF:ced71f97(21) +F(00,03)::VEC(0, 3, ffffffff, 0):: FNV :00000003, DF=00000001(01) DS(00000001), FNV1 :030004b9, DF=0100079f(10) DS(01000193), FNV1a:0e769314, DF=010007b3(09) DS(01000193), FNV1c:1310d1a2, DF=07000297(09) DS(01000193), F___RC=b2fb099b, DF:6a068d9f(16) , F1__RC=5c301f01, DF:c726cd4d(17) , F1A_RC=94cf402e, DF:55b04ee5(16) , F1C_RC=aea1a025, DF:0b1f2e5d(17) +F(00,04)::VEC(0, 4, ffffffff, 0):: FNV :00000004, DF=00000007(03) DS(00000001), FNV1 :0400064c, DF=070002f5(10) DS(01000193), FNV1a:0d769181, DF=03000295(07) DS(01000193), FNV1c:0e10c9c3, DF=1d001861(09) DS(050007df), F___RC=8cf88f32, DF:3e0386a9(14) , F1__RC=1d496bb6, DF:417974b7(17) , F1A_RC=89401d59, DF:1d8f5d77(20) , F1C_RC=e4e96c7c, DF:4a48cc59(13) +F(00,05)::VEC(0, 5, ffffffff, 0):: FNV :00000005, DF=00000001(01) DS(00000001), FNV1 :050007df, DF=01000193(06) DS(01000193), FNV1a:0c768fee, DF=01001e6f(11) DS(01000193), FNV1c:0d10c830, DF=030001f3(09) DS(01000193), F___RC=66f614c9, DF:ea0e9bfb(20) , F1__RC=de62b86b, DF:c32bd3dd(19) , F1A_RC=346e222c, DF:bd2e3f75(21) , F1C_RC=502e5f82, DF:b4c733fe(20) +F(00,06)::VEC(0, 6, ffffffff, 0):: FNV :00000006, DF=00000003(02) DS(00000001), FNV1 :06000972, DF=03000ead(10) DS(01000193), FNV1a:0b768e5b, DF=070001b5(09) DS(01000193), FNV1c:1010cce9, DF=1d0004d9(10) DS(030004b9), F___RC=40f39a60, DF:26058ea9(13) , F1__RC=9f7c0520, DF:411ebd4b(16) , F1A_RC=b376a527, DF:8718870b(13) , F1C_RC=1241a9a4, DF:426ff626(17) +F(00,07)::VEC(0, 7, ffffffff, 0):: FNV :00000007, DF=00000001(01) DS(00000001), FNV1 :07000b05, DF=01000277(08) DS(01000193), FNV1a:0a768cc8, DF=01000293(06) DS(01000193), FNV1c:0f10cb56, DF=1f0007bf(15) DS(01000193), F___RC=1af11ff7, DF:5a028597(13) , F1__RC=609551d5, DF:ffe954f5(22) , F1A_RC=14293bea, DF:a75f9ecd(21) , F1C_RC=49d34bba, DF:5b92e21e(16) +F(00,08)::VEC(0, 8, ffffffff, 0):: FNV :00000008, DF=0000000f(04) DS(00000001), FNV1 :08000c98, DF=0f00079d(12) DS(01000193), FNV1a:09768b35, DF=030007fd(12) DS(01000193), FNV1c:1a10dca7, DF=150017f1(12) DS(0b001151), F___RC=f4eea58e, DF:ee1fba79(21) , F1__RC=21ae9e8a, DF:413bcf5f(19) , F1A_RC=eeebb7a5, DF:fac28c4f(17) , F1C_RC=7da04f47, DF:347304fd(16) +F(00,09)::VEC(0, 9, ffffffff, 0):: FNV :00000009, DF=00000001(01) DS(00000001), FNV1 :09000e2b, DF=010002b3(07) DS(01000193), FNV1a:087689a2, DF=01000297(07) DS(01000193), FNV1c:1910db14, DF=030007b3(10) DS(01000193), F___RC=ceec2b25, DF:3a028eab(14) , F1__RC=e2c7eb3f, DF:c36975b5(18) , F1A_RC=54e1aef8, DF:ba0a195d(15) , F1C_RC=d425e1af, DF:a985aee8(16) +F(00,0a)::VEC(0, a, ffffffff, 0):: FNV :0000000a, DF=00000003(02) DS(00000001), FNV1 :0a000fbe, DF=03000195(07) DS(01000193), FNV1a:0776880f, DF=0f0001ad(10) DS(01000193), FNV1c:1c10dfcd, DF=050004d9(08) DS(030004b9), F___RC=a8e9b0bc, DF:66059b99(15) , F1__RC=a3e137f4, DF:4126dccb(15) , F1A_RC=213fcd63, DF:75de639b(20) , F1C_RC=7e1d2751, DF:aa38c6fe(18) +F(00,0b)::VEC(0, b, ffffffff, 0):: FNV :0000000b, DF=00000001(01) DS(00000001), FNV1 :0b001151, DF=01001eef(12) DS(01000193), FNV1a:0676867c, DF=01000e73(09) DS(01000193), FNV1c:1b10de3a, DF=070001f7(11) DS(01000193), F___RC=82e73653, DF:2a0e86ef(16) , F1__RC=64fa84a9, DF:c71bb35d(19) , F1A_RC=5598ce46, DF:74a70325(14) , F1C_RC=6400c630, DF:1a1de161(14) +F(00,0c)::VEC(0, c, ffffffff, 0):: FNV :0000000c, DF=00000007(03) DS(00000001), FNV1 :0c0012e4, DF=070003b5(10) DS(01000193), FNV1a:057684e9, DF=03000295(07) DS(01000193), FNV1c:1610d65b, DF=0d000861(07) DS(050007df), F___RC=5ce4bbea, DF:de038db9(17) , F1__RC=2613d15e, DF:42e955f7(18) , F1A_RC=6a220ff1, DF:3fbac1b7(20) , F1C_RC=6e781da4, DF:0a78db94(15) +F(00,0d)::VEC(0, d, ffffffff, 0):: FNV :0000000d, DF=00000001(01) DS(00000001), FNV1 :0d001477, DF=01000693(07) DS(01000193), FNV1a:04768356, DF=010007bf(11) DS(01000193), FNV1c:1510d4c8, DF=03000293(07) DS(01000193), F___RC=36e24181, DF:6a06fa6b(17) , F1__RC=e72d1e13, DF:c13ecf4d(18) , F1A_RC=168d4944, DF:7caf46b5(19) , F1C_RC=65bbcfa1, DF:0bc3d205(13) +F(00,0e)::VEC(0, e, ffffffff, 0):: FNV :0000000e, DF=00000003(02) DS(00000001), FNV1 :0e00160a, DF=0300027d(09) DS(01000193), FNV1a:037681c3, DF=07000295(08) DS(01000193), FNV1c:1810d981, DF=0d000d49(09) DS(030004b9), F___RC=10dfc718, DF:263d8699(15) , F1__RC=a8466ac8, DF:4f6b74db(20) , F1A_RC=93e667bf, DF:856b2efb(19) , F1C_RC=76f80ee3, DF:1343c142(11) +F(00,0f)::VEC(0, f, ffffffff, 0):: FNV :0000000f, DF=00000001(01) DS(00000001), FNV1 :0f00179d, DF=01000197(07) DS(01000193), FNV1a:02768030, DF=010001f3(08) DS(01000193), FNV1c:1710d7ee, DF=0f000e6f(13) DS(01000193), F___RC=eadd4caf, DF:fa028bb7(17) , F1__RC=695fb77d, DF:c119ddb5(17) , F1A_RC=0f485682, DF:9cae313d(17) , F1C_RC=3667e8dc, DF:409fe63f(18) +F(00,10)::VEC(0, 10, ffffffff, 0):: FNV :00000010, DF=0000001f(05) DS(00000001), FNV1 :10001930, DF=1f000ead(13) DS(01000193), FNV1a:01767e9d, DF=0300fead(14) DS(01000193), FNV1c:0210b6df, DF=15006131(09) DS(1500210f), F___RC=c4dad246, DF:2e079ee9(17) , F1__RC=2a790432, DF:4326b34f(16) , F1A_RC=d10adebd, DF:de42883f(16) , F1C_RC=1ce48e12, DF:2a8366ce(15) +``` + +`F(00,01)` : is input x,y + +`VEC(0, 1, ffffffff, 0)` : is `fnv_reduce` input vector (a,b,c,d) + +`FNV :00000001, DF=00000001(01) DS(00000001)` : + * `FNV(00,01)` result is 00000001 , + * `DF` : is changed bitcounts, compared with previous outputs, in this case prev[00,00] current[00,01] input is 1bit changed, and output result 1bit changed. + * `DS` : is distances of previous result and current result , ABS(prev_fnvresult,current_fnvresult). + +** Basically, `DF` is higher is best on hash algorithm. + +`F___RC=fefffe6d, DF:111e47a9(14)` : `fnv_reduce = fnv(fnv(fnv(a,b),c),d) ` result is fefffe6d , and Different Bits counts are `14` bits. + + +## Rationale + +In case of ethash algorithm, it can't prevent ASIC forever. + +And, current ethash algorithm's FNV function is deprecated. + +So, It needs to be upgraded and it will make current ethash based ASICs obsolete. + +And current TETHASHV1 FNV1A implementation is based on most of ethash , which is verified for a long time. + +Another propose of big differencing the Ethash algorithm need to crypto analysis for a long times and need to GPU code optimization times. + +**Verification and Optimization timeline Examples** + +original ethminer (2015) -> claymore optimized miner (2016) [1year] + +genoil ethminer (2015) -> ethereum-mining/ethminer (2017) [2year] + +## Test Results:: + +Tethash miner has 2~3% of hashrate degrade on GPU, due to more core computation time. + +## Copyright + +This work is licensed under a [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-nc-sa/4.0/). diff --git a/EIPS/eip-1491.md b/EIPS/eip-1491.md new file mode 100644 index 0000000..9007dcb --- /dev/null +++ b/EIPS/eip-1491.md @@ -0,0 +1,528 @@ +--- +eip: 1491 +title: Human Cost Accounting Standard (Like Gas but for humans) +author: Iamnot Chris (@cohabo) +discussions-to: https://github.com/freeworkculture/kazini/issues/11 +status: Stagnant +type: Standards Track +category: ERC +created: 2018-10-12 +--- + +## Simple Summary +A standard interface for Human Capital Accounting tokens. + +## Abstract +The following standard allows for the implementation of a standard API for HUCAP tokens within smart contracts. This standard provides basic functionality to discover, track and transfer the motivational hierarchy of human resources. While blockchain architecture has succeeded in the financialisation of integrity by way of transparency; correspondingly real world outcomes will be proportional to the degree of individualisation of capital by way of knowledge. + +## Motivation +The Ethereum protocol architecture has a deterministic world-view bounded to the random reality of the human domain that supplies the intentions and logic. The yellow paper formally defines the EVM as a state machine with only deterministic parameters and state transition operators. Oracle requests to another on-chain contract, and/or off-chain HTTP lookups still make for multiple deterministic transactions. + +A standard interface that allows the appraisal of individual capabilities concurrently with output and the overall knowledge-base will reduce market search costs and increase the autonomous insertion of mindful innovation into the blockchain ecosystem. We provide for simple smart contracts to define and track an arbitrarily large number of HUCAP assets. Additional applications are discussed below. + +The Belief-Desire-Intention model is a plan-theoretic framework for establishing means-end coherence in agent based modelling system. +The blockchain's cryptographic security architecture reliably scales to a blockchain based PKI web-of-trust hierarchies. +ERC-20 token standard allows any tokens on Ethereum to be re-used by other applications: from wallets to decentralized exchanges. +ERC-721 token standard allows wallet/broker/auction applications to work with any NFT on Ethereum. +ERC-1155 Crypto Item standard allows a smart contract interface where one can represent any number of ERC-20 and ERC-721 assets in a single contract. + +This standard is inspired by the belief–desire–intention (BDI) model of human practical reasoning developed by Michael Bratman as a way of explaining future-directed intention. A BDI agent is a particular type of bounded rational software agent, imbued with particular mental attitudes, viz: Beliefs, Desires and Intentions (BDI). The model identifies commitment as the distinguishing factor between desire and intention, and a noteworthy property that leads to (1) temporal persistence in plans and in the sense of explicit reference to time, (2) further plans being made on the basis of those to which it is already committed, (3) hierarchical nature of plans, since the overarching plan remains in effect while subsidiary plans are being executed. + +The BDI software model is an attempt to solve a problem of plans and planning choice and the execution thereof. The complement of which tenders a sufficient metric for indicating means-end coherence and ascribing cost baselines to such outcomes. + +## Specification + +#### Main Interface +```solidity +pragma solidity ^0.4.25; +pragma experimental ABIEncoderV2; + +/** + @title ERC-**** Human Capital Accounting Standard + @dev See https://github.com/freeworkculture/kazini/issues/11 + Note: the ERC-165 identifier for this interface is 0xf23a6e61. + */ + +interface IERC_HUCAP { + + /** + @notice Compute the index value of an Agents BDI in the ecosystem. + @param _address Set the stance of an agent + @dev For the purpose of + Throws on any error rather than return a false flag to minimize user errors + */ + function updateIndex() internal returns (bool); + + /** + @notice Get the active/inactive and states of an Agent in the ecosystem. + @param _address Set the stance of an agent + @dev For the purpose of + Throws on any error rather than return a false flag to minimize user errors + */ + function iam() view public returns (bool iam_, IERC_HUCAP_TYPES.IS state_); + + /** + @notice Fetch the bdi index value of an Agent in the ecosystem. + @param _address Set the stance of an agent + @dev For the purpose of + Throws on any error rather than return a false flag to minimize user errors + */ + function index() view public returns (uint8 index_); + + /** + @notice Count of Public Keys in key ring of an Agent in the ecosystem. + @param _address Set the stance of an agent + @dev For the purpose of + Throws on any error rather than return a false flag to minimize user errors + */ + function ringLength() view public returns (uint ringlength_); + + /** + @notice Get the PGP Public Key Id of an Agent in the ecosystem. + @param "" Set the stance of an agent + @dev For the purpose of + Throws on any error rather than return a false flag to minimize user errors + */ + function keyId() view public returns (bytes32 KEYID_); + + /** + @notice Get the merit data of an Agent in the ecosystem. + @param "" Set the stance of an agent + @dev For the purpose of + Throws on any error rather than return a false flag to minimize user errors + */ + function merits() view public returns ( + uint experience_, + bytes32 reputation_, + bytes32 talent_, + uint8 index_, + bytes32 hash_); + + /** + @notice Get the accreditation of an Agent in the ecosystem. + @param "" Set the stance of an agent + @dev For the purpose of + Throws on any error rather than return a false flag to minimize user errors + */ + function kbase() view public returns (IERC_HUCAP_TYPES.KBase kbase_); + + /** + @notice Get the desire of an Agent in the ecosystem. + @param _desire Pro-attitude + @dev For the purpose of + Throws on any error rather than return a false flag to minimize user errors + + */ + function desire(bytes1 _desire) view external returns (bytes32); + + /** + @notice Get the intention of an Agent in the ecosystem. + @param _intention Conduct-controlling pro-attitude + @dev For the purpose of + Throws on any error rather than return a false flag to minimize user errors + + */ + function intention(bool _intention) view external returns (bytes32); + + /** + @notice Cycle the intention of an Agent in the ecosystem. + @param _intention Conduct-controlling pro-attitude + @dev For the purpose of + Throws on any error rather than return a false flag to minimize user errors + */ + function flipIntention() external returns (bool); + + + /** + @notice Get the user data of an Agent in the ecosystem. + @param "" Conduct-controlling pro-attitude + @dev For the purpose of + Throws on any error rather than return a false flag to minimize user errors + */ + function getDoer() view external returns ( + bytes32 fPrint, + bool iam_, + bytes32 email, + bytes32 fName, + bytes32 lName, + uint age, + bytes32 data_); + + /** + @notice Get the belief data of an Agent in the ecosystem. + @param _kbase Source address + @dev For the purpose of + Throws on any error rather than return a false flag to minimize user errors + + */ + function getBelief(IERC_HUCAP_TYPES.KBase _kbase) view external returns ( + bytes32 country_, + bytes32 cAuthority_, + bytes32 score_); + + /** + @notice Get the desire data of an Agent in the ecosystem. + @param _desire Pro-attitides + @dev For the purpose of + Throws on any error rather than return a false flag to minimize user errors + + */ + function getDesire(bytes1 _desire) view external returns (bytes32,bool); + + /** + @notice Get the intention of an Agent in the ecosystem. + @param _intention Conduct-controlling pro-attitude + @dev For the purpose of + Throws on any error rather than return a false flag to minimize user errors + + */ + function getIntention(bool _intention) view external returns (IERC_HUCAP_TYPES.IS,bytes32,uint256); + + /** + @notice Sign the Public Key of an Agent in the ecosystem. + @param _address Address of key to sign, must belong to an Agent + @dev For the purpose of + Throws on any error rather than return a false flag to minimize user errors + + */ + function sign(address _address) public onlyOwner returns (uint, bool signed); + + /** + @notice Sign the Public Key of an Agent in the ecosystem. + @param "" internal helper function to add key in keyring + @dev For the purpose of + Throws on any error rather than return a false flag to minimize user errors + */ + function sign() external onlyDoer returns (uint, bool signed); + + /** + @notice Revoke the Public Key of an Agent in the ecosystem. + @param _address Address of key to revoke, must belong to an Agent + @dev For the purpose of + Throws on any error rather than return a false flag to minimize user errors + + */ + function revoke(address _address) external onlyDoer returns (uint, bool revoked); + + /** + @notice Revoke the Public Key of an Agent in the ecosystem. + @param "" internal helper function to remove key from keyring + @dev For the purpose of + Throws on any error rather than return a false flag to minimize user errors + */ + function revoke() external onlyDoer returns (uint, bool revoked); + + /** + @notice Set the trust level for a Public Key of an Agent in the ecosystem. + @param _level Degree of trust + @dev For the purpose of + Throws on any error rather than return a false flag to minimize user errors + + */ + function trust(Trust _level) returns (bool); + + /** + @notice Increment the number of keys in the keyring of an Agent in the ecosystem. + @param _keyd Target key + @dev For the purpose of + Throws on any error rather than return a false flag to minimize user errors + + */ + function incSigns(bytes32 _keyd) external ProxyKey returns (uint); + + /** + @notice Decrement the number of keys in the keyring of an Agent in the ecosystem. + @param _keyd Target key + @dev For the purpose of + Throws on any error rather than return a false flag to minimize user errors + + */ + function decSigns(bytes32 _keyd) external ProxyKey returns (uint); + + /** + @notice Set the knowledge credentials of an Agent in the ecosystem. + @param _kbase Level of accreditation + @param _country Source country + @param _cAuthority Accreditation authority + @param _score Accreditation + @param _year Year of Accreditation + @dev For the purpose of + Throws on any error rather than return a false flag to minimize user errors + */ + function setbdi( + KBase _kbase, + bytes32 _country, + bytes32 _cAuthority, + bytes32 _score, + uint _year + ) external ProxyBDI returns (bool qualification_); + + /** + @notice Set the SNA metrics of an Agent in the ecosystem + @param _refMSD Minimum shortest distance + @param _refRank Rank of target key + @param _refSigned No of keys signed I have signed + @param _refSigs No. of keys that have signed my key + @param _refTrust Degree of tructThrows on any error rather than return a false flag to minimize user errors + @dev For the purpose of + Throws on any error rather than return a false flag to minimize user errors + */ + function setbdi( + uint _refMSD, + uint _refRank, + uint _refSigned, + uint _refSigs, + bytes32 _refTrust + ) external ProxyBDI returns (bool reputation_); + + /** + @notice Set the talents of an Agent in the ecosystem + @param _talent Agent's talent + @dev For the purpose of + Throws on any error rather than return a false flag to minimize user errors + */ + function setbdi(bytes32 _talent) external ProxyBDI returns (bool talent_); + + /** + @notice Set the desires of an Agent in the ecosystem + @param _desire Pro-attitude + @param _goal A goal is an instatiated pro-attitude + @dev For the purpose of + Throws on any error rather than return a false flag to minimize user errors + */ + function setbdi(bytes1 _desire, Desire _goal) public onlyDoer returns (bool); + + /** + @notice Set the intention of an Agent in the ecosystem + @param _service Conducting-controlling pro-attitude + @dev For the purpose of + Throws on any error rather than return a false flag to minimize user errors + */ + function setbdi(Intention _service) public onlyDoer returns (bool); + + /** + @notice Set the targeted intention of an Agent in the ecosystem. + @param _intention Conduct-controlling pro-attitude + @param _state Agent stance + @dev For the purpose of + Throws on any error rather than return a false flag to minimize user errors + + */ + function intention(bool _intention, IERC_HUCAP_TYPES.IS _state) external returns (IERC_HUCAP_TYPES.IS); + +/* End of interface IERC_HUCAP */ +} + + +``` +#### User Defined Types Extension Interface + +```solidity + +interface IERC_HUCAP_TYPES { + +/* Enums*/ + + // Weights 1, 2, 4, 8, 16, 32, 64, 128 256 + enum KBase {PRIMARY,SECONDARY,TERTIARY,CERTIFICATION,DIPLOMA,LICENSE,BACHELOR,MASTER,DOCTORATE} + + + enum IS { CLOSED, CREATOR, CURATOR, ACTIVE, INACTIVE, RESERVED, PROVER } + +/* Structus */ + + struct Clearance { + bytes32 Zero; + bytes32 Unknown; + bytes32 Generic; + bytes32 Poor; + bytes32 Casual; + bytes32 Partial; + bytes32 Complete; + bytes32 Ultimate; + } +/* End of interface IERC_HUCAP_TYPES */ +} + +``` +#### Web-of-trust Extension Interface + +```solidity +pragma solidity ^0.4.25; +pragma experimental ABIEncoderV2; + +interface IERC_HUCAP_KEYSIGNING_EXTENSION { + + bytes32 constant public _InterfaceId_ERC165_ = "CREATOR 0.0118 XOR OF ALL FUNCTIONS IN THE INTERFACE"; // Complies to ERC165 + +// KEY MASKING TABLE +// bytes32 constant public MASK = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; +// bytes32 constant public KEYID = 0xffffffffffffffffffffffffffffffffff90EBAC34FC40EAC30FC9CB464A2E56; // EXAMPLE PGP PUBLIC KEY ID +// bytes32 constant public KEY_CERTIFICATION = 0x01ffffffffffffff << 192; // “C” Key Certification +// bytes32 constant public SIGN_DATA = 0x02ffffffffffffff << 192; // “S” Sign Data +// bytes32 constant public ENCRYPT_COMMUNICATIONS = 0x04ffffffffffffff << 192; // “E” Encrypt Communications +// Clearance constant public Trust = 0x03ff << 192; // Trust: Unknown + // BYTES32 Value with + // Public Key Id, masking + // Key Certification masking + // Split Key masking + // Generic masking + // Ordinary masking + // Trust.Unknown masking + // bytes32 constant public DOER = 0x11ff10ff100f03ffff00ffffffffffffffff90EBAC34FC40EAC30FC9CB464A2E56; + + bytes32 constant public KEY_CERTIFICATION = 0x01ffffffffffffff << 192; // “C” Key Certification + bytes32 constant public SIGN_DATA = 0x02ffffffffffffff << 192; // “S” Sign Data + bytes32 constant public ENCRYPT_COMMUNICATIONS = 0x04ffffffffffffff << 192; // “E” Encrypt Communications + bytes32 constant public ENCRYPT_STORAGE = 0x08ffffffffffffff << 192; // “E” Encrypt Storage + bytes32 constant public SPLIT_KEY = 0x10ffffffffffffff << 192; // Split key + bytes32 constant public AUTHENTICATION = 0x20ffffffffffffff << 192; // “A” Authentication + bytes32 constant public MULTI_SIGNATURE = 0x80ffffffffffffff << 192; // Held by more than one person + bytes32 constant public TRUST_AMOUNT = 0xffffffffffff00ff << 192; + bytes32 constant public BINARY_DOCUMENT = 0xffff00ffffffffff << 192; // 0x00: Signature of a binary document. + bytes32 constant public CANONICAL_DOCUMENT = 0xffff01ffffffffff << 192; // 0x01: Signature of a canonical text document. + bytes32 constant public STANDALONE_SIGNATURE = 0xffff02ffffffffff << 192; // 0x02: Standalone signature. + bytes32 constant public GENERIC = 0xffff10ffffffffff << 192; // 0x10: Generic certification of a User ID and Public-Key packet. + bytes32 constant public PERSONA = 0xffff11ffffffffff << 192; // 0x11: Persona certification of a User ID and Public-Key packet. + bytes32 constant public CASUAL = 0xffff12ffffffffff << 192; // 0x12: Casual certification of a User ID and Public-Key packet. + bytes32 constant public POSITIVE = 0xffff13ffffffffff << 192; // 0x13: Positive certification of a User ID and Public-Key packet. + bytes32 constant public SUBKEY_BINDING = 0xffff18ffffffffff << 192; // 0x18: Subkey Binding Signature + bytes32 constant public PRIMARY_KEY_BINDING = 0xffff19ffffffffff << 192; // 0x19: Primary Key Binding Signature + bytes32 constant public DIRECTLY_ON_KEY = 0xffff1Fffffffffff << 192; // 0x1F: Signature directly on a key + bytes32 constant public KEY_REVOCATION = 0xffff20ffffffffff << 192; // 0x20: Key revocation signature + bytes32 constant public SUBKEY_REVOCATION = 0xffff28ffffffffff << 192; // 0x28: Subkey revocation signature + bytes32 constant public CERTIFICATION_REVOCATION = 0xffff30ffffffffff << 192; // 0x30: Certification revocation signature + bytes32 constant public TIMESTAMP = 0xffff40ffffffffff << 192; // 0x40: Timestamp signature. + bytes32 constant public THIRD_PARTY_CONFIRMATION = 0xffff50ffffffffff << 192; // 0x50: Third-Party Confirmation signature. + bytes32 constant public ORDINARY = 0xffffffff100fffff << 192; + bytes32 constant public INTRODUCER = 0xffffffff010fffff << 192; + bytes32 constant public ISSUER = 0xffffffff001fffff << 192; + +// EDGES MASKING TABLE + Clearance internal TRUST = Clearance({ + Zero: 0x01ff << 192, + Unknown: 0x03ff << 192, + Generic: 0x07ff << 192, + Poor: 0xF0ff << 192, + Casual: 0xF1ff << 192, + Partial: 0xF3ff << 192, + Complete: 0xF7ff << 192, + Ultimate: 0xFFff << 192 + }); + + /** + /// @notice Cycle through state transition of an Agent in the ecosystem. + /// @param _address toggle on/off a doer agent + // @dev `anybody` can retrieve the talent data in the contract + */ + function flipTo(address _address) external onlyOwner returns (IS); + + /** + /// @notice Turn Agent in the ecosystem to on/off. + /// @param _address toggle on/off a doer agent + // @dev `anybody` can retrieve the talent data in the contract + */ + function toggle(address _address) external onlyOwner returns (bool); + + /** + /// @notice Set the trust level of an Agent in the ecosystem. + /// @param _level toggle on/off a doer agent + // @dev `anybody` can retrieve the talent data in the contract + */ + function trust(Trust _level) returns (bytes32 Trust); + + event LogCall(address indexed from, address indexed to, address indexed origin, bytes _data); + +/* End of interface IERC_HUCAP_KEYSIGNING_EXTENSION */ +} + +``` +#### Human Capital Accounting Extension Interface + +```solidity +pragma solidity ^0.4.25; +pragma experimental ABIEncoderV2; + +interface IERC_HUCAP_TRACKUSERS_EXTENSION { + + /// @notice Instantiate an Agent in the ecosystem with default data. + /// @param _address initialise a doer agent + // @dev `anybody` can retrieve the talent data in the contract + function initAgent(Doers _address) external onlyControlled returns (bool); + + /// @notice Get the data by uuid of an Agent in the ecosystem. + /// @param _uuid Get the address of a unique uid + // @dev `anybody` can retrieve the talent data in the contract + function getAgent(bytes32 _uuid) view external returns (address); + + /// @notice Get the data of all Talents in the ecosystem. + /// @param _address Query if address belongs to an agent + // @dev `anybody` can retrieve the talent data in the contract + function iam(address _address) view public returns (bool); + + /// @notice Get the data of all Talents in the ecosystem. + /// @param _address Query if address belongs to a doer + // @dev `anybody` can retrieve the talent data in the contract + function isDoer(address _address) view public returns (IS); + + /// @notice Get the number of doers that can be spawned by a Creators. + /// The query condition of the contract + // @dev `anybody` can retrieve the count data in the contract + function getAgent(address _address) + view public returns (bytes32 keyid_, IS state_, bool active_, uint myDoers_); + + /// @notice Get the data of all Talents in the ecosystem. + /// @param _talent The talent whose frequency is being queried + // @dev `anybody` can retrieve the talent data in the contract + function getTalents(bytes32 _talent) + view external returns (uint talentK_, uint talentI_, uint talentR_, uint talentF_); + + /// @notice Increment a kind of talent in the ecosystem. + /// @param The talent whose frequency is being queried + // @dev `anybody` can retrieve the talent data in the contract + function incTalent() payable public onlyDoer returns (bool); + + /// @notice Decrement a kind of talent in the ecosystem.. + /// @param The talent whose frequency is being queried + // @dev `anybody` can retrieve the talent data in the contract + function decTalent() payable public onlyDoer returns (bool); + + /// @notice Set the Public-Key Id of an Agent in the ecosystem. + /// @param _address Set the Public-key Id of an agent + // @dev `anybody` can retrieve the talent data in the contract + function setAgent(address _address, bytes32 _keyId) external onlyControlled returns (bytes32); + + /// @notice Transition the states of an Agent in the ecosystem. + /// @param _address Set the stance of an agent + // @dev `anybody` can retrieve the talent data in the contract + function setAgent(address _address, IS _state) external onlyControlled returns (IS); + + /// @notice Set the active status of an Agent in the ecosystem. + /// @param _address Toggle the true/false status of an agent + // @dev `anybody` can retrieve the talent data in the contract + function setAgent(address _address, bool _active) external onlyControlled returns (bool); + + /// @notice Set the data of all Intentions of Agents in the ecosystem. + /// @param _serviceId Track number of offers available + // @dev `anybody` can retrieve the talent data in the contract + function setAllPromises(bytes32 _serviceId) external onlyControlled; + +/* End of interface IERC_HUCAP_TRACKUSERS_EXTENSION */ +} + + +``` +## Rationale +[WIP] + +## Backwards Compatibility +[WIP] + +## Test Cases +[WIP] + +## Implementation +[WIP] + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-150.md b/EIPS/eip-150.md new file mode 100644 index 0000000..34acf72 --- /dev/null +++ b/EIPS/eip-150.md @@ -0,0 +1,73 @@ +--- +eip: 150 +title: Gas cost changes for IO-heavy operations +author: Vitalik Buterin (@vbuterin) +type: Standards Track +category: Core +status: Final +created: 2016-09-24 +--- + +### Meta reference + +[Tangerine Whistle](./eip-608.md). + +### Parameters + +| FORK_BLKNUM | CHAIN_ID | CHAIN_NAME | +|-----------------|------------|-------------| +| 2,463,000 | 1 | Main net | + +### Specification + +If `block.number >= FORK_BLKNUM`, then: +- Increase the gas cost of EXTCODESIZE to 700 (from 20). +- Increase the base gas cost of EXTCODECOPY to 700 (from 20). +- Increase the gas cost of BALANCE to 400 (from 20). +- Increase the gas cost of SLOAD to 200 (from 50). +- Increase the gas cost of CALL, DELEGATECALL, CALLCODE to 700 (from 40). +- Increase the gas cost of SELFDESTRUCT to 5000 (from 0). +- If SELFDESTRUCT hits a newly created account, it triggers an additional gas cost of 25000 (similar to CALLs). +- Increase the recommended gas limit target to 5.5 million. +- Define "all but one 64th" of `N` as `N - floor(N / 64)`. +- If a call asks for more gas than the maximum allowed amount (i.e. the total amount of gas remaining in the parent after subtracting the gas cost of the call and memory expansion), do not return an OOG error; instead, if a call asks for more gas than all but one 64th of the maximum allowed amount, call with all but one 64th of the maximum allowed amount of gas (this is equivalent to a version of EIP-90[1](https://github.com/ethereum/EIPs/issues/90) plus EIP-114[2](https://github.com/ethereum/EIPs/issues/114)). CREATE only provides all but one 64th of the parent gas to the child call. + +That is, substitute: + +``` + extra_gas = (not ext.account_exists(to)) * opcodes.GCALLNEWACCOUNT + \ + (value > 0) * opcodes.GCALLVALUETRANSFER + if compustate.gas < gas + extra_gas: + return vm_exception('OUT OF GAS', needed=gas+extra_gas) + submsg_gas = gas + opcodes.GSTIPEND * (value > 0) +``` + +With: + +``` + def max_call_gas(gas): + return gas - (gas // 64) + + extra_gas = (not ext.account_exists(to)) * opcodes.GCALLNEWACCOUNT + \ + (value > 0) * opcodes.GCALLVALUETRANSFER + if compustate.gas < extra_gas: + return vm_exception('OUT OF GAS', needed=extra_gas) + if compustate.gas < gas + extra_gas: + gas = min(gas, max_call_gas(compustate.gas - extra_gas)) + submsg_gas = gas + opcodes.GSTIPEND * (value > 0) +``` + +### Rationale + +Recent denial-of-service attacks have shown that opcodes that read the state tree are under-priced relative to other opcodes. There are software changes that have been made, are being made and can be made in order to mitigate the situation; however, the fact will remain that such opcodes will be by a substantial margin the easiest known mechanism to degrade network performance via transaction spam. The concern arises because it takes a long time to read from disk, and is additionally a risk to future sharding proposals as the "attack transactions" that have so far been most successful in degrading network performance would also require tens of megabytes to provide Merkle proofs for. This EIP increases the cost of storage reading opcodes to address this concern. The costs have been derived from an updated version of the calculation table used to generate the 1.0 gas costs: https://docs.google.com/spreadsheets/d/15wghZr-Z6sRSMdmRmhls9dVXTOpxKy8Y64oy9MvDZEQ/edit#gid=0; the rules attempt to target a limit of 8 MB of data that needs to be read in order to process a block, and include an estimate of 500 bytes for a Merkle proof for SLOAD and 1000 for an account. + +This EIP aims to be simple, and adds a flat penalty of 300 gas on top of the costs calculated in this table to account for the cost of loading the code (~17–21 kb in the worst case). + +The EIP 90 gas mechanic is introduced because without it, all current contracts that make calls would stop working as they use an expression like `msg.gas - 40` to determine how much gas to make a call with, relying on the gas cost of calls being 40. Additionally, EIP 114 is introduced because, given that we are making the cost of a call higher and less predictable, we have an opportunity to do it at no extra cost to currently available guarantees, and so we also achieve the benefit of replacing the call stack depth limit with a "softer" gas-based restriction, thereby eliminating call stack depth attacks as a class of attack that contract developers have to worry about and hence increasing contract programming safety. Note that with the given parameters, the de-facto maximum call stack depth is limited to ~340 (down from ~1024), mitigating the harm caused by any further potential quadratic-complexity DoS attacks that rely on calls. + +The gas limit increase is recommended so as to preserve the de-facto transactions-per-second processing capability of the system for average contracts. + +## References + +1. EIP-90, https://github.com/ethereum/EIPs/issues/90 +2. EIP-114, https://github.com/ethereum/EIPs/issues/114 diff --git a/EIPS/eip-1504.md b/EIPS/eip-1504.md new file mode 100644 index 0000000..afed611 --- /dev/null +++ b/EIPS/eip-1504.md @@ -0,0 +1,351 @@ +--- +eip: 1504 +title: Upgradable Smart Contract +author: Kaidong Wu , Chuqiao Ren , Ruthia He , Yun Ma , Xuanzhe Liu +discussions-to: https://github.com/ethereum/EIPs/issues/1503 +status: Stagnant +type: Standards Track +category: ERC +created: 2018-10-17 +--- + +## Simple Summary + +A standard interface/guideline that makes a smart contract upgradable. + +## Abstract + +Ethereum smart contracts have suffered a number of security issues in the past few years. The cost of fixing such a bug in smart contract is significant; for example, the consequences of The DAO attack in June 2016 caused tremendous financial loss and the hard fork of Ethereum blockchain. + +The following standard makes it possible to upgrade a standard API within smart contracts. This standard provides basic functionalities to upgrade the operations of the contract without data migration. To ensure the decentralization/community interests, it also contains a voting mechanism to control the upgrading process. + +## Motivation + +Smart contract is immutable after deployment. If any security risk is identified or program bug is detected, developers always have to destruct the old contract, deploy a new one and potentially migrate the data (hard fork) to the new contract. In some cases, deploying a smart contract with bugs and potential security vulnerabilities can cause a significant amount of financial loss. + +We propose this upgradable contract to fix the current situation. With the upgradable contract, developers can deploy a new version of smart contract after previous deployment and retain the data at the same time. + +For example, after an ERC20-compliant token contract is deployed, the users exploit a vulnerability in the source code. Without the support of upgradable contract, developers have to fix this issue by deploy a new, secured contract otherwise the attackers would take advantage of the security hole, which may cause a tremendous financial loss. A challenge is how to migrate data from the old contract to a new one. With the upgradable contract below, this will become relatively easy as developers only have to upgrade the Handler contract to fix bugs while the Data contract will remain the same. + +## Specification + +The upgradable contract consists of three parts: + +- **Handler contract** (implements **Handler interface**) defines operations and provides services. This contract can be upgraded; +- **Data contract** keeps the resources (data) and is controlled by the Handler contract; +- **Upgrader contract (optional)** deals with the voting mechanism and upgrades the Handler contract. The voters are pre-defined by the contract owner. + +> The following codes are exact copies of the [ERC-1504 Upgradable Smart Contract.](https://gist.github.com/swordghost/77c96a972106af6ec6ccea9c2d66e768) + +### Handler contract and Handler interface + +Functions of the Handler contract vary with requirements, so developers would better design interfaces for Handler contracts to limit them and make sure external applications are always supported. + +Below is the specification of Handler interface. In the Handler interface we define the following actions: + +- Initialize the Data contract; +- Register the Upgrader contract address; +- Destruct the Handler contract after upgrading is done; +- Verify the current Handler is the working one → it should always return true. + +Developers have to define their business-related functions as well. + + +```solidity +/// Handler interface. +/// Handler defines business related functions. +/// Use the interface to ensure that your external services are always supported. +/// Because of function live(), we design IHandler as an abstract contract rather than a true interface. +contract IHandler { + + /// Initialize the data contarct. + /// @param _str value of exmStr of Data contract. + /// @param _int value of exmInt of Data contract. + /// @param _array value of exmArray of Data contract. + function initialize (string _str, uint256 _int, uint16 [] _array) public; + + /// Register Upgrader contract address. + /// @param _upgraderAddr address of the Upgrader contract. + function registerUpgrader (address _upgraderAddr) external; + + /// Upgrader contract calls this to check if it is registered. + /// @return if the Upgrader contract is registered. + function isUpgraderRegistered () external view returns(bool); + + /// Handler has been upgraded so the original one has to self-destruct. + function done() external; + + /// Check if the Handler contract is a working Handler contract. + /// It is used to prove the contract is a Handler contract. + /// @return always true. + function live() external pure returns(bool) { + return true; + } + + /** Functions - define functions here */ + + /** Events - add events here */ +} +``` + + +The process of deploying a Handler contract: + +1. Deploy Data contract; +2. Deploy a Handler contract at a given address specified in the Data contract; +3. Register the Handler contract address by calling setHandler() in the Data contract, or use an Upgrader contract to switch the Handler contract, which requires that Data contract is initialized; +4. Initialize Data contract if haven’t done it already. + +### Data Contract + +Below is the specification of Data contract. There are three parts in the Data contract: + +- **Administrator Data**: owner’s address, Handler contract’s address and a boolean indicating whether the contract is initialized or not; +- **Upgrader Data**: Upgrader contract’s address, upgrade proposal’s submission timestamp and proposal’s time period; +- **Resource Data**: all other resources that the contract needs to keep and manage. + + +```solidity +/// Data Contract +contract DataContract { + + /** Management data */ + /// Owner and Handler contract + address private owner; + address private handlerAddr; + + /// Ready? + bool private valid; + + /** Upgrader data */ + address private upgraderAddr; + uint256 private proposalBlockNumber; + uint256 private proposalPeriod; + /// Upgrading status of the Handler contract + enum UpgradingStatus { + /// Can be upgraded + Done, + /// In upgrading + InProgress, + /// Another proposal is in progress + Blocked, + /// Expired + Expired, + /// Original Handler contract error + Error + } + + /** Data resources - define variables here */ + + /** Modifiers */ + + /// Check if msg.sender is the Handler contract. It is used for setters. + /// If fail, throw PermissionException. + modifier onlyHandler; + + /// Check if msg.sender is not permitted to call getters. It is used for getters (if necessary). + /// If fail, throw GetterPermissionException. + modifier allowedAddress; + + /// Check if the contract is working. + /// It is used for all functions providing services after initialization. + /// If fail, throw UninitializationException. + modifier ready; + + /** Management functions */ + + /// Initializer. Just the Handler contract can call it. + /// @param _str default value of this.exmStr. + /// @param _int default value of this.exmInt. + /// @param _array default value of this.exmArray. + /// exception PermissionException msg.sender is not the Handler contract. + /// exception ReInitializationException contract has been initialized. + /// @return if the initialization succeeds. + function initialize (string _str, uint256 _int, uint16 [] _array) external onlyHandler returns(bool); + + /// Set Handler contract for the contract. Owner must set one to initialize the Data contract. + /// Handler can be set by owner or Upgrader contract. + /// @param _handlerAddr address of a deployed Handler contract. + /// @param _originalHandlerAddr address of the original Handler contract, only used when an Upgrader contract want to set the Handler contract. + /// exception PermissionException msg.sender is not the owner nor a registered Upgrader contract. + /// exception UpgraderException Upgrader contract does not provide a right address of the original Handler contract. + /// @return if Handler contract is successfully set. + function setHandler (address _handlerAddr, address _originalHandlerAddr) external returns(bool); + + /** Upgrader contract functions */ + + /// Register an Upgrader contract in the contract. + /// If a proposal has not been accepted until proposalBlockNumber + proposalPeriod, it can be replaced by a new one. + /// @param _upgraderAddr address of a deployed Upgrader contract. + /// exception PermissionException msg.sender is not the owner. + /// exception UpgraderConflictException Another Upgrader contract is working. + /// @return if Upgrader contract is successfully registered. + function startUpgrading (address _upgraderAddr) public returns(bool); + + /// Getter of proposalPeriod. + /// exception UninitializationException uninitialized contract. + /// exception GetterPermissionException msg.sender is not permitted to call the getter. + /// @return this.proposalPeriod. + function getProposalPeriod () public view isReady allowedAddress returns(uint256); + + /// Setter of proposalPeriod. + /// @param _proposalPeriod new value of this.proposalPeriod. + /// exception UninitializationException uninitialized contract. + /// exception PermissionException msg.sender is not the owner. + /// @return if this.proposalPeriod is successfully set. + function setProposalPeriod (uint256 _proposalPeriod) public isReady returns(bool); + + /// Return upgrading status for Upgrader contracts. + /// @param _originalHandlerAddr address of the original Handler contract. + /// exception UninitializationException uninitialized contract. + /// @return Handler contract's upgrading status. + function canBeUpgraded (address _originalHandlerAddr) external view isReady returns(UpgradingStatus); + + /// Check if the contract has been initialized. + /// @return if the contract has been initialized. + function live () external view returns(bool); + + /** Getters and setters of data resources: define functions here */ +} +``` + + +### Upgrader Contract (Optional) + +Handler contract can be upgraded by calling setHandler() of Data contract. If the owner wants to collect ideas from users, an Upgrader contract will help him/her manage voting and upgrading. + +Below is the specification of Upgrader contract: + +- The Upgrader contract has the ability to take votes from the registered voters. + - The contract owner is able to add voters any time before the proposal expires; + - Voter can check the current status of the proposal (succeed or expired). +- Developers are able to delete this Upgrader contract by calling done() any time after deployment. + +The Upgrader contract works as follows: + +1. Verify the Data contract, its corresponding Handler contract and the new Handler contract have all been deployed; +2. Deploy an Upgrader contract using Data contract address, previous Handler contract address and new Handler contract address; +3. Register upgrader address in the new Handler contract first, then the original handler and finally the Data contract; +4. Call startProposal() to start the voting process; +5. Call getResolution() before the expiration; +6. Upgrading succeed or proposal is expired. + +Note: + +- Function done() can be called at any time to let upgrader destruct itself. +- Function status() can be called at any time to show caller status of the upgrader. + + +```solidity +/// Handler upgrader +contract Upgrader { + // Data contract + DataContract public data; + // Original Handler contract + IHandler public originalHandler; + // New Handler contract + address public newHandlerAddr; + + /** Marker */ + enum UpgraderStatus { + Preparing, + Voting, + Success, + Expired, + End + } + UpgraderStatus public status; + + /// Check if the proposal is expired. + /// If so, contract would be marked as expired. + /// exception PreparingUpgraderException proposal has not been started. + /// exception ReupgradingException upgrading has been done. + /// exception ExpirationException proposal is expired. + modifier notExpired { + require(status != UpgraderStatus.Preparing, "Invalid proposal!"); + require(status != UpgraderStatus.Success, "Upgrading has been done!"); + require(status != UpgraderStatus.Expired, "Proposal is expired!"); + if (data.canBeUpgraded(address(originalHandler)) != DataContract.UpgradingStatus.InProgress) { + status = UpgraderStatus.Expired; + require(false, "Proposal is expired!"); + } + _; + } + + /// Start voting. + /// Upgrader must do upgrading check, namely checking if Data contract and 2 Handler contracts are ok. + /// exception RestartingException proposal has been already started. + /// exception PermissionException msg.sender is not the owner. + /// exception UpgraderConflictException another upgrader is working. + /// exception NoPreparationException original or new Handler contract is not prepared. + function startProposal () external; + + /// Anyone can try to get resolution. + /// If voters get consensus, upgrade the Handler contract. + /// If expired, self-destruct. + /// Otherwise, do nothing. + /// exception PreparingUpgraderException proposal has not been started. + /// exception ExpirationException proposal is expired. + /// @return status of proposal. + function getResolution() external returns(UpgraderStatus); + + /// Destruct itself. + /// exception PermissionException msg.sender is not the owner. + function done() external; + + /** Other voting mechanism related variables and functions */ +} +``` + + +### Caveats + +Since the Upgrader contract in [ERC-1504](./eip-1504.md) has a simple voting mechanism, it is prone to all the limitations that the voting contract is facing: + +- The administrator can only be the owner of data and Handler contracts. Furthermore, only the administrator has the power to add voters and start a proposal. +- It requires voters to be constantly active, informative and attentive to make a upgrader succeed. +- The voting will only be valid in a given time period. If in a given time period the contract cannot collect enough “yes” to proceed, the proposal will be marked expired. + +## Rationale + +### Data Contract and Handler Contract + +A smart contract is actually a kind of software, which provides some kind of services. From the perspective of software engineering, a service consists of **resources** that abstract the data and **operations** that abstract the process logic on the data. The requirement of upgrading is mostly on the logic part. Therefore, in order to make a smart contract upgradable, we divide it into two parts: + +1. Data contract keeps the resources; +2. Handler contract contains operations. + +The Handler contract can be upgraded in the future while the Data contract is permanent. Handler contract can manipulate the variables in Data contract through the getter and setter functions provided by Data contract. + +### Upgrader Contract and Voting Mechanism + +In order to prevent centralization and protect the interests of the community and stakeholders, we also design a voting mechanism in the Upgrader contract. Upgrader contract contains addresses of Data contract and two Handler contracts, and collects votes from pre-defined voters to upgrade the Handler contract when the pre-set condition is fulfilled. + +For simplicity, the upgradable contract comes with a very minimal version of the voting mechanism. If the contract owner wants to implement a more complex voting mechanism, he/she can modify the existing voting mechanism to incorporate upgradability. The expiration mechanism (see modifier notExpried in Upgrader contract and related functions in Data contract) and upgrading check (see function startProposal() in Upgrader contract) to the contract are mandatory. + +### Gas and Complexity (regarding the enumeration extension) + +Using an upgrader will cost some gas. If the Handler contract is upgraded by the owner, it just costs gas that a contract call will cost, which is usually significantly lower than creating and deploying a new contract. + +Although upgrading contract may take some efforts and gas, it is a much less painful than deprecating the insecure contract/creating a new contract or hard fork (e.g. DAO attack). Contract creation requires a significant amount of effort and gas. One of the advantages of upgradable contracts is that the contract owners don’t have to create new contracts; instead, they only need to upgrade parts of contract that cause issues, which is less expensive compared to data loss and blockchain inconsistency. In other words, upgradable contracts make Data contract more scalable and flexible. + +### Community Consensus + +Thank you to those who helped on review and revise the proposal: + +- [@lsankar4033](https://github.com/lsankar4033) from MIT +- more + +The proposal is initiated and developed by the team Renaissance and the Research Group of Blockchain System @ Center for Operating System at Peking University. + +We have been very inclusive in this process and invite anyone with questions or contributions into our discussion. However, this standard is written only to support the identified use cases which are listed herein. + +## Implementations + +1. [Renaissance](https://www.renaissance.app) - a protocol that connect creators and fans financially +2. [ERC-1504](./eip-1504.md) - a reference implementation + + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-152.md b/EIPS/eip-152.md new file mode 100644 index 0000000..80aa708 --- /dev/null +++ b/EIPS/eip-152.md @@ -0,0 +1,267 @@ +--- +eip: 152 +title: Add BLAKE2 compression function `F` precompile +author: Tjaden Hess , Matt Luongo (@mhluongo), Piotr Dyraga (@pdyraga), James Hancock (@MadeOfTin) +discussions-to: https://github.com/ethereum/EIPs/issues/152 +status: Final +type: Standards Track +category: Core +created: 2016-10-04 +--- + +## Simple Summary + +This EIP will enable the BLAKE2b hash function and other higher-round 64-bit BLAKE2 variants to run cheaply on the EVM, allowing easier interoperability between Ethereum and Zcash as well as other Equihash-based PoW coins. + +## Abstract + +This EIP introduces a new precompiled contract which implements the compression function `F` used in the BLAKE2 cryptographic hashing algorithm, for the purpose of allowing interoperability between the EVM and Zcash, as well as introducing more flexible cryptographic hash primitives to the EVM. + +## Motivation + +Besides being a useful cryptographic hash function and SHA3 finalist, BLAKE2 allows for efficient verification of the Equihash PoW used in Zcash, making a BTC Relay - style SPV client possible on Ethereum. A single verification of an Equihash PoW verification requires 512 iterations of the hash function, making verification of Zcash block headers prohibitively expensive if a Solidity implementation of BLAKE2 is used. + +BLAKE2b, the common 64-bit BLAKE2 variant, is highly optimized and faster than MD5 on modern processors. + +Interoperability with Zcash could enable contracts like trustless atomic swaps between the chains, which could provide a much needed aspect of privacy to the very public Ethereum blockchain. + +## Specification + +We propose adding a precompiled contract at address `0x09` wrapping the [BLAKE2 `F` compression function](https://tools.ietf.org/html/rfc7693#section-3.2). + +The precompile requires 6 inputs tightly encoded, taking exactly 213 bytes, as explained below. The encoded inputs are corresponding to the ones specified in the [BLAKE2 RFC Section 3.2](https://tools.ietf.org/html/rfc7693#section-3.2): + +- `rounds` - the number of rounds - 32-bit unsigned big-endian word +- `h` - the state vector - 8 unsigned 64-bit little-endian words +- `m` - the message block vector - 16 unsigned 64-bit little-endian words +- `t_0, t_1` - offset counters - 2 unsigned 64-bit little-endian words +- `f` - the final block indicator flag - 8-bit word + +``` +[4 bytes for rounds][64 bytes for h][128 bytes for m][8 bytes for t_0][8 bytes for t_1][1 byte for f] +``` + +The boolean `f` parameter is considered as `true` if set to `1`. +The boolean `f` parameter is considered as `false` if set to `0`. +All other values yield an invalid encoding of `f` error. + +The precompile should compute the `F` function as [specified in the RFC](https://tools.ietf.org/html/rfc7693#section-3.2) and return the updated state vector `h` with unchanged encoding (little-endian). + +### Example Usage in Solidity + +The precompile can be wrapped easily in Solidity to provide a more development-friendly interface to `F`. + +```solidity +function F(uint32 rounds, bytes32[2] memory h, bytes32[4] memory m, bytes8[2] memory t, bool f) public view returns (bytes32[2] memory) { + bytes32[2] memory output; + + bytes memory args = abi.encodePacked(rounds, h[0], h[1], m[0], m[1], m[2], m[3], t[0], t[1], f); + + assembly { + if iszero(staticcall(not(0), 0x09, add(args, 32), 0xd5, output, 0x40)) { + revert(0, 0) + } + } + + return output; +} + +function callF() public view returns (bytes32[2] memory) { + uint32 rounds = 12; + + bytes32[2] memory h; + h[0] = hex"48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5"; + h[1] = hex"d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b"; + + bytes32[4] memory m; + m[0] = hex"6162630000000000000000000000000000000000000000000000000000000000"; + m[1] = hex"0000000000000000000000000000000000000000000000000000000000000000"; + m[2] = hex"0000000000000000000000000000000000000000000000000000000000000000"; + m[3] = hex"0000000000000000000000000000000000000000000000000000000000000000"; + + bytes8[2] memory t; + t[0] = hex"03000000"; + t[1] = hex"00000000"; + + bool f = true; + + // Expected output: + // ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d1 + // 7d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923 + return F(rounds, h, m, t, f); +} +``` + +### Gas costs and benchmarks + +Each operation will cost `GFROUND * rounds` gas, where `GFROUND = 1`. Detailed benchmarks are presented in the benchmarks appendix section. + +## Rationale + +BLAKE2 is an excellent candidate for precompilation. BLAKE2 is heavily optimized for modern 64-bit CPUs, specifically utilizing 24 and 63-bit rotations to allow parallelism through SIMD instructions and little-endian arithmetic. These characteristics provide exceptional speed on native CPUs: 3.08 cycles per byte, or 1 gibibyte per second on an Intel i5. + +In contrast, the big-endian 32 byte semantics of the EVM are not conducive to efficient implementation of BLAKE2, and thus the gas cost associated with computing the hash on the EVM is disproportionate to the true cost of computing the function natively. + +An obvious implementation would be a direct BLAKE2b hash function precompile. At first glance, a BLAKE2b precompile satisfies most hashing and interoperability requirements on the EVM. Once we started digging in, however, it became clear that any BLAKE2b implementation would need specific features and internal modifications based on different projects' requirements and libraries. + +A [thread with the Zcash team](https://github.com/ethereum/EIPs/issues/152#issuecomment-499240310) makes the issue clear. + +> The minimal thing that is necessary for a working ZEC-ETH relay is an implementation of BLAKE2b Compression F in a precompile. + +> A BLAKE2b Compression Function F precompile would also suffice for the Filecoin and Handshake interop goals. + +> A full BLAKE2b precompile would suffice for a ZEC-ETH relay, provided that the implementation provided the parts of the BLAKE2 API that we need (personalization, maybe something else—I'm not sure). + +> I'm not 100% certain if a full BLAKE2b precompile would also suffice for the Filecoin and Handshake goals. It almost certainly could, provided that it supports all the API that they need. + +> BLAKE2s — whether the Compression Function F or the full hash — is only a nice-to-have for the purposes of a ZEC-ETH relay. + +From this and other conversations with teams in the space, we believe we should focus first on the `F` precompile as a strictly necessary piece for interoperability projects. A BLAKE2b precompile is a nice-to-have, and we support any efforts to add one-- but it's unclear whether complete requirements and a flexible API can be found in time for Istanbul. + +Implementation of only the core F compression function also allows substantial flexibility and extensibility while keeping changes at the protocol level to a minimum. This will allow functions like tree hashing, incremental hashing, and keyed, salted, and personalized hashing as well as variable length digests, none of which are currently available on the EVM. + +## Backwards Compatibility + +There is very little risk of breaking backwards-compatibility with this EIP, the sole issue being if someone were to build a contract relying on the address at `0x09` being empty. The likelihood of this is low, and should specific instances arise, the address could be chosen to be any arbitrary value with negligible risk of collision. + +## Test Cases + +#### Test vector 0 +* input: (empty) +* output: error "input length for BLAKE2 F precompile should be exactly 213 bytes" + +#### Test vector 1 +* input: +`00000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001` +* output: error "input length for BLAKE2 F precompile should be exactly 213 bytes" + +#### Test vector 2 +* input: +`000000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001` +* output: error "input length for BLAKE2 F precompile should be exactly 213 bytes" + +#### Test vector 3 +* input: +`0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000002` +* output: error "incorrect final block indicator flag" + +#### Test vector 4 +* input: +`0000000048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001` +* output: +`08c9bcf367e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d282e6ad7f520e511f6c3e2b8c68059b9442be0454267ce079217e1319cde05b` + +#### Test vector 5 +* input: +`0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001` +* output: `ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923` + +#### Test vector 6 +* input: +`0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000` +* output: +`75ab69d3190a562c51aef8d88f1c2775876944407270c42c9844252c26d2875298743e7f6d5ea2f2d3e8d226039cd31b4e426ac4f2d3d666a610c2116fde4735` + +#### Test vector 7 +* input: +`0000000148c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001` +* output: +`b63a380cb2897d521994a85234ee2c181b5f844d2c624c002677e9703449d2fba551b3a8333bcdf5f2f7e08993d53923de3d64fcc68c034e717b9293fed7a421` + +#### Test vector 8 +* input: +`ffffffff48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001` +* output: +`fc59093aafa9ab43daae0e914c57635c5402d8e3d2130eb9b3cc181de7f0ecf9b22bf99a7815ce16419e200e01846e6b5df8cc7703041bbceb571de6631d2615` + +## Implementation + +An initial implementation of the `F` function in Go, adapted from the standard library, can be found in our [Golang BLAKE2 library fork](https://github.com/keep-network/blake2-f). There's also an implementation of the precompile in our fork of [go-ethereum](https://github.com/keep-network/go-ethereum/pull/4). + +## References + +For reference, further discussion on this EIP also occurred in the following PRs and issues + + * [Original Issue](https://github.com/ethereum/EIPs/issues/152) + * [Ethereum Magicians](https://ethereum-magicians.org/t/blake2b-f-precompile/3157) + * [PR 2129](https://github.com/ethereum/EIPs/pull/2129) + +## Appendix - benchmarks + +Assuming ecRecover precompile is perfectly priced, we executed a set of benchmarks comparing Blake2b F compression function precompile with ecRecover precompile. For benchmarks, we used 3.1 GHz Intel Core i7 64-bit machine. + +```sh +$ sysctl -n machdep.cpu.brand_string +Intel(R) Core(TM) i7-7920HQ CPU @ 3.10GHz +``` + +### 12 rounds + +An average gas price of F precompile call with 12 rounds compared to ecRecover should have been `6.74153` and it gives `0.5618` gas per round. + +``` +Name Gascost Time (ns) MGas/S Gasprice for 10MGas/S Gasprice for ECDSA eq +----------------------------------------- --------- ---------------- --------- ----------------------- ----------------------- +PrecompiledEcrecover/ 3000 152636 19.6546 1526.36 3000 +PrecompiledBlake2F/testVectors2bX_0 12 338 35.503 3.38 6.64326 +PrecompiledBlake2F/testVectors2bX_3 12 336 35.7143 3.36 6.60395 +PrecompiledBlake2F/testVectors2bX_70 12 362 33.1492 3.62 7.11497 +PrecompiledBlake2F/testVectors2bX_140 12 339 35.3982 3.39 6.66291 +PrecompiledBlake2F/testVectors2bX_230 12 339 35.3982 3.39 6.66291 +PrecompiledBlake2F/testVectors2bX_300 12 343 34.9854 3.43 6.74153 +PrecompiledBlake2F/testVectors2bX_370 12 336 35.7143 3.36 6.60395 +PrecompiledBlake2F/testVectors2bX_440 12 337 35.6083 3.37 6.6236 +PrecompiledBlake2F/testVectors2bX_510 12 345 34.7826 3.45 6.78084 +PrecompiledBlake2F/testVectors2bX_580 12 355 33.8028 3.55 6.97738 +``` + +Columns + +* `MGas/S` - Shows what MGas per second was measured on that machine at that time +* `Gasprice for 10MGas/S` shows what the gasprice should have been, in order to reach 10 MGas/second +* `Gasprice for ECDSA eq` shows what the gasprice should have been, in order to have the same cost/cycle as ecRecover + +### 1200 rounds + +An average gas price of F precompile call with 1200 rounds compared to ecRecover should have been `436.1288` and it gives `0.3634` gas per round. + +``` +Name Gascost Time (ns) MGas/S Gasprice for 10MGas/S Gasprice for ECDSA eq +----------------------------------------- --------- ---------------- --------- ----------------------- ----------------------- +PrecompiledEcrecover/ 3000 156152 19.212 1561.52 3000 +PrecompiledBlake2F/testVectors2bX_0 1200 22642 52.9989 226.42 434.999 +PrecompiledBlake2F/testVectors2bX_3 1200 22885 52.4361 228.85 439.668 +PrecompiledBlake2F/testVectors2bX_70 1200 22737 52.7774 227.37 436.824 +PrecompiledBlake2F/testVectors2bX_140 1200 22602 53.0926 226.02 434.231 +PrecompiledBlake2F/testVectors2bX_230 1200 22501 53.331 225.01 432.29 +PrecompiledBlake2F/testVectors2bX_300 1200 22435 53.4879 224.35 431.022 +PrecompiledBlake2F/testVectors2bX_370 1200 22901 52.3995 229.01 439.975 +PrecompiledBlake2F/testVectors2bX_440 1200 23134 51.8717 231.34 444.452 +PrecompiledBlake2F/testVectors2bX_510 1200 22608 53.0786 226.08 434.346 +PrecompiledBlake2F/testVectors2bX_580 1200 22563 53.1844 225.63 433.481 +``` + +### 1 round + +An average gas price of F precompile call with 1 round compared to ecRecover should have been `2.431701`. However, in this scenario the call cost would totally overshadow the dynamic cost anyway. + +``` +Name Gascost Time (ns) MGas/S Gasprice for 10MGas/S Gasprice for ECDSA eq +----------------------------------------- --------- ---------------- ---------- ----------------------- ----------------------- +PrecompiledEcrecover/ 3000 157544 19.0423 1575.44 3000 +PrecompiledBlake2F/testVectors2bX_0 1 126 7.93651 1.26 2.39933 +PrecompiledBlake2F/testVectors2bX_3 1 127 7.87402 1.27 2.41837 +PrecompiledBlake2F/testVectors2bX_70 1 128 7.8125 1.28 2.43741 +PrecompiledBlake2F/testVectors2bX_140 1 125 8 1.25 2.38029 +PrecompiledBlake2F/testVectors2bX_230 1 128 7.8125 1.28 2.43741 +PrecompiledBlake2F/testVectors2bX_300 1 127 7.87402 1.27 2.41837 +PrecompiledBlake2F/testVectors2bX_370 1 131 7.63359 1.31 2.49454 +PrecompiledBlake2F/testVectors2bX_440 1 129 7.75194 1.29 2.45646 +PrecompiledBlake2F/testVectors2bX_510 1 125 8 1.25 2.38029 +PrecompiledBlake2F/testVectors2bX_580 1 131 7.63359 1.31 2.49454 +``` + + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1523.md b/EIPS/eip-1523.md new file mode 100644 index 0000000..2469e83 --- /dev/null +++ b/EIPS/eip-1523.md @@ -0,0 +1,125 @@ +--- +eip: 1523 +title: Standard for Insurance Policies as ERC-721 Non Fungible Tokens +author: Christoph Mussenbrock (@christoph2806) +discussions-to: https://github.com/ethereum/EIPs/issues/1523 +status: Stagnant +type: Standards Track +category: ERC +created: 2018-10-10 +requires: 721 +--- + +## Simple Summary +A standard interface for insurance policies, based on ERC 721. + +## Abstract +The following standard allows for the implementation of a standard API for insurance policies within smart contracts. +Insurance policies are financial assets which are unique in some aspects, as they are connected to a customer, a specific risk, or have other unique properties like premium, period, carrier, underwriter etc. +Nevertheless, there are many potential applications where insurance policies can be traded, transferred or otherwise treated as an asset. +The ERC 721 standard already provides the standard and technical means to handle policies as a specific class of non fungible tokens. +insurance In this proposal, we define a minimum metadata structure with properties which are common to the greatest possible class of policies. + +## Motivation +For a decentralized insurance protocol, a standard for insurance policies is crucial for interoperability of the involved services and application. +It allows policies to be bundled, securitized, traded in a uniform and flexible way by many independent actors like syndicates, brokers, and insurance companies. + +## Specification +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. + +An ERC-1523 compliant insurance policy is a non-fungible token which **MUST adhere to the ERC-721 token standard** and **MUST implement theERC721Metadata and the ERC721Enumerable interface**: + +```solidity +/// @title ERC-1523 Insurance Policy Standard +/// Note: the ERC-165 identifier for this interface is 0x5a04be32 +interface ERC1523 /* is ERC721, ERC721Metadata, ERC721Enumerable */ { + +} +``` + +The implementor MAY choose values for the ```name``` and ```symbol```. + +The **policy metadata extension** is **RECOMMENDED** for ERC-1523 smart contracts. +This allows your smart contract to be interrogated for policy metadata. + +```solidity +/// @title ERC-1523 Insurance Policy Standard, optional policy metadata extension +/// @dev See ... +/// Note: the ERC-165 identifier for this interface is 0x5a04be32 +interface ERC1523PolicyMetadata /* is ERC1523 */ { + + /// @notice Metadata string for a given property. + /// Properties are identified via hash of their property path. + /// e.g. the property "name" in the ERC721 Metadata JSON Schema has the path /properties/name + /// and the property path hash is the keccak256() of this property path. + /// this allows for efficient addressing of arbitrary properties, as the set of properties is potentially unlimited. + /// @dev Throws if `_propertyPathHash` is not a valid property path hash. + function policyMetadata(uint256 _tokenId, bytes32 _propertyPathHash) external view returns (string _property); + +} +``` + +In analogy to the “ERC721 Metadata JSON Schema”, the tokenURI **MUST** point to a JSON file with the following properties: +```json +{ + "title": "Asset Metadata", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Identifies the asset to which this NFT represents", + }, + "description": { + "type": "string", + "description": "Describes the asset to which this NFT represents", + }, + \[additional parameters according to the following table\] + } +} +``` + +### Additional parameters for the metadata JSON Schema + +| Parameter | Type | Mandatory | Description | +| ------------- | ------------- | ----------| ---------------------------------------------------------------------------------- | +| carrier | string | yes | Describes the carrier which takes the primary risk | +| risk | string | yes | Describes the risk | +| status | string | yes | Describes the status of the policy, e.g. applied for, underwritten, expired | +| parameters | string | no | Describes further parameters characterizing the risk | +| terms | string | no | Describes legal terms & conditions which apply for this policy | +| premium | string | no | A string representation of the premium, **MAY** contain currency denominator | +| sum_insured | string | no | A string representation of the sum insured, **MAY** contain currency denominator | + +Parameters which are mandatory **MUST** be included in the metadata JSON. Other parameters **MAY** be included. However, the proposed optional parameters **SHOULD** be used for the intended purpose, so e.g. if the premium amount would be included in the metadata, the parameter name **SHOULD** be "premium". +All parameters **MAY** be plain text or **MAY** also be URIs pointing to resources which contain the respective information, and which **MAY** be protected by an authentication mechanism. + +## Rationale +Insurance policies form an important class of financial assets, and it is natural to express those assets as a class of non-fungible tokens which adhere to the established ERC-721 standard. +We propose a standard for the accompanying metadata structures which are needed to uniquely define an insurance policy. Standardization is key because we expect decentralized insurance to receive widespread adoption and it is crucial to establish a unified standard to enable composability and the creation of universal toolsets. +We therefore propose a standardized naming scheme for the different parameters describing an insurance policy. We propose three mandatory parameters which need to be included in every NFT and further parameters which **MAY** be used, and for which we only standardize the naming conventions. +### Mandatory parameters +While policies can have a multitude of possible properties, it is common that policies are issued by some entity, which is basically the entity responsible for paying out claims. +Second, an insurance policy is typically related to a specific risk. Some risks are unique, but there are cases where many policies share the same risk +(e.g. all flight delay policies for the same flight). +In general, the relation of policies to risks is a many-to-one relation with the special case of a one-to-one relation. +Third, a policy has a lifecycle of different statuses. Therefore the NFT +We believe that those four properties are necessary to describe a policy. For many applications, those properties may be even sufficient. + +### Optional parameters +Most policies need more parameters to characterize the risk and other features, like premium, period etc. The naming conventions are listed in the above table. +However, any implementation **MAY** chose to implement more properties. + +### On-chain vs. off-chain metadata +For some applications it will be sufficient to store the metadata in an off-chain repository or database which can be addressed by the tokenURI resource locator. +For more advanced applications, it can be desirable to have metadata available on-chain. +Therefore, we require that the ```tokenURI``` **MUST** point to a JSON with the above structure, while the implementation of the ```policyMetadata``` function is **OPTIONAL**. + + +## Backwards Compatibility + +## Test Cases + +## Implementation + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1538.md b/EIPS/eip-1538.md new file mode 100644 index 0000000..afa991a --- /dev/null +++ b/EIPS/eip-1538.md @@ -0,0 +1,468 @@ +--- +eip: 1538 +title: Transparent Contract Standard +author: Nick Mudge +discussions-to: https://github.com/ethereum/EIPs/issues/1538 +status: Withdrawn +type: Standards Track +category: ERC +created: 2018-10-31 +--- + +Replaced by [EIP-2535 Diamond Standard](./eip-2535.md). + +## Simple Summary +This standard provides a contract architecture that makes upgradeable contracts flexible, unlimited in size, and transparent. + +A transparent contract publicly documents the full history of all changes made to it. + +All changes to a transparent contract are reported in a standard format. + +## Abstract +A transparent contract is a proxy contract design pattern that provides the following: + +1. A way to add, replace and remove multiple functions of a contract atomically (at the same time). +1. Standard events to show what functions are added, replaced and removed from a contract, and why the changes are made. +2. A standard way to query a contract to discover and retrieve information about all functions exposed by it. +3. Solves the 24KB maximum contract size limitation, making the maximum contract size of a transparent contract practically unlimited. This standard makes the worry about contract size a thing of the past. +4. Enables an upgradeable contract to become immutable in the future if desired. + +## Motivation +A fundamental benefit of Ethereum contracts is that their code is immutable, thereby acquiring trust by trustlessness. People do not have to trust others if it is not possible for a contract to be changed. + +However, a fundamental problem with trustless contracts that cannot be changed is that they cannot be changed. + +#### Bugs + +Bugs and security vulnerabilities are unwittingly written into immutable contracts that ruin them. + +#### Improvements + +Immutable, trustless contracts cannot be improved, resulting in increasingly inferior contracts over time. + +Contract standards evolve, new ones come out. People, groups and organizations learn over time what people want and what is better and what should be built next. Contracts that cannot be improved not only hold back the authors that create them, but everybody who uses them. + +#### Upgradeable Contracts vs. Centralized Private Database +Why have an upgradeable contract instead of a centralized, private, mutable database? +Here are some reasons: +1. Because of the openness of storage data and verified code, it is possible to show a provable history of trustworthiness. +2. Because of the openness, bad behavior can be spotted and reported when it happens. +3. Independent security and domain experts can review the change history of contracts and vouch for their history of trustworthiness. +4. It is possible for an upgradeable contract to become immutable and trustless. +5. An upgradeable contract can have parts of it that are not upgradeable and so are partially immutable and trustless. + +#### Immutability + +In some cases immutable, trustless contracts are the right fit. This is the case when a contract is only needed for a short time or it is known ahead of time that there will never be any reason to change or improve it. + +### Middle Ground + +Transparent contracts provide a middle ground between immutable trustless contracts that can't be improved and upgradeable contracts that can't be trusted. + +### Purposes + +1. Create upgradeable contracts that earn trust by showing a provable history of trustworthiness. +2. Document the development of contracts so their development and change is provably public and can be understood. +3. Create upgradeable contracts that can become immutable in the future if desired. +4. Create contracts that are not limited by a max size. + +### Benefits & Use Cases +This standard is for use cases that benefit from the following: +1. The ability to add, replace or remove multiple functions of a contract atomically (at the same time). +2. Each time a function is added, replaced or removed, it is documented with events. +3. Build trust over time by showing all changes made to a contract. +4. Unlimited contract size. +5. The ability to query information about functions currently supported by the contract. +6. One contract address that provides all needed functionality and never needs to be replaced by another contract address. +7. The ability for a contract to be upgradeable for a time, and then become immutable. +8. Add trustless guarantees to a contract with "unchangeable functions". + +### New Software Possibilities + +This standard enables a form of contract version control software to be written. + +Software and user interfaces can be written to filter the `FunctionUpdate` and `CommitMessage` events of a contract address. Such software can show the full history of changes of any contract that implements this standard. + +User interfaces and software can also use this standard to assist or automate changes of contracts. + +## Specification + +> **Note:** +The solidity `delegatecall` opcode enables a contract to execute a function from another contract, but it is executed as if the function was from the calling contract. Essentially `delegatecall` enables a contract to "borrow" another contract's function. Functions executed with `delegatecall` affect the storage variables of the calling contract, not the contract where the functions are defined. + +### General Summary + +A transparent contract delegates or forwards function calls to it to other contracts using `delegatecode`. + +A transparent contract has an `updateContract` function that enables multiple functions to be added, replaced or removed. + +An event is emitted for every function that is added, replaced or removed so that all changes to a contract can be tracked in a standard way. + +A transparent contract is a contract that implements and complies with the design points below. + +### Terms + +1. In this standard a **delegate contract** is a contract that a transparent contract fallback function forwards function calls to using `delegatecall`. +2. In this standard an **unchangeable function** is a function that is defined directly in a transparent contract and so cannot be replaced or removed. + +### Design Points + +A contract is a transparent contract if it implements the following design points: + +1. A transparent contract is a contract that contains a fallback function, a constructor, and zero or more unchangeable functions that are defined directly within it. +2. The constructor of a transparent contract associates the `updateContract` function with a contract that implements the ERC1538 interface. The `updateContract` function can be an "unchangeable function" that is defined directly in the transparent contract or it can be defined in a delegate contract. Other functions can also be associated with contracts in the constructor. +3. After a transparent contract is deployed functions are added, replaced and removed by calling the `updateContract` function. +4. The `updateContract` function associates functions with contracts that implement those functions, and emits the `CommitMessage` and `FunctionUpdate` events that document function changes. +5. The `FunctionUpdate` event is emitted for each function that is added, replaced or removed. The `CommitMessage` event is emitted one time for each time the `updateContract` function is called and is emitted after any `FunctionUpdate` events are emitted. +6. The `updateContract` function can take a list of multiple function signatures in its `_functionSignatures` parameter and so add/replace/remove multiple functions at the same time. +7. When a function is called on a transparent contract it executes immediately if it is an "unchangeable function". Otherwise the fallback function is executed. The fallback function finds the delegate contract associated with the function and executes the function using `delegatecall`. If there is no delegate contract for the function then execution reverts. +8. The source code of a transparent contract and all delegate contracts used by it are publicly viewable and verified. + +The transparent contract address is the address that users interact with. The transparent contract address never changes. Only delegate addresses can change by using the `updateContracts` function. + +Typically some kind of authentication is needed for adding/replacing/removing functions from a transparent contract, **however the scheme for authentication or ownership is not part of this standard**. + +### Example + +Here is an example of an implementation of a transparent contract. Please note that the example below is an **example only. It is not the standard**. A contract is a transparent contract when it implements and complies with the design points listed above. + +```solidity +pragma solidity ^0.5.7; + +contract ExampleTransparentContract { + // owner of the contract + address internal contractOwner; + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); + + // maps functions to the delegate contracts that execute the functions + // funcId => delegate contract + mapping(bytes4 => address) internal delegates; + + // maps each function signature to its position in the funcSignatures array. + // signature => index+1 + mapping(bytes => uint256) internal funcSignatureToIndex; + + event CommitMessage(string message); + event FunctionUpdate(bytes4 indexed functionId, address indexed oldDelegate, address indexed newDelegate, string functionSignature); + + // this is an example of an "unchangeable function". + // return the delegate contract address for the supplied function signature + function delegateAddress(string calldata _functionSignature) external view returns(address) { + require(funcSignatureToIndex[bytes(_functionSignature)] != 0, "Function signature not found."); + return delegates[bytes4(keccak256(bytes(_functionSignature)))]; + } + + // add a function using the updateContract function + // this is an internal helper function + function addFunction(address _erc1538Delegate, address contractAddress, string memory _functionSignatures, string memory _commitMessage) internal { + // 0x03A9BCCF == bytes4(keccak256("updateContract(address,string,string)")) + bytes memory funcdata = abi.encodeWithSelector(0x03A9BCCF, contractAddress, _functionSignatures, _commitMessage); + bool success; + assembly { + success := delegatecall(gas, _erc1538Delegate, add(funcdata, 0x20), mload(funcdata), funcdata, 0) + } + require(success, "Adding a function failed"); + } + + constructor(address _erc1538Delegate) public { + contractOwner = msg.sender; + emit OwnershipTransferred(address(0), msg.sender); + + // adding ERC1538 updateContract function + bytes memory signature = "updateContract(address,string,string)"; + bytes4 funcId = bytes4(keccak256(signature)); + delegates[funcId] = _erc1538Delegate; + emit FunctionUpdate(funcId, address(0), _erc1538Delegate, string(signature)); + emit CommitMessage("Added ERC1538 updateContract function at contract creation"); + + // associate "unchangeable functions" with this transparent contract address + // prevents function selector clashes with delegate contract functions + // uses the updateContract function + string memory functions = "delegateAddress(string)"; + addFunction(_erc1538Delegate, address(this), functions, "Associating unchangeable functions"); + + // adding ERC1538Query interface functions + functions = "functionByIndex(uint256)functionExists(string)delegateAddresses()delegateFunctionSignatures(address)functionById(bytes4)functionBySignature(string)functionSignatures()totalFunctions()"; + // "0x01234567891011121314" is an example address of an ERC1538Query delegate contract + addFunction(_erc1538Delegate, 0x01234567891011121314, functions, "Adding ERC1538Query functions"); + + // additional functions could be added at this point + } + + // Making the fallback function payable makes it work for delegate contract functions + // that are payable and not payable. + function() external payable { + // Delegate every function call to a delegate contract + address delegate = delegates[msg.sig]; + require(delegate != address(0), "Function does not exist."); + assembly { + let ptr := mload(0x40) + calldatacopy(ptr, 0, calldatasize) + let result := delegatecall(gas, delegate, ptr, calldatasize, 0, 0) + let size := returndatasize + returndatacopy(ptr, 0, size) + switch result + case 0 {revert(ptr, size)} + default {return (ptr, size)} + } + } +} +``` +As can be seen in the above example, every function call is delegated to a delegate contract, unless the function is defined directly in the transparent contract (making it an unchangeable function). + +The constructor function adds the `updateContract` function to the transparent contract, which is then used to add other functions to the transparent contract. + +Each time a function is added to a transparent contract the events `CommitMessage` and `FunctionUpdate` are emitted to document exactly what functions where added or replaced and why. + +The delegate contract that implements the `updateContract` function implements the following interface: +### ERC1538 Interface + +```solidity +pragma solidity ^0.5.7; + +/// @title ERC1538 Transparent Contract Standard +/// @dev Required interface +/// Note: the ERC-165 identifier for this interface is 0x61455567 +interface ERC1538 { + /// @dev This emits when one or a set of functions are updated in a transparent contract. + /// The message string should give a short description of the change and why + /// the change was made. + event CommitMessage(string message); + + /// @dev This emits for each function that is updated in a transparent contract. + /// functionId is the bytes4 of the keccak256 of the function signature. + /// oldDelegate is the delegate contract address of the old delegate contract if + /// the function is being replaced or removed. + /// oldDelegate is the zero value address(0) if a function is being added for the + /// first time. + /// newDelegate is the delegate contract address of the new delegate contract if + /// the function is being added for the first time or if the function is being + /// replaced. + /// newDelegate is the zero value address(0) if the function is being removed. + event FunctionUpdate( + bytes4 indexed functionId, + address indexed oldDelegate, + address indexed newDelegate, + string functionSignature + ); + + /// @notice Updates functions in a transparent contract. + /// @dev If the value of _delegate is zero then the functions specified + /// in _functionSignatures are removed. + /// If the value of _delegate is a delegate contract address then the functions + /// specified in _functionSignatures will be delegated to that address. + /// @param _delegate The address of a delegate contract to delegate to or zero + /// to remove functions. + /// @param _functionSignatures A list of function signatures listed one after the other + /// @param _commitMessage A short description of the change and why it is made + /// This message is passed to the CommitMessage event. + function updateContract(address _delegate, string calldata _functionSignatures, string calldata _commitMessage) external; +} +``` +### Function Signatures String Format + +The text format for the `_functionSignatures` parameter is simply a string of function signatures. For example: `"myFirstFunction()mySecondFunction(string)"` This format is easy to parse and is concise. + +Here is an example of calling the `updateContract` function that adds the ERC721 standard functions to a transparent contract: +```javascript +functionSignatures = "approve(address,uint256)balanceOf(address)getApproved(uint256)isApprovedForAll(address,address)ownerOf(uint256)safeTransferFrom(address,address,uint256)safeTransferFrom(address,address,uint256,bytes)setApprovalForAll(address,bool)transferFrom(address,address,uint256)" +tx = await transparentContract.updateContract(erc721Delegate.address, functionSignatures, "Adding ERC721 functions"); +``` + +### Removing Functions + +Functions are removed by passing `address(0)` as the first argument to the `updateContract` function. The list of functions that are passed in are removed. + +### Source Code Verification + +The transparent contract source code and the source code for the delegate contracts should be verified in a provable way by a third party source such as etherscan.io. + + +### Function Selector Clash +A function selector clash occurs when a function is added to a contract that hashes to the same four-byte hash as an existing function. This is unlikely to occur but should be prevented in the implementation of the `updateContract` function. See the [reference implementation of ERC1538](https://github.com/mudgen/transparent-contracts-erc1538) to see an example of how function clashes can be prevented. + +### ERC1538Query + +Optionally, the function signatures of a transparent contract can be stored in an array in the transparent contract and queried to get what functions the transparent contract supports and what their delegate contract addresses are. + +The following is an optional interface for querying function information from a transparent contract: + +```solidity +pragma solidity ^0.5.7; + +interface ERC1538Query { + + /// @notice Gets the total number of functions the transparent contract has. + /// @return The number of functions the transparent contract has, + /// not including the fallback function. + function totalFunctions() external view returns(uint256); + + /// @notice Gets information about a specific function + /// @dev Throws if `_index` >= `totalFunctions()` + /// @param _index The index position of a function signature that is stored in an array + /// @return The function signature, the function selector and the delegate contract address + function functionByIndex(uint256 _index) + external + view + returns( + string memory functionSignature, + bytes4 functionId, + address delegate + ); + + /// @notice Checks to see if a function exists + /// @param The function signature to check + /// @return True if the function exists, false otherwise + function functionExists(string calldata _functionSignature) external view returns(bool); + + /// @notice Gets all the function signatures of functions supported by the transparent contract + /// @return A string containing a list of function signatures + function functionSignatures() external view returns(string memory); + + /// @notice Gets all the function signatures supported by a specific delegate contract + /// @param _delegate The delegate contract address + /// @return A string containing a list of function signatures + function delegateFunctionSignatures(address _delegate) external view returns(string memory); + + /// @notice Gets the delegate contract address that supports the given function signature + /// @param The function signature + /// @return The delegate contract address + function delegateAddress(string calldata _functionSignature) external view returns(address); + + /// @notice Gets information about a function + /// @dev Throws if no function is found + /// @param _functionId The id of the function to get information about + /// @return The function signature and the contract address + function functionById(bytes4 _functionId) + external + view + returns( + string memory signature, + address delegate + ); + + /// @notice Get all the delegate contract addresses used by the transparent contract + /// @return An array of all delegate contract addresses + function delegateAddresses() external view returns(address[] memory); +} +``` + +See the [reference implementation of ERC1538](https://github.com/mudgen/transparent-contracts-erc1538) to see how this is implemented. + +The text format for the list of function signatures returned from the `delegateFunctionSignatures` and `functionSignatures` functions is simply a string of function signatures. Here is an example of such a string: `"approve(address,uint256)balanceOf(address)getApproved(uint256)isApprovedForAll(address,address)ownerOf(uint256)safeTransferFrom(address,address,uint256)safeTransferFrom(address,address,uint256,bytes)setApprovalForAll(address,bool)transferFrom(address,address,uint256)"` + +### How To Deploy A Transparent Contract +1. Create and deploy to a blockchain a contract that implements the ERC1538 interface. You can skip this step if there is already such a contract deployed to the blockchain. +2. Create your transparent contract with a fallback function as given above. Your transparent contract also needs a constructor that adds the `updateContract` function. +3. Deploy your transparent contract to a blockchain. Pass in the address of the ERC1538 delegate contract to your constructor if it requires it. + +See the [reference implementation](https://github.com/mudgen/transparent-contracts-erc1538) for examples of these contracts. + +### Wrapper Contract for Delegate Contracts that Depend on Other Delegate Contracts +In some cases some delegate contracts may need to call external/public functions that reside in other delegate contracts. A convenient way to solve this problem is to create a contract that contains empty implementations of functions that are needed and import and extend this contract in delegate contracts that call functions from other delegate contracts. This enables delegate contracts to compile without having to provide implementations of the functions that are already given in other delegate contracts. This is a way to save gas, prevent reaching the max contract size limit, and prevent duplication of code. This strategy was given by @amiromayer. [See his comment for more information.](https://github.com/ethereum/EIPs/issues/1538#issuecomment-451985155) Another way to solve this problem is to use assembly to call functions provided by other delegate contracts. + +### Decentralized Authority +It is possible to extend this standard to add consensus functionality such as an approval function that multiple different people call to approve changes before they are submitted with the `updateContract` function. Changes only go into effect when the changes are fully approved. The `CommitMessage` and ` FunctionUpdate` events should only be emitted when changes go into effect. + +## Security +> This standard refers to **owner(s)** as one or more individuals that have the power to add/replace/remove functions of an upgradeable contract. + +### General + +The owners(s) of an upgradeable contract have the ability to alter, add or remove data from the contract's data storage. Owner(s) of a contract can also execute any arbitrary code in the contract on behalf of any address. Owners(s) can do these things by adding a function to the contract that they call to execute arbitrary code. This is an issue for upgradeable contracts in general and is not specific to transparent contracts. + +>**Note:** The design and implementation of contract ownership is **not** part of this standard. The examples given in this standard and in the reference implementation are just **examples** of how it could be done. + +### Unchangeable Functions + +"Unchangeable functions" are functions defined in a transparent contract itself and not in a delegate contract. The owner(s) of a transparent contract are not able to replace these functions. The use of unchangeable functions is limited because in some cases they can still be manipulated if they read or write data to the storage of the transparent contract. Data read from the transparent contract's storage could have been altered by the owner(s) of the contract. Data written to the transparent contract's storage can be undone or altered by the owner(s) of the contract. + +In some cases unchangeble functions add trustless guarantees to a transparent contract. + +### Transparency + +Contracts that implement this standard emit an event every time a function is added, replaced or removed. This enables people and software to monitor the changes to a contract. If any bad acting function is added to a contract then it can be seen. To comply with this standard all source code of a transparent contract and delegate contracts must be publicly available and verified. + +Security and domain experts can review the history of change of any transparent contract to detect any history of foul play. + +## Rationale + +### String of Function Signatures Instead of bytes4[] Array of Function Selectors + +The `updateContract` function takes a `string` list of functions signatures as an argument instead of a `bytes4[]` array of function selectors for three reasons: + +1. Passing in function signatures enables the implementation of `updateContract` to prevent selector clashes. +2. A major part of this standard is to make upgradeable contracts more transparent by making it easier to see what has changed over time and why. When a function is added, replaced or removed its function signature is included in the FunctionUpdate event that is emitted. This makes it relatively easy to write software that filters the events of a contract to display to people what functions have been added/removed and changed over time without needing access to the source code or ABI of the contract. If only four-byte function selectors were provided this would not be possible. +3. By looking at the source code of a transparent contract it is not possible to see all the functions that it supports. This is why the ERC1538Query interface exists, so that people and software have a way to look up and examine or show all functions currently supported by a transparent contract. Function signatures are used so that ERC1538Query functions can show them. + +### Gas Considerations + +Delegating function calls does have some gas overhead. This is mitigated in two ways: +1. Delegate contracts can be small, reducing gas costs. Because it costs more gas to call a function in a contract with many functions than a contract with few functions. +2. Because transparent contracts do not have a max size limitation it is possible to add gas optimizing functions for use cases. For example someone could use a transparent contract to implement the ERC721 standard and implement batch transfer functions from the [ERC1412 standard](https://github.com/ethereum/EIPs/issues/1412) to help reduce gas (and make batch transfers more convenient). + +### Storage + +The standard does not specify how data is stored or organized by a transparent contract. But here are some suggestions: + +**Inherited Storage** + +1. The storage variables of a transparent contract consist of the storage variables defined in the transparent contract source code and the source code of delegate contracts that have been added. + +2. A delegate contract can use any storage variable that exists in a transparent contract as long as it defines within it all the storage variables that exist, in the order that they exist, up to and including the ones being used. + +3. A delegate contract can create new storage variables as long as it has defined, in the same order, all storage variables that exist in the transparent contract. + +Here is a simple way inherited storage could be implemented: + +1. Create a storage contract that contains the storage variables that your transparent contract and delegate contracts will use. +2. Make your delegate contracts inherit the storage contract. +3. If you want to add a new delegate contract that adds new storage variables then create a new storage contract that adds the new storage variables and inherits from the old storage contract. Use your new storage contract with your new delegate contract. +4. Repeat steps 2 or 3 for every new delegate contract. + + +**Unstructured Storage** + +Assembly is used to store and read data at specific storage locations. An advantage to this approach is that previously used storage locations don't have to be defined or mentioned in a delegate contract if they aren't used by it. + +**Eternal Storage** + +Data can be stored using a generic API based on the type of data. [See ERC930 for more information.](https://github.com/ethereum/EIPs/issues/930) + +### Becoming Immutable +It is possible to make a transparent contract become immutable. This is done by calling the `updateContract` function to remove the `updateContract` function. With this gone it is no longer possible to add, replace and remove functions. + +### Versions of Functions + +Software or a user can verify what version of a function is called by getting the delegate contract address of the function. This can be done by calling the `delegateAddress` function from the ERC1538Query interface if it is implemented. This function takes a function signature as an argument and returns the delegate contract address where it is implemented. + +### Best Practices, Tools and More Information + +> More information, tools, tutorials and best practices concerning transparent contracts need to be developed and published. + +Below is a growing list of articles concerning transparent contracts and their use. If you have an article about transparent contracts you would like to share then please submit a comment to this issue about it to get it added. + +[ERC1538: Future Proofing Smart Contracts and Tokens](https://coinjournal.net/erc1538-future-proofing-smart-contacts-and-tokens/) + +[The ERC1538 improving towards the “transparent contract” standard](https://www.crypto-economy.net/en/ethereum-eth-erc1538-transparent-contract-standard/) + +### Inspiration + +This standard was inspired by ZeppelinOS's implementation of [Upgradeability with vtables](https://github.com/zeppelinos/labs/tree/master/upgradeability_with_vtable). + +This standard was also inspired by the design and implementation of the [Mokens contract](https://etherscan.io/address/0xc1eab49cf9d2e23e43bcf23b36b2be14fc2f8838#code) from the [Mokens project](https://github.com/Mokens/MIPs/blob/master/MIPS/mip-2-Goals-and-Objectives.md). The Mokens contract has been [upgraded to implement this standard](https://etherscan.io/address/0x0ac5637fe62ec14fd9e237a81a9679d4adef701f#code). + + +## Backwards Compatibility +This standard makes a contract compatible with future standards and functionality because new functions can be added and existing functions can be replaced or removed. + +This standard future proofs a contract. + +## Implementation +A reference implementation of this standard is given in the [transparent-contracts-erc1538](https://github.com/mudgen/transparent-contracts-erc1538) repository. + + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-155.md b/EIPS/eip-155.md new file mode 100644 index 0000000..17024fa --- /dev/null +++ b/EIPS/eip-155.md @@ -0,0 +1,70 @@ +--- +eip: 155 +title: Simple replay attack protection +author: Vitalik Buterin (@vbuterin) +type: Standards Track +category: Core +status: Final +created: 2016-10-14 +--- + +### Hard fork +[Spurious Dragon](./eip-607.md) + +### Parameters +- `FORK_BLKNUM`: 2,675,000 +- `CHAIN_ID`: 1 (main net) + +### Specification + +If `block.number >= FORK_BLKNUM` and `CHAIN_ID` is available, then when computing the hash of a transaction for the purposes of signing, instead of hashing only six rlp encoded elements `(nonce, gasprice, startgas, to, value, data)`, you **SHOULD** hash nine rlp encoded elements `(nonce, gasprice, startgas, to, value, data, chainid, 0, 0)`. If you do, then the `v` of the signature **MUST** be set to `{0,1} + CHAIN_ID * 2 + 35` where `{0,1}` is the parity of the `y` value of the curve point for which `r` is the x-value in the secp256k1 signing process. If you choose to only hash 6 values, then `v` continues to be set to `{0,1} + 27` as previously. + +If `block.number >= FORK_BLKNUM` and `v = CHAIN_ID * 2 + 35` or `v = CHAIN_ID * 2 + 36`, then when computing the hash of a transaction for purposes of recovering, instead of hashing six rlp encoded elements `(nonce, gasprice, startgas, to, value, data)`, hash nine rlp encoded elements `(nonce, gasprice, startgas, to, value, data, chainid, 0, 0)`. The currently existing signature scheme using `v = 27` and `v = 28` remains valid and continues to operate under the same rules as it did previously. + +### Example + +Consider a transaction with `nonce = 9`, `gasprice = 20 * 10**9`, `startgas = 21000`, `to = 0x3535353535353535353535353535353535353535`, `value = 10**18`, `data=''` (empty). + +The "signing data" becomes: + +``` +0xec098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a764000080018080 +``` + +The "signing hash" becomes: + +``` +0xdaf5a779ae972f972197303d7b574746c7ef83eadac0f2791ad23db92e4c8e53 +``` + +If the transaction is signed with the private key `0x4646464646464646464646464646464646464646464646464646464646464646`, then the v,r,s values become: + +``` +(37, 18515461264373351373200002665853028612451056578545711640558177340181847433846, 46948507304638947509940763649030358759909902576025900602547168820602576006531) +``` + +Notice the use of 37 instead of 27. The signed tx would become: + +``` +0xf86c098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a76400008025a028ef61340bd939bc2195fe537567866003e1a15d3c71ff63e1590620aa636276a067cbe9d8997f761aecb703304b3800ccf555c9f3dc64214b297fb1966a3b6d83 +``` + +### Rationale + +This would provide a way to send transactions that work on Ethereum without working on ETC or the Morden testnet. ETC is encouraged to adopt this EIP but replacing `CHAIN_ID` with a different value, and all future testnets, consortium chains and alt-etherea are encouraged to adopt this EIP replacing `CHAIN_ID` with a unique value. + + +### List of Chain ID's: + +| `CHAIN_ID` | Chain(s) | +| ---------------| -------------------------------------------| +| 1 | Ethereum mainnet | +| 2 | Morden (disused), Expanse mainnet | +| 3 | Ropsten | +| 4 | Rinkeby | +| 5 | Goerli | +| 42 | Kovan | +| 1337 | Geth private chains (default) | + + +Find more chain ID's on [chainid.network](https://chainid.network) and contribute to [ethereum-lists/chains](https://github.com/ethereum-lists/chains). diff --git a/EIPS/eip-1559.md b/EIPS/eip-1559.md new file mode 100644 index 0000000..3a99f1e --- /dev/null +++ b/EIPS/eip-1559.md @@ -0,0 +1,329 @@ +--- +eip: 1559 +title: Fee market change for ETH 1.0 chain +author: Vitalik Buterin (@vbuterin), Eric Conner (@econoar), Rick Dudley (@AFDudley), Matthew Slipper (@mslipper), Ian Norden (@i-norden), Abdelhamid Bakhta (@abdelhamidbakhta) +discussions-to: https://ethereum-magicians.org/t/eip-1559-fee-market-change-for-eth-1-0-chain/2783 +status: Final +type: Standards Track +category: Core +created: 2019-04-13 +requires: 2718, 2930 +--- + +## Simple Summary +A transaction pricing mechanism that includes fixed-per-block network fee that is burned and dynamically expands/contracts block sizes to deal with transient congestion. + +## Abstract +We introduce a new [EIP-2718](./eip-2718.md) transaction type, with the format `0x02 || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination, amount, data, access_list, signature_y_parity, signature_r, signature_s])`. + +There is a base fee per gas in protocol, which can move up or down each block according to a formula which is a function of gas used in parent block and gas target (block gas limit divided by elasticity multiplier) of parent block. +The algorithm results in the base fee per gas increasing when blocks are above the gas target, and decreasing when blocks are below the gas target. +The base fee per gas is burned. +Transactions specify the maximum fee per gas they are willing to give to miners to incentivize them to include their transaction (aka: priority fee). +Transactions also specify the maximum fee per gas they are willing to pay total (aka: max fee), which covers both the priority fee and the block's network fee per gas (aka: base fee). +The transaction will always pay the base fee per gas of the block it was included in, and they will pay the priority fee per gas set in the transaction, as long as the combined amount of the two fees doesn't exceed the transaction's maximum fee per gas. + +## Motivation +Ethereum historically priced transaction fees using a simple auction mechanism, where users send transactions with bids ("gasprices") and miners choose transactions with the highest bids, and transactions that get included pay the bid that they specify. This leads to several large sources of inefficiency: + +* **Mismatch between volatility of transaction fee levels and social cost of transactions**: bids to include transactions on mature public blockchains, that have enough usage so that blocks are full, tend to be extremely volatile. It's absurd to suggest that the cost incurred by the network from accepting one more transaction into a block actually is 10x more when the cost per gas is 10 nanoeth compared to when the cost per gas is 1 nanoeth; in both cases, it's a difference between 8 million gas and 8.02 million gas. +* **Needless delays for users**: because of the hard per-block gas limit coupled with natural volatility in transaction volume, transactions often wait for several blocks before getting included, but this is socially unproductive; no one significantly gains from the fact that there is no "slack" mechanism that allows one block to be bigger and the next block to be smaller to meet block-by-block differences in demand. +* **Inefficiencies of first price auctions**: The current approach, where transaction senders publish a transaction with a bid a maximum fee, miners choose the highest-paying transactions, and everyone pays what they bid. This is well-known in mechanism design literature to be highly inefficient, and so complex fee estimation algorithms are required. But even these algorithms often end up not working very well, leading to frequent fee overpayment. +* **Instability of blockchains with no block reward**: In the long run, blockchains where there is no issuance (including Bitcoin and Zcash) at present intend to switch to rewarding miners entirely through transaction fees. However, there are known issues with this that likely leads to a lot of instability, incentivizing mining "sister blocks" that steal transaction fees, opening up much stronger selfish mining attack vectors, and more. There is at present no good mitigation for this. + +The proposal in this EIP is to start with a base fee amount which is adjusted up and down by the protocol based on how congested the network is. When the network exceeds the target per-block gas usage, the base fee increases slightly and when capacity is below the target, it decreases slightly. Because these base fee changes are constrained, the maximum difference in base fee from block to block is predictable. This then allows wallets to auto-set the gas fees for users in a highly reliable fashion. It is expected that most users will not have to manually adjust gas fees, even in periods of high network activity. For most users the base fee will be estimated by their wallet and a small priority fee, which compensates miners taking on orphan risk (e.g. 1 nanoeth), will be automatically set. Users can also manually set the transaction max fee to bound their total costs. + +An important aspect of this fee system is that miners only get to keep the priority fee. The base fee is always burned (i.e. it is destroyed by the protocol). This ensures that only ETH can ever be used to pay for transactions on Ethereum, cementing the economic value of ETH within the Ethereum platform and reducing risks associated with miner extractable value (MEV). Additionally, this burn counterbalances Ethereum inflation while still giving the block reward and priority fee to miners. Finally, ensuring the miner of a block does not receive the base fee is important because it removes miner incentive to manipulate the fee in order to extract more fees from users. + +## Specification +Block validity is defined in the reference implementation below. +The `GASPRICE` (`0x3a`) opcode **MUST** return the `effective_gas_price` as defined in the reference implementation below. + +As of `FORK_BLOCK_NUMBER`, a new [EIP-2718](./eip-2718.md) transaction is introduced with `TransactionType` 2. + +The intrinsic cost of the new transaction is inherited from [EIP-2930](./eip-2930.md), specifically `21000 + 16 * non-zero calldata bytes + 4 * zero calldata bytes + 1900 * access list storage key count + 2400 * access list address count`. + +The [EIP-2718](./eip-2718.md) `TransactionPayload` for this transaction is `rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination, amount, data, access_list, signature_y_parity, signature_r, signature_s])`. + +The `signature_y_parity, signature_r, signature_s` elements of this transaction represent a secp256k1 signature over `keccak256(0x02 || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination, amount, data, access_list]))`. + +The [EIP-2718](./eip-2718.md) `ReceiptPayload` for this transaction is `rlp([status, cumulative_transaction_gas_used, logs_bloom, logs])`. + +*Note: `//` is integer division, round down.* +```python +from typing import Union, Dict, Sequence, List, Tuple, Literal +from dataclasses import dataclass, field +from abc import ABC, abstractmethod + +@dataclass +class TransactionLegacy: + signer_nonce: int = 0 + gas_price: int = 0 + gas_limit: int = 0 + destination: int = 0 + amount: int = 0 + payload: bytes = bytes() + v: int = 0 + r: int = 0 + s: int = 0 + +@dataclass +class Transaction2930Payload: + chain_id: int = 0 + signer_nonce: int = 0 + gas_price: int = 0 + gas_limit: int = 0 + destination: int = 0 + amount: int = 0 + payload: bytes = bytes() + access_list: List[Tuple[int, List[int]]] = field(default_factory=list) + signature_y_parity: bool = False + signature_r: int = 0 + signature_s: int = 0 + +@dataclass +class Transaction2930Envelope: + type: Literal[1] = 1 + payload: Transaction2930Payload = Transaction2930Payload() + +@dataclass +class Transaction1559Payload: + chain_id: int = 0 + signer_nonce: int = 0 + max_priority_fee_per_gas: int = 0 + max_fee_per_gas: int = 0 + gas_limit: int = 0 + destination: int = 0 + amount: int = 0 + payload: bytes = bytes() + access_list: List[Tuple[int, List[int]]] = field(default_factory=list) + signature_y_parity: bool = False + signature_r: int = 0 + signature_s: int = 0 + +@dataclass +class Transaction1559Envelope: + type: Literal[2] = 2 + payload: Transaction1559Payload = Transaction1559Payload() + +Transaction2718 = Union[Transaction1559Envelope, Transaction2930Envelope] + +Transaction = Union[TransactionLegacy, Transaction2718] + +@dataclass +class NormalizedTransaction: + signer_address: int = 0 + signer_nonce: int = 0 + max_priority_fee_per_gas: int = 0 + max_fee_per_gas: int = 0 + gas_limit: int = 0 + destination: int = 0 + amount: int = 0 + payload: bytes = bytes() + access_list: List[Tuple[int, List[int]]] = field(default_factory=list) + +@dataclass +class Block: + parent_hash: int = 0 + uncle_hashes: Sequence[int] = field(default_factory=list) + author: int = 0 + state_root: int = 0 + transaction_root: int = 0 + transaction_receipt_root: int = 0 + logs_bloom: int = 0 + difficulty: int = 0 + number: int = 0 + gas_limit: int = 0 # note the gas_limit is the gas_target * ELASTICITY_MULTIPLIER + gas_used: int = 0 + timestamp: int = 0 + extra_data: bytes = bytes() + proof_of_work: int = 0 + nonce: int = 0 + base_fee_per_gas: int = 0 + +@dataclass +class Account: + address: int = 0 + nonce: int = 0 + balance: int = 0 + storage_root: int = 0 + code_hash: int = 0 + +INITIAL_BASE_FEE = 1000000000 +INITIAL_FORK_BLOCK_NUMBER = 10 # TBD +BASE_FEE_MAX_CHANGE_DENOMINATOR = 8 +ELASTICITY_MULTIPLIER = 2 + +class World(ABC): + def validate_block(self, block: Block) -> None: + parent_gas_target = self.parent(block).gas_limit // ELASTICITY_MULTIPLIER + parent_gas_limit = self.parent(block).gas_limit + + # on the fork block, don't account for the ELASTICITY_MULTIPLIER to avoid + # unduly halving the gas target. + if INITIAL_FORK_BLOCK_NUMBER == block.number: + parent_gas_target = self.parent(block).gas_limit + parent_gas_limit = self.parent(block).gas_limit * ELASTICITY_MULTIPLIER + + parent_base_fee_per_gas = self.parent(block).base_fee_per_gas + parent_gas_used = self.parent(block).gas_used + transactions = self.transactions(block) + + # check if the block used too much gas + assert block.gas_used <= block.gas_limit, 'invalid block: too much gas used' + + # check if the block changed the gas limit too much + assert block.gas_limit < parent_gas_limit + parent_gas_limit // 1024, 'invalid block: gas limit increased too much' + assert block.gas_limit > parent_gas_limit - parent_gas_limit // 1024, 'invalid block: gas limit decreased too much' + + # check if the gas limit is at least the minimum gas limit + assert block.gas_limit >= 5000 + + # check if the base fee is correct + if INITIAL_FORK_BLOCK_NUMBER == block.number: + expected_base_fee_per_gas = INITIAL_BASE_FEE + elif parent_gas_used == parent_gas_target: + expected_base_fee_per_gas = parent_base_fee_per_gas + elif parent_gas_used > parent_gas_target: + gas_used_delta = parent_gas_used - parent_gas_target + base_fee_per_gas_delta = max(parent_base_fee_per_gas * gas_used_delta // parent_gas_target // BASE_FEE_MAX_CHANGE_DENOMINATOR, 1) + expected_base_fee_per_gas = parent_base_fee_per_gas + base_fee_per_gas_delta + else: + gas_used_delta = parent_gas_target - parent_gas_used + base_fee_per_gas_delta = parent_base_fee_per_gas * gas_used_delta // parent_gas_target // BASE_FEE_MAX_CHANGE_DENOMINATOR + expected_base_fee_per_gas = parent_base_fee_per_gas - base_fee_per_gas_delta + assert expected_base_fee_per_gas == block.base_fee_per_gas, 'invalid block: base fee not correct' + + # execute transactions and do gas accounting + cumulative_transaction_gas_used = 0 + for unnormalized_transaction in transactions: + # Note: this validates transaction signature and chain ID which must happen before we normalize below since normalized transactions don't include signature or chain ID + signer_address = self.validate_and_recover_signer_address(unnormalized_transaction) + transaction = self.normalize_transaction(unnormalized_transaction, signer_address) + + signer = self.account(signer_address) + + signer.balance -= transaction.amount + assert signer.balance >= 0, 'invalid transaction: signer does not have enough ETH to cover attached value' + # the signer must be able to afford the transaction + assert signer.balance >= transaction.gas_limit * transaction.max_fee_per_gas + + # ensure that the user was willing to at least pay the base fee + assert transaction.max_fee_per_gas >= block.base_fee_per_gas + + # Prevent impossibly large numbers + assert transaction.max_fee_per_gas < 2**256 + # Prevent impossibly large numbers + assert transaction.max_priority_fee_per_gas < 2**256 + # The total must be the larger of the two + assert transaction.max_fee_per_gas >= transaction.max_priority_fee_per_gas + + # priority fee is capped because the base fee is filled first + priority_fee_per_gas = min(transaction.max_priority_fee_per_gas, transaction.max_fee_per_gas - block.base_fee_per_gas) + # signer pays both the priority fee and the base fee + effective_gas_price = priority_fee_per_gas + block.base_fee_per_gas + signer.balance -= transaction.gas_limit * effective_gas_price + assert signer.balance >= 0, 'invalid transaction: signer does not have enough ETH to cover gas' + gas_used = self.execute_transaction(transaction, effective_gas_price) + gas_refund = transaction.gas_limit - gas_used + cumulative_transaction_gas_used += gas_used + # signer gets refunded for unused gas + signer.balance += gas_refund * effective_gas_price + # miner only receives the priority fee; note that the base fee is not given to anyone (it is burned) + self.account(block.author).balance += gas_used * priority_fee_per_gas + + # check if the block spent too much gas transactions + assert cumulative_transaction_gas_used == block.gas_used, 'invalid block: gas_used does not equal total gas used in all transactions' + + # TODO: verify account balances match block's account balances (via state root comparison) + # TODO: validate the rest of the block + + def normalize_transaction(self, transaction: Transaction, signer_address: int) -> NormalizedTransaction: + # legacy transactions + if isinstance(transaction, TransactionLegacy): + return NormalizedTransaction( + signer_address = signer_address, + signer_nonce = transaction.signer_nonce, + gas_limit = transaction.gas_limit, + max_priority_fee_per_gas = transaction.gas_price, + max_fee_per_gas = transaction.gas_price, + destination = transaction.destination, + amount = transaction.amount, + payload = transaction.payload, + access_list = [], + ) + # 2930 transactions + elif isinstance(transaction, Transaction2930Envelope): + return NormalizedTransaction( + signer_address = signer_address, + signer_nonce = transaction.payload.signer_nonce, + gas_limit = transaction.payload.gas_limit, + max_priority_fee_per_gas = transaction.payload.gas_price, + max_fee_per_gas = transaction.payload.gas_price, + destination = transaction.payload.destination, + amount = transaction.payload.amount, + payload = transaction.payload.payload, + access_list = transaction.payload.access_list, + ) + # 1559 transactions + elif isinstance(transaction, Transaction1559Envelope): + return NormalizedTransaction( + signer_address = signer_address, + signer_nonce = transaction.payload.signer_nonce, + gas_limit = transaction.payload.gas_limit, + max_priority_fee_per_gas = transaction.payload.max_priority_fee_per_gas, + max_fee_per_gas = transaction.payload.max_fee_per_gas, + destination = transaction.payload.destination, + amount = transaction.payload.amount, + payload = transaction.payload.payload, + access_list = transaction.payload.access_list, + ) + else: + raise Exception('invalid transaction: unexpected number of items') + + @abstractmethod + def parent(self, block: Block) -> Block: pass + + @abstractmethod + def block_hash(self, block: Block) -> int: pass + + @abstractmethod + def transactions(self, block: Block) -> Sequence[Transaction]: pass + + # effective_gas_price is the value returned by the GASPRICE (0x3a) opcode + @abstractmethod + def execute_transaction(self, transaction: NormalizedTransaction, effective_gas_price: int) -> int: pass + + @abstractmethod + def validate_and_recover_signer_address(self, transaction: Transaction) -> int: pass + + @abstractmethod + def account(self, address: int) -> Account: pass +``` + +## Backwards Compatibility +Legacy Ethereum transactions will still work and be included in blocks, but they will not benefit directly from the new pricing system. This is due to the fact that upgrading from legacy transactions to new transactions results in the legacy transaction's `gas_price ` entirely being consumed either by the `base_fee_per_gas` and the `priority_fee_per_gas`. + +### Block Hash Changing +The datastructure that is passed into keccak256 to calculate the block hash is changing, and all applications that are validating blocks are valid or using the block hash to verify block contents will need to be adapted to support the new datastructure (one additional item). If you only take the block header bytes and hash them you should still correctly get a hash, but if you construct a block header from its constituent elements you will need to add in the new one at the end. + +### GASPRICE +Previous to this change, `GASPRICE` represented both the ETH paid by the signer per gas for a transaction as well as the ETH received by the miner per gas. As of this change, `GASPRICE` now only represents the amount of ETH paid by the signer per gas, and the amount a miner was paid for the transaction is no longer accessible directly in the EVM. + +## Security Considerations +### Increased Max Block Size/Complexity +This EIP will increase the maximum block size, which could cause problems if miners are unable to process a block fast enough as it will force them to mine an empty block. Over time, the average block size should remain about the same as without this EIP, so this is only an issue for short term size bursts. It is possible that one or more clients may handle short term size bursts poorly and error (such as out of memory or similar) and client implementations should make sure their clients can appropriately handle individual blocks up to max size. + +### Transaction Ordering +With most people not competing on priority fees and instead using a baseline fee to get included, transaction ordering now depends on individual client internal implementation details such as how they store the transactions in memory. It is recommended that transactions with the same priority fee be sorted by time the transaction was received to protect the network from spamming attacks where the attacker throws a bunch of transactions into the pending pool in order to ensure that at least one lands in a favorable position. Miners should still prefer higher gas premium transactions over those with a lower gas premium, purely from a selfish mining perspective. + +### Miners Mining Empty Blocks +It is possible that miners will mine empty blocks until such time as the base fee is very low and then proceed to mine half full blocks and revert to sorting transactions by the priority fee. While this attack is possible, it is not a particularly stable equilibrium as long as mining is decentralized. Any defector from this strategy will be more profitable than a miner participating in the attack for as long as the attack continues (even after the base fee reached 0). Since any miner can anonymously defect from a cartel, and there is no way to prove that a particular miner defected, the only feasible way to execute this attack would be to control 50% or more of hashing power. If an attacker had exactly 50% of hashing power, they would make no Ether from priority fee while defectors would make double the Ether from priority fees. For an attacker to turn a profit, they need to have some amount over 50% hashing power, which means they can instead execute double spend attacks or simply ignore any other miners which is a far more profitable strategy. + +Should a miner attempt to execute this attack, we can simply increase the elasticity multiplier (currently 2x) which requires they have even more hashing power available before the attack can even be theoretically profitable against defectors. + +### ETH Burn Precludes Fixed Supply +By burning the base fee, we can no longer guarantee a fixed Ether supply. This could result in economic instability as the long term supply of ETH will no longer be constant over time. While a valid concern, it is difficult to quantify how much of an impact this will have. If more is burned on base fee than is generated in mining rewards then ETH will be deflationary and if more is generated in mining rewards than is burned then ETH will be inflationary. Since we cannot control user demand for block space, we cannot assert at the moment whether ETH will end up inflationary or deflationary, so this change causes the core developers to lose some control over Ether's long term quantity. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1571.md b/EIPS/eip-1571.md new file mode 100644 index 0000000..5338fd0 --- /dev/null +++ b/EIPS/eip-1571.md @@ -0,0 +1,551 @@ +--- +eip: 1571 +title: EthereumStratum/2.0.0 +author: Andrea Lanfranchi (@AndreaLanfranchi), Pawel Bylica (@chfast), Marius Van Der Wijden (@MariusVanDerWijden) +discussions-to: https://github.com/AndreaLanfranchi/EthereumStratum-2.0.0/issues +status: Stagnant +type: Standards Track +category: Interface +created: 2018-11-09 +--- + +## Abstract +This draft contains the guidelines to define a new standard for the Stratum protocol used by Ethereum miners to communicate with mining pool servers. + +### Conventions +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://tools.ietf.org/html/rfc2119). +The definition `mining pool server`, and it's plural form, is to be interpreted as `work provider` and later in this document can be shortened as `pool` or `server`. +The definition `miner(s)`, and it's plural form, is to be interpreted as `work receiver/processor` and later in this document can be shortened as `miner` or `client`. + +### Rationale +Ethereum does not have an official Stratum implementation yet. It officially supports only getWork which requires miners to constantly pool the work provider. Only recently go-ethereum have implemented a [push mechanism](https://github.com/ethereum/go-ethereum/pull/17347) to notify clients for mining work, but whereas the vast majority of miners do not run a node, it's main purpose is to facilitate mining pools rather than miners. +The Stratum protocol on the other hand relies on a standard stateful TCP connection which allows two-way exchange of line-based messages. Each line contains the string representation of a JSON object following the rules of either [JSON-RPC 1.0](https://www.jsonrpc.org/specification_v1) or [JSON-RPC 2.0](https://www.jsonrpc.org/specification). +Unfortunately, in absence of a well defined standard, various flavours of Stratum have bloomed for Ethereum mining as a derivative work for different mining pools implementations. The only attempt to define a standard was made by NiceHash with their [EthereumStratum/1.0.0](https://github.com/nicehash/Specifications/blob/master/EthereumStratum_NiceHash_v1.0.0.txt) implementation which is the main source this work inspires from. +Mining activity, thus the interaction among pools and miners, is at it's basics very simple, and can be summarized with "_please find a number (nonce) which coupled to this data as input for a given hashing algorithm produces, as output, a result which is below a certain target_". Other messages which may or have to be exchanged among parties during a session are needed to support this basic concept. +Due to the simplicity of the subject, the proponent, means to stick with JSON formatted objects rather than investigating more verbose solutions, like for example [Google's Protocol Buffers](https://developers.google.com/protocol-buffers/docs/overview) which carry the load of strict object definition. + +### Stratum design flaws +The main Stratum design flaw is the absence of a well defined standard. This implies that miners (and mining software developers) have to struggle with different flavours which make their life hard when switching from one pool to another or even when trying to "guess" which is the flavour implemented by a single pool. Moreover all implementations still suffer from an excessive verbosity for a chain with a very small block time like Ethereum. A few numbers may help understand. A normal `mining.notify` message weigh roughly 240 bytes: assuming the dispatch of 1 work per block to an audience of 50k connected TCP sockets means the transmission of roughly 1.88TB of data a month. And this can be an issue for large pools. But if we see the same figures the other way round, from a miner's perspective, we totally understand how mining decentralization is heavily affected by the quality of internet connections. + +### Sources of inspiration +- [NiceHash EthereumStratum/1.0.0](https://github.com/nicehash/Specifications/blob/master/EthereumStratum_NiceHash_v1.0.0.txt) +- [Zcash variant of the Stratum protocol](https://github.com/zcash/zips/blob/23d74b0373c824dd51c7854c0e3ea22489ba1b76/drafts/str4d-stratum/draft1.rst#json-rpc-1-0) + +## Specification +The Stratum protocol is an instance of [JSON-RPC-2.0](https://www.jsonrpc.org/specification). The miner is a JSON-RPC client, and the server is a JSON-RPC server. All communications exist within the scope of a `session`. A session starts at the moment a client opens a TCP connection to the server till the moment either party do voluntary close the very same connection or it gets broken. Servers **MAY** support session resuming if this is initially negotiated (on first session handshaking) between the client and the server. During a session all messages exchanged among server and client are line-based which means all messages are JSON strings terminated by ASCII LF character (which may also be denoted as `\n` in this document). The LF character **MUST NOT** appear elsewhere in a message. Client and server implementations **MUST** assume that once they read a LF character, the current message has been completely received and can be processed. +Line messages are of three types : +- `Requests` : messages for which the issuer expects a response. The receiver **MUST** reply back to any request individually +- `Responses` : solicited messages by a previous request. The responder **MUST** label the response with the same identifier of the originating request. +- `Notifications` : unsolicited messages for which the issuer is not interested nor is expecting a response. Nevertheless a response (eg. an aknowledgement) **MAY** be sent by the receiver. + +During a `session` both parties **CAN** exchange messages of the above depicted three types. + +### JSON-RPC-2.0 Compliances + +As per [JSON-RPC-2.0](https://www.jsonrpc.org/specification) specification requests and responses differ from notifications by the identifier (`id`) member in the JSON object: +- Requests **MUST** have an `id` member +- Responses **MUST** have an `id` member valued exactly as the `id` member of the request this response is for +- Notifications **MUST NOT** have an `id` member + +### JSON-RPC-2.0 Defiances + +In order to get the most concise messages among parties of a session/conversation this implementation enforces the following defiances : +- JSON member `jsonrpc` (always valued to "2.0") **MUST ALWAYS BE OMITTED** +- JSON member `id` **MUST NOT** be `null`. When member is present, mandatorily in requests and responses, it **MUST** be valued to an integer Number ranging from 0 to 65535. Please note that a message with `"id": 0` **MUST NOT** be interpreted as a notification: it's a request with identifier 0 +- JSON member `id` **MUST** be only of type primitive number. The removal of other identifier types (namely strings) is due to the need to reduce the number of bytes transferred. + +## Conventions +- The representation of a JSON object is, at it's base, a string +- The conversation among the client and the server is made of strings. Each string ending with a LF (ASCII char 10) denotes a `line`. Each line **MUST** contain only one JSON root object. Eventually the root object **MAY** contain additional JSON objects in it's members. +- Aside the `LF` delimiter each `line` **MUST** be made of printable ASCII chars in the range 32..126 +- It's implicit and mandatory that each line message corresponds to a well formatted JSON object: see [JSON documentation](https://www.json.org/) +- JSON objects are made of `members` which can be of type : primitive of string/number, JSON object, JSON arrays +- JSON `member`'s names are strings which **MUST** be composed of printable chars only in the ASCII range 48..57 (numbers) and 97..122 (lower case letters). +- JSON `member`'s names **MUST NOT** begin with a number. +- JSON values `arrays` : although the JSON notation allows the insertion of different data types within the same array, this behavior is generally not accepted in coding languages. Due to this, by the means of EthereumStratum/2.0.0, all implementers **MUST** assume that an array is made of elements of the very same data type. +- JSON values `booleans` : the JSON notation allows to express boolean values as `true` or `false`. In EthereumStratum/2.0.0, for better compatibility within arrays, boolean values will be expressed in the hex form of "0" (false) or "1" (true) +- JSON values `strings` : any string value **MUST** be composed of printable ASCII chars only in the ASCII range 32..126. Each string is delimited by a `"` (ASCII 34) at the beginning and at the end. Should the string value contain a `"` this must be escaped as `\"` +- Hex values : a Hexadecimal representation of a number is actually a string data type. As a convention, and to reduce the number of transmitted bytes, the prefix "0x" **MUST** always be omitted. In addition any hex number **MUST** be transferred only for their significant part i.e. the non significant zeroes **MUST** be omitted (example : the decimal `456` must be represented as `"1c8"` and not as `"01c8"` although the conversion produces the same decimal value). This directive **DOES NOT APPLY** to hashes and extranonces +- Hex values casing : all letters in Hexadecimal values **MUST** be lower case. (example : the decimal `456` must be represented as `"1c8"` and not as `"1C8"` although the conversion produces the same decimal value). This directive **DOES NOT APPLY** to hashes. +- Numbers : any non-fractional number **MUST** be transferred by it's hexadecimal representation + +### Requests +The JSON representation of `request` object is made of these parts: +- mandatory `id` member of type Integer : the identifier established by the issuer +- mandatory `method` member of type String : the name of the method to be invoked on the receiver side +- optional `params` member : in case the method invocation on the receiver side requires the application of additional parameters to be executed. The type **CAN** be Object (with named members of different types) or Array of single type. In case of an array the parameters will be applied by their ordinal position. If the method requested for invocation on the receiver side does not require the application of additional parameters this member **MUST NOT** be present. The notation `"params" : null` **IS NOT PERMITTED** + +### Responses +The JSON representation of `response` object is made of these parts: +- mandatory `id` member of type Integer : the identifier of the request this response corresponds to +- optional `error` member : whether an error occurred during the parsing of the method or during it's execution this member **MUST** be present and valued. If no errors occurred this member **MUST NOT** be present. For a detailed structure of the `error` member see below. +- optional `result` member : This has to be set, if the corresponding request requires a result from the user. If no errors occurred by invoking the corresponding function, this member **MUST** be present even if one or more information are null. The type can be of Object or single type Array or Primitive string/number. If no data is meant back for the issuer (the method is void on the receiver) or an error occurred this member **MUST NOT** be present. + +You'll notice here some differences with standard JSON-RPC-2.0. Namely the result member is not always required. Basically a response like this : +```json +{"id": 2} +``` +means "request received and processed correctly with no data to send back". + +To better clarify the concept and clear the field of free interpretations let's take another example of **wrong response** +```json +{"id": 2, "result": false} +``` +This response syntax leaves room to many interpretations : is it an error ? is `false` the legit response value to the issued request ? + +For this reason responses, we reiterate, **MUST BE** of two types: +- success responses : no error occurred during the processing, the request was legit, syntactically correct, and the receiver had no issues processing it. This kind of responses **MUST NOT** have the `error` member and **MAY** have the `result` member if a value is expected back to the issuer. +- failure responses : something wrong with the request, it's syntax, it's validity scope, or server processing problems. This kind of responses **MUST HAVE** the `error` member and **MAY** have the `result` member. + +The latter deserves a better explanation: failure responses can be distinguished by a severity degree. +Example 1 : a client submits a solution and server rejects it cause it's not below target. Server **MUST** respond like this +```json +{ + "id": 31, + "error": { + "code": 406, + "message" : "Bad nonce" + } +} +``` +Example 2 : a client submits a solution and server **accepts** it **but** it accounts the share as stale. Server **MUST** respond like this +```json +{ + "id": 31, + "error": { + "code": 202, + "message" : "Stale" + } +} +``` +Example 3 : a client submits an authorization request specifying an invalid workername. Server authorizes the account but rejects worker name. Server **MUST** respond like this +```json +{ + "id": 1, + "error": { + "code": 215, + "message" : "Invalid Worker Name" + } +} +``` + +Example 1 depicts the condition of a severe failure while Example 2 and 3 depict a situation where the request has been accepted and processed properly but the result **MAY NOT** be what expected by the client. +It's up to the client to evaluate the severity of the error and decide whether to proceed or not. + +Using proper error codes pools may properly inform miners of the condition of their requests. Error codes **MUST** honor this scheme: + +- Error codes 2xx : request accepted and processed but some additional info in the `error` member may give hint +- Error codes 3xx : server could not process the request due to a lack of authorization by the client +- Error codes 4xx : server could not process the request due to syntactic problems (method not found, missing params, wrong data types ecc.) or passed `param` values are not valid. +- Error codes 5xx : server could not process the request due to internal errors + +### Notifications +A notification message has the very same representation of a `request` with the only difference the `id` member **MUST NOT** be present. This means the issuer is not interested nor expects any response to this message. It's up to the receiver to take actions accordingly. For instance the receiver **MAY** decide to execute the method, or, in case of errors or methods not allowed, drop the connection thus closing the session. + +#### Error member +As seen above a `response` **MAY** contain an `error` member. When present this member **MUST** be an Object with: +- mandatory member `code` : a Number which identifies the error occurred +- mandatory member `message` : a short human readable sentence describing the error occurred +- optional member `data` : a Structured or Primitive value that contains additional information about the error. The value of this member is defined by the Server (e.g. detailed error information, nested errors etc.). + +## Protocol Flow +- Client starts session by opening a TCP socket to the server +- Client advertises and request protocol compatibility +- Server confirms compatibility and declares ready +- Client starts/resumes a session +- Client sends request for authorization for each of it's workers +- Server replies back with responses for each authorization +- Server sends `mining.set` for constant values to be adopted for following mining jobs +- Server sends `mining.notify` with minimal job info +- Client mines on job +- Client sends `mining.submit` if any solution found for the job +- Server replies whether solution is accepted +- Server optionally sends `mining.set` for constant values to be adopted for following mining jobs (if something changed) +- Server sends `mining.notify` with minimal job info +- ... (continue) +- Eventually either party closes session and TCP connection + +### Session Handling - Hello +~~One of the worst annoyances until now is that server, at the very moment of socket connection, does not provide any useful information about the stratum flavour implemented. This means the client has to start a conversation by iteratively trying to connect via different protocol flavours. This proposal amends the situation making mandatory for the server to advertise itself to the client. +When a new client connects to the server, the server **MUST** send a `mining.hello` notification :~~ + +It's been noted that charging the server of the duty to advertise itself as first message of the conversation could potentially be harmful in respect of traffic amplification attacks using spoofed IP addresses or in traditional DDos attacks where an attacker need to spend very little resources to force the server to send a large packet back. +For this reason the duty of first advertisement is kept on client which will issue a `mining.hello` request like this: + +```json +{ + "id" : 0, + "method": "mining.hello", + "params": + { + "agent": "ethminer-0.17", + "host" : "somemininigpool.com", + "port" : "4d2", + "proto": "EthereumStratum/2.0.0" + } +} +``` +The `params` member object has these mandatory members: +- `agent` (string) the mining software version +- `host` (string) the host name of the server this client is willing to connect to +- `port` (hex) the port number of the server this client is willing to connect to +- `proto` (string) which reports the stratum flavour requested and expected to be implemented by the server; + +The rationale behind sending host and port is it enables virtual hosting, where virtual pools or private URLs might be used for DDoS protection, but that are aggregated on Stratum server backends. As with HTTP, the server CANNOT trust the host string. The port is included separately to parallel the client.reconnect method (see below). + +If the server is prepared to start/resume a session with such requirements it **MUST** reply back with a response like this: +```json +{ + "id" : 0, + "result": + { + "proto" : "EthereumStratum/2.0.0", + "encoding" : "gzip", + "resume" : "1", + "timeout" : "b4", + "maxerrors" : "5", + "node" : "Geth/v1.8.18-unstable-f08f596a/linux-amd64/go1.10.4" + } +} +``` +Where the `result` is an object made of 5 mandatory members +- `proto` (string) which **MUST** match the exact version requested by the client +- `encoding` (string) which value states whether or not all **next messages** should be gzip compressed or not. Possible values are "gzip" or "plain" +- `resume` (hex) which value states whether or not the host can resume a previously created session; +- `timeout` (hex) which reports the number of seconds after which the server is allowed to drop connection if no messages from the client +- `maxerrors` (hex) the maximum number of errors the server will bear before abruptly close connection +- `node` (string) the node software version underlying the pool + +When the server replies back with `"encoding" : "gzip"` to the client, both parties **MUST** gzip compress all next messages. In case the client is not capable of compression it **MUST** close the connection immediately. +Should the server, after this reply, receive other messages as plain text, it **MUST** close the connection. + +Eventually the client will continue with `mining.subscribe` (further on descripted) + +Otherwise, in case of errors or rejection to start the conversation, the server **MAY** reply back with an error giving the other party useful information or, at server's maintainers discretion, abruptly close the connection. +```json +{ + "id" : 0, + "error": + { + "code": 400, + "message" : "Bad protocol request" + } +} +``` +or +```json +{ + "id" : 0, + "error": + { + "code": 403, + "message" : "Forbidden - Banned IP address" + } +} +``` +_The above two JSON error values are only samples_ +Eventually the server will close the connection. + +Why a pool should advertise the node's version ? It's a matter of transparency : miners should know whether or not pool have upgraded to latest patches/releases for node's software. + +### Session Handling - Bye +Disconnection are not gracefully handled in Stratum. Client disconnections from pool may be due to several errors and this leads to waste of TCP sockets on server's side which wait for keepalive timeouts to trigger. A useful notification is `mining.bye` which, once processed, allows both parties of the session to stop receiving and gracefully close TCP connections +```json +{ + "method": "mining.bye" +} +``` +The party receiving this message aknowledges the other party wants to stop the conversation and closes the socket. The issuer will close too. The explicit issuance of this notification implies the session gets abandoned so no session resuming will be possible even on server which support session-resuming. Client reconnecting to the same server which implements session resuming **SHOULD** expect a new session id and **MUST** re-authorize all their workers. + +### Session Handling - Session Subscription +After receiving the response to `mining.hello` from server, the client, in case the server does support session resuming **MAY** request to resume a previously interrupted session with `mining.subscribe` request: +```json +{ + "id": 1, + "method": "mining.subscribe", + "params": "s-12345" +} +``` +where `params` is the id of the session the client wants to resume. + +Otherwise, if client wants to start a new session **OR** server does not support session resuming, the request of subscription **MUST** omit the `params` member: +```json +{ + "id": 1, + "method": "mining.subscribe" +} +``` + +### Session Handling - Response to Subscription +A server receiving a client session subscription **MUST** reply back with +```json +{ + "id": 1, + "result": "s-12345" +} +``` +A server receiving a subscription request with `params` being a string holding the session id. This cases may apply +- If session resuming is not supported `result` will hold a new session Id which **MUST** be a different value from the `session` member issued by client in previous `mining.subscribe` method +- If session resuming is supported it will retrieve working values from cache and `result` will have the same id requested by the client. This means a session is "resumed": as a consequence the server **CAN** start pushing jobs omitting to precede them with `mining.set` (see below) and the client **MUST** continue to use values lastly received within the same session scope. In addition the client **CAN** omit to re-authorize all it's workers. +- If session resuming is supported but the requested session has expired or it's cache values have been purged `result` will hold a new session Id which **MUST** be a different value from the `session` member issued by client in previous `mining.subscribe` method. As a consequence the server **MUST** wait for client to request authorization for it's workers and **MUST** send `mining.set` values before pushing jobs. The client **MUST** prepare for a new session discarding all previously cached values (if any). + +A server implementing session-resuming **MUST** cache : +- The session Ids +- Any active job per session +- The extraNonce +- Any authorized worker + +Servers **MAY** drop entries from the cache on their own schedule. It's up to server to enforce session validation for same agent and/or ip. + +A client which successfully subscribes and resumes session (the `session` value in server response is identical to `session` value requested by client on `mining.subscribe`) **CAN** omit to issue the authorization request for it's workers. + +### Session Handling - Noop +There are cases when a miner struggles to find a solution in a reasonable time so it may trigger the timeout imposed by the server in case of no communications (the server, in fact, may think the client got disconnected). To mitigate the problem a new method `mining.noop`(with no additional parameters) may be requested by the client. +```json +{ + "id": 50, + "method": "mining.noop" +} +``` +### Session Handling - Reconnect +Under certain circumstances the server may need to free some resources and or to relocate miners to another machine. Until now the only option for servers was to abruptly close the connection. On the miner's side this action is interpreted as a server malfunction and they, more often than not, switch to a failover pool. +The implementation of the notification `mining.reconnect` helps client to better merge with logic of handling of large mining pools. +```json +{ + "method": "mining.reconnect", + "params": { + "host": "someotherhost.com", + "port": "d80", + "resume": "1" + } +} +``` +This notification is meant only from servers to clients. Should a server receive such a notification it will simply ignore it. After the notification has been properly sent, the server is ALLOWED to close the connection, while the client will take the proper actions to reconnect to the suggested end-point. +The `host` member in `params` object **SHOULD** report an host DNS name and not an IP address: TLS encrypted connections require to validate the CN name in the certificate which, 99% of the cases, is an host name. +The third member `resume` of the `params` object sets whether or not the receiving server is prepared for session resuming. +After this notification has been issued by the server, the client should expect no further messages and **MUST** disconnect. + +### Workers Authorization +The miner **MUST** authorize at least one worker in order to begin receiving jobs and submit solutions or hashrates. The miner **MAY** authorize multiple workers in the same session. The server **MUST** allow authorization for multiple workers within a session and **MUST** validate at least one authorization from the client before starting to send jobs. A `worker` is a tuple of the address where rewards must be credited coupled with identifier of the machine actually doing the work. For Ethereum the most common form is `.`. The same account can be bound to multiple machines. For pool's allowing anonymous mining the account is the address where rewards must be credited, while, for pools requiring registration, the account is the login name. Each time a solution is submitted by the client it must be labelled with the Worker identifier. It's up to server to keep the correct accounting for different addresses. + +The syntax for the authorization request is the following: +```json +{ + "id": 2, + "method": "mining.authorize", + "params": ["[.]", "password"] +} +``` +`params` member must be an Array of 2 string elements. For anonymous mining the "password" can be any string value or empty but not null. Pools allowing anonymous mining will simply ignore the value. +The server **MUST** reply back either with an error or, in case of success, with +```json +{ + "id": 2, + "result": "w-123" +} +``` +Where the `result` member is a string which holds an unique - within the scope of the `session` - token which identifies the authorized worker. For every further request issued by the client, and related to a Worker action, the client **MUST** use the token given by the server in response to an `mining.authorize` request. This reduces the number of bytes transferred for solution and /or hashrate submission. + +If client is resuming a previous session it **CAN** omit the authorization request for it's workers and, in this case, **MUST** use the tokens assigned in the originating session. It's up to the server to keep the correct map between tokens and workers. +The server receiving an authorization request where the credentials match previously authorized ones within the same session **MUST** reply back with the previously generated unique token. + +### Prepare for mining +A lot of data is sent over the wire multiple times with useless redundancy. For instance the seed hash is meant to change only every 30000 blocks (roughly 5 days) while fixed-diff pools rarely change the work target. Moreover pools must optimize the search segments among miners trying to assign to every session a different "startNonce" (AKA extraNonce). +For this purpose the `notification` method `mining.set` allows to set (on miner's side) only those params which change less frequently. The server will keep track of seed, target and extraNonce at session level and will push a notification `mining.set` whenever any (or all) of those values change to the connected miner. +```json +{ + "method": "mining.set", + "params": { + "epoch" : "dc", + "target" : "0112e0be826d694b2e62d01511f12a6061fbaec8bc02357593e70e52ba", + "algo" : "ethash", + "extranonce" : "af4c" + } +} +``` +At the beginning of each `session` the server **MUST** send this notification before any `mining.notify`. All values passed by this notification will be valid for all **NEXT** jobs until a new `mining.set` notification overwrites them. Description of members is as follows: +- optional `epoch` (hex) : unlike all actual Stratum implementations the server should inform the client of the epoch number instead of passing the seed hash. This is enforced by two reasons : the main one is that client has only one way to compute the epoch number and this is by a linear search from epoch 0 iteratively trying increasing epochs till the hash matches the seed hash. Second reason is that epoch number is more concise than seed hash. In the end the seed hash is only transmitted to inform the client about the epoch and is not involved in the mining algorithm. +- optional `target` (hex) : this is the boundary hash already adjusted for pool difficulty. Unlike in EthereumStratum/1.0.0, which provides a `mining.set_difficulty` notification of an _index of difficulty_, the proponent opt to pass directly the boundary hash. If omitted the client **MUST** assume a boundary of `"0x00000000ffff0000000000000000000000000000000000000000000000000000"` +- optional `algo` (string) : the algorithm the miner is expected to mine on. If omitted the client **MUST** assume `"algo": "ethash"` +- optional `extranonce` (hex) : a starting search segment nonce assigned by server to clients so they possibly do not overlap their search segments. If server wants to "cancel" a previously set extranonce it must pass this member valued as an empty string. + +Whenever the server detects that one, or two, or three or four values change within the session, the server will issue a notification with one, or two or three or four members in the `param` object. For this reason on each **new** session the server **MUST** pass all four members. As a consequence the miner is instructed to adapt those values on **next** job which gets notified. +The new `algo` member is defined to be prepared for possible presence of algorithm variants to ethash, namely ethash1a or ProgPow. +Pools providing multicoin switching will take care to send a new `mining.set` to miners before pushing any job after a switch. +The client which can't support the data provided in the `mining.set` notification **MAY** close connection or stay idle till new values satisfy it's configuration (see `mining.noop`). +All client's implementations **MUST** be prepared to accept new extranonces during the session: unlike in EthereumStratum/1.0.0 the optional client advertisement `mining.extranonce.subscribe` is now implicit and mandatory. + +The miner receiving the `extranonce` **MUST** initialize the search segment for next job resizing the extranonce to a hex of 16 bytes thus appending as many zeroes as needed. +Extranonce "af4c" means "_search segment of next jobs starts from 0xaf4c000000000000_" +If `extranonce` is valued to an empty string, or it's never been set within the session scope, the client is free pick any starting point of it's own search segment on subsequent `mining.notify` jobs. + +### A detail of "extranonce" + +Miners connected to a pool might likely process the very same nonces thus wasting a lot of duplicate jobs. A `nonce` is any valid number which, applied to algorithm and job specifications, produces a result which is below a certain target. For every job pushed by server to client(s) there are 2^64 possible nonces to test. + +To be noted that : +- Any nonce in the 2^64 has the very same possibility to be the right one. +- A certain hashing job can be solved by more than one nonce + +Every "test" over a number is called a hash. Assuming a miner should receive a job for each block and considering the actual average block time of 15 seconds that would mean a miner should try + +``` + ( 2^64 / 15 ) / 1T ~ 1,229,782.94 TeraHashes per second +``` + +This computation capacity is well beyond any miner on the market (including ASICs). For this reason single miners can process only small chunks (segments) of this humongous range. The way miners pick the segments to search on is beyond the scope of this work. Fact is as miners are not coordinated there is no knowledge - for a single miner - of segments picked by other miners. +Extranonce concept is here to mitigate this possibility of duplicate jobs charging the server (the work provider) to give miners, at the maximum possible extent, different segments to search on. + +Giving the above assumptions we can depict a nonce as any number in the hex range : + +``` + Min 0x0000000000000000 + Max 0xffffffffffffffff +``` +_the prefix 0x is voluntarily inserted here only to give a better visual representation_. + +The `extranonce` is, at it's basics, the message of the server saying the client "_I give you the first number to start search from_". More in detail the `extranoce` is the leftmost part of that number. +Assume a pool notifies the client the usage of extranonce `ab5d` this means the client will see it's search segment narrowed as +``` + Min 0xab5d000000000000 + Max 0xab5dffffffffffff +``` +Pushing an extranonce of 4 bytes (like in the example) will give pool the possibility to separate segment 65535 different miners ( or if you prefer 0xffff miners ) while leaving the miner still a segment of 2^48 possible nonces to search on. +Recalculating, as above, the computation capacity needed to search this segment we get +``` + ( 2^48 / 15 ) / 1T ~ 18.76 TeraHashes per second +``` +Which is still a wide segment where miners can randomly (or using other ergodic techniques) pick their internal search segments. + +Extranonce **MUST** be passed with all relevant bytes (no omission of left zeroes) for a specific reason. Assume an extranonce of "01ac" : it has the same decimal value of "1ac" but the number of bytes changes thus changing available search segment + +``` + When "01ac" When "1ac" + Segment is Segment is + Min 0x01ac000000000000 Min 0x1ac0000000000000 + Max 0x01acffffffffffff Max 0x1acfffffffffffff +``` +As you can see resulting segments are quite different + +This all said pools (server), when making use of extranonce, **MUST** observe a maximum length of 6 bytes (hex). + +### Jobs notification +When available server will dispatch jobs to connected miners issuing a `mining.notify` notification. +```json +{ + "method": "mining.notify", + "params": [ + "bf0488aa", + "6526d5" + "645cf20198c2f3861e947d4f67e3ab63b7b2e24dcc9095bd9123e7b33371f6cc", + "0" + ] +} +``` +`params` member is made of 4 mandatory elements: +- 1st element is jobId as specified by pool. To reduce the amount of data sent over the wire pools **SHOULD** keep their job IDs as concise as possible. Pushing a Job id which is identical to headerhash is a bad practice and is highly discouraged. +- 2nd element is the hex number of the block id +- 3rd element is the headerhash. +- 4th element is an hex boolean indicating whether or not eventually found shares from previous jobs will be accounted for sure as stale. + +### Solution submission +When a miner finds a solution for a job he is mining on it sends a `mining.submit` request to server. +```json +{ + "id": 31, + "method": "mining.submit", + "params": [ + "bf0488aa", + "68765fccd712", + "w-123" + ] +} +``` +First element of `params` array is the jobId this solution refers to (as sent in the `mining.notify` message from the server). Second element is the `miner nonce` as hex. Third element is the token given to the worker previous `mining.authorize` request. Any `mining.submit` request bound to a worker which was not successfully authorized - i.e. the token does not exist in the session - **MUST** be rejected. + +You'll notice in the sample above the `miner nonce` is only 12 bytes wide (should be 16). Why ? +That's because in the previous `mining.set` the server has set an `extranonce` of `af4c`. This means the full nonce is `af4c68765fccd712` +In presence of extranonce the miner **MUST** submit only the chars to append to the extranonce to build the final hex value. If no extranonce is set for the session or for the work the miner **MUST** send all 16 bytes. + +It's server duty to keep track of the tuples `job ids <-> extranonces` per session. + +When the server receives this request it either responds success using the short form +```json +{"id": 31} +``` +or, in case of any error or condition with a detailed error object +```json +{ + "id": 31, + "error": { + "code": 404, + "message" : "Job not found" + } +} +``` + +Client **should** treat errors as "soft" errors (stales) or "hard" (bad nonce computation, job not found etc.). Errors in 5xx range are server errors and suggest the miner to abandon the connection and switch to a failover. + +### Hashrate +Most pools offer statistic information, in form of graphs or by API calls, about the calculated hashrate expressed by the miner while miners like to compare this data with the hashrate they read on their devices. Communication about parties of these information have never been coded in Stratum and most pools adopt the method from getWork named `eth_submitHashrate`. +In this document we propose an official implementation of the `mining.hashrate` request. +This method behaves differently when issued from client or from server. +#### Client communicates it's hashrate to server. +```json +{ + "id" : 16, + "method": "mining.hashrate", + "params": [ + "500000", + "w-123" + ] +} +``` +where `params` is an array made of two elements: the first is a hexadecimal string representation (32 bytes) of the hashrate the miner reads on it's devices and the latter is the authorization token issued to worker this hashrate is refers to (see above for `mining.authorization`). +Server **MUST** respond back with either an aknowledgment message +```json +{"id": 16 } +``` +Optionally the server can reply back reporting it's findings about calculated hashrate **for the same worker**. +```json +{ + "id": 16, + "result" : [ + "4f0000", + "w-123" + ] +} +``` +In case of errors - for example when the client submits too frequently - with +```json +{ + "id": 16, + "error" : { + "code": 220, + "message": "Enhance your calm. Too many requests" + } +} +``` +#### Server communicates hashrate to client +Optionally the server can **notify** client about it's overall performance (according to schedule set on server) with a `mining.hashrate` notification composed like this +```json +{ + "method": "mining.hashrate", + "params": { + "interval": 60, + "hr": "500000", + "accepted": [3692,20], + "rejected": 0, + } +} +``` +Where `params` is an object which holds these members for values of the **whole session**: +- `interval` (number) the width, in minutes, of the observation window. "_in the last x minutes we calculated ..._" +- `hr` (hex) representation of the hashrate the pool has calculated for the miner +- `accepted` is an array of two number elements : the first is the overall count of accepted shares and the second is the number of stale shares. The array must be interpreted as "total accepted of which x are stale" +- `rejected` (number) the overall number of rejected shares + +The client will eventually take internal actions to reset/restart it's workers. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1577.md b/EIPS/eip-1577.md new file mode 100644 index 0000000..c555e53 --- /dev/null +++ b/EIPS/eip-1577.md @@ -0,0 +1,116 @@ +--- +eip: 1577 +title: contenthash field for ENS +author: Dean Eigenmann , Nick Johnson +type: Standards Track +category: ERC +status: Stagnant +created: 2018-11-13 +--- + +## Abstract + +This EIP introduces the new `contenthash` field for ENS resolvers, allowing for a better defined system of mapping names to network and content addresses. Additionally the `content` and `multihash` fields are deprecated. + +## Motivation + +Multiple applications including [Metamask](https://metamask.io/) and mobile clients such as [Status](https://status.im) have begun resolving ENS names to content hosted on distributed systems such as [IPFS](https://ipfs.io/) and [Swarm](https://swarm-guide.readthedocs.io). Due to the various ways content can be stored and addressed, a standard is required so these applications know how to resolve names and that domain owners know how their content will be resolved. + +The `contenthash` field allows for easy specification of network and content addresses in ENS. + +## Specification + +The field `contenthash` is introduced, which permits a wide range of protocols to be supported by ENS names. Resolvers supporting this field MUST return `true` when the `supportsInterface` function is called with argument `0xbc1c58d1`. + +The fields `content` and `multihash` are deprecated. + +The value returned by `contenthash` MUST be represented as a machine-readable [multicodec](https://github.com/multiformats/multicodec). The format is specified as follows: + +``` + +``` + +protoCodes and their meanings are specified in the [multiformats/multicodec](https://github.com/multiformats/multicodec) repository. + +The encoding of the value depends on the content type specified by the protoCode. Values with protocodes of 0xe3 and 0xe4 represent IPFS and Swarm content; these values are encoded as v1 [CIDs](https://github.com/multiformats/cid) without a base prefix, meaning their value is formatted as follows: + +``` + +``` + +When resolving a `contenthash`, applications MUST use the protocol code to determine what type of address is encoded, and resolve the address appropriately for that protocol, if supported. + +### Example + +#### IPFS + +Input data: + +``` +storage system: IPFS (0xe3) +CID version: 1 (0x01) +content type: dag-pb (0x70) +hash function: sha2-256 (0x12) +hash length: 32 bytes (0x20) +hash: 29f2d17be6139079dc48696d1f582a8530eb9805b561eda517e22a892c7e3f1f +``` + +Binary format: + +``` +0xe3010170122029f2d17be6139079dc48696d1f582a8530eb9805b561eda517e22a892c7e3f1f +``` + +Text format: + +``` +ipfs://QmRAQB6YaCyidP37UdDnjFY5vQuiBrcqdyoW1CuDgwxkD4 +``` + +### Swarm + +Input data: + +``` +storage system: Swarm (0xe4) +CID version: 1 (0x01) +content type: swarm-manifest (0xfa) +hash function: keccak256 (0x1b) +hash length: 32 bytes (0x20) +hash: d1de9994b4d039f6548d191eb26786769f580809256b4685ef316805265ea162 +``` + +Binary format: +``` +0xe40101fa011b20d1de9994b4d039f6548d191eb26786769f580809256b4685ef316805265ea162 +``` + +Text format: +``` +bzz://d1de9994b4d039f6548d191eb26786769f580809256b4685ef316805265ea162 +``` + +Example usage with swarm hash: +``` +$ swarm hash ens contenthash d1de9994b4d039f6548d191eb26786769f580809256b4685ef316805265ea162 +> e40101fa011b20d1de9994b4d039f6548d191eb26786769f580809256b4685ef316805265ea162 +``` + +### Fallback + +In order to support names that have an IPFS or Swarm hash in their `content` field, a grace period MUST be implemented offering those name holders time to update their names. If a resolver does not support the `multihash` interface, it MUST be checked whether they support the `content` interface. If they do, the value of that field SHOULD be treated in a context dependent fashion and resolved. This condition MUST be enforced until at least March 31st, 2019. + +### Implementation + +To support `contenthash`, a new resolver has been developed and can be found [here](https://github.com/ensdomains/resolvers/blob/master/contracts/PublicResolver.sol), you can also find this smart contract deployed on: + +* Mainnet : [0xd3ddccdd3b25a8a7423b5bee360a42146eb4baf3](https://etherscan.io/address/0xd3ddccdd3b25a8a7423b5bee360a42146eb4baf3) +* Ropsten : [0xde469c7106a9fbc3fb98912bb00be983a89bddca](https://ropsten.etherscan.io/address/0xde469c7106a9fbc3fb98912bb00be983a89bddca) + +There are also implementations in multiple languages to encode and decode `contenthash`: + +* [JavaScript](https://github.com/pldespaigne/content-hash) +* [Python](https://github.com/filips123/ContentHashPy) + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-158.md b/EIPS/eip-158.md new file mode 100644 index 0000000..905e1d3 --- /dev/null +++ b/EIPS/eip-158.md @@ -0,0 +1,41 @@ +--- +eip: 158 +title: State clearing +author: Vitalik Buterin (@vbuterin) +type: Standards Track +category: Core +status: Final +created: 2016-10-16 +--- + +# Specification + +For all blocks where `block.number >= FORK_BLKNUM` (TBA): +1. In all cases where a state change is made to an account, and this state change results in the account state being saved with nonce = 0, balance = 0, code empty, storage empty (hereinafter "empty account"), the account is instead deleted. +2. If a address is "touched" and that address contains an empty account, then it is deleted. A "touch" is defined as any situation where if the account at the given address were nonexistent it would be created. +3. Whenever the EVM checks if an account exists, emptiness is treated as equivalent to nonexistence. Particularly, note that this implies that, once this change is enabled, there is no longer a meaningful difference between emptiness and nonexistence from the point of view of EVM execution. +4. Zero-value calls and zero-value suicides no longer consume the 25000 account creation gas cost in any circumstance + +The cases where a "touch" takes place can be enumerated as follows: +- Zero-value-bearing CALLs +- CREATEs (if the code that is ultimately saved is empty and there is no ether remaining in the account when it is saved) +- Zero-value-bearing SUICIDEs +- Transaction recipients +- Contracts created in contract creation transactions +- Miners receiving transaction fees (note the case where the gasprice is zero, and the account does not yet exist because it only receives the block/uncle/nephew rewards _after_ processing every transaction) +### Specification (1b) + +When the EVM checks for emptiness (for the purpose of possibly applying the 25000 gas cost), emptiness is defined by `is_empty(acct): return get_balance(acct) == 0 and get_code(acct) == "" and get_nonce(acct) == 0`; emptiness of storage does not matter. This simplifies client implementation because there is no need to add extra complexity to make caches enumerable in the correct way and does not significantly affect the intended result, as the cases where balance/code/nonce are empty but storage is nonempty where this change would lead to an extra 25000 gas being paid are pathological and have no real use value. +### Specification (1c) + +Do not implement point 2 above (ie. no new empty accounts can be created, but existing ones are not automatically destroyed unless their state is actually _changed_). Instead, during each block starting from (and including) N and ending when there are no null accounts left, select the 1000 null accounts that are left-most in order of sha3(address), and delete them (ordering by hash is necessary so as to allow the accounts to be easily found by iterating the tree). +# Rationale + +This removes a large number of empty accounts that have been put in the state at very low cost due to flaws in earlier versions of the Ethereum protocol, thereby greatly reducing state size and hence both reducing the hard disk load of a full client and reducing the time for a fast sync. Additionally, it simplifies the protocol in the long term, as once all "empty" objects are cleared out there is no longer any meaningful distinction between an account being empty and being nonexistent, and indeed one can simply view nonexistence as a compact representation of emptiness. + +Note that this proposal does introduce a **temporary** breaking of existing guarantees, in that by repeatedly zero-value-calling already existing empty accounts one can create a state change at a cost of 700 gas per account instead of the usual 5000 per gas minimum (with SUICIDE refunds this goes down further to 350 gas per account). Allowing such a large number of state writes per block will lead to heightened block processing times and increase uncle rates in the short term while the existing empty accounts are being cleared, and eventually once all empty accounts are cleared this issue will no longer exist. + +# References + +1. EIP-158 issue and discussion: https://github.com/ethereum/EIPs/issues/158 +2. EIP-161 issue and discussion: https://github.com/ethereum/EIPs/issues/161 diff --git a/EIPS/eip-1581.md b/EIPS/eip-1581.md new file mode 100644 index 0000000..d4634d7 --- /dev/null +++ b/EIPS/eip-1581.md @@ -0,0 +1,49 @@ +--- +eip: 1581 +title: Non-wallet usage of keys derived from BIP-32 trees +description: A derivation path structure for BIP32 trees to generate key pairs not meant to hold crypto assets. +author: Michele Balistreri (@bitgamma) +discussions-to: https://ethereum-magicians.org/t/non-wallet-usage-of-keys-derived-from-bip-32-trees/1817 +status: Stagnant +type: Standards Track +category: ERC +created: 2018-11-13 +--- +## Abstract +BIP32 defines a way to generate hierarchical trees of keys which can be derived from a common master key. BIP32 and [BIP44](https://https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki) defines the usage of these keys as wallets. In this EIP we describe the usage of such keys outside the scope of the blockchain defining a logical tree for key usage which can coexist (and thus share the same master) with existing BIP44 compatible wallets. + +## Motivation +Applications interacting with the blockchain often make use of additional, non-blockchain technologies to perform the task they are designed for. For privacy and security sensitive mechanisms, sets of keys are needed. Reusing keys used for wallets can prove to be insecure, while keeping completely independent keys make backup and migration of the full set of credentials more complex. Defining a separate (from BIP44 compliant wallets) derivation branch allows combining the security of independent keys with the convenience of having a single piece of information which needs to be backup or migrated. + +## Specification + +### Path levels +We define the following levels in BIP32 path: + +```m / purpose' / coin_type' / subpurpose' / key_type' / key_index``` + +Apostrophe in the path indicates that BIP32 hardened derivation is used. + +This structure follows the [BIP43](https://github.com/bitcoin/bips/blob/master/bip-0043.mediawiki) recommendations and its [amendments for non-Bitcoin usage](https://github.com/bitcoin/bips/pull/523/files). Each level has a special meaning, described in the chapters below. + +### Purpose/Coin Type/Subpurpose +This part is constant and set to ```m / 43' / 60' / 1581'```, meaning BIP 43 -> Ethereum -> This EIP. + +All subtrees under this prefix are the scope of this EIP. + +### Key type +Describes the purpose for which the key is being used. Key types should be generic. "Instant messaging" is a good example whereas "Whisper" is not. The reason is that you want to be able to use the same identity across different services. Key types are defined at: TBD + +Hardened derivation is used at this level. + +### Key index +The key index is a field of variable length identifying a specific key. In its simplest case, it is a number from 0 to 2^31-1. If a larger identifier is desired (for example representing a hash or a GUID), the value must be split +across several BIP32 nesting levels, most significant bit first and left aligned, bit-padded with 0s if needed. All levels, except the last one must used hardened key derivation. The last level must use public derivation. This means that every level can carry 31-bit of the identifier to represent. + +As an example, let's assume we have a key with key type 4' and a key_index representing a 62-bit ID represented as hexadecimal 0x2BCDEFFEDCBAABCD the complete keypath would be ```m / 43' / 60' / 1581' / 4' / ‭1469833213‬' / ‭1555737549‬ ```. If you are using random identifiers, it might be convenient to generate a conventional GUID, for example 128-bit just fix the value of the most significant bit of each 32-bit word to 1 for all of them, except the last one which will be 0. + +## Rationale +The structure proposed above follows the BIP43 generic structure and is similar to the widely adopted BIP44 specification. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1588.md b/EIPS/eip-1588.md new file mode 100644 index 0000000..f7e4202 --- /dev/null +++ b/EIPS/eip-1588.md @@ -0,0 +1,26 @@ +--- +eip: 1588 +title: "Hardfork Meta: Ethereum ProgPoW" +author: Ikmyeong Na (@naikmyeong) +status: Stagnant +type: Meta +created: 2018-11-16 +requires: 1057 +--- + +## Abstract + +This meta-EIP specifies the changes included in the alternative Ethereum hardfork named Ethereum ProgPoW. + +## Specification + +- Codename: Ethereum ProgPoW +- Aliases: N/A +- Activation: + - `Block >= 7280000` on the Ethereum mainnet +- Included EIPs: + - [EIP-1057](./eip-1057.md): ProgPoW, a Programmatic Proof-of-Work + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1592.md b/EIPS/eip-1592.md new file mode 100644 index 0000000..dd97e59 --- /dev/null +++ b/EIPS/eip-1592.md @@ -0,0 +1,171 @@ +--- +eip: 1592 +title: Address and ERC20-compliant transfer rules +author: Cyril Lapinte , Laurent Aapro +discussions-to: https://github.com/ethereum/EIPs/issues/1597 +type: Standards Track +category: ERC +status: Stagnant +created: 2018-11-09 +--- + +## Simple Summary + +We propose a standard and an interface to define transfer rules, in the context of ERC20 tokens and possibly beyond. + + +A rule can act based on sender, destination and amount, and is triggered (and rejects the transfer) according to any required business logic. + + +To ease rule reusability and composition, we also propose an interface and base implementation for a rule engine. + +## Abstract + +This standard proposal should answer the following challenges: +- Enable integration of rules with interacting platforms such as exchanges, decentralized wallets and DApps. +- Externale code and storage, improve altogether reusability, gas costs and contracts' memory footprint. +- Highlight contract behavior and its evolution, in order to ease user interaction with such contract. + + +If these challenges are answered, this proposal will provide a unified basis for transfer rules and hopefully address the transfer restriction needs of other EIPs as well, e.g. +[EIP-902](./eip-902.md), +[EIP-1066](./eip-1066.md) +and [EIP-1175](./eip-1175.md). + +This document proposes specifications for a standard of **transfer rules** and interfaces to both the rules and the rule engine, which was made to be inherited by a token, but may have a much broader scope in the authors' opinion. + +The last section of this document illustrates the proposal with a rule template and links to rule implementations. + +## Motivation + +ERC20 was designed as a standard interface allowing any token on Ethereum to be handled by other applications: from wallets to decentralized exchanges. This has been extremely powerful, but future developments in the industry of tokenization are bringing new challenges. For example it is already hard to know exactly why an ERC20 transfer failed, and it will become even harder when many tokens add their own transfer rules to the mix; we propose that it should be trivial to determine before a tx is sent, whether the transfer should turn out valid or invalid, and why (unless conditions change in the meantime obviously). On the other hand, if the rules were changed, it should also be easily detected, so that the interacting party knows it must adjust its expectations or model. + +## Specification + +We define below an interface for a rule. Rules are meant to be as simple as possible, to limit gas expenditure, since that logic will be executed on every transfer. Another reason for keeping rules simple and short, and strive for atomicity, is to facilitate both composition and interpretation of rejected transfers. By knowing which rule was triggered, we obtain a clear picture of the reason for rejection. + +The engine we propose executes all the rules defined by its owner, on every transfer and it is easy to add and remove rules individually, although we have chosen to use quite a raw rule update method, to save on deployment costs, which are often tight when it comes to token smart contracts. + +Rules are deployed on the blockchain as individual smart contracts, and called upon by the rule engine they were attached to. But any third party, for example an exchange preparing a cashout for a customer, can very cheaply query the rule engine of the token, or a single rule directly, to verify the validity of a transfer before execution, so as to never get a rejected transaction. + +## Rule interface + +`IRule` interface should provide a way to validate if an address or a transfer is valid. + +If one of these two methods is not applicable, it can simply be made to return true systematically. +If any parameter of `isTransferValid` is not needed, its name should be commented out with `/* */`. + +```js +pragma solidity ^0.4.25; + +interface IRule { + function isAddressValid(address _address) external view returns (bool); + function isTransferValid(address _from, address _to, uint256 _amount) + external view returns (bool); +} +``` + +## WithRules interface + +`WithRules` interface describes the integration of rules to a rule engine. +Developers may choose to not implement this interface if their code will only deal with one rule, or if it is not desirable to update the rules. + +The rules ordering must be thought through carefully. +Rules which are cheaper to validate or have a higher chance to break should be put first to reduce global gas expenditure, then business logic should guide the ordering of rules. That is why rules for a given context should be defined as a whole and not individually. + +```js +pragma solidity ^0.4.25; + +import "./IRule.sol"; + +interface IWithRules { + function ruleLength() public view returns (uint256); + function rule(uint256 _ruleId) public view returns (IRule); + function validateAddress(address _address) public view returns (bool); + function validateTransfer(address _from, address _to, uint256 _amount) + public view returns (bool); + + function defineRules(IRule[] _rules) public; + + event RulesDefined(uint256 count); +} +``` + +## WithRules implementation + +We also propose a simple implementation of the rule engine, available [here](https://github.com/MtPelerin/MtPelerin-protocol/blob/master/contracts/rule/WithRules.sol). It has been kept minimal both to save on gas costs on each transfer, and to reduce the deployment cost overhead for the derived smart contract. + + +On top of implementing the interface above, this engine also defines two modifiers (`whenAddressRulesAreValid`and `whenTransferRulesAreValid`), which can be used throughout the token contract to restrict `transfer()`, `transferFrom` and any other function that needs to respect either a simple whitelist or complex transfer rules. + + +## Integration + +To use rules within a token is as easy as having the token inherit from WithRules, then writing rules according to the IRule interface and deploying each rule individually. The token owner can then use `defineRules()` to attach all rules in the chosen order, within a single transaction. + +Below is a template for a rule. + +```solidity +import "../interface/IRule.sol"; + +contract TemplateRule is IRule { + + // state vars for business logic + + constructor(/* arguments for init */) public { + + // initializations + + } + + function isAddressValid(address _from) public view returns (bool) { + boolean isValid; + + // business logic + + return isValid; + } + + function isTransferValid( + address _from, + address _to, + uint256 _amount) + public view returns (bool) + { + boolean isValid; + + // business logic + + return isValid; + } +} +``` + +*** Notes *** +The MPS (Mt Pelerin's Share) token is the current live implementation of this standard. +Other implementations may be written with different trade-offs: from gas savings to improved security. + +#### Example of rules implementations + +- [YesNo rule](https://github.com/MtPelerin/MtPelerin-protocol/tree/master/contracts/rule/YesNoRule.sol): Trivial rule used to demonstrate both a rule and the rule engine. + +- [Freeze rule](https://github.com/MtPelerin/MtPelerin-protocol/tree/master/contracts/rule/FreezeRule.sol): This rule allows to prevent any transfer of tokens to or from chosen addresses. A smart blacklist. + +- [Lock rule](https://github.com/MtPelerin/MtPelerin-protocol/tree/master/contracts/rule/LockRule.sol): Define a global transfer policy preventing either sending or receiving tokens within a period of time. Exceptions may be granted to some addresses by the token admin. A smart whitelist. + +- [User Kyc Rule](https://github.com/MtPelerin/MtPelerin-protocol/tree/master/contracts/rule/UserKycRule.sol): Rule example relying on an existing whitelist to assert transfer and addresses validity. It is a good example of a rule that completely externalizes it's tasks. + +#### Example implementations are available at +- [Mt Pelerin Bridge protocol rules implementation](https://github.com/MtPelerin/MtPelerin-protocol/tree/master/contracts/rule) +- [Mt Pelerin Token with rules](https://github.com/MtPelerin/MtPelerin-protocol/blob/master/contracts/token/component/TokenWithRules.sol) + +## History + +Historical links related to this standard: + +- The first regulated tokenized share issued by Mt Pelerin (MPS token) is using an early version of this proposal: https://www.mtpelerin.com/blog/world-first-tokenized-shares +The rule engine was updated several times, after the token issuance and during the tokensale, to match changing business and legal requirements, showcasing the solidity and flexibility of the rule engine. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). +External references outside this repository will have their own specific copyrights. diff --git a/EIPS/eip-160.md b/EIPS/eip-160.md new file mode 100644 index 0000000..0caf02b --- /dev/null +++ b/EIPS/eip-160.md @@ -0,0 +1,28 @@ +--- +eip: 160 +title: EXP cost increase +author: Vitalik Buterin (@vbuterin) +type: Standards Track +category: Core +status: Final +created: 2016-10-20 +--- + +### Hard fork +[Spurious Dragon](./eip-607.md) + +### Parameters +- `FORK_BLKNUM`: 2,675,000 +- `CHAIN_ID`: 1 + +### Specification + +If `block.number >= FORK_BLKNUM`, increase the gas cost of EXP from 10 + 10 per byte in the exponent to 10 + 50 per byte in the exponent. + +### Rationale + +Benchmarks suggest that EXP is currently underpriced by a factor of about 4–8. + +### References + +1. EIP-160 issue and discussion: https://github.com/ethereum/EIPs/issues/160 diff --git a/EIPS/eip-161.md b/EIPS/eip-161.md new file mode 100644 index 0000000..0d63e92 --- /dev/null +++ b/EIPS/eip-161.md @@ -0,0 +1,69 @@ +--- +eip: 161 +title: State trie clearing (invariant-preserving alternative) +author: Gavin Wood (@gavofyork) +type: Standards Track +category: Core +status: Final +created: 2016-10-24 +--- + +### Hard fork +[Spurious Dragon](./eip-607.md) + +### Parameters +- `FORK_BLKNUM`: 2,675,000 +- `CHAIN_ID`: 1 (Mainnet) + +### Specification + +a. 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** (for normal networks, this will be simply 1, however test-nets with non-zero default starting nonces will be different). + +b. Whereas `CALL` and `SUICIDE` would charge 25,000 gas when the destination is non-existent, now the charge SHALL **only** be levied if the operation transfers **more than zero value** and the destination account is _dead_. + +c. No account may _change state_ from non-existent to existent-but-_empty_. If an operation would do this, the account SHALL instead remain non-existent. + +d. _At the end of the transaction_, any account _touched_ by the execution of that transaction which is now _empty_ SHALL instead become non-existent (i.e. **deleted**). + +Where: + +An account is considered to be _touched_ when it is involved in any potentially _state-changing_ operation. This includes, but is not limited to, being the recipient of a **transfer of zero value**. + +An account is considered _empty_ when it has **no code** and **zero nonce** and **zero balance**. + +An account is considered _dead_ when either it is non-existent or it is _empty_. + +_At the end of the transaction_ is immediately following the execution of the suicide list, prior to the determination of the state trie root for receipt population. + +An account _changes state_ when: +- it is the target or refund of a `SUICIDE` operation for **zero or more** value; +- it is the source or destination of a `CALL` operation or message-call transaction transferring **zero or more** value; +- it is the source or creation of a `CREATE` operation or contract-creation transaction endowing **zero or more** value; +- as the block author ("miner") it is the recipient of block-rewards or transaction-fees of **zero or more** value. + +#### Notes + +In the present Ethereum protocol, it should be noted that very few state changes can ultimately result in accounts that are empty following the execution of the transaction. In fact there are only four contexts that current implementations need track: +- an empty account has zero value transferred to it through `CALL`; +- an empty account has zero value transferred to it through `SUICIDE`; +- an empty account has zero value transferred to it through a message-call transaction; +- an empty account has zero value transferred to it through a zero-gas-price fees transfer. + +### Rationale + +Same as #158 except that several edge cases are avoided since we do not break invariants: +- ~~that an account can go from having code and storage to not having code or storage mid-way through the execution of a transaction;~~ [corrected] +- that a newly created account cannot be deleted prior to being deployed. + +`CREATE` avoids zero in the nonce to avoid any suggestion of the oddity of `CREATE`d accounts being reaped half-way through their creation. + +### Addendum (2017-08-15) + +On 2016-11-24, a consensus bug occurred due to two implementations having different behavior in the case of state reverts.[3] The specification was amended to clarify that empty account deletions are reverted when the state is reverted. + +### References + +1. EIP-158 issue and discussion: https://github.com/ethereum/EIPs/issues/158 +2. EIP-161 issue and discussion: https://github.com/ethereum/EIPs/issues/161 +3. https://blog.ethereum.org/2016/11/25/security-alert-11242016-consensus-bug-geth-v1-4-19-v1-5-2/ +> Details: Geth was failing to revert empty account deletions when the transaction causing the deletions of empty accounts ended with an an out-of-gas exception. An additional issue was found in Parity, where the Parity client incorrectly failed to revert empty account deletions in a more limited set of contexts involving out-of-gas calls to precompiled contracts; the new Geth behavior matches Parity’s, and empty accounts will cease to be a source of concern in general in about one week once the state clearing process finishes. diff --git a/EIPS/eip-1613.md b/EIPS/eip-1613.md new file mode 100644 index 0000000..287b5c7 --- /dev/null +++ b/EIPS/eip-1613.md @@ -0,0 +1,300 @@ +--- +eip: 1613 +title: Gas stations network +author: Yoav Weiss , Dror Tirosh , Alex Forshtat +discussions-to: https://github.com/yoav-tabookey/EIPs/issues/1 +status: Stagnant +type: Standards Track +category: ERC +created: 2018-11-18 +requires: 1077 +--- + +## Simple Summary +Make smart contracts (e.g. dapps) accessible to non-ether users by allowing contracts to accept "[collect-calls](https://en.wikipedia.org/wiki/Collect_call)", paying for incoming calls. +Let contracts "listen" on publicly accessible channels (e.g. web URL or a whisper address). +Incentivize nodes to run "gas stations" to facilitate this. +Require no network changes, and minimal contract changes. + +## Abstract +Communicating with dapps currently requires paying ETH for gas, which limits dapp adoption to ether users. +Therefore, contract owners may wish to pay for the gas to increase user acquisition, or let their users pay for gas with fiat money. +Alternatively, a 3rd party may wish to subsidize the gas costs of certain contracts. +Solutions such as described in [EIP-1077](./eip-1077.md) could allow transactions from addresses that hold no ETH. + +The gas stations network is an [EIP-1077](./eip-1077.md) compliant effort to solve the problem by creating an incentive for nodes to run gas stations, where gasless transactions can be "fueled up". +It abstracts the implementation details from both the dapp maintainer and the user, making it easy to convert existing dapps to accept "collect-calls". + +The network consists of a single public contract trusted by all participating dapp contracts, and a decentralized network of relay nodes (gas stations) incentivized to listen on non-ether interfaces such as web or whisper, +pay for transactions and get compensated by that contract. The trusted contract can be verified by anyone, and the system is otherwise trustless. +Gas stations cannot censor transactions as long as there's at least one honest gas station. Attempts to undermine the system can be proven on-chain and offenders can be penalized. + +## Motivation + +* Increase user adoption of smart contracts by: + * Removing the user hassle of acquiring ETH. Transactions are still paid by ETH but costs can be borne by the dapp or paid by the user through other means. + * Removing the need to interact directly with the blockchain, while maintaining decentralization and censorship-resistance. + Contracts can "listen" on multiple public channels, and users can interact with the contracts through common protocols that are generally permitted even in restrictive environments. +* Ethereum nodes get a revenue source without requiring mining equipment. The entire network benefits from having more nodes. +* No protocol changes required. The gas station network is self-organized via a smart contract, and dapps interact with the network by implementing an interface. + +## Specification + +The system consists of a `RelayHub` singleton contract, participating contracts inheriting the `RelayRecipient` contract, a decentralized network of `Relay` nodes, a.k.a. Gas Stations, +and user applications (e.g. mobile or web) interacting with contracts via relays. + +Roles of the `RelayHub`: + +* Maintain a list of active relays. Senders select a `Relay` from this list for each transaction. The selection process is discussed below. +* Mediate all communication between relays and contracts. +* Provide contracts with trusted versions of the real msg.sender and msg.data. +* Hold ETH stakes placed by relays. A minimum stake size is enforced. Stake can be withdrawn after a relay unregisters and waits for a cooldown period. +* Hold ETH prepayments made by contracts and use them to compensate relays. +* Penalize provably-offensive relays by giving their stakes to an address providing the proof, thus keeping relays honest. +* Provide a free way for relays to know whether they'll be compensated for a future transaction. + +Roles of a `Relay` node: + +* Maintain a hot wallet with a small amount of ETH, to pay for gas. +* Provide a public interface for user apps to send gasless transactions via channels such as https or whisper. +* Publish it's public interfaces and its price (as a multiplier of the actual transaction gas cost) in `RelayHub`. +* Optionally monitor reverted transactions of other relays through RelayHub, catching offending relays and claiming their stakes. This can be done by anyone, not just a relay. + +Implementing a `RelayRecipient` contract: + +* Know the address of `RelayHub` and trust it to provide information about the transaction. +* Maintain a small balance of ETH gas prepayment deposit in `RelayHub`. Can be paid directly by the `RelayRecipient` contract, or by the dapp's owner on behalf of the `RelayRecipient` address. + The dapp owner is responsible for ensuring sufficient balance for the next transactions, and can stop depositing if something goes wrong, thus limiting the potential for abuse of system bugs. In DAO usecases it will be up to the DAO logic to maintain a sufficient deposit. +* Use `getSender()` and `getMessageData()` instead of `msg.sender` and `msg.data`, everywhere. `RelayRecipient` provides these functions and gets the information from `RelayHub`. +* Implement a `acceptRelayedCall(address relay, address from, bytes memory encodedFunction, uint gasPrice, uint transactionFee, bytes memory approval)` view function that returns **zero** if and only if it is willing to accept a transaction and pay for it. + `acceptRelayedCall` is called by `RelayHub` as a view function when a `Relay` inquires it, and also during the actual transaction. Transactions are reverted if **non-zero**, and `Relay` only gets compensated for transactions (whether successful or reverted) if `acceptRelayedCall` returns **zero**. Some examples of `acceptRelayedCall()` implementations: + * Whitelist of trusted dapp members. + * Balance sheet of registered users, maintained by the dapp owner. Users pay the dapp with a credit card or other non-ETH means, and are credited in the `RelayRecipient` balance sheet. + Users can never cost the dapp more than they were credited for. + * A dapp can provide off-chain a signed message called `approval` to a transaction sender and validate it. + * Whitelist of known transactions used for onboarding new users. This allows certain anonymous calls and is subject to Sybil attacks. + Therefore it should be combined with a restricted gasPrice, and a whitelist of trusted relays, to reduce the incentive for relays to create bogus transactions and rob the dapp's prepaid gas deposit. + Dapps allowing anonymous onboarding transactions might benefit from registering their own `Relay` and accepting anonymous transactions only from that `Relay`, whereas other transactions can be accepted from any relay. + Alternatively, dapps may use the balance sheet method for onboarding as well, by applying the methods suggested in the attacks/mitigations section below. +* Implement `preRelayedCall(address relay, address from, bytes memory encodedFunction, uint transactionFee) returns (bytes32)`. This method is called before a transaction is relayed. By default, it does nothing. + +* Implement `postRelayedCall(ddress relay, address from, bytes memory encodedFunction, bool success, uint usedGas, uint transactionFee, bytes32 preRetVal)`. This method is called after a transaction is relayed. By default, it does nothing. + + These two methods can be used to charge the user in dapp-specific manner. + +Glossary of terms used in the processes below: + +* `RelayHub` - the RelayHub singleton contract, used by everyone. +* `Recipient` - a contract implementing `RelayRecipient`, accepting relayed transactions from the RelayHub contract and paying for the incoming transactions. +* `Sender` - an external address with a valid key pair but no ETH to pay for gas. +* `Relay` - a node holding ETH in an external address, listed in RelayHub and relaying transactions from Senders to RelayHub for a fee. + +![Sequence Diagram](/assets/eip-1613/sequence.png) + +The process of registering/refreshing a `Relay`: + +* Relay starts listening as a web app (or on some other communication channel). +* If starting for the first time (no key yet), generate a key pair for Relay's address. +* If Relay's address doesn't hold sufficient funds for gas (e.g. because it was just generated), Relay stays inactive until its owner funds it. +* Relay's owner funds it. +* Relay's owner sends the required stake to `RelayHub` by calling `RelayHub.stake(address relay, uint unstakeDelay)`. +* `RelayHub` puts the `owner` and `unstake delay` in the relays map, indexed by `relay` address. +* Relay calls `RelayHub.registerRelay(uint transactionFee, string memory url)` with the relay's `transaction fee` (as a multiplier on transaction gas cost), and a URL for incoming transactions. +* `RelayHub` ensures that Relay has a sufficient stake. +* `RelayHub` puts the `transaction fee` in the relays map. +* `RelayHub` emits an event, `RelayAdded(Relay, owner, transactionFee, relayStake, unstakeDelay, url)`. +* Relay starts a timer to perform a `keepalive` transaction every 6000 blocks. +* `Relay` goes to sleep and waits for signing requests. + +The process of sending a relayed transaction: + +* `Sender` selects a live `Relay` from RelayHub's list by looking at `RelayAdded` events from `RelayHub`, and sorting based on its own criteria. Selection may be based on a mix of: + * Relay published transaction fees. + * Relay stake size and lock-up time. + * Recent relay transactions (visible through `TransactionRelayed` events from `RelayHub`). + * Optionally, reputation/blacklist/whitelist held by the sender app itself, or its backend, on per-app basis (not part of the gas stations network). +* Sender prepares the transaction with Sender's address, the recipient address, the actual transaction data, Relay's transaction fee, gas price, gas limit, its current nonce from `RelayHub.nonces`, RelayHub's address, and Relay's address, and then signs it. +* Sender verifies that `RelayHub.balances[recipient]` holds enough ETH to pay Relay's fee. +* Sender verifies that `Relay.balance` has enough eth to send the transaction +* Sender reads the Relay's current `nonce` value and decides on the `max_nonce` parameter. +* Sender sends the signed transaction amd metadata to Relay's web interface. +* `Relay` wraps the transaction with a transaction to `RelayHub`, with zero ETH value. +* `Relay` signs the wrapper transaction with its key in order to pay for gas. +* `Relay` verifies that: + * The transaction's recipient contract will accept this transaction when submitted, by calling `RelayHub.canRelay()`, a view function, + which checks the recipient's `acceptRelayedCall`, also a view function, stating whether it's willing to accept the charges). + * The transaction nonce matches `RelayHub.nonces[sender]`. + * The relay address in the transaction matches Relay's address. + * The transaction's recipient has enough ETH deposited in `RelayHub` to pay the transaction fee. + * Relay has enough ETH to pay for the gas required by the transaction. + * Value of `max_nonce` is higher than current Relay's `nonce` +* If any of Relay's checks fail, it returns an error to sender, and doesn't proceed. +* Relay submits the signed wrapped transaction to the blockchain. +* Relay immediately returns the signed wrapped transaction to the sender. This step is discussed below, in attacks/mitigations. +* `Sender` receives the wrapped transaction and verifies that: + * It's a valid relay call to `RelayHub`. from Relay's address. + * The transaction's ethereum nonce matches Relay's current nonce. + * The transaction's ethereum nonce is lower than or equal to `max_nonce`. + * `Relay` is sufficiently funded to pay for it. + * The wrapped transaction is valid and signed by `sender`. + * Recipient contract has sufficient funds in `RelayHub.balances` to pay for Relay's fee as stated in the transaction. +* If any of sender's checks fails, it goes back to selecting a new Relay. Sender may also file a report on the unresponsive relay to its backend or save it locally, to down-sort this relay in future transactions. +* `Sender` may also submit the raw wrapped transaction to the blockchain without paying for gas, through any Ethereum node. + This submission is likely ignored because an identical transaction is already in the network's pending transactions, but no harm in putting it twice, to ensure that it happens. + This step is not strictly necessary, for reasons discussed below in attacks/mitigations, but may speed things up. +* `Sender` monitors the blockchain, waiting for the transaction to be mined. + The transaction was verified, with Relay's current nonce, so mining must be successful unless Relay submitted another (different) transaction with the same nonce. + If mining fails due to such attack, sender may call `RelayHub.penalizeRepeatedNonce` through another relay, to collect his reward and burn the remainder of the offending relay's stake, and then go back to selecting a new Relay for the transaction. + See discussion in the attacks/mitigations section below. +* `RelayHub` receives the transaction: + * Records `gasleft()` as `initialGas` for later payment. + * Verifies the transaction is sent from a registered relay. + * Verifies that the signature of the internal transaction matches its stated origin (sender's key). + * Verifies that the relay address written in the transaction matches msg.sender. + * Verifies that the transaction's `nonce` matches the stated origin's nonce in `RelayHub.nonces`. + * Calls recipient's `acceptRelayedCall` function, asking whether it's going to accept the transaction. If not, the `TransactionRelayed` will be emitted with status `CanRelayFailed`, and `chargeOrCanRelayStatus` will contain the return value of `acceptRelayedCall`. In this case, Relay doesn't get paid, as it was its responsibility to check `RelayHub.canRelay` before releasing the transaction. + * Calls recipient's `preRelayedCall` function. If this call reverts the `TransactionRelayed` will be emitted with status `PreRelayedFailed`. + * Sends the transaction to the recipient. If this call reverts the `TransactionRelayed` will be emitted with status `RelayedCallFailed`. + When passing gas to `call()`, enough gas is preserved by `RelayHub`, for post-call handling. Recipient may run out of gas, but `RelayHub` never does. + `RelayHub` also sends sender's address at the end of `msg.data`, so `RelayRecipient.getSender()` will be able to extract the real sender, and trust it because the transaction came from the known `RelayHub` address. +* Recipient contract handles the transaction. +* `RelayHub` calls recipient's `postRelayedCall`. +* `RelayHub` checks call's return value of call, and emits `TransactionRelayed(address relay, address from, address to, bytes4 selector, uint256 status, uint256 chargeOrCanRelayStatus)`. +* `RelayHub` increases `RelayHub.nonces[sender]`. +* `RelayHub` transfers ETH balance from recipient to `Relay.owner`, to pay the transaction fee, based on the measured transaction cost. + Note on relay payment: The relay gets paid for actual gas used, regardless of whether the recipient reverted. + The only case where the relay sustains a loss, is if `canRelay` returns non-zero, since the relay was responsible to verify this view function prior to submitting. + Any other revert is caught and paid for. See attacks/mitigations below. +* `Relay` keeps track of transactions it sent, and waits for `TransactionRelayed` events to see the charge. + If a transaction reverts and goes unpaid, which means the recipient's `acceptRelayedCall()` function was inconsistent, `Relay` refuses service to that recipient for a while (or blacklists it indefinitely, if it happens often). + See attacks/mitigations below. + +The process of winding a `Relay` down: + +* Relay's owner (the address that initially funded it) calls `RelayHub.removeRelayByOwner(Relay)`. +* `RelayHub` ensures that the sender is indeed Relay's owner, then removes `Relay`, and emits `RelayRemoved(Relay)`. +* `RelayHub` starts the countdown towards releasing the owner's stake. +* `Relay` receives its `RelayRemoved` event. +* `Relay` sends all its remaining ETH to its owner. +* `Relay` shuts down. +* Once the owner's unstake delay is over, owner calls `RelayHub.unstake()`, and withdraws the stake. + +## Rationale +The rationale for the gas stations network design is a combination of two sets of requirements: Easy adoption, and robustness. + +For easy adoption, the design goals are: + +* No network changes. +* Minimal changes to contracts, apps and frameworks. + +The robustness requirement translates to decentralization and attack resistance. The gas stations network is decentralized, and we have to assume that any entity may attack other entities in the system. + +Specifically we've considered the following types of attacks: + +* Denial-of-service attacks against individual senders, i.e. transactions censorship. +* Denial-of-service and financial attacks against individual relays. +* Denial-of-service and financial attacks against individual contracts. +* Denial-of-service attacks against the entire network, either by attacking existing entities, or by introducing any number of malicious entities. + +#### Attacks and mitigations + +##### Attack: Relay attempts to censor a transaction by not signing it, or otherwise ignoring a user request. +Relay is expected to return the signed transaction to the sender, immediately. +Sender doesn't need to wait for the transaction to be mined, and knows immediately whether it's request has been served. +If a relay doesn't return a signed transaction within a couple of seconds, sender cancels the operation, drops the connection, and switches to another relay. +It also marks Relay as unresponsive in its private storage to avoid using it in the near future. + +Therefore, the maximal damage a relay can cause with such attack, is a one-time delay of a couple of seconds. After a while, senders will avoid it altogether. + +##### Attack: Relay attempts to censor a transaction by signing it, returning it to the sender, but never putting it on the blockchain. +This attack will backfire and not censor the transaction. +The sender can submit the transaction signed by Relay to the blockchain as a raw transaction through any node, so the transaction does happen, +but Relay may be unaware and therefore be stuck with a bad nonce which will break its next transaction. + +##### Attack: Relay attempts to censor a transaction by signing it, but publishing a different transaction with the same nonce. +Reusing the nonce is the only DoS performed by a Relay, that cannot be detected within a couple of seconds during the http request. +It will only be detected when the malicious transaction with the same nonce gets mined and triggers the `RelayHub.TransactionRelayed` event. +However, the attack will backfire and cost Relay its entire stake. + +Sender has a signed transaction from Relay with nonce N, and also gets a mined transaction from the blockchain with nonce N, also signed by Relay. +This proves that Relay performed a DoS attack against the sender. +The sender calls `RelayHub.penalizeRepeatedNonce(bytes transaction1, bytes transaction2)`, which verifies the attack, confiscates Relay's stake, +and sends half of it to the sender who delivered the `penalizeRepeatedNonce` call. The other half of the stake is burned by sending it to `address(0)`. Burning is done to prevent cheating relays from effectively penalizing themselves and getting away without any loss. +The sender then proceeds to select a new relay and send the original transaction. + +The result of such attack is a delay of a few blocks in sending the transaction (until the attack is detected) but the relay gets removed and loses its entire stake. +Scaling such attack would be prohibitively expensive, and actually quite profitable for senders and honest relays. + +##### Attack: Relay attempts to censor a transaction by signing it, but using a nonce higher than it's current nonce. +In this attack, the Relay did create and return a perfectly valid transaction, but it will not be mined until this Relay fills the gap in the nonce with 'missing' transactions. +This may delay the relaying of some transactions indefinitely. In order to mitigate that, the sender includes a `max_nonce` parameter with it's signing request. +It is suggested to be higher by 2-3 from current nonce, to allow the relay process several transactions. + +When the sender receives a transaction signed by a Relay he validates that the nonce used is valid, and if it is not, the client will ignore the given relay and use other relays to relay given transaction. Therefore, there will be no actual delay introduced by such attack. + +##### Attack: Dapp attempts to burn relays funds by implementing an inconsistent acceptRelayedCall() and using multiple sender addresses to generate expensive transactions, thus performing a DoS attack on relays and reducing their profitability. +In this attack, a contract sets an inconsistent acceptRelayedCall (e.g. return zero for even blocks, nonzero for odd blocks), and uses it to exhaust relay resources through unpaid transactions. +Relays can easily detect it after the fact. +If a transaction goes unpaid, the relay knows that the recipient contract's acceptRelayedCall has acted inconsistently, because the relay has verified its view function before sending the transaction. +It might be the result of a rare race condition where the contract's state has changed between the view call and the transaction, but if it happens too frequently, relays will blacklist this contract and refuse to serve transactions to it. +Each offending contract can only cause a small damage (e.g. the cost of 2-3 transactions) to a relay, before getting blacklisted. + +Relays may also look at recipients' history on the blockchain, looking for past unpaid transactions (reverted by RelayHub without pay), and denying service to contracts with a high failure rate. +If a contract caused this minor loss to a few relays, all relays will stop serving it, so it can't cause further damage. + +This attack doesn't scale because the cost of creating a malicious contract is in the same order of magnitude as the damage it can cause to the network. +Causing enough damage to exhaust the resources of all relays, would be prohibitively expensive. + +The attack can be made even more impractical by setting RelayHub to require a stake from dapps before they can be served, and enforcing an unstaking delay, +so that attackers will have to raise a vast amount of ETH in order to simultaneously create enough malicious contracts and attack relays. +This protection is probably an overkill, since the attack doesn't scale regardless. + +##### Attack: User attempts to rob dapps by registering its own relay and sending expensive transactions to dapps. +If a malicious sender repeatedly abuses a recipient by sending meaningless/reverted transactions and causing the recipient to pay a relay for nothing, +it is the recipient's responsibility to blacklist that sender and have its acceptRelayedCall function return nonzero for that sender. +Collect calls are generally not meant for anonymous senders unknown to the recipient. +Dapps that utilize the gas station networks should have a way to blacklist malicious users in their system and prevent Sybil attacks. + +A simple method that mitigates such Sybil attack, is that the dapp lets users buy credit with a credit card, and credit their account in the dapp contract, +so acceptRelayedCall() only returns zero for users that have enough credit, and deduct the amount paid to the relay from the user's balance, whenever a transaction is relayed for the user. +With this method, the attacker can only burn its own resources, not the dapp's. + +A variation of this method, for free dapps (that don't charge the user, and prefer to pay for their users transactions) is to require a captcha during user creation in their web interface, +or to login with a Google/Facebook account, which limits the rate of the attack to the attacker's ability to open many Google/Facebook accounts. +Only a user that passed that process is given credit in RelayRecipient. The rate of such Sybil attack would be too low to cause any real damage. + +##### Attack: Attacker attempts to reduce network availability by registering many unreliable relays. +Registering a relay requires placing a stake in RelayHub, and the stake can only be withdrawn after the relay is unregistered and a long cooldown period has passed, e.g. a month. + +Each unreliable relay can only cause a couple of seconds delay to senders, once, and then it gets blacklisted by them, as described in the first attack above. +After it caused this minor delay and got blacklisted, the attacker must wait a month before reusing the funds to launch another unreliable relay. +Simultaneously bringing up a number of unreliable relays, large enough to cause a noticeable network delay, would be prohibitively expensive due to the required stake, +and even then, all those relays will get blacklisted within a short time. + +##### Attack: Attacker attempts to replay a relayed transaction. +Transactions include a nonce. RelayHub maintains a nonce (counter) for each sender. Transactions with bad nonces get reverted by RelayHub. Each transaction can only be relayed once. + +##### Attack: User does not execute the raw transaction received from the Relayer, therefore blocking the execution of all further transactions signed by this relayer +The user doesn't really have to execute the raw transaction. It's enough that the user can. The relationship between relay and sender is mutual distrust. The process described above incentivizes the relay to execute the transaction, so the user doesn't need to wait for actual mining to know that the transaction has been executed. + +Once relay returns the signed transaction, which should happen immediately, the relay is incentivized to also execute it on chain, so that it can advance its nonce and serve the next transaction. The user can (but doesn't have to) also execute the transaction. To understand why the attack isn't viable, consider the four possible scenarios after the signed transaction was returned to the sender: + +1. Relay executes the transaction, and the user doesn't. In this scenario the transaction is executed, so no problem. This is the case described in this attack. +2. Relay doesn't execute the transaction, but the user does. Similarly to 1, the transaction is executed, so no problem. +3. Both of them execute the transaction. The transactions are identical in the pending transactions pool, so the transaction gets executed once. No problem. +4. None of them execute the transaction. In this case the transaction doesn't get executed, but the relay is stuck. It can't serve the next transaction with the next nonce, because its nonce hasn't been advanced on-chain. It also can't serve the next transaction with the current nonce, as this can be proven by the user, having two different transactions signed by the same relay, with the same nonce. The user could use this to take the relay's nonce. So the relay is stuck unless it executes the transaction. + +As this matrix shows, the relay is __always__ incentivized to execute the transaction, once it returned it to the user, in order to end up in #1 or #3, and avoid the risk of #4. It's just a way to commit the relay to do its work, without requiring the user to wait for on-chain confirmation. + +## Backwards Compatibility + +The gas stations network is implemented as smart contracts and external entities, and does not require any network changes. + +Dapps adding gas station network support remain backwards compatible with their existing apps/users. The added methods apply on top of the existing ones, so no changes are required for existing apps. + +## Implementation + +A working implementation of the [**gas stations network**](https://github.com/tabookey-dev/tabookey-gasless) is being developed by **TabooKey**. It consists of `RelayHub`, `RelayRecipient`, `web3 hooks`, an implementation of a gas station inside `geth`, and sample dapps using the gas stations network. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1616.md b/EIPS/eip-1616.md new file mode 100644 index 0000000..52107d4 --- /dev/null +++ b/EIPS/eip-1616.md @@ -0,0 +1,387 @@ +--- +eip: 1616 +title: Attribute Registry Standard +author: 0age (@0age), Santiago Palladino (@spalladino), Leo Arias (@elopio), Alejo Salles (@fiiiu), Stephane Gosselin (@thegostep) +discussions-to: https://github.com/ethereum/EIPs/issues/1616 +status: Stagnant +type: Standards Track +category: ERC +created: 2018-11-23 +requires: 165 +--- + + +## Simple Summary +EIP-1616 provides a basic interface for querying a registry for attribute metadata assigned to Ethereum accounts. + +## Abstract +This EIP contains the following core ideas: +1. Instead of relying directly on the reputation of a claims issuer to assess the veracity of a given claim, trust can be brought up to the level of a registry curator. This registry which we call an "**Attribute Registry**" allows for reduced complexity in implementation since a party needing to verify an attribute can now work with a trusted claims aggregator instead of relying on individual claim providers. +2. Claims are abstracted as standard "attributes" which represent metadata assigned to an account, with claims decoupled from the issuing party. Attributes are registered as a flat `uint256 -> uint256` key-value pair on each account, with the important property that **each attribute type has one canonical value per address**. This property allows for composability of attribute registries and advanced attribute formation. +3. There is a generic method for determining the set of attribute keys or IDs made available by the registry. The standard does not specify requirements or recommendations for how attributes and their values are managed, or what additional metadata may be associated with attributes. It is likely that a standard set of attribute names and metadata schema could be proposed in a separate EIP. + +Potential advanced uses of attribute registries include: +* Encoding complex boolean expressions which combine multiple attributes into a single uint256 key, which is then parsed and evaluated by the registry logic. +* Using values associated with an attribute to query additional on-chain or off-chain metadata. +* Resolving attribute values by calling into separate attribute registries or other contracts, delegating authority without changing the interface of the registry. + +## Motivation +This EIP is motivated by the need for contracts and external accounts to be able to verify information about a given address from a single trusted source **without concerning themselves with the particular details of how the information was obtained**, and to do so in as simple a manner as possible. It is also motivated by the desire to promote broad **cross-compatibility and composability** between attribute registries, a property which is amplified by both the simplicity of the interface as well as by the guarantees on uniqueness provided by the proposed standard. + +Existing EIPs for assigning metadata to an account include EIP-735 and EIP-780, which both allow for multiple claims to be issued on the same address for any given claim topic. This forces verifiers of said metadata to assess the veracity of each claim, taking into account the relative reputation of each claim issuer. It also prescribes a methodology for adding and removing claims, which may not be appropriate for all use cases. + +This EIP proposes a light-weight abstraction layer for a standard account metadata registry interface. This abstraction layer can sit on top of claims registries like EIP-735 and EIP-780 or others as the attribute registry curator selects trusted data sources. + +## Specification +The Attribute Registry interface contains four functions, outlined as follows: +```solidity +/** + * @title EIP-1616 Attribute Registry Standard interface. EIP-165 ID: 0x5f46473f + */ +interface AttributeRegistryInterface { + function hasAttribute(address account, uint256 attributeTypeID) external view returns (bool); + function getAttributeValue(address account, uint256 attributeTypeID) external view returns (uint256); + function countAttributeTypes() external view returns (uint256); + function getAttributeTypeID(uint256 index) external view returns (uint256); +} +``` + +Contracts that comply with the Attribute Registry EIP MUST implement the above interface. + +As an additional requirement, the ERC-165 interface MUST be included: +```solidity +/** + * @title EIP-165 interface. EIP-165 ID: 0x01ffc9a7 + */ +interface EIP-165 { + /** + * @notice EIP-165 support. Attribute Registry interface ID is 0x5f46473f. + * @param _interfaceID The interface identifier, as specified in EIP-165 + * @return True for 0x01ffc9a7 & 0x5f46473f, false for unsupported interfaces. + */ + function supportsInterface(bytes4 _interfaceID) external view returns (bool); +} +``` + +The implementation MUST follow the specifications described below. + +### View Functions +The view functions detailed below MUST be implemented. + +#### `hasAttribute` function +```solidity +function hasAttribute(address account, uint256 attributeTypeID) external view returns (bool) +``` + +Check if an attribute has been assigned to a given account on the registry and is currently valid. + +_**NOTE**_: This function MUST return either true or false - i.e. calling this function MUST NOT cause the caller to revert. Implementations that wish to call into another contract during execution of this function MUST catch any `revert` and instead return `false`. + +_**NOTE**_: This function MUST return two equal values when performing two directly consecutive function calls with identical `account` and `attributeTypeID` parameters, regardless of differences in the caller's address, the transaction origin, or other out-of-band information. + + + +#### `getAttributeValue` function +```solidity +function getAttributeValue(address account, uint256 attributeTypeID) external view returns (uint256) +``` + +Retrieve the `uint256` value of an attribute on a given account on the registry, assuming the attribute is currently valid. + +_**NOTE**_: This function MUST revert if a directly preceding or subsequent function call to `hasAttribute` with identical `account` and `attributeTypeID` parameters would return false. + +_**NOTE**_: This function MUST return two equal values when performing two directly consecutive function calls with identical `account` and `attributeTypeID` parameters, regardless of differences in the caller's address, the transaction origin, or other out-of-band information. + +#### `countAttributeTypes` function +```solidity +function countAttributeTypes() external view returns (uint256) +``` + +Retrieve the total number of valid attribute types defined on the registry. Used alongside `getAttributeTypeID` to determine all of the attribute types that are available on the registry. + +_**NOTE**_: This function MUST return a positive integer value - i.e. calling this function MUST NOT cause the caller to revert. + +_**NOTE**_: This function MUST return a value that encompasses all indexes of attribute type IDs whereby a call to `hasAttribute` on some address with an attribute type ID at the given index would return `true`. + +#### `getAttributeTypeID` function +```solidity +function getAttributeTypeID(uint256 index) external view returns (uint256) +``` + +Retrieve an ID of an attribute type defined on the registry by index. Used alongside `countAttributeTypes` to determine all of the attribute types that are available on the registry. + +_**NOTE**_: This function MUST revert if the provided `index` value falls outside of the range of the value returned from a directly preceding or subsequent function call to `countAttributeTypes`. It MUST NOT revert if the provided `index` value falls inside said range. + +_**NOTE**_: This function MUST return an `attributeTypeID` value on *some* index if the same `attributeTypeID` value would cause a given call to `hasAttribute` to return `true` when passed as a parameter. + +## Rationale +This standard extends the applicability of metadata assignment to those use cases that are not adequately represented by EIP-735, EIP-780, or similar proposals. Namely, it enforces the constraint of one attribute value per attribute ID per address, as opposed to one value per ID per address *per issuer*. + +Aside from the prescribed attribute value, attribute properties are deliberately omitted from the standard. While many attribute registries will require additional metadata on attributes at both the instance and the class level, reliable and flexible interoperability between highly variable registry extensions is facilitated more effectively by enforcing a widely-applicable base layer for attributes. + +## Backwards Compatibility +There are no backwards compatibility concerns. + +## Test Cases +Targeted test cases with 100% code coverage can be found at [this repository](https://github.com/0age/AttributeRegistry). See [here](https://github.com/TPL-protocol/tpl-contracts) for tests on a more complex contract that implements the application registry interface. + +## Implementation +The basic implementation that follows can be found at [this repository](https://github.com/0age/AttributeRegistry) (see [here](https://github.com/TPL-protocol/tpl-contracts/blob/master/contracts/BasicJurisdiction.sol#L399) for an example of a more complex implementing contract): + +```solidity +pragma solidity ^0.4.25; + +/** + * @title Attribute Registry interface. EIP-165 ID: 0x5f46473f + */ +interface AttributeRegistryInterface { + /** + * @notice Check if an attribute of the type with ID `attributeTypeID` has + * been assigned to the account at `account` and is currently valid. + * @param account address The account to check for a valid attribute. + * @param attributeTypeID uint256 The ID of the attribute type to check for. + * @return True if the attribute is assigned and valid, false otherwise. + * @dev This function MUST return either true or false - i.e. calling this + * function MUST NOT cause the caller to revert. + */ + function hasAttribute( + address account, + uint256 attributeTypeID + ) external view returns (bool); + + /** + * @notice Retrieve the value of the attribute of the type with ID + * `attributeTypeID` on the account at `account`, assuming it is valid. + * @param account address The account to check for the given attribute value. + * @param attributeTypeID uint256 The ID of the attribute type to check for. + * @return The attribute value if the attribute is valid, reverts otherwise. + * @dev This function MUST revert if a directly preceding or subsequent + * function call to `hasAttribute` with identical `account` and + * `attributeTypeID` parameters would return false. + */ + function getAttributeValue( + address account, + uint256 attributeTypeID + ) external view returns (uint256); + + /** + * @notice Count the number of attribute types defined by the registry. + * @return The number of available attribute types. + * @dev This function MUST return a positive integer value - i.e. calling + * this function MUST NOT cause the caller to revert. + */ + function countAttributeTypes() external view returns (uint256); + + /** + * @notice Get the ID of the attribute type at index `index`. + * @param index uint256 The index of the attribute type in question. + * @return The ID of the attribute type. + * @dev This function MUST revert if the provided `index` value falls outside + * of the range of the value returned from a directly preceding or subsequent + * function call to `countAttributeTypes`. It MUST NOT revert if the provided + * `index` value falls inside said range. + */ + function getAttributeTypeID(uint256 index) external view returns (uint256); +} + + +/** + * @title A simple example of an Attribute Registry implementation. + */ +contract AttributeRegistry is AttributeRegistryInterface { + // This particular implementation just defines two attribute types. + enum Affiliation { Whitehat, Blackhat } + + // Top-level information about attribute types held in a static array. + uint256[2] private _attributeTypeIDs; + + // The number of attributes currently issued tracked in a static array. + uint256[2] private _issuedAttributeCounters; + + // Issued attributes held in a nested mapping by account & attribute type. + mapping(address => mapping(uint256 => bool)) private _issuedAttributes; + + // Issued attribute values held in a nested mapping by account & type. + mapping(address => mapping(uint256 => uint256)) private _issuedAttributeValues; + + /** + * @notice The constructor function, defines the two attribute types available + * on this particular registry. + */ + constructor() public { + // Set the attribute type IDs for whitehats (8008) and blackhats (1337). + _attributeTypeIDs = [8008, 1337]; + } + + /** + * @notice Assign a "whitehat" attribute type to `msg.sender`. + * @dev The function may not be called by accounts with a "blackhat" attribute + * type already assigned. This function is arbitrary and not part of the + * Attribute Registry specification. + */ + function joinWhitehats() external { + // Get the index of the blackhat attribute type on the attribute registry. + uint256 blackhatIndex = uint256(Affiliation.Blackhat); + + // Get the attribute type ID of the blackhat attribute type. + uint256 blackhatAttributeTypeID = _attributeTypeIDs[blackhatIndex]; + + // Do not allow the whitehat attribute to be set if blackhat is already set. + require( + !_issuedAttributes[msg.sender][blackhatAttributeTypeID], + "no blackhats allowed!" + ); + + // Get the index of the whitehat attribute type on the attribute registry. + uint256 whitehatIndex = uint256(Affiliation.Whitehat); + + // Get the attribute type ID of the whitehat attribute type. + uint256 whitehatAttributeTypeID = _attributeTypeIDs[whitehatIndex]; + + // Mark the attribute as issued on the given address. + _issuedAttributes[msg.sender][whitehatAttributeTypeID] = true; + + // Calculate the new number of total whitehat attributes. + uint256 incrementCounter = _issuedAttributeCounters[whitehatIndex] + 1; + + // Set the attribute value to the new total assigned whitehat attributes. + _issuedAttributeValues[msg.sender][whitehatAttributeTypeID] = incrementCounter; + + // Update the value of the counter for total whitehat attributes. + _issuedAttributeCounters[whitehatIndex] = incrementCounter; + } + + /** + * @notice Assign a "blackhat" attribute type to `msg.sender`. + * @dev The function may be called by any account, but assigned "whitehat" + * attributes will be removed. This function is arbitrary and not part of the + * Attribute Registry specification. + */ + function joinBlackhats() external { + // Get the index of the blackhat attribute type on the attribute registry. + uint256 blackhatIndex = uint256(Affiliation.Blackhat); + + // Get the attribute type ID of the blackhat attribute type. + uint256 blackhatAttributeTypeID = _attributeTypeIDs[blackhatIndex]; + + // Mark the attribute as issued on the given address. + _issuedAttributes[msg.sender][blackhatAttributeTypeID] = true; + + // Calculate the new number of total blackhat attributes. + uint256 incrementCounter = _issuedAttributeCounters[blackhatIndex] + 1; + + // Set the attribute value to the new total assigned blackhat attributes. + _issuedAttributeValues[msg.sender][blackhatAttributeTypeID] = incrementCounter; + + // Update the value of the counter for total blackhat attributes. + _issuedAttributeCounters[blackhatIndex] = incrementCounter; + + // Get the index of the whitehat attribute type on the attribute registry. + uint256 whitehatIndex = uint256(Affiliation.Whitehat); + + // Get the attribute type ID of the whitehat attribute type. + uint256 whitehatAttributeTypeID = _attributeTypeIDs[whitehatIndex]; + + // Determine if a whitehat attribute type has been assigned. + if (_issuedAttributes[msg.sender][whitehatAttributeTypeID]) { + // If so, delete the attribute. + delete _issuedAttributes[msg.sender][whitehatAttributeTypeID]; + + // Delete the attribute value as well. + delete _issuedAttributeValues[msg.sender][whitehatAttributeTypeID]; + + // Set the attribute value to the new total assigned whitehat attributes. + uint256 decrementCounter = _issuedAttributeCounters[whitehatIndex] - 1; + + // Update the value of the counter for total whitehat attributes. + _issuedAttributeCounters[whitehatIndex] = decrementCounter; + } + } + + /** + * @notice Get the total number of assigned whitehat and blackhat attributes. + * @return Array with counts of assigned whitehat and blackhat attributes. + * @dev This function is arbitrary and not part of the Attribute Registry + * specification. + */ + function totalHats() external view returns (uint256[2]) { + // Return the array containing counter values. + return _issuedAttributeCounters; + } + + /** + * @notice Check if an attribute of the type with ID `attributeTypeID` has + * been assigned to the account at `account` and is currently valid. + * @param account address The account to check for a valid attribute. + * @param attributeTypeID uint256 The ID of the attribute type to check for. + * @return True if the attribute is assigned and valid, false otherwise. + * @dev This function MUST return either true or false - i.e. calling this + * function MUST NOT cause the caller to revert. + */ + function hasAttribute( + address account, + uint256 attributeTypeID + ) external view returns (bool) { + // Return assignment status of attribute by account and attribute type ID + return _issuedAttributes[account][attributeTypeID]; + } + + /** + * @notice Retrieve the value of the attribute of the type with ID + * `attributeTypeID` on the account at `account`, assuming it is valid. + * @param account address The account to check for the given attribute value. + * @param attributeTypeID uint256 The ID of the attribute type to check for. + * @return The attribute value if the attribute is valid, reverts otherwise. + * @dev This function MUST revert if a directly preceding or subsequent + * function call to `hasAttribute` with identical `account` and + * `attributeTypeID` parameters would return false. + */ + function getAttributeValue( + address account, + uint256 attributeTypeID + ) external view returns (uint256 value) { + // Revert if attribute with given account & attribute type ID is unassigned + require( + _issuedAttributes[account][attributeTypeID], + "could not find a value with the provided account and attribute type ID" + ); + + // Return the attribute value. + return _issuedAttributeValues[account][attributeTypeID]; + } + + /** + * @notice Count the number of attribute types defined by the registry. + * @return The number of available attribute types. + * @dev This function MUST return a positive integer value - i.e. calling + * this function MUST NOT cause the caller to revert. + */ + function countAttributeTypes() external view returns (uint256) { + // Return the length of the attribute type IDs array. + return _attributeTypeIDs.length; + } + + /** + * @notice Get the ID of the attribute type at index `index`. + * @param index uint256 The index of the attribute type in question. + * @return The ID of the attribute type. + * @dev This function MUST revert if the provided `index` value falls outside + * of the range of the value returned from a directly preceding or subsequent + * function call to `countAttributeTypes`. It MUST NOT revert if the provided + * `index` value falls inside said range. + */ + function getAttributeTypeID(uint256 index) external view returns (uint256) { + // Revert if the provided index is out of range. + require( + index < _attributeTypeIDs.length, + "provided index is outside of the range of defined attribute type IDs" + ); + + // Return the attribute type ID at the given index in the array. + return _attributeTypeIDs[index]; + } +} +``` + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-162.md b/EIPS/eip-162.md new file mode 100644 index 0000000..0376443 --- /dev/null +++ b/EIPS/eip-162.md @@ -0,0 +1,248 @@ +--- +eip: 162 +title: Initial ENS Hash Registrar +author: Maurelian, Nick Johnson , Alex Van de Sande +status: Final +type: Standards Track +category: ERC +created: 2016-10-25 +--- + +## Contents +- Abstract +- Motivations +- Specification + - Initial restrictions + - Name format for hash registration + - Auctioning names + - Deeds + - Deployment and Upgrade process + - Registrar Interface +- Rationale + - Not committing to a permanent registrar at the outset + - Valid names >= 7 characters + - Restricting TLD to `.eth` + - Holding ether as collateral +- Prior work + + + +## Abstract + +This ERC describes the implementation, as deployed to the main ethereum network on 2017-05-04, of a registrar contract to govern the allocation of names in the Ethereum Name Service (ENS). The corresponding source code is [here](https://github.com/ethereum/ens/blob/mainnet/contracts/HashRegistrarSimplified.sol). + +For more background, refer to [EIP-137](./eip-137.md). + +> Registrars are responsible for allocating domain names to users of the system, and are the only entities capable of updating the ENS; the owner of a node in the ENS registry is its registrar. Registrars may be contracts or externally owned accounts, though it is expected that the root and top-level registrars, at a minimum, will be implemented as contracts. +> +> \- EIP 137 + +A well designed and governed registrar is essential to the success of the ENS described in EIP 137, but is described separately in this document as it is external to the core ENS protocol. + +In order to maximize utility and adoption of a new namespace, the registrar should mitigate speculation and "name squatting", however the best approach for mitigation is unclear. Thus an "initial" registrar is proposed, which implements a simple approach to name allocation. During the initial period, the available namespace will be significantly restricted to the `.eth` top level domain, and subdomain shorter than 7 characters in length disallowed. This specification largely describes @alexvandesande and @arachnid's [hash registrar implementation](https://github.com/ethereum/ens/blob/mainnet/contracts/HashRegistrarSimplified.sol) in order to facilitate discussion. + +The intent is to replace the Initial Registrar contract with a permanent registrar contract. The Permanent Registrar will increase the available namespace, and incorporate lessons learned from the performance of the Initial Registrar. This upgrade is expected to take place within approximately 2 years of initial deployment. + +## Motivations + +The following factors should be considered in order to optimize for adoption of the ENS, and good governance of the Initial Registrar's namespace. + +**Upgradability:** The Initial Registrar should be safely upgradeable, so that knowledge gained during its deployment can be used to replace it with an improved and permanent registrar. + +**Effective allocation:** Newly released namespaces often create a land grab situation, resulting in many potentially valuable names being purchased but unused, with the hope of re-selling at a profit. This reduces the availability of the most useful names, in turn decreasing the utility of the name service to end users. + +Achieving an effective allocation may or may not require human intervention for dispute resolution and other forms of curation. The Initial Registrar should not aim to create to most effective possible allocation, but instead limit the cost of misallocation in the long term. + +**Security:** The registrar will hold a balance of ether without an explicit limit. It must be designed securely. + +**Simplicity:** The ENS specification itself emphasizes a separation of concerns, allowing the most essential element, the registry to be as simple as possible. The interim registrar in turn should be as simple as possible while still meeting its other design goals. + +**Adoption:** Successful standards become more successful due to network effects. The registrar should consider what strategies will encourage the adoption of the ENS in general, and the namespace it controls in particular. + +## Specification + +### Initial restrictions + +The Initial Registrar is expected to be in service for approximately two years, prior to upgrading. This should be sufficient time to learn, observe, and design an updated system. + +During the initial two year period, the available name space will be restricted to the `.eth` TLD. + +This restriction is enforced by the owner of the ENS root node who should not assign any nodes other than `.eth` to the Initial Registrar. The ENS's root node should be controlled by multiple parties using a multisig contract. + +The Initial Registrar will also prohibit registration of names 6 characters or less in length. + +### Name format for hash registration + +Names submitted to the initial registrar must be hashed using Ethereum's sha3 function. Note that the hashes submitted to the registrar are the hash of the subdomain label being registered, not the namehash as defined in EIP 137. + +For example, in order to register `abcdefg.eth`, one should submit `sha3('abcdefg')`, not `sha3(sha3(0, 'eth'), 'abcdefg')`. + +### Auctioning names + +The registrar will allocate the available names through a Vickrey auction: + +> A Vickrey auction is a type of sealed-bid auction. Bidders submit written bids without knowing the bid of the other people in the auction. The highest bidder wins but the price paid is the second-highest bid. This type of auction... gives bidders an incentive to bid their true value. +> +> \- [Vickrey Auction, Wikipedia](https://en.wikipedia.org/wiki/Vickrey_auction) + +The auction lifecycle of a name has 5 possible states, or Modes. + +1. **Not-yet-available:** The majority of names will be initially unavailable for auction, and will become available some time during the 8 weeks after launch. +2. **Open:** The earliest availability for a name is determined by the most significant byte of its sha3 hash. `0x00` would become available immediately, `0xFF` would become available after 8 weeks, and the availability of other names is distributed accordingly. Once a name is available, it is possible to start an auction on it. +3. **Auction:** Once the auction for a name has begun, there is a 72 hour bidding period. Bidders must submit a payment of ether, along with sealed bids as a hash of `sha3(bytes32 hash, address owner, uint value, bytes32 salt)`. The bidder may obfuscate the true bid value by sending a greater amount of ether. +4. **Reveal:** After the bidding period, a 48 hour reveal period commences. During this time, bidders must reveal the true parameters of their sealed bid. As bids are revealed, ether payments are returned according to the schedule of "refund ratios" outlined in the table below. If no bids are revealed, the name will return to the Open state. +5. **Owned:** After the reveal period has finished, the winning bidder must submit a transaction to finalize the auction, which then calls the ENS's `setSubnodeOwner` function, recording the winning bidder's address as the owner of the hash of the name. + +The following table outlines important parameters which define the Registrar's auction mechanism. + +#### Registrar Parameters + +| Name | Description | Value | +|--------------------|----------------------------------------------------------------------------------------------------|------------| +| totalAuctionLength | The full time period from start of auction to end of the reveal period. | 5 days | +| revealPeriod | The length of the time period during which bidding is no longer allowed, and bids must be revealed. | 48 hours | +| launchLength | The time period during which all names will become available for auction. | 8 weeks | +| minPrice | The minimum amount of ether which must be locked up in exchange for ownership of a name. | 0.01 ether | + +### Deeds + +The Initial Registrar contract does not hold a balance itself. All ether sent to the Registrar will be held in a separate `Deed` contracts. A deed contract is first created and funded when a sealed bid is submitted. After an auction is completed and a hash is registered, the deed for the winning bid is held in exchange for ownership of the hash. Non-winning bids are refunded. + +A deed for an owned name may be transferred to another account by its owner, thus transferring ownership and control of the name. + +After 1 year of registration, the owner of a hash may choose to relinquish ownership and have the value of the deed returned to them. + +Deeds for non-winning bids can be closed by various methods, at which time any ether held will either be returned to the bidder, burnt, or sent to someone else as a reward for actions which help the registrar. + +The following table outlines what portion of the balance held in a deed contract will be returned upon closure, and to whom. The remaining balance will be burnt. + +#### Refund schedule + +| Reason for Deed closure | Refund Recipient | Refund Percentage | +| --- | --- | --- | +| A valid non-winning bid is revealed. | Bidder | 99.5% | +| A bid submitted after the auction period is revealed. | Bidder | 99.5% | +| An otherwise valid bid is revealed on an owned name. 1 | Bidder | 0.5% | +| An expired sealed bid is cancelled. 2 | Canceler | 0.5% | +| A registered hash is reported as invalid. 3 | Reporter | 50% | +| A registered hash is reported as invalid. 3 | Owner | 50% | + +##### Notes: + +1. This incentivizes all bids to be revealed in time. If bids could be revealed late, an extortion attack on the current highest bidder could be made by threatening to reveal a new second highest bid. +2. A bid which remains sealed after more than 2 weeks and 5 days may be cancelled by anyone to collect a small reward. +2. Since names are hashed before auctioning and registration, the Initial Registrar is unable to enforce character length restrictions independently. A reward is therefore provided for reporting invalid names. + +### Deployment and Upgrade process + +The Initial Registrar requires the ENS's address as a constructor, and should be deployed after the ENS. The multisig account owning the root node in the ENS should then set the Initial Registrar's address as owner of the `eth` node. + +The Initial Registrar is expected to be replaced by a Permanent Registrar approximately 2 years after deployment. The following process should be used for the upgrade: +1. The Permanent Registrar contract will be deployed. +2. The multisig account owning the root node in the ENS will assign ownership of the `.eth` node to the Permanent Registrar. +3. Owners of hashes in the Initial Registrar will be responsible for registering their deeds to the Permanent Registrar. A couple options are considered here: + 1. Require owners to transfer their ownership prior to a cutoff date in order to maintain ownership and/or continue name resolution services. + 2. Have the Permanent Registrar query the Initial Registrar for ownership if it is lacking an entry. + +### Planned deactivation + +In order to limit dependence on the Initial Registrar, new auctions will stop after 4 years, and all ether held in deeds after 8 years will become unreachable. + +### Registrar Interface + +`function state(bytes32 _hash) constant returns (Mode)` +- Implements a state machine returning the current state of a name + +`function entries(bytes32 _hash) constant returns (Mode, address, uint, uint, uint)` +- Returns the following information regarding a registered name: + * state + * deed address + * registration date + * balance of the deed + * highest value bid at auction + +`function getAllowedTime(bytes32 _hash) constant returns (uint timestamp)` +- Returns the time at which the hash will no longer be in the initial `not-yet-available` state. + +`function isAllowed(bytes32 _hash, uint _timestamp) constant returns (bool allowed)` +- Takes a hash and a time, returns true if and only if it has passed the initial `not-yet-available` state. + +`function startAuction(bytes32 _hash);` +- Moves the state of a hash from Open to Auction. Throws if state is not Open. + +`function startAuctions(bytes32[] _hashes);` +- Starts multiple auctions on an array of hashes. This enables someone to open up an auction for a number of dummy hashes when they are only really interested in bidding for one. This will increase the cost for an attacker to simply bid blindly on all new auctions. Dummy auctions that are open but not bid on are closed after a week. + +`function shaBid(bytes32 hash, address owner, uint value, bytes32 salt) constant returns (bytes32 sealedBid);` +- Takes the parameters of a bid, and returns the sealedBid hash value required to participate in the bidding for an auction. This obfuscates the parameters in order to mimic the mechanics of placing a bid in an envelope. + +`function newBid(bytes32 sealedBid);` +- Bids are sent by sending a message to the main contract with a sealedBid hash and an amount of ether. The hash contains information about the bid, including the bidded name hash, the bid value, and a random salt. Bids are not tied to any one auction until they are revealed. The value of the bid itself can be masqueraded by sending more than the value of your actual bid. This is followed by a 48h reveal period. Bids revealed after this period will be burned and the ether unrecoverable. Since this is an auction, it is expected that most public hashes, like known domains and common dictionary words, will have multiple bidders pushing the price up. + +`function startAuctionsAndBid(bytes32[] hashes, bytes32 sealedBid)` +- A utility function allowing a call to `startAuctions` followed by `newBid` in a single transaction. + + +`function unsealBid(bytes32 _hash, address _owner, uint _value, bytes32 _salt);` +- Once the bidding period is completed, there is a reveal period during with the properties of a bid are submitted to reveal them. The registrar hashes these properties using the `shaBid()` function above to verify that they match a pre-existing sealed bid. If the unsealedBid is the new best bid, the old best bid is returned to its bidder. + +`function cancelBid(bytes32 seal);` +- Cancels an unrevealed bid according to the rules described in the notes on the refund schedule above. + +`function finalizeAuction(bytes32 _hash);` + +After the registration date has passed, this function can be called to finalize the auction, which then calls the ENS function `setSubnodeOwner()` updating the ENS record to set the winning bidder as owner of the node. + +`function transfer(bytes32 _hash, address newOwner);` +- Update the owner of the ENS node corresponding to the submitted hash to a new owner. This function must be callable only by the current owner. + +`function releaseDeed(bytes32 _hash);` +- After some time, the owner can release the property and get their ether back. + +`function invalidateName(string unhashedName);` +- Since registration is done on the hash of a name, the registrar itself cannot validate names. This function can be used to report a name which is 6 characters long or less. If it has been registered, the submitter will earn 10% of the deed value. We are purposefully handicapping the simplified registrar as a way to force it into being restructured in a few years. + +`function eraseNode(bytes32[] labels)` +- Allows anyone to delete the owner and resolver records for a subdomain of a name that is not currently owned in the registrar. For instance, to zero `foo.bar.eth` on a registrar that owns `.eth`, pass an array containing `[sha3('foo'), sha3('bar')]`. + +`function transferRegistrars(bytes32 _hash) onlyOwner(_hash);` +- Used during the upgrade process to a permanent registrar. If this registrar is no longer the owner of the its root node in the ENS, this function will transfers the deed to the current owner, which should be a new registrar. This function throws if this registrar still owns its root node. + +## Rationale + +### Starting with a temporary registrar + +Anticipating and designing for all the potential issues of name allocation names is unlikely to succeed. This approach chooses not to be concerned with getting it perfect, but allows us to observe and learn with training wheels on, and implement improvements before expanding the available namespace to shorter names or another TLD. + +### Valid names >= 7 characters + +Preserving the shortest, and often most valuable, domain names for the upgraded registrar provides the opportunity to implement processes for dispute resolution (assuming they are found to be necessary). + +### Delayed release of names + +A slower release allows for extra time to identify, and address any issues which may arise after launch. + +### Restricting TLD to `.eth` + +Choosing a single TLD helps to maximize network effects by focusing on one namespace. + +A three letter TLD is a pattern made familiar by it's common usage in internet domain names. This familiarity significantly increases the potential of the ENS to be integrated into pre-existing DNS systems, and reserved as a [special-use domain name](https://www.iana.org/assignments/special-use-domain-names/special-use-domain-names.xhtml#special-use-domain). A recent precedent for this is the [reservation of the `.onion` domain](https://tools.ietf.org/html/rfc7686). + +### Holding ether as collateral + +This approach is simpler than the familiar model of requiring owners to make recurring payments to retain ownership of a domain name. It also makes the initial registrar a revenue neutral service. + +## Prior work + +This document borrows heavily from several sources: +- [EIP-137](./eip-137.md) outlines the initial implementation of the Registry Contract (ENS.sol) and associated Resolver contracts. +- [ERC-26](https://github.com/ethereum/EIPs/issues/26) was the first ERC to propose a name service at the contract layer +- @alexvandesande's current implementation of the [HashRegistrar](https://github.com/ethereum/ens/blob/mainnet/contracts/HashRegistrarSimplified.sol) + +### Edits: +- 2016-10-26 Added link Alex's design in abstract +- 2016-11-01 change 'Planned deactivation' to h3' +- 2017-03-13 Update timelines for bidding and reveal periods + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1620.md b/EIPS/eip-1620.md new file mode 100644 index 0000000..694c9d6 --- /dev/null +++ b/EIPS/eip-1620.md @@ -0,0 +1,296 @@ +--- +eip: 1620 +title: Money Streaming +author: Paul Berg (@PaulRBerg) +discussions-to: https://github.com/ethereum/EIPs/issues/1620 +status: Stagnant +type: Standards Track +category: ERC +created: 2018-11-24 +--- + +## Simple Summary +Money streaming represents the idea of continuous payments over a finite period of time. Block numbers are used as a proxy of time to continuously update balances. + +## Abstract +The following describes a standard whereby time is measured using block numbers and streams are mappings in a master contract. + +1. A provider sets up a money streaming contract. +2. A prospective payer can interact with the contract and start the stream right away by depositing the funds required for the chosen period. +3. The payee is able to withdraw money from the contract based on its ongoing solvency. That is: `payment rate * (current block height - starting block height)` +4. The stream terms (payment rate, length, metadata) can be updated at any time if both parties pledge their signatures. +5. The stream can be stopped at any point in time by any party without on-chain consensus. +6. If the stream period ended and it was not previously stopped by any party, the payee is entitled to withdraw all the deposited funds. + +## Motivation +This standardised interface aims to change the way we think about long-term financial commitments. Thanks to blockchains, payments need not be sent in chunks (e.g. monthly salaries), as there is much less overhead in paying-as-you-go. Money as a function of time would better align incentives in a host of scenarios. + +### Use Cases + +This is just a preliminary list of use cases. There are other spooky ideas interesting to explore, such as time-dependent disincetivisation, but, for brevity, we have not included them here. + +- Salaries +- Subscriptions +- Consultancies +- CDPs +- Rent +- Parking + +### Crowdsales +[RICOs](https://github.com/lukso-network/rico), or Reversible ICOs, were introduced at Devcon4 by @frozeman. The idea is to endow investors with more power and safety guarantees by allowing them to "reverse" the investment based on the evolution of the project. We previously discussed a similar concept called SICOs, or Streamable ICOs, in this research [thread](https://ethresear.ch/t/chronos-a-quirky-application-proposal-for-plasma/2928/14?u=paulrberg). + +Instead of investing a lump sum and giving the money away to the project developers, funds are held in a smart contract which allocates money based on the passage of time. Project developers can withdraw funds as the stream stays active, while investors have the power to get back a significant percentage of their initial commitment if the project halts. + +## Specification + +### Structs + +The structure of a `stream` should be as follows: + +- `stream` + - `sender`: the `address` of the entity funding the stream + - `recipient`: the `address` where the money is being delivered to + - `tokenAddress`: the `address` of the ERC20 token used as payment asset + - `balance`: the total funds left in the stream + - `timeframe`: as defined below + - `rate`: as defined below + +```solidity + struct Stream { + address sender; + address recipient; + address tokenAddress; + uint256 balance; + Timeframe timeframe; + Rate rate; + } +``` + +- `timeframe` + - `start`: the starting block number of the stream + - `stop`: the stopping block number of the stream + +```solidity +struct Timeframe { + uint256 start; + uint256 stop; +} +``` + +- `rate` + - `payment`: how much money moves from `sender` to `recipient` + - `interval`: how often `payment` moves from `sender` to `recipient` + +```solidity +struct Rate { + uint256 payment; + uint256 interval; +} +``` + +--- + +### Methods + +#### balanceOf + +Returns available funds for the given stream id and address. + +```solidity +function balanceOf(uint256 _streamId, address _addr) +``` + +#### getStream + +Returns the full stream data, if the id points to a valid stream. + +```solidity +function getStream(uint256 _streamId) returns (address sender, address recipient, address tokenAddress, uint256 balance, uint256 startBlock, uint256 stopBlock, uint256 payment, uint256 interval) +``` + +#### create + +Creates a new stream between `msg.sender` and `_recipient`. + +MUST allow senders to create multiple streams in parallel. SHOULD not accept Ether and only use ERC20-compatible tokens. + +**Triggers Event**: [LogCreate](#logcreate) + +```solidity +function create(address _recipient, address _tokenAddress, uint256 _startBlock, uint256 _stopBlock, uint256 _payment, uint256 _interval) +``` + +#### withdraw + +Withdraws all or a fraction of the available funds. + +MUST allow only the recipient to perform this action. + +**Triggers Event**: [LogWithdraw](#logwithdraw) + +```solidity +function withdraw(uint256 _streamId, uint256 _funds) +``` + +#### redeem + +Redeems the stream by distributing the funds to the sender and the recipient. + +SHOULD allow any party to redeem the stream. + +**Triggers Event**: [LogRedeem](#logredeem) + +```solidity +function redeem(uint256 _streamId) +``` + +#### confirmUpdate + +Signals one party's willingness to update the stream + +SHOULD allow any party to do this but MUST NOT be executed without consent from all involved parties. + +**Triggers Event**: [LogConfirmUpdate](#logconfirmupdate) + +**Triggers Event**: [LogExecuteUpdate](#logexecuteupdate) when the last involved party calls this function + +```solidity +function update(uint256 _streamId, address _tokenAddress, uint256 _stopBlock, uint256 _payment, uint256 _interval) +``` + +#### revokeUpdate + +Revokes an update proposed by one of the involved parties. + +MUST allow any party to do this. + +**Triggers Event**: [LogRevokeUpdate](#logrevokeupdate) + +```solidity +function confirmUpdate(uint256 _streamId, address _tokenAddress, uint256 _stopBlock, uint256 _payment, uint256 _interval) +``` + +--- + +### Events + +#### LogCreate + +MUST be triggered when `create` is successfully called. + +```solidity +event LogCreate(uint256 indexed _streamId, address indexed _sender, address indexed _recipient, address _tokenAddress, uint256 _startBlock, uint256 _stopBlock, uint256 _payment, uint256 _interval) +``` + +#### LogWithdraw + +MUST be triggered when `withdraw` is successfully called. + +```solidity +event LogWithdraw(uint256 indexed _streamId, address indexed _recipient, uint256 _funds) +``` + +#### LogRedeem + +MUST be triggered when `redeem` is successfully called. + +```solidity +event LogRedeem(uint256 indexed _streamId, address indexed _sender, address indexed _recipient, uint256 _senderBalance, uint256 _recipientBalance) +``` + +#### LogConfirmUpdate + +MUST be triggered when `confirmUpdate` is successfully called. + +```solidity +event LogConfirmUpdate(uint256 indexed _streamId, address indexed _confirmer, address _newTokenAddress, uint256 _newStopBlock, uint256 _newPayment, uint256 _newInterval); +``` + +#### LogRevokeUpdate + +MUST be triggered when `revokeUpdate` is successfully called. + +```solidity +event LogRevokeUpdate(uint256 indexed _streamId, address indexed revoker, address _newTokenAddress, uint256 _newStopBlock, uint256 _newPayment, uint256 _newInterval) +``` + +#### LogExecuteUpdate + +MUST be triggered when an update is approved by all involved parties. + +```solidity +event LogExecuteUpdate(uint256 indexed _newStreamId, address indexed _sender, address indexed _recipient, address _newTokenAddress, uint256 _newStopBlock, uint256 _newPayment, uint256 _newInterval) +``` + +## Rationale + +This specification was designed to serve as an entry point to the quirky concept of money as a function of time and it is definitely not set in stone. Several other designs, including payment channels and Plasma chains were also considered, but they were eventually deemed dense in assumptions unnecessary for an initial version. + + + +Block times are a reasonable, trustless proxy for time on the blockchain. Between 2016 and 2018, the Ethereum block time average value [hovered](https://etherscan.io/chart/blocktime) around 14 seconds, excluding the last two quarters of 2017. Mathematically speaking, it would be ideal to have a standard deviation as close to 0 as possible, but that is not how things work in the real world. This has huge implications on the feasibility of this ERC which we shall investigate below. + +### GCD +When setting up a stream, a payer and a payee may want to make the total streaming duration a multiple of the "greatest common denominator" (GCD) of the chain they operate on; that is, the average block time. This is not imperative in the smart contracts per se, but there needs to be an off-chain process to map streams to real world time units in order to create a sound and fair payment mechanism. + +### Block Times +Because there is uncertainty regarding block times, streams may not be settled on the blockchain as initially planned. Let `$d` be the total streaming duration measured in seconds, `$t` the average block time before the stream started and `$t'` the actual average block time over `$d` after the stream started. We distinguish two undesirable scenarios: + +1. `$t` < `$t'`: the payee will get their funds *later* than expected + +2. `$t` > `$t'`: the payee will get their funds *sooner* than expected + +If the combined error delta is smaller than the payment rate (fifth parameter of the `create` method, measured in wei), there is no problem at all. Conversely, we stumble upon trust issues because real-world time frames do not correspond to the stream terms. For instance, if an employee is normally entitled to withdraw all the funds from the stream at the end of the month, but block times cause case 1 from above to occur, the employee is in a financial disadvantage because their continuous effort is not compensated as promised. + +Limiting the problem scope only to Ethereum, we propose two remedies: + +1. Consensus on calling the `update` function to correct the stream terms. This might sound preposterous, but in most cases the stakes are low and stream participants are involved in long-term financial commitments. There is a high disincentive to refuse to cooperate. + +2. Autonomously fix significant error deltas. In theory, we could achieve this using previous blocks' timestamps, "checkpointing" the stream once in a predefined number of blocks. This is still an area of active research because of potentially high overheads in gas costs. + +Nonetheless, it is important to note that this is still a major improvement on the traditional model where absolute trust is required. + +### Sidechains + +It could be more efficient to implement this standard on independent sidechains like [POA Network](https://poa.network) or [xDai](https://medium.com/poa-network/poa-network-partners-with-makerdao-on-xdai-chain-the-first-ever-usd-stable-blockchain-65a078c41e6a) - thanks to their rather predictable nature. Admittedly, security is traded for scalability, but proper cryptoeconomic stakes could alleviate potential problems. + +Furthermore, it is intriguing to explore the prospect of stream-specific sidechains. + +### Oracles + +The proposed specification uses block numbers to proxy time, but this need not be the only method. Albeit it would imply different trust assumptions, oracles could be used to provide a feed of timestamps. Coupled with the aforementioned idea of stream-specific sidechains, oracles could efficiently solve the problems outlined in [Block Times](#block-times). + +### Multi-Hop Streams + +Future or upgraded versions of this standard may describe "multi-hop" streams. If: + +1. There is a stream between A and B +2. There is another stream between B and C + +There could be a way to avoid running two different streams in parallel. That is, a fraction or all of the funds being streamed from A to B could be automatically wired to C. An interesting use case for this is taxes. Instead of manually moving money around, proactively calculating how much you owe and then transfer it, a stream could atomically perform those operations for you. + +## Implementation + +- [ChronosProtocol WIP implementation](https://github.com/ChronosProtocol/monorepo) + +## Additional References +- [Chronos Protocol Ethresear.ch Plasma Proposal](https://ethresear.ch/t/chronos-a-quirky-application-proposal-for-plasma/2928?u=paulrberg) +- [Chronos Protocol White Paper](http://chronosprotocol.org/chronos-white-paper.pdf) +- [Flipper: Streaming Salaries @ CryptoLife Hackathon](https://devpost.com/software/flipper-3gvl4b) +- [SICOs or Streamed ICOs](https://ethresear.ch/t/chronos-a-quirky-application-proposal-for-plasma/2928/14?u=paulrberg) +- [RICOs or Reversible ICOs](https://twitter.com/feindura/status/1058057076306518017) +- [Andreas Antonopoulos' Keynote on Bitcoin, Lightning and Money Streaming](https://www.youtube.com/watch?v=gF_ZQ_eijPs) + +## Final Notes + +Many thanks to @mmilton41 for countless brainstorming sessions. We have been doing research on the topic of money streaming for quite a while within the context of @ChronosProtocol. In August this year, we published the first version of our white paper describing a Plasma approach. However, in the meantime, we realised that it would be much more [fun](https://twitter.com/PaulRBerg/status/1056595919116910592) and easier to start small on Ethereum itself and sidechains like [xDai](https://blockscout.com/poa/dai). + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1633.md b/EIPS/eip-1633.md new file mode 100644 index 0000000..17461d4 --- /dev/null +++ b/EIPS/eip-1633.md @@ -0,0 +1,174 @@ +--- +eip: 1633 +title: Re-Fungible Token Standard (RFT) +author: Billy Rennekamp (@okwme), Dan Long , Kiryl Yermakou , Nate van der Ende +discussions-to: https://github.com/ethereum/EIPs/issues/1634 +status: Stagnant +type: Standards Track +category: ERC +created: 2018-11-18 +requires: 20, 165, 721 +--- + +## Simple Summary +[ERC-20](./eip-20.md) extension for proportional ownership of an [ERC-721](./eip-721.md) token. + +## Abstract +The intention of this proposal, the Re-Fungible Token Standard, is to extend the ERC-20 Token Standard and utilize ERC-165 Standard Interface Detection in order to represent the shared ownership of an ERC-721 Non-Fungible Token. The ERC-20 Token Standard was modified as little as possible in order to allow this new class of token to operate in all of the ways and locations which are familiar to assets that follow the original ERC-20 specification. While there are many possible variations of this specification that would enable many different capabilities and scenarios for shared ownership, this proposal is focused on the minimal commonalities to enable as much flexibility as possible for various further extensions. This proposal makes it possible to verify, from the contract level or from an external query, whether a fungible token represents a form of shared ownership of a non-fungible token. The inclusion of ERC-165 makes it possible to verify, from the contract level or from an external query, whether a non-fungible token is owned by ERC-20 token representing shared ownership. + +## Motivation +Shared ownership occurs across many industries and for many reasons. As more assets are registered, regulated and/or represented by the ERC-721 Non-Fungible Token Standard there will be more instances where the need for shared ownership of these assets will arise. For example, ARTBLX Inc. is working towards facilitating a protocol for collective ownership of physical, digital and conceptual artworks. The fungible tokens created from this process will have a value attached to the non-fungible tokens which they represent. This will be useful for price discovery of the underlying asset, liquidity for shared owners and as a new class of asset which can be used as collateral for loans or other financial instruments like stable coins. Providing an interface to this special class of fungible tokens is necessary to allow third parties to recognize them as a special class of fungible token and to recognize when a non-fungible token is collectively owned. This might be useful in the case of a wallet who would want to utilize the metadata of the underlying NFT to show additional info next to an RFT, or on an exchange who might want to make that sort of info similarly available, or an NFT marketplace who may want to direct customers to a relevant exchange who wish to purchase shares in a NFT which is owned by an RFT. Anywhere an ERC-20 is applicable it would be useful for a user to know whether that token represents a shared NFT, and what attributes that NFT may have. + +## Specification +At a minimum, third parties need two things: 1) to be able to distinguish re-fungible tokens from other token standards and 2) to determine when a non-fungible token is collectively owned. These two scenarios can be encountered from the perspective of initial contact with the non-fungible token or from the perspective of initial contact with the re-fungible token. + +#### Initial Contact with the Re-Fungible Token + +In order for a third party to confirm which non-fungible token is owned by the re-fungible token there needs to be a pointer from the RFT contract to the NFT contract and the relevant token id. This is possible with two public getters named `parentToken()` and `parentTokenId()`. The first getter returns a variable of type `address` and designates the contract address of the Non-Fungible Token contract. The second getter returns a variable of type `uint256` and designates the token ID of the Non-Fungible Token. With these getters, the identity of the Non-Fungible Token can be determined. Below is an example of the Re-Fungible Token Standard interface that includes these getter functions: + +```solidity +pragma solidity ^0.4.20; + +/// @dev Note: the ERC-165 identifier for this interface is 0x5755c3f2. +interface RFT /* is ERC20, ERC165 */ { + + function parentToken() external view returns(address _parentToken); + function parentTokenId() external view returns(uint256 _parentTokenId); + +} +``` + +The validity of this claim can be confirmed from another contract (on-chain) or from interacting with an RPC endpoint (off-chain). Below is an example of the on-chain scenario: + +```solidity +pragma solidity ^0.4.20; + +import './RFT.sol'; +import './ERC721.sol'; + +contract ConfirmRFT { + + function confirmRFT(address _RFT) external view returns(bool) { + address _NFT = RFT(_RFT).parentToken(); // returns address of NFT contract + uint256 _tokenId = RFT(_RFT).parentTokenId(); // returns id of ID of NFT + + return + NFT(_NFT).supportsInterface(0x80ac58cd) && // confirm it is ERC-721 + NFT(_NFT).ownerOf(_tokenId) == _RFT; // confirm the owner of the NFT is the RFT contract address + } + +} +``` + +Below is an off-chain example using an instance of web3.js in javascript: +```javascript +async function confirmRFT(web3) { + + const ERC721ABI = [...] // abi for ERC721 + const RFTABI = [...] // abi for RFT + const RFTAddress = '0x0123456789abcdef0123456789abcdef' // address for the deployed RFT + + const RFTContract = new web3.eth.Contract(RFTABI, RFTAddress) // deployed RFT contract instance + const ERC721Address = await RFTcontract.methods.parentToken().call() // returns address of NFT contract + const ERC721TokenId = await RFTcontract.methods.parentTokenId().call() // returns id of ID of NFT + + const ERC721Contract = new web3.eth.Contract(ERC721ABI, ERC721Address) // deployed ERC721 (as reported by RFT) + const isERC721 = await ERC721Contract.methods.supportsInterface('0x80ac58cd').call() // confirm it is ERC-721 + const ownerOfAddress = await ERC721Contract.methods.ownerOf(ERC721TokenId).call() // get the owner of the NFT + + return ERC721Response.toLowerCase() === RFTAddress.toLowerCase() // confirm the owner of the NFT is the RFT contract +} +``` + +#### Initial Contact with the Non-Fungible Token + +When checking the owner of a specific non-fungible token it's important to be able to determine whether owner is in fact a re-fungible token contract. This is possible by utilizing ERC-165 Standard Interface Detection. In order to comply with that standard a contract must include the following getter function which returns `true` when passed the `bytes4` parameter `0x01ffc9a7`: +``` +function supportsInterface(bytes4 interfaceID) external view returns (bool); +``` +After establishing support for this interface it becomes useful in determining whether the contract adheres to the Re-Fungible Token Standard. To do so the `supportsInterface(bytes4 interfaceID)` getter function must return `true` when passed the `bytes4` parameter `0x5755c3f2` which is the result of `bytes4(keccak256('parentToken()')) ^ bytes4(keccak256('parentTokenId()'))` or `parentToken.selector ^ parentTokenId.selector`. This could be achieved with the following code: +```solidity +pragma solidity ^0.4.20; + +import "./ERC20.sol"; + +/// @dev Note: the ERC-165 identifier for this interface is 0x5755c3f2. +interface RFT is ERC20 /*, ERC165 */ { + + function supportsInterface(bytes4 interfaceID) external view returns(bool) { + return + interfaceID == this.supportsInterface.selector || // ERC165 + interfaceID == this.parentToken.selector || // parentToken() + interfaceID == this.parentTokenId.selector || // parentTokenId() + interfaceID == this.parentToken.selector ^ this.parentTokenId.selector; // RFT + } + + function parentToken() external view returns(address _parentToken); + function parentTokenId() external view returns(uint256 _parentTokenId); + +} +``` +The flow of actually checking the status of a non-fungible token owner as a re-fungible token contract can be done from another contract (on-chain) as well as with an RPC endpoint (off-chain). Below is an example of the on-chain scenario: +```solidity +pragma solidity ^0.4.20; + +import './RFT.sol'; +import './ERC721.sol'; + +contract ConfirmRFT { + + function confirmRFT(address _NFT, uint256 _tokenId) external view returns(bool) { + address _RFT = ERC721(_NFT).ownerOf(_tokenId); // get the owner of the NFT + + return + RFT(_RFT).supportsInterface(0x01ffc9a7) && // confirm it supports ERC-165 + RFT(_RFT).supportsInterface(0x5755c3f2) // confirm it is RFT + } + +} +``` +Below is an off-chain example using web3.js in javascript: +```javascript +async function confirmRFT(web3) { + + const ERC721ABI = [...] // abi for ERC721 + const RFTABI = [...] // abi for RFT + const ERC721Address = '0x0123456789abcdef0123456789abcdef' // address for the deployed NFT + const ERC721TokenId = '7' // token Id of the NFT + + const ERC721Contract = new web3.eth.Contract(ERC721ABI, ERC721Address) // deployed ERC721 + const RFTAddress = await ERC721Contract.methods.ownerOf(ERC721TokenId).call() // owner address of the NFT + + + const RFTContract = new web3.eth.Contract(RFTABI, RFTAddress) // deployed RFT contract instance + const isERC165 = await RFTContract.methods.supportsInterface('0x01ffc9a7').call() // confirm it is ERC-165 + return isERC165 && await RFTContract.methods.supportsInterface('0x5755c3f2').call() // confirm it is RFT + +} +``` +## Rationale +Most of the decisions made around the design of this standard were done in the hopes of keeping it as flexible as possible for as many use cases as possible. This includes making the standard 100% backwards compatible with ERC-20 Token Standard and able to interact with any previously deployed or future ERC-721 non-fungible token. This allows for each project to determine their own system for minting, burning and governing their re-fungible tokens depending on their specific use case. + +## Backwards Compatibility +The Re-Fungible Token Standard is 100% backwards compatible with ERC-20 Token Standard. It is a small extension to the original specification and meant to be further extended for more specific use cases. Keeping the standard compatible with ERC-20 is important to allow for this token to benefit from the ecosystem that has grown around supporting the ubiquitous ERC-20 Token Standard. + +The Re-Fungible Token Standard is intended to interact with the ERC-721 Non-Fungible Token Standard. It is kept purposefully agnostic to extensions beyond the standard in order to allow specific projects to design their own token relationships such as governance over, rights to or permissions on each non-fungible token relative to the respective re-fungible token owners. + +## Implementation +```solidity +pragma solidity ^0.4.20; + +/// @dev Note: the ERC-165 identifier for this interface is 0x5755c3f2. +interface RFT /* is ERC20, ERC165 */ { + + function parentToken() external view returns(address _parentToken); + function parentTokenId() external view returns(uint256 _parentTokenId); + +} +``` + +## Security Considerations +TBD + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-165.md b/EIPS/eip-165.md new file mode 100644 index 0000000..77e6cbc --- /dev/null +++ b/EIPS/eip-165.md @@ -0,0 +1,235 @@ +--- +eip: 165 +title: Standard Interface Detection +author: Christian Reitwießner , Nick Johnson , Fabian Vogelsteller , Jordi Baylina , Konrad Feldmeier , William Entriken +type: Standards Track +category: ERC +status: Final +created: 2018-01-23 +requires: 214 +--- + +## Simple Summary + +Creates a standard method to publish and detect what interfaces a smart contract implements. + +## Abstract + +Herein, we standardize the following: + +1. How interfaces are identified +2. How a contract will publish the interfaces it implements +3. How to detect if a contract implements ERC-165 +4. How to detect if a contract implements any given interface + +## Motivation + +For some "standard interfaces" like [the ERC-20 token interface](./eip-20.md), it is sometimes useful to query whether a contract supports the interface and if yes, which version of the interface, in order to adapt the way in which the contract is to be interacted with. Specifically for ERC-20, a version identifier has already been proposed. This proposal standardizes the concept of interfaces and standardizes the identification (naming) of interfaces. + +## Specification + +### How Interfaces are Identified + +For this standard, an *interface* is a set of [function selectors as defined by the Ethereum ABI](https://solidity.readthedocs.io/en/develop/abi-spec.html#function-selector). This a subset of [Solidity's concept of interfaces](https://solidity.readthedocs.io/en/develop/abi-spec.html) and the `interface` keyword definition which also defines return types, mutability and events. + +We define the interface identifier as the XOR of all function selectors in the interface. This code example shows how to calculate an interface identifier: + +```solidity +pragma solidity ^0.4.20; + +interface Solidity101 { + function hello() external pure; + function world(int) external pure; +} + +contract Selector { + function calculateSelector() public pure returns (bytes4) { + Solidity101 i; + return i.hello.selector ^ i.world.selector; + } +} +``` + +Note: interfaces do not permit optional functions, therefore, the interface identity will not include them. + +### How a Contract will Publish the Interfaces it Implements + +A contract that is compliant with ERC-165 shall implement the following interface (referred as `ERC165.sol`): + +```solidity +pragma solidity ^0.4.20; + +interface ERC165 { + /// @notice Query if a contract implements an interface + /// @param interfaceID The interface identifier, as specified in ERC-165 + /// @dev Interface identification is specified in ERC-165. This function + /// uses less than 30,000 gas. + /// @return `true` if the contract implements `interfaceID` and + /// `interfaceID` is not 0xffffffff, `false` otherwise + function supportsInterface(bytes4 interfaceID) external view returns (bool); +} +``` + +The interface identifier for this interface is `0x01ffc9a7`. You can calculate this by running `bytes4(keccak256('supportsInterface(bytes4)'));` or using the `Selector` contract above. + +Therefore the implementing contract will have a `supportsInterface` function that returns: + +- `true` when `interfaceID` is `0x01ffc9a7` (EIP165 interface) +- `false` when `interfaceID` is `0xffffffff` +- `true` for any other `interfaceID` this contract implements +- `false` for any other `interfaceID` + +This function must return a bool and use at most 30,000 gas. + +Implementation note, there are several logical ways to implement this function. Please see the example implementations and the discussion on gas usage. + +### How to Detect if a Contract Implements ERC-165 + +1. The source contract makes a `STATICCALL` to the destination address with input data: `0x01ffc9a701ffc9a700000000000000000000000000000000000000000000000000000000` and gas 30,000. This corresponds to `contract.supportsInterface(0x01ffc9a7)`. +2. If the call fails or return false, the destination contract does not implement ERC-165. +3. If the call returns true, a second call is made with input data `0x01ffc9a7ffffffff00000000000000000000000000000000000000000000000000000000`. +4. If the second call fails or returns true, the destination contract does not implement ERC-165. +5. Otherwise it implements ERC-165. + +### How to Detect if a Contract Implements any Given Interface + +1. If you are not sure if the contract implements ERC-165, use the above procedure to confirm. +2. If it does not implement ERC-165, then you will have to see what methods it uses the old-fashioned way. +3. If it implements ERC-165 then just call `supportsInterface(interfaceID)` to determine if it implements an interface you can use. + +## Rationale + +We tried to keep this specification as simple as possible. This implementation is also compatible with the current Solidity version. + +## Backwards Compatibility + +The mechanism described above (with `0xffffffff`) should work with most of the contracts previous to this standard to determine that they do not implement ERC-165. + +Also [the ENS](./eip-137.md) already implements this EIP. + +## Test Cases + +Following is a contract that detects which interfaces other contracts implement. From @fulldecent and @jbaylina. + +```solidity +pragma solidity ^0.4.20; + +contract ERC165Query { + bytes4 constant InvalidID = 0xffffffff; + bytes4 constant ERC165ID = 0x01ffc9a7; + + function doesContractImplementInterface(address _contract, bytes4 _interfaceId) external view returns (bool) { + uint256 success; + uint256 result; + + (success, result) = noThrowCall(_contract, ERC165ID); + if ((success==0)||(result==0)) { + return false; + } + + (success, result) = noThrowCall(_contract, InvalidID); + if ((success==0)||(result!=0)) { + return false; + } + + (success, result) = noThrowCall(_contract, _interfaceId); + if ((success==1)&&(result==1)) { + return true; + } + return false; + } + + function noThrowCall(address _contract, bytes4 _interfaceId) constant internal returns (uint256 success, uint256 result) { + bytes4 erc165ID = ERC165ID; + + assembly { + let x := mload(0x40) // Find empty storage location using "free memory pointer" + mstore(x, erc165ID) // Place signature at beginning of empty storage + mstore(add(x, 0x04), _interfaceId) // Place first argument directly next to signature + + success := staticcall( + 30000, // 30k gas + _contract, // To addr + x, // Inputs are stored at location x + 0x24, // Inputs are 36 bytes long + x, // Store output over input (saves space) + 0x20) // Outputs are 32 bytes long + + result := mload(x) // Load the result + } + } +} +``` + +## Implementation + +This approach uses a `view` function implementation of `supportsInterface`. The execution cost is 586 gas for any input. But contract initialization requires storing each interface (`SSTORE` is 20,000 gas). The `ERC165MappingImplementation` contract is generic and reusable. + +```solidity +pragma solidity ^0.4.20; + +import "./ERC165.sol"; + +contract ERC165MappingImplementation is ERC165 { + /// @dev You must not set element 0xffffffff to true + mapping(bytes4 => bool) internal supportedInterfaces; + + function ERC165MappingImplementation() internal { + supportedInterfaces[this.supportsInterface.selector] = true; + } + + function supportsInterface(bytes4 interfaceID) external view returns (bool) { + return supportedInterfaces[interfaceID]; + } +} + +interface Simpson { + function is2D() external returns (bool); + function skinColor() external returns (string); +} + +contract Lisa is ERC165MappingImplementation, Simpson { + function Lisa() public { + supportedInterfaces[this.is2D.selector ^ this.skinColor.selector] = true; + } + + function is2D() external returns (bool){} + function skinColor() external returns (string){} +} +``` + +Following is a `pure` function implementation of `supportsInterface`. The worst-case execution cost is 236 gas, but increases linearly with a higher number of supported interfaces. + +```solidity +pragma solidity ^0.4.20; + +import "./ERC165.sol"; + +interface Simpson { + function is2D() external returns (bool); + function skinColor() external returns (string); +} + +contract Homer is ERC165, Simpson { + function supportsInterface(bytes4 interfaceID) external view returns (bool) { + return + interfaceID == this.supportsInterface.selector || // ERC165 + interfaceID == this.is2D.selector + ^ this.skinColor.selector; // Simpson + } + + function is2D() external returns (bool){} + function skinColor() external returns (string){} +} +``` + +With three or more supported interfaces (including ERC165 itself as a required supported interface), the mapping approach (in every case) costs less gas than the pure approach (at worst case). + +## Version history +* PR 1640, finalized 2019-01-23 -- This corrects the noThrowCall test case to use 36 bytes rather than the previous 32 bytes. The previous code was an error that still silently worked in Solidity 0.4.x but which was broken by new behavior introduced in Solidity 0.5.0. This change was discussed at [#1640](https://github.com/ethereum/EIPs/pull/1640). + +* EIP 165, finalized 2018-04-20 -- Original published version. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1679.md b/EIPS/eip-1679.md new file mode 100644 index 0000000..a6bf39f --- /dev/null +++ b/EIPS/eip-1679.md @@ -0,0 +1,42 @@ +--- +eip: 1679 +title: "Hardfork Meta: Istanbul" +author: Alex Beregszaszi (@axic), Afri Schoedon (@5chdn) +discussions-to: https://ethereum-magicians.org/t/hardfork-meta-istanbul-discussion/3207 +type: Meta +status: Final +created: 2019-01-04 +requires: 152, 1108, 1344, 1716, 1884, 2028, 2200 +--- + +## Abstract + +This meta-EIP specifies the changes included in the Ethereum hardfork named Istanbul. + +## Specification + +- Codename: Istanbul + +### Activation + - `Block >= 9,069,000` on the Ethereum Mainnet + - `Block >= 6,485,846` on the Ropsten testnet + - `Block >= 14,111,141` on the Kovan testnet + - `Block >= 5,435,345` on the Rinkeby testnet + - `Block >= 1,561,651` on the Görli testnet + +### Included EIPs + - [EIP-152](./eip-152.md): Add Blake2 compression function `F` precompile + - [EIP-1108](./eip-1108.md): Reduce alt_bn128 precompile gas costs + - [EIP-1344](./eip-1344.md): Add ChainID opcode + - [EIP-1884](./eip-1884.md): Repricing for trie-size-dependent opcodes + - [EIP-2028](./eip-2028.md): Calldata gas cost reduction + - [EIP-2200](./eip-2200.md): Rebalance net-metered SSTORE gas cost with consideration of SLOAD gas cost change + +## References + +1. Included EIPs were finalized in [All Core Devs Call #68](https://github.com/ethereum/pm/blob/master/All%20Core%20Devs%20Meetings/Meeting%2068.md) +2. https://medium.com/ethereum-cat-herders/istanbul-testnets-are-coming-53973bcea7df + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1681.md b/EIPS/eip-1681.md new file mode 100644 index 0000000..a17f2c9 --- /dev/null +++ b/EIPS/eip-1681.md @@ -0,0 +1,91 @@ +--- +eip: 1681 +title: Temporal Replay Protection +author: Martin Holst Swende (@holiman) +discussions-to: https://ethereum-magicians.org/t/temporal-replay-protection/2355 +status: Stagnant +type: Standards Track +category: Core +created: 2019-01-08 +--- + +## Simple Summary + +This EIP proposes adding a 'temporal' replay protection to transactions, in the form of a `valid-until` timestamp. +This EIP is very similar to https://github.com/ethereum/EIPs/pull/599 by Nick Johnson and Konrad Feldmeier, the main difference +being that this EIP is based on clock-time / walltime instead of block numbers. + + +## Motivation + +There are a couple of different motivators for introducing a timebased transaction validity. + +- If any form of dust-account clearing is introduced, e.g. (https://github.com/ethereum/EIPs/issues/168), it will be necessary +to introduce a replay protection, such as https://github.com/ethereum/EIPs/issues/169 . Having temporal replay protection removes the need +to change nonce-behaviour in the state, since transactions would not be replayable at a later date than explicitly set by the user. +- In many cases, such as during ICOs, a lot of people want their transactions to either become included soon (within a couple of hours) or not at all. Currently, +transactions are queued and may not execute for several days, at a cost for both the user (who ends up paying gas for a failing purchase) and the network, dealing with the large transaction queues. +- Node implementations have no commonly agreed metric for which transactions to keep, discard or propagate. Having a TTL on transactions would make it easier to remove stale transactions from the system. + +## Specification + +The roll-out would be performed in two phases, `X` (hardfork), and `Y` (softfork). + +At block `X`, + +- Add an optional field `valid-until` to the RLP-encoded transaction, defined as a `uint64` (same as `nonce`). +- If the field is present in transaction `t`, then + - `t` is only eligible for inclusion in a block if `block.timestamp` < `t.valid-until`. + +At block `Y`, +- Make `valid-until` mandatory, and consider any transaction without `valid-until` to be invalid. + +## Rationale + +### Rationale for this EIP + +For the dust-account clearing usecase, +- This change is much less invasive in the consensus engine. + - No need to maintain a consensus-field of 'highest-known-nonce' or cap the number of transactions from a sender in a block. + - Only touches the transaction validation part of the consensus engine + - Other schemas which uses the `nonce` can have unintended side-effects, + - such as inability to create contracts at certain addresses. + - more difficult to integrate with offline signers, since more elaborate nonce-schemes requires state access to determine. + - More intricate schemes like `highest-nonce` are a lot more difficult, since highest-known-nonce will be a consensus-struct that is incremented and possibly reverted during transaction execution, requiring one more journalled field. + + +### Rationale for walltime + +Why use walltime instead of block numbers, as proposed in https://github.com/ethereum/EIPs/pull/599 ? + +- The UTC time is generally available in most settings, even on a computer which is offline. This means that even a setup where blockchain information is unavailable, the party signing a transaction can generate a transaction with the desired properties. +- The correlation between time and block number is not fixed; even though a 14s blocktime is 'desired', this varies due to both network hashrate and difficulty bomb progression. +- The block number is even more unreliable as a timestamp for testnets and private networks. +- UTC time is more user-friendly, a user can more easily decide on reasonable end-date for a transaction, rather than a suitalbe number of valid blocks. + + +## Backwards Compatibility + +This EIP means that all software/hardware that creates transactions need to add timestamps to the transactions, or will otherwise be incapable of signing transactions after block `Y`. Note: this EIP does not introduce any maximum `valid-until` date, so it would still be possible to create +transactions with near infinite validity. + +## Test Cases + +todo + +## Implementation + +None yet + +## Security considerations + +The most notable security impact is that pre-signed transactions stored on paper backups, will become invalid as of block `Y`. There are a couple of cases where this might be used + - Pregenerated onetime 'bootstrap' transactions, e.g. to onboard a user into Ethereum. Instead of giving a user a giftcard with actual ether on it, someone may instead give the person a one-time pregenerated transaction that will only send those ether to the card once the +user actively wants to start using it. + - If a user has an offline paper-wallet, he may have pregenerated transactions to send value to e.g. an exchange. This is sometimes done to be able to send ether to an exchange without having to go through all the hoops of bringing the paper wallet back to 'life'. + +Secondary security impacts are that the addition of a timestamp would make the transactions a little bit larger. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). + diff --git a/EIPS/eip-1682.md b/EIPS/eip-1682.md new file mode 100644 index 0000000..3a0f0a5 --- /dev/null +++ b/EIPS/eip-1682.md @@ -0,0 +1,220 @@ +--- +eip: 1682 +title: Storage Rent +author: Felix J Lange (@fjl), Martin Holst Swende (@holiman) +discussions-to: https://ethereum-magicians.org/t/storage-rent-eip/2357 +status: Withdrawn +type: Standards Track +category: Core +created: 2018-11-10 +--- + +## Abstract + +This EIP describes a scheme to charge for data in state, and 'archive' data which is no longer being paid for. It also describes how resurrection of 'archived' data happens. + +## Motivation + +The Ethereum blockchain in its current form is not sustainable because it grows +indefinitely. This is true of any blockchain, but Ethereum grows faster than most chains. +Many implementation strategies to slow down growth exist. A common strategy is 'state +pruning' which discards historical state, keeping only the active copy of contract data +and a few recent versions to deal with short-range chain reorganizations. Several +implementations also employ compression techniques to keep the active copy of the state as +small as possible. + +A full node participating in consensus today requires storing large amounts of data even +with advanced storage optimizations applied. Future storage requirements are unbounded +because any data stored in a contract must be retained forever as dictated by the +protocol. This EIP attempts to correct this by adding new consensus rules that put an +upper bound on the size of the Ethereum state. + +Adding these new rules changes fundamental guarantees of the system and requires a hard +fork. Users of Ethereum already pay for the creation and modification of accounts and +their storage entries. Under the rules introduced in this EIP, users must also pay to keep +accounts accessible. A similar rent scheme was proposed in [EIP-103] but rejected +even back then because the proposal would've upset peoples expectations. As implementers +of Ethereum, we still feel that state rent is the right path to long-term sustainability +of the Ethereum blockchain and that its undesirable implications can be overcome with +off-protocol tooling and careful design. + +[EIP-103]: https://github.com/ethereum/EIPs/issues/35 + +## Specification + +The cost of storing an account over time is called `rent`. The amount of `rent` due depends +on the size of the account. The `ether` that is paid for `rent` is destroyed. The `rent` is deducted whenever an account is touched. + +`rent` can be paid from the account's regular `balance` or from its 'rent balance'. Accounts +can be endowed with `rent balance` through a new EVM opcode. When `rent` is charged, it is +first taken from the `rent balance`. When `rent balance` is zero, it is instead charged from the account's regular `balance` instead. + +The reason to separate `balance` and `rent balance` is that certain contracts do not accept `ether` sends, or always send the entire balance off to some other destination. For these cases, a separate`rent balance` is required. + +When an account's `balance` is insufficient to pay rent, the account becomes `inactive`. Its +storage and contract code are removed. Inactive accounts cannot be interacted with, i.e. +it behaves as if it has no contract code. + +Inactive accounts can be restored by re-uploading their storage. To restore an inactive +account `A`, a new account `B` is created with arbitrary code and its storage modified +with `SSTORE` operations until it matches the storage root of `A`. Account `B` can restore +`A` through the `RESTORETO` opcode. This means the cost of restoring an account is +equivalent to recreating it via successive `SSTORE` operations. + +### Changes To State + +At the top level, a new key `size` is added to the accounts trie. This key tracks the +total number of trie nodes across all accounts, including storage trie nodes. To track +rent, the structure of account entries is changed as well. + +Before processing the block in which this EIP becomes active, clients iterate the whole +state once to count the number of trie nodes and to change the representation of all +accounts to the new format. + +#### Account Representation + +```text +account = [nonce, balance, storageroot, codehash, rentbalance, rentblock, storagesize] +``` + +Each account gets three additional properties: `rentbalance`, `rentblock` and +`storagesize`. + +The `rentbalace` field tracks the amount of `rent balance` available to the account. Upon +self-destruction any remaining `rent balance` is transferred to the beneficiary. Any +modification of the account recomputes its current `rent balance`. + +The `rentblock` field tracks the block number in which the `rent balance` was last +recomputed. Upon creation, this field is initialized with the current block number. +`rentblock` is also updated with the current block number whenever the account is +modified. + +The `storagesize` field tracks the amount of storage related to the account. It is a +number containing the number of storage slots currently set. The `storagesize` of an +inactive account is zero. + +### Charging Rent + +There is a new protocol constant `MAX_STORAGE_SIZE` that specifies the upper bound on the +number of state tree nodes: + +```python +MAX_STORAGE_SIZE = 2**32 # ~160GB of state +``` + +A 'storage fee factor' for each block is derived from this constant such that fees +increase as the limit is approached. + +```python +def storagefee_factor(block): + ramp = MAX_STORAGE_SIZE / (MAX_STORAGE_SIZE - total_storage_size(block)) + return 2**22 * ramp +``` + +When a block is processed, `rent` is deducted from all accounts modified by transactions in +the block after the transactions have been processed. The amount due for each account is +based on the account's storage size. + +```python +def rent(prestate, poststate, addr, currentblock): + fee = 0 + for b in range(prestate[addr].rentblock+1, currentblock-1): + fee += storagefee_factor(b) * prestate[addr].storagesize + return fee + storagefee_factor(currentblock) * poststate[addr].storagesize + +def charge_rent(prestate, poststate, addr, currentblock): + fee = rent(prestate, poststate, addr, currentblock) + if fee <= poststate[addr].rentbalance: + poststate[addr].rentbalance -= fee + else: + fee -= poststate[addr].rentbalance + poststate[addr].rentbalance = 0 + poststate[addr].balance -= min(poststate[addr].balance, fee) + poststate[addr].rentblock = currentblock +``` + +### New EVM Opcodes + +#### `PAYRENT ` + +At any time, the `rent balance` of an account may be topped up by the `PAYRENT` opcode. +`PAYRENT` deducts the given amount of `ether` from the account executing the opcode and adds +it to the `rent balance` of the address specified as beneficiary. + +Any participant can pay the rent for any other participant. + +Gas cost: TBD + +#### `RENTBALANCE ` + +The `rent balance` of an account may be queried through the `RENTBALANCE` opcode. It pushes the +`rentbalance` field of the given address to the stack. + +Gas cost: like `EXTCODEHASH`. + +#### `SSIZE ` + +This opcode pushes the `storagesize` field of the given account to the stack. + +Gas cost: like `EXTCODEHASH`. + +#### `RESTORETO ` + +This opcode restores the inactive account at the given address. This is a bit like +`SELFDESTRUCT` but has more specific semantics. + +The account at `addr` must be `inactive` (i.e. have `storagesize` zero) and its +`storageroot` must match the `storageroot` of the contract executing `RESTORETO`. The +`codeaddr` specifies the address of a contract from which code is taken. The code of the +`codeaddr` account must match the `codehash` of `addr`. + +If all these preconditions are met, `RESTORETO` transfers the storage of the account +executing the opcode to `addr` and resets its `storagesize` to the full size of the +storage. The code of `addr` is restored as well. `RESTORETO` also transfers any remaining +balance and rent balance to `addr`. The contract executing `RESTORETO` is deleted. + +Gas cost: TBD + +## Rationale + +### Why do we need a separate rent balance? + +Accounts need a separate rent balance because some contracts are non-payable, i.e. they +reject regular value transfers. Such contracts might not be able to keep themselves alive, +but users of those contracts can keep them alive by paying rent for them. + +Having the additional balance also makes things easier for contracts that hold balance on +behalf of a user. Consider the canonical crowdfunding example, a contract which changes +behavior once a certain balance is reached and which tracks individual user's balances. +Deducting rent from the main balance of the contract would mess up the contract's +accounting, leaving it unable to pay back users accurately if the threshold balance isn't +reached. + +### Why restoration? + +One of the fundamental guarantees provided by Ethereum is that changes to contract storage +can only be made by the contract itself and storage will persist forever. If you hold a +token balance in a contract, you'll have those tokens forever. By adding restoration, we +can maintain this guarantee to a certain extent. + +### Implementation Impact + +The proposed changes tries to fit within the existing state transition model. Note that +there is no mechanism for deactivating accounts the moment they can't pay rent. Users must +touch accounts to ensure their storage is removed because we'd need to track all accounts +and their rent requirements in an auxlilary data structure otherwise. + +## Backwards Compatibility + +TBA + +## Test Cases + +TBA + +## Implementation + +TBA + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-170.md b/EIPS/eip-170.md new file mode 100644 index 0000000..632f216 --- /dev/null +++ b/EIPS/eip-170.md @@ -0,0 +1,30 @@ +--- +eip: 170 +title: Contract code size limit +author: Vitalik Buterin (@vbuterin) +type: Standards Track +category: Core +status: Final +created: 2016-11-04 +--- + +### Hard fork +[Spurious Dragon](./eip-607.md) + +### Parameters +- `MAX_CODE_SIZE`: `0x6000` (`2**14 + 2**13`) +- `FORK_BLKNUM`: 2,675,000 +- `CHAIN_ID`: 1 (Mainnet) + +### Specification + +If `block.number >= FORK_BLKNUM`, then if contract creation initialization returns data with length of **more than** `MAX_CODE_SIZE` bytes, contract creation fails with an out of gas error. + +### Rationale + +Currently, there remains one slight quadratic vulnerability in Ethereum: when a contract is called, even though the call takes a constant amount of gas, the call can trigger O(n) cost in terms of reading the code from disk, preprocessing the code for VM execution, and also adding O(n) data to the Merkle proof for the block's proof-of-validity. At current gas levels, this is acceptable even if suboptimal. At the higher gas levels that could be triggered in the future, possibly very soon due to dynamic gas limit rules, this would become a greater concern—not nearly as serious as recent denial of service attacks, but still inconvenient especially for future light clients verifying proofs of validity or invalidity. The solution is to put a hard cap on the size of an object that can be saved to the blockchain, and do so non-disruptively by setting the cap at a value slightly higher than what is feasible with current gas limits. + +### References + +1. EIP-170 issue and discussion: https://github.com/ethereum/EIPs/issues/170 +2. pyethereum implementation: https://github.com/ethereum/pyethereum/blob/5217294871283d8dc4fb3ca9d8a78c7d416490e8/ethereum/messages.py#L397 diff --git a/EIPS/eip-1702.md b/EIPS/eip-1702.md new file mode 100644 index 0000000..bfcc9a7 --- /dev/null +++ b/EIPS/eip-1702.md @@ -0,0 +1,241 @@ +--- +eip: 1702 +title: Generalized Account Versioning Scheme +author: Wei Tang (@sorpaas) +discussions-to: https://github.com/sorpaas/EIPs/issues/2 +status: Stagnant +type: Standards Track +category: Core +created: 2017-12-30 +--- + +## Simple Summary + +Introduce account versioning for smart contracts so upgrading the VM +or introducing new VMs can be easier. + +## Abstract + +This defines a method of hard forking while maintaining the exact +functionality of existing account by allowing multiple versions of the +virtual machines to execute in the same block. This is also useful to +define future account state structures when we introduce the on-chain +WebAssembly virtual machine. + +## Motivation + +By allowing account versioning, we can execute different virtual +machine for contracts created at different times. This allows breaking +features to be implemented while making sure existing contracts work +as expected. + +Note that this specification might not apply to all hard forks. We +have emergency hard forks in the past due to network attacks. Whether +they should maintain existing account compatibility should be +evaluated in individual basis. If the attack can only be executed once +against some particular contracts, then the scheme defined here might +still be applicable. Otherwise, having a plain emergency hard fork +might still be a good idea. + +## Specification + +### Account State + +Re-define account state stored in the world state trie to have 5 +items: `nonce`, `balance`, `storageRoot`, `codeHash`, and +`version`. The newly added field `version` is a 256-bit **scalar**. We +use the definition of "scalar" from Yellow Paper. Note that this is +the same type as `nonce` and `balance`, and it is equivalent to a RLP +variable-sized byte array with no leading zero, of maximum length 32. + +When `version` is zero, the account is RLP-encoded with the first 4 +items. When `version` is not zero, the account is RLP-encoded with 5 +items. + +Account versions can also optionally define additional account state +RLP fields, whose meaning are specified through its `version` +field. In those cases, the parsing strategy is defined in "Additional +Fields in Account State RLP" section. + +### Contract Execution + +When fetching an account code from state, we always fetch the +associated version field together. We refer to this as the *code's +version* below. The code of the account is always executed in the +*code's version*. + +In particular, this means that for `DELEGATECALL` and `CALLCODE`, the +version of the execution call frame is the same as +delegating/receiving contract's version. + +### Contract Deployment + +In Ethereum, a contract has a deployment method, either by a contract +creation transaction, or by another contract. If we regard this +deployment method a contract's *parent*, then we find them forming a +family of contracts, with the *root* being a contract creation +transaction. + +We let a family of contracts to always have the same `version`. That +is, `CREATE` and `CREATE2` will always deploy contract that has the +same `version` as the *code's version*. + +In other words, `CREATE` and `CREATE2` will execute the init code +using the current *code's version*, and deploy the contract of the +current *code's version*. This holds even if the to-be-deployed code +is empty. + +### Validation + +A new phrase, *validation* is added to contract deployment (by +`CREATE` / `CREATE2` opcodes, or by contract creation +transaction). When `version` is `0`, the phrase does nothing and +always succeeds. Future VM versions can define additional validation +that has to be passed. + +If the validation phrase fails, deployment does not proceed and return +out-of-gas. + +### Contract Creation Transaction + +Define `LATEST_VERSION` in a hard fork to be the latest supported VM +version. A contract creation transaction is always executed in +`LATEST_VERSION` (which means the *code's version* is +`LATEST_VERSION`), and deploys contracts of `LATEST_VERSION`. Before a +contract creation transaction is executed, run *validation* on the +contract creation code. If it does not pass, return out-of-gas. + +### Precompiled Contract and Externally-owned Address + +Precompiled contracts and externally-owned addresses do not have +`version`. If a message-call transaction or `CALL` / `CALLCODE` / +`STATICCALL` / `DELEGATECALL` touches a new externally-owned address +or a non-existing precompiled contract address, it is always created +with `version` field being `0`. + +### Additional Fields in Account State RLP + +In the future we may need to associate more information into an +account, and we already have some EIPs that define new additional +fields in the account state RLP. In this section, we define the +parsing strategy when additional fields are added. + +* Check the RLP list length, if it is 4, then set account version to + `0`, and do not parse any additional fields. +* If the RLP list length more than 4, set the account version to the + scalar at position `4` (counting from `0`). + * Check version specification for the number of additional fields + defined `N`, if the RLP list length is not equal to `5 + N`, + return parse error. + * Parse RLP position `5` to `4 + N` as the meaning specified in + additional fields. + +## Extensions + +In relation to the above "Specification" section, we have defined the +base account versioning layer. The base account versioning layer is +already useful by itself and can handle most EVM improvements. Below +we define two specifications that can be deployed separately, which +improves functionality of base layer account versioning. + +Note that this section is provided only for documentation +purpose. When "enabling EIP-1702", those extensions should not be +enabled unless the extension specification is also included. + +- [44-VERTXN: Account Versioning Extension for Contract Creation + Transaction](https://specs.corepaper.org/44-vertxn/) +- [45-VEROP: Account Versioning Extension for CREATE and + CREATE2](https://specs.corepaper.org/45-verop/) + +## Usage Template + +This section defines how other EIPs might use this account +versioning specification. Note that currently we only define the usage +template for base layer. + +Account versioning is usually applied directly to a hard fork +meta. EIPs in the hard fork are grouped by the virtual machine +type, for example, EVM and eWASM. For each of them, we define: + +* **Version**: a non-zero scalar less than `2^256` that uniquely + identifies this version. Note that it does not need to be + sequential. +* **Parent version**: the base that all new features derived + from. With parent version of `0` we define the base to be legacy + VM. Note that once a version other than `0` is defined, the legacy + VM's feature set must be frozen. When defining an entirely new VM + (such as eWASM), parent version does not apply. +* **Features**: all additional features that are enabled upon this + version. + +If a meta EIP includes EIPs that provide additional account state RLP +fields, we also define: + +* **Account fields**: all account fields up to the end of this meta + EIP, excluding the basic 5 fields (`nonce`, `balance`, + `storageRoot`, `codeHash` and `version`). If EIPs included that are + specific to modifying account fields do not modify VM execution + logic, it is recommended that we specify an additional version whose + execution logic is the same as previous version, but only the + account fields are changed. + +## Rationale + +This introduces account versioning via a new RLP item in account +state. The design above gets account versioning by making the contract +*family* always have the same version. In this way, versions are only +needed to be provided by contract creation transaction, and there is +no restrictions on formats of code for any version. If we want to +support multiple newest VMs (for example, EVM and WebAssembly running +together), then this will requires extensions such as 44-VERTXN and +45-VEROP. + +Alternatively, account versioning can also be done through: + +* **[26-VER](https://specs.corepaper.org/26-ver/)** and + **[40-UNUSED](https://specs.corepaper.org/40-unused/)**: This makes an + account's versioning soly dependent on its code header prefix. If + with only 26-VER, it is not possible to certify any code is valid, + because current VM allows treating code as data. This can be fixed + by 40-UNUSED, but the drawback is that it's potentially backward + incompatible. +* **EIP-1891**: Instead of writing version field into account RLP + state, we write it in a separate contract. This can accomplish the + same thing as this EIP and potentially reduces code complexity, but + the drawback is that every code execution will require an additional + trie traversal, which impacts performance. + +## Backwards Compatibility + +Account versioning is fully backwards compatible, and it does not +change how current contracts are executed. + +## Discussions + +### Performance + +Currently nearly all full node implementations uses config parameters +to decide which virtual machine version to use. Switching virtual +machine version is simply an operation that changes a pointer using a +different set of config parameters. As a result, this scheme has +nearly zero impact to performance. + +### WebAssembly + +This scheme can also be helpful when we deploy on-chain WebAssembly +virtual machine. In that case, WASM contracts and EVM contracts can +co-exist and the execution boundary and interaction model are clearly +defined as above. + +## Test Cases and Implementations + +To be added. + +## References + +The source of this specification can be found at +[43-VER](https://specs.corepaper.org/43-ver/). + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1706.md b/EIPS/eip-1706.md new file mode 100644 index 0000000..b392f65 --- /dev/null +++ b/EIPS/eip-1706.md @@ -0,0 +1,62 @@ +--- +eip: 1706 +title: Disable SSTORE with gasleft lower than call stipend +author: Alex Forshtat , Yoav Weiss +discussions-to: https://github.com/alex-forshtat-tbk/EIPs/issues/1 +status: Withdrawn +withdrawal-reason: The authors prefer [EIP-2200](./eip-2200.md) +type: Standards Track +category: Core +created: 2019-01-15 +requires: 1283 +--- + +## Simple Summary +The proposal that had been accepted changes security properties of a large portion of an existing contract code base that may be infeasible to update and validate. This proposal will make the old assumptions hold even after a network upgrade. + +## Abstract +[EIP-1283](./eip-1283.md) significantly lowers the gas costs of writing to contract's storage. This created a danger of a new kind of reentrancy attacks on existing contracts as Solidity by default grants a 'stipend' of 2300 gas to simple transfer calls. +This danger is easily mitigated if SSTORE is not allowed in low gasleft state, without breaking the backward compatibility and the original intention of this EIP. + +## Motivation + +An attack that is described in [this article](https://medium.com/chainsecurity/constantinople-enables-new-reentrancy-attack-ace4088297d9). +Explicitly specifying the call stipend as an invariant will have a positive effect on Ethereum protocol security: +https://www.reddit.com/r/ethereum/comments/agdqsm/security_alert_ethereum_constantinople/ee5uvjt + +## Specification + +Add the following condition to to the SSTORE opcode gas cost calculation: + +* If *gasleft* is less than or equal to 2300, fail the current call frame + with 'out of gas' exception. + +## Rationale +In order to keep in place the implicit reentrancy protection of existing contracts, transactions should not be allowed to modify state if the remaining gas is lower then the 2300 stipend given to 'transfer'/'send' in Solidity. +These are other proposed remediations and objections to implementing them: + +* Drop EIP-1283 and abstain from modifying SSTORE cost + * EIP-1283 is an important update + * It was accepted and implemented on test networks and in clients. +* Add a new call context that permits LOG opcodes but not changes to state. + * Adds another call type beyond existing regular/staticcall +* Raise the cost of SSTORE to dirty slots to >=2300 gas + * Makes net gas metering much less useful. +* Reduce the gas stipend + * Makes the stipend almost useless. +* Increase the cost of writes to dirty slots back to 5000 gas, but add 4800 gas to the refund counter + * Still doesn’t make the invariant explicit. + * Requires callers to supply more gas, just to have it refunded +* Add contract metadata specifying per-contract EVM version, and only apply SSTORE changes to contracts deployed with the new version. + + +## Backwards Compatibility +Performing SSTORE has never been possible with less than 5000 gas, so it does not introduce incompatibility to the Ethereum mainnet. Gas estimation should account for this requirement. + +## Test Cases +Test cases for an implementation are mandatory for EIPs that are affecting consensus changes. Other EIPs can choose to include links to test cases if applicable. +TODO +## Implementation +TODO +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1710.md b/EIPS/eip-1710.md new file mode 100644 index 0000000..91503f8 --- /dev/null +++ b/EIPS/eip-1710.md @@ -0,0 +1,59 @@ +--- +eip: 1710 +title: URL Format for Web3 Browsers +author: Bruno Barbieri (@brunobar79) +discussions-to: https://ethereum-magicians.org/t/standarize-url-format-for-web3-browsers/2422 +status: Stagnant +type: Standards Track +category: ERC +created: 2019-01-13 +requires: 155 +--- + +## Simple Summary + +A standard way of representing web3 browser URLs for decentralized applications. + +## Abstract + +Since most normal web browsers (specifically on mobile devices) can not run decentralized applications correctly because of the lack of web3 support, it is necessary to differentiate them from normal urls, so they can be opened in web3 browsers if available. + +## Motivation + +Lots of dApps that are trying to improve their mobile experience are currently (deep)linking to specific mobile web3 browsers which are currently using their own url scheme. + +In order to make the experience more seamless, dApps should still be able to recommend a specific mobile web3 browser via [deferred deeplinking](https://en.wikipedia.org/wiki/Deferred_deep_linking) but by having a standard url format, if the user already has a web3 browser installed that implements this standard, it will be automatically linked to it. + +There is also a compatibility problem with the current `ethereum:` url scheme described in [EIP-831](./eip-831.md) where any ethereum related app (wallets, identity management, etc) already registered it and because of iOS unpredictable behavior for multiple apps handling a single url scheme, users can end up opening an `ethereum:` link in an app that doesn not include a web3 browser and will not be able to handle the deeplink correctly. + +## Specification + +### Syntax + +Web3 browser URLs contain "dapp" in their schema (protocol) part and are constructed as follows: + + request = "dapp" ":" [chain_id "@"] dapp_url + chain_id = 1*DIGIT + dapp_url = URI + +### Semantics + +`chain_id` is optional and it is a parameter for the browser to automatically select the corresponding chain ID as specified in [EIP-155](./eip-155.md) before opening the dApp. + +`dapp_url` is a valid [RFC3986](https://www.ietf.org/rfc/rfc3986.txt) URI + +This a complete example url: + +`dapp:1@peepeth.com/brunobar79?utm_source=github` + +which will open the web3 browser, select `mainnet` (chain_id = 1) and then navigate to: + +`https://peepeth.com/brunobar79?utm_source=github` + +## Rationale + +The proposed format attempts to solve the problem of vendor specific protocols for web3 browsers, avoiding conflicts with the existing 'ethereum:' URL scheme while also adding an extra feature: `chain_id` which will help dApps to be accessed with the right network preselected, optionally extracting away that complexity from end users. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1716.md b/EIPS/eip-1716.md new file mode 100644 index 0000000..031f20d --- /dev/null +++ b/EIPS/eip-1716.md @@ -0,0 +1,39 @@ +--- +eip: 1716 +title: "Hardfork Meta: Petersburg" +author: Afri Schoedon (@5chdn), Marius van der Wijden (@MariusVanDerWijden) +type: Meta +status: Final +created: 2019-01-21 +requires: 1013, 1283 +--- + +## Abstract + +This meta-EIP specifies the changes included in the Ethereum hardfork that removes [EIP-1283](./eip-1283.md) from [Constantinople](./eip-1013.md). + +## Specification + +- Codename: Petersburg +- Aliases: St. Petersfork, Peter's Fork, Constantinople Fix +- Activation: + - `Block >= 7_280_000` on the Ethereum Mainnet + - `Block >= 4_939_394` on the Ropsten testnet + - `Block >= 10_255_201` on the Kovan testnet + - `Block >= 4_321_234` on the Rinkeby testnet + - `Block >= 0` on the Görli testnet +- Removed EIPs: + - [EIP-1283](./eip-1283.md): Net gas metering for SSTORE without dirty maps + +If `Petersburg` and `Constantinople` are applied at the same block, `Petersburg` takes precedence: with the net effect of EIP-1283 being _disabled_. + +If `Petersburg` is defined with an earlier block number than `Constantinople`, then there is _no immediate effect_ from the `Petersburg` fork. However, when `Constantinople` is later activated, EIP-1283 should be _disabled_. + +## References + +1. The list above includes the EIPs that had to be removed from Constantinople due to a [potential reentrancy attack vector](https://medium.com/chainsecurity/constantinople-enables-new-reentrancy-attack-ace4088297d9). Removing this was agreed upon at the [All-Core-Devs call #53 in January 2019](https://github.com/ethereum/pm/issues/70). +2. https://blog.ethereum.org/2019/02/22/ethereum-constantinople-st-petersburg-upgrade-announcement/ + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-173.md b/EIPS/eip-173.md new file mode 100644 index 0000000..e79033e --- /dev/null +++ b/EIPS/eip-173.md @@ -0,0 +1,97 @@ +--- +eip: 173 +title: Contract Ownership Standard +description: A standard interface for ownership of contracts +author: Nick Mudge (@mudgen), Dan Finlay +discussions-to: https://github.com/ethereum/EIPs/issues/173 +type: Standards Track +category: ERC +status: Final +created: 2018-06-07 +--- + +## Abstract + +This specification defines standard functions for owning or controlling a contract. + +An implementation allows reading the current owner (`owner() returns (address)`) and transferring ownership (`transferOwnership(address newOwner)`) along with a standardized event for when ownership is changed (`OwnershipTransferred(address indexed previousOwner, address indexed newOwner)`). + +## Motivation + +Many smart contracts require that they be owned or controlled in some way. For example to withdraw funds or perform administrative actions. It is so common that the contract interface used to handle contract ownership should be standardized to allow compatibility with user interfaces and contracts that manage contracts. + +Here are some examples of kinds of contracts and applications that can benefit from this standard: +1. Exchanges that buy/sell/auction ethereum contracts. This is only widely possible if there is a standard for getting the owner of a contract and transferring ownership. +2. Contract wallets that hold the ownership of contracts and that can transfer the ownership of contracts. +3. Contract registries. It makes sense for some registries to only allow the owners of contracts to add/remove their contracts. A standard must exist for these contract registries to verify that a contract is being submitted by the owner of it before accepting it. +4. User interfaces that show and transfer ownership of contracts. + +## Specification + +Every ERC-173 compliant contract must implement the `ERC173` interface. Contracts should also implement `ERC165` for the ERC-173 interface. + +```solidity + +/// @title ERC-173 Contract Ownership Standard +/// Note: the ERC-165 identifier for this interface is 0x7f5828d0 +interface ERC173 /* is ERC165 */ { + /// @dev This emits when ownership of a contract changes. + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); + + /// @notice Get the address of the owner + /// @return The address of the owner. + function owner() view external returns(address); + + /// @notice Set the address of the new owner of the contract + /// @dev Set _newOwner to address(0) to renounce any ownership. + /// @param _newOwner The address of the new owner of the contract + function transferOwnership(address _newOwner) external; +} + +interface ERC165 { + /// @notice Query if a contract implements an interface + /// @param interfaceID The interface identifier, as specified in ERC-165 + /// @dev Interface identification is specified in ERC-165. + /// @return `true` if the contract implements `interfaceID` and + /// `interfaceID` is not 0xffffffff, `false` otherwise + function supportsInterface(bytes4 interfaceID) external view returns (bool); +} +``` + +The `owner()` function may be implemented as `pure` or `view`. + +The `transferOwnership(address _newOwner)` function may be implemented as `public` or `external`. + +To renounce any ownership of a contract set `_newOwner` to the zero address: `transferOwnership(address(0))`. If this is done then a contract is no longer owned by anybody. + +The OwnershipTransferred event should be emitted when a contract is created. + +## Rationale + +Key factors influencing the standard: +- Keeping the number of functions in the interface to a minimum to prevent contract bloat. +- Backwards compatibility with existing contracts. +- Simplicity +- Gas efficient + +Several ownership schemes were considered. The scheme chosen in this standard was chosen because of its simplicity, low gas cost and backwards compatibility with existing contracts. + +Here are other schemes that were considered: +1. **Associating an Ethereum Name Service (ENS) domain name with a contract.** A contract's `owner()` function could look up the owner address of a particular ENS name and use that as the owning address of the contract. Using this scheme a contract could be transferred by transferring the ownership of the ENS domain name to a different address. Short comings to this approach are that it is not backwards compatible with existing contracts and requires gas to make external calls to ENS related contracts to get the owner address. +2. **Associating an ERC721-based non-fungible token (NFT) with a contract.** Ownership of a contract could be tied to the ownership of an NFT. The benefit of this approach is that the existing ERC721-based infrastructure could be used to sell/buy/auction contracts. Short comings to this approach are additional complexity and infrastructure required. A contract could be associated with a particular NFT but the NFT would not track that it had ownership of a contract unless it was programmed to track contracts. In addition handling ownership of contracts this way is not backwards compatible. + +This standard does not exclude the above ownership schemes or other schemes from also being implemented in the same contract. For example a contract could implement this standard and also implement the other schemes so that ownership could be managed and transferred in multiple ways. This standard does provide a simple ownership scheme that is backwards compatible, is light-weight and simple to implement, and can be widely adopted and depended on. + +This standard can be (and has been) extended by other standards to add additional ownership functionality. + +## Security Considerations + +If the address returned by `owner()` is an externally owned account then its private key must not be lost or compromised. + +## Backwards Compatibility + +Many existing contracts already implement this standard. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1753.md b/EIPS/eip-1753.md new file mode 100644 index 0000000..09cb027 --- /dev/null +++ b/EIPS/eip-1753.md @@ -0,0 +1,246 @@ +--- +eip: 1753 +title: Smart Contract Interface for Licences +author: Lucas Cullen (@BitcoinBrisbane), Kai Yeung (@CivicKai), Anna Crowley , Caroline Marshall , Katrina Donaghy +status: Stagnant +type: Standards Track +category: ERC +created: 2019-02-06 +--- + +## Abstract + +This Ethereum Improvement Proposal (EIP) proposes an Ethereum standard for the issuance of licences, permits and grants (Licences). + +A Licence is a limited and temporary authority, granted to a natural (e.g. you) or legal person (e.g. a corporation), to do something that would otherwise be unlawful pursuant to a legal framework. A public Licence is granted by the government, directly (e.g. by the New South Wales Department of Primary Industries, Australia) or indirectly (e.g. by an agent operating under the government’s authority), and derives its authority from legislation, though this is often practically achieved via delegated legislation such as regulations. This can be contrasted to a private licence – for example, the licence you grant to a visitor who comes onto your property. + +A Licence has the following properties: + +* granted personally to the licencee (Licencee), though it may be transferrable to another person or company; +* conferring a temporary right to the Licencee to own, use or do something that would otherwise be prohibited, without conferring any property interest in the underlying thing. For example, you may be granted a licence to visit a national park without acquiring any ownership in or over the park itself; +* allowing the government authority responsible for the Licence to amend, revoke, renew, suspend or deny the issuance of the Licence, or to impose conditions or penalties for non-compliance; and +* usually issued only after the payment of a fee or the meeting of some criteria. + +Additionally, a Licence may be granted in respect of certain information. For example, a Licence may be issued in respect of a vehicle registration number and attaching to that specific registered vehicle. + +## Motivation + +Governments are responsible for the issuance and management of Licences. However, maintaining and sharing this data can be complicated and inefficient. The granting of Licences usually requires the filing of paper-based application forms, manual oversight of applicable legislation and data entry into registries, as well as the issuance of paper based Licences. If individuals wish to sight information on Licence registries, they often need to be present at the government office and complete further paper-based enquiry forms in order to access that data (if available publicly). + +This EIP seeks to define a standard that will allow for the granting and/or management of Licences via Ethereum smart contracts. The motivation is, in essence, to address the inefficiencies inherent in current licencing systems. + +## Specification + +### Methods + +**NOTES**: + - The following specifications use syntax from Solidity `0.4.17` (or above) + - Callers MUST handle `false` from `returns (bool success)`. Callers MUST NOT assume that `false` is never returned! + + +#### name + +Returns the name of the permit - e.g. `"MyPermit"`. + +``` js +function name() public view returns (string); +``` + +#### totalSupply + +Returns the total permit supply. + +``` js +function totalSupply() public view returns (uint256); +``` + +#### grantAuthority + +Adds an ethereum address to a white list of addresses that have authority to modify a permit. + +``` js +function grantAuthority(address who) public; +``` + +#### revokeAuthority + +Removes an ethereum address from a white list of addresses that have authority to modify a permit. + +``` js +function revokeAuthority(address who) public; +``` + +#### hasAuthority + +Checks to see if the address has authority to grant or revoke permits. + +``` js +function hasAuthority(address who) public view; +``` + +#### issue + +Issues an ethereum address a permit between the specified date range. + +``` js +function issue(address who, uint256 validFrom, uint256 validTo) public; +``` + +#### revoke + +Revokes a permit from an ethereum address. + +``` js +function revoke(address who) public; +``` + +#### hasValid + +Checks to see if an ethereum address has a valid permit. + +``` js +function hasValid(address who) external view returns (bool); +``` + +#### purchase + +Allows a user to self procure a licence. + +``` js +function purchase(uint256 validFrom, uint256 validTo) external payable; +``` + +## Rationale + +The use of smart contracts to apply for, renew, suspend and revoke Licences will free up much needed government resources and allow for the more efficient management of Licences. The EIP also seeks to improve the end user experience of the Licence system. In an era of open government, there is also an increased expectation that individuals will be able to easily access Licence registries, and that the process will be transparent and fair. + +By creating an EIP, we hope to increase the use of Ethereum based and issued Licences, which will address these issues. + +The Ethereum blockchain is adaptable to various Licences and government authorities. It will also be easily translatable into other languages and can be used by other governmental authorities across the world. Moreover, a blockchain will more effectively protect the privacy of Licence-holders’ data, particularly at a time of an ever-increasing volume of government data breaches. + +The EIP has been developed following the review of a number of licensing regulations at the national and state level in Australia. The review allowed the identification of the common licence requirements and criteria for incorporation into the EIP. We have included these in the proposed standard but seek feedback on whether these criteria are sufficient and universal. + +## Test Cases + +A real world example of a Licence is a permit required to camp in a national park in Australia (e.g. Kakadu national park in the Northern Territory of Australia) under the Environment Protection and Biodiversity Conservation Regulations 2000 (Cth) (EPBC Act) and the Environment Protection and Biodiversity Conservation Regulations 2000 (the Regulations). Pursuant to the EPBC Act and the Regulations, the Director of National Parks oversees a camping permit system, which is intended to help regulate certain activities in National Parks. Permits allowing access to National Parks can be issued to legal or natural persons if the applicant has met certain conditions. + +The current digital portal and application form to camp at Kakadu National Park (the Application) can be accessed at: https://www.environment.gov.au/system/files/resources/b3481ed3-164b-4e72-a9f8-91fc987d90e7/files/kakadu-camping-permit-form-19jan2015-pdf.pdf + +The user must provide the following details when making an Application: + +* The full name and contact details of each person to whom the permit is to be issued; + +* If the applicant is a company or other incorporated body: + +o the name, business address and postal address of the company or incorporated body; + +o if the applicant is a company— + +* the full name of each of the directors of the company; + +* the full name and contact details of the person completing the application form; + +* the ACN or ABN of the company or other incorporated body (if applicable); + +* Details of the proposed camping purpose (e.g. private camping, school group, etc.); + +* A start date and duration for the camping (up to the maximum duration allowed by law); + +* Number of campers (up to the maximum allowed by law); + +* All other required information not essential to the issuance of the Licence (e.g. any particular medical needs of the campers); and + +* Fees payable depending on the site, duration and number of campers. + +The Regulations also set out a number of conditions that must be met by licensees when the permit has been issued. The Regulations allow the Director of National Parks to cancel, renew or transfer the licence. The above workflow could be better performed by way of a smart contract. + +The key criteria required as part of this process form part of the proposed Ethereum standard. We have checked this approach by also considering the issuance of a Commercial Fishing Licence under Part 8 “Licensing and other commercial fisheries management” of the Fisheries Management (General) Regulation 2010 (NSW) (Fisheries Regulations) made pursuant to the Fisheries Management Act 1994 (NSW) (Fisheries Act). + +## Implementation + +The issuance and ownership of a Licence can be digitally represented on the Ethereum blockchain. + +Smart contracts can be used to embed regulatory requirements with respect to the relevant Licence in the blockchain. The Licence would be available electronically in the form of a token. This might be practically represented by a QR code, for example, displaying the current Licence information. The digital representation of the Licence would be stored in a digital wallet, typically an application on a smartphone or tablet computer. The proposed standard allows issuing authorities or regulators to amend, revoke or deny Licences from time to time, with the result of their determinations reflected in the Licence token in near real-time. Licence holders will therefore be notified almost instantly of any amendments, revocations or issues involving their Licence. + +## Interface + +### Solidity Example +```solidity +interface EIP1753 { + + function grantAuthority(address who) external; + function revokeAuthority(address who) external; + function hasAuthority(address who) external view returns (bool); + + function issue(address who, uint256 from, uint256 to) external; + function revoke(address who) external; + + function hasValid(address who) external view returns (bool); + function purchase(uint256 validFrom, uint256 validTo) external payable; +} + +pragma solidity ^0.5.3; + +contract EIP is EIP1753 { + + string public name = "Kakadu National Park Camping Permit"; + uint256 public totalSupply; + + address private _owner; + mapping(address => bool) private _authorities; + mapping(address => Permit) private _holders; + + struct Permit { + address issuer; + uint256 validFrom; + uint256 validTo; + } + + constructor() public { + _owner = msg.sender; + } + + function grantAuthority(address who) public onlyOwner() { + _authorities[who] = true; + } + + function revokeAuthority(address who) public onlyOwner() { + delete _authorities[who]; + } + + function hasAuthority(address who) public view returns (bool) { + return _authorities[who] == true; + } + + function issue(address who, uint256 start, uint256 end) public onlyAuthority() { + _holders[who] = Permit(_owner, start, end); + totalSupply += 1; + } + + function revoke(address who) public onlyAuthority() { + delete _holders[who]; + } + + function hasValid(address who) external view returns (bool) { + return _holders[who].validFrom > now && _holders[who].validTo < now; + } + + function purchase(uint256 validFrom, uint256 validTo) external payable { + require(msg.value == 1 ether, "Incorrect fee"); + issue(msg.sender, validFrom, validTo); + } + + modifier onlyOwner() { + require(msg.sender == _owner, "Only owner can perform this function"); + _; + } + + modifier onlyAuthority() { + require(hasAuthority(msg.sender), "Only an authority can perform this function"); + _; + } +} +``` + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1761.md b/EIPS/eip-1761.md new file mode 100644 index 0000000..1418368 --- /dev/null +++ b/EIPS/eip-1761.md @@ -0,0 +1,175 @@ +--- +eip: 1761 +title: Scoped Approval Interface +author: Witek Radomski , Andrew Cooke , James Therien , Eric Binet +type: Standards Track +category: ERC +status: Stagnant +created: 2019-02-18 +discussions-to: https://github.com/ethereum/EIPs/issues/1761 +requires: 165 +--- + +## Simple Summary + +A standard interface to permit restricted approval in token contracts by defining "scopes" of one or more Token IDs. + +## Abstract + +This interface is designed for use with token contracts that have an "ID" domain, such as ERC-1155 or ERC-721. This enables restricted approval of one or more Token IDs to a specific "scope". When considering a smart contract managing tokens from multiple different domains, it makes sense to limit approvals to those domains. Scoped approval is a generalization of this idea. Implementors can define scopes as needed. + +Sample use cases for scopes: + +* A company may represent its fleet of vehicles on the blockchain and it could create a scope for each regional office. +* Game developers could share an [ERC-1155](./eip-1155.md) contract where each developer manages tokens under a specified scope. +* Tokens of different value could be split into separate scopes. High-value tokens could be kept in smaller separate scopes while low-value tokens might be kept in a shared scope. Users would approve the entire low-value token scope to a third-party smart contract, exchange, or other application without concern about losing their high-value tokens in the event of a problem. + +## Motivation + +It may be desired to restrict approval in some applications. Restricted approval can prevent losses in cases where users do not audit the contracts they're approving. No standard API is supplied to manage scopes as this is implementation specific. Some implementations may opt to offer a fixed number of scopes, or assign a specific set of scopes to certain types. Other implementations may open up scope configuration to its users and offer methods to create scopes and assign IDs to them. + +# Specification + +```solidity +pragma solidity ^0.5.2; + +/** + Note: The ERC-165 identifier for this interface is 0x30168307. +*/ +interface ScopedApproval { + /** + @dev MUST emit when approval changes for scope. + */ + event ApprovalForScope(address indexed _owner, address indexed _operator, bytes32 indexed _scope, bool _approved); + + /** + @dev MUST emit when the token IDs are added to the scope. + By default, IDs are in no scope. + The range is inclusive: _idStart, _idEnd, and all IDs in between have been added to the scope. + _idStart must be lower than or equal to _idEnd. + */ + event IdsAddedToScope(uint256 indexed _idStart, uint256 indexed _idEnd, bytes32 indexed _scope); + + /** + @dev MUST emit when the token IDs are removed from the scope. + The range is inclusive: _idStart, _idEnd, and all IDs in between have been removed from the scope. + _idStart must be lower than or equal to _idEnd. + */ + event IdsRemovedFromScope(uint256 indexed _idStart, uint256 indexed _idEnd, bytes32 indexed _scope); + + /** @dev MUST emit when a scope URI is set or changes. + URIs are defined in RFC 3986. + The URI MUST point a JSON file that conforms to the "Scope Metadata JSON Schema". + */ + event ScopeURI(string _value, bytes32 indexed _scope); + + /** + @notice Returns the number of scopes that contain _id. + @param _id The token ID + @return The number of scopes containing the ID + */ + function scopeCountForId(uint256 _id) public view returns (uint32); + + /** + @notice Returns a scope that contains _id. + @param _id The token ID + @param _scopeIndex The scope index to query (valid values are 0 to scopeCountForId(_id)-1) + @return The Nth scope containing the ID + */ + function scopeForId(uint256 _id, uint32 _scopeIndex) public view returns (bytes32); + + /** + @notice Returns a URI that can be queried to get scope metadata. This URI should return a JSON document containing, at least the scope name and description. Although supplying a URI for every scope is recommended, returning an empty string "" is accepted for scopes without a URI. + @param _scope The queried scope + @return The URI describing this scope. + */ + function scopeUri(bytes32 _scope) public view returns (string memory); + + /** + @notice Enable or disable approval for a third party ("operator") to manage the caller's tokens in the specified scope. + @dev MUST emit the ApprovalForScope event on success. + @param _operator Address to add to the set of authorized operators + @param _scope Approval scope (can be identified by calling scopeForId) + @param _approved True if the operator is approved, false to revoke approval + */ + function setApprovalForScope(address _operator, bytes32 _scope, bool _approved) external; + + /** + @notice Queries the approval status of an operator for a given owner, within the specified scope. + @param _owner The owner of the Tokens + @param _operator Address of authorized operator + @param _scope Scope to test for approval (can be identified by calling scopeForId) + @return True if the operator is approved, false otherwise + */ + function isApprovedForScope(address _owner, address _operator, bytes32 _scope) public view returns (bool); +} +``` + +## Scope Metadata JSON Schema + +This schema allows for localization. `{id}` and `{locale}` should be replaced with the appropriate values by clients. + +```json +{ + "title": "Scope Metadata", + "type": "object", + "required": ["name"], + "properties": { + "name": { + "type": "string", + "description": "Identifies the scope in a human-readable way.", + }, + "description": { + "type": "string", + "description": "Describes the scope to allow users to make informed approval decisions.", + }, + "localization": { + "type": "object", + "required": ["uri", "default", "locales"], + "properties": { + "uri": { + "type": "string", + "description": "The URI pattern to fetch localized data from. This URI should contain the substring `{locale}` which will be replaced with the appropriate locale value before sending the request." + }, + "default": { + "type": "string", + "description": "The locale of the default data within the base JSON" + }, + "locales": { + "type": "array", + "description": "The list of locales for which data is available. These locales should conform to those defined in the Unicode Common Locale Data Repository (http://cldr.unicode.org/)." + } + } + } + } +} +``` + +### Localization + +Metadata localization should be standardized to increase presentation uniformity across all languages. As such, a simple overlay method is proposed to enable localization. If the metadata JSON file contains a `localization` attribute, its content may be used to provide localized values for fields that need it. The `localization` attribute should be a sub-object with three attributes: `uri`, `default` and `locales`. If the string `{locale}` exists in any URI, it MUST be replaced with the chosen locale by all client software. + +## Rationale + +The initial design was proposed as an extension to ERC-1155: [Discussion Thread - Comment 1](https://github.com/ethereum/EIPs/issues/1155#issuecomment-459505728). After some discussion: [Comment 2](https://github.com/ethereum/EIPs/issues/1155#issuecomment-460603439) and suggestions by the community to implement this approval mechanism in an external contract [Comment 3](https://github.com/ethereum/EIPs/issues/1155#issuecomment-461758755), it was decided that as an interface standard, this design would allow many different token standards such as ERC-721 and ERC-1155 to implement scoped approvals without forcing the system into all implementations of the tokens. + +### Metadata JSON + +The Scope Metadata JSON Schema was added in order to support human-readable scope names and descriptions in more than one language. + +## References + +**Standards** +- [ERC-1155 Multi Token Standard](./eip-1155.md) +- [ERC-165 Standard Interface Detection](./eip-165.md) +- [JSON Schema](https://json-schema.org/) + +**Implementations** +- [Enjin Coin](https://enjincoin.io) ([github](https://github.com/enjin)) + +**Articles & Discussions** +- [GitHub - Original Discussion Thread](https://github.com/ethereum/EIPs/issues/1761) +- [GitHub - ERC-1155 Discussion Thread](https://github.com/ethereum/EIPs/issues/1155) + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1767.md b/EIPS/eip-1767.md new file mode 100644 index 0000000..6166041 --- /dev/null +++ b/EIPS/eip-1767.md @@ -0,0 +1,418 @@ +--- +eip: 1767 +title: GraphQL interface to Ethereum node data +author: Nick Johnson (@arachnid), Raúl Kripalani (@raulk), Kris Shinn (@kshinn) +discussions-to: https://ethereum-magicians.org/t/graphql-interface-to-ethereum-node-data/2710 +status: Stagnant +type: Standards Track +category: Interface +created: 2019-02-14 +--- + +## Abstract +This EIP specifies a GraphQL schema for accessing data stored on an Ethereum node. It aims to provide a complete replacement to the read-only information exposed via the present JSON-RPC interface, while improving on usability, consistency, efficiency, and future-proofing. + +## Motivation +The current JSON-RPC interface for Ethereum nodes has a number of shortcomings. It's informally and incompletely specified in areas, which has led to incompatibilities around issues such as representation of empty byte strings ("" vs "0x" vs "0x0"), and it has to make educated guesses about the data a user will request, which often leads to unnecessary work. + +For example, the `totalDifficulty` field is stored separately from the block header in common Ethereum node implementations, and many callers do not require this field. However, every call to `eth_getBlock` still retrieves this field, requiring a separate disk read, because the RPC server has no way of knowing if the user requires this field or not. + +Similarly, transaction receipts in go-ethereum are stored on disk as a single binary blob for each block. Fetching a receipt for a single transaction requires fetching and deserializing this blob, then finding the relevant entry and returning it; this is accomplished by the `eth_getTransactionReceipt` API call. A common task for API consumers is to fetch all the receipts in a block; as a result, node implementations end up fetching and deserializing the same data repeatedly, leading to `O(n^2)` effort to fetch all transaction receipts from a block instead of `O(n)`. + +Some of these issues could be fixed with changes to the existing JSON-RPC interface, at the cost of complicating the interface somewhat. Instead, we propose adopting a standard query language, GraphQL, which facilitates more efficient API implementations, while also increasing flexibility. + +## Prior Art + +Nick Johnson and [EthQL](https://github.com/ConsenSys/ethql) independently developed a GraphQL schema for node data. Once the parties were made aware of the shared effort, they made efforts to bring their schemas into alignment. The current schema proposed in this EIP is derived primarily from the EthQL schema. + +## Specification + +### Node API + +Compatible nodes MUST provide a GraphQL endpoint available over HTTP. This SHOULD be offered on port 8547 by default. The path to the GraphQL endpoint SHOULD be '/graphql'. + +Compatible nodes MAY offer a GraphiQL interactive query explorer on the root path ('/'). + +### Schema + +The GraphQL schema for this service is defined as follows: +``` +# Bytes32 is a 32 byte binary string, represented as 0x-prefixed hexadecimal. +scalar Bytes32 +# Address is a 20 byte Ethereum address, represented as 0x-prefixed hexadecimal. +scalar Address +# Bytes is an arbitrary length binary string, represented as 0x-prefixed hexadecimal. +# An empty byte string is represented as '0x'. Byte strings must have an even number of hexadecimal nybbles. +scalar Bytes +# BigInt is a large integer. Input is accepted as either a JSON number or as a string. +# Strings may be either decimal or 0x-prefixed hexadecimal. Output values are all +# 0x-prefixed hexadecimal. +scalar BigInt +# Long is a 64 bit unsigned integer. +scalar Long + +schema { + query: Query + mutation: Mutation +} + +# Account is an Ethereum account at a particular block. +type Account { + # Address is the address owning the account. + address: Address! + # Balance is the balance of the account, in wei. + balance: BigInt! + # TransactionCount is the number of transactions sent from this account, + # or in the case of a contract, the number of contracts created. Otherwise + # known as the nonce. + transactionCount: Long! + # Code contains the smart contract code for this account, if the account + # is a (non-self-destructed) contract. + code: Bytes! + # Storage provides access to the storage of a contract account, indexed + # by its 32 byte slot identifier. + storage(slot: Bytes32!): Bytes32! +} + +# Log is an Ethereum event log. +type Log { + # Index is the index of this log in the block. + index: Int! + # Account is the account which generated this log - this will always + # be a contract account. + account(block: Long): Account! + # Topics is a list of 0-4 indexed topics for the log. + topics: [Bytes32!]! + # Data is unindexed data for this log. + data: Bytes! + # Transaction is the transaction that generated this log entry. + transaction: Transaction! +} + +# Transaction is an Ethereum transaction. +type Transaction { + # Hash is the hash of this transaction. + hash: Bytes32! + # Nonce is the nonce of the account this transaction was generated with. + nonce: Long! + # Index is the index of this transaction in the parent block. This will + # be null if the transaction has not yet been mined. + index: Int + # From is the account that sent this transaction - this will always be + # an externally owned account. + from(block: Long): Account! + # To is the account the transaction was sent to. This is null for + # contract-creating transactions. + to(block: Long): Account + # Value is the value, in wei, sent along with this transaction. + value: BigInt! + # GasPrice is the price offered to miners for gas, in wei per unit. + gasPrice: BigInt! + # Gas is the maximum amount of gas this transaction can consume. + gas: Long! + # InputData is the data supplied to the target of the transaction. + inputData: Bytes! + # Block is the block this transaction was mined in. This will be null if + # the transaction has not yet been mined. + block: Block + + # Status is the return status of the transaction. This will be 1 if the + # transaction succeeded, or 0 if it failed (due to a revert, or due to + # running out of gas). If the transaction has not yet been mined, this + # field will be null. + status: Long + # GasUsed is the amount of gas that was used processing this transaction. + # If the transaction has not yet been mined, this field will be null. + gasUsed: Long + # CumulativeGasUsed is the total gas used in the block up to and including + # this transaction. If the transaction has not yet been mined, this field + # will be null. + cumulativeGasUsed: Long + # CreatedContract is the account that was created by a contract creation + # transaction. If the transaction was not a contract creation transaction, + # or it has not yet been mined, this field will be null. + createdContract(block: Long): Account + # Logs is a list of log entries emitted by this transaction. If the + # transaction has not yet been mined, this field will be null. + logs: [Log!] +} + +# BlockFilterCriteria encapsulates log filter criteria for a filter applied +# to a single block. +input BlockFilterCriteria { + # Addresses is list of addresses that are of interest. If this list is + # empty, results will not be filtered by address. + addresses: [Address!] + # Topics list restricts matches to particular event topics. Each event has a list + # of topics. Topics matches a prefix of that list. An empty element array matches any + # topic. Non-empty elements represent an alternative that matches any of the + # contained topics. + # + # Examples: + # - [] or nil matches any topic list + # - [[A]] matches topic A in first position + # - [[], [B]] matches any topic in first position, B in second position + # - [[A], [B]] matches topic A in first position, B in second position + # - [[A, B]], [C, D]] matches topic (A OR B) in first position, (C OR D) in second position + topics: [[Bytes32!]!] +} + +# Block is an Ethereum block. +type Block { + # Number is the number of this block, starting at 0 for the genesis block. + number: Long! + # Hash is the block hash of this block. + hash: Bytes32! + # Parent is the parent block of this block. + parent: Block + # Nonce is the block nonce, an 8 byte sequence determined by the miner. + nonce: Bytes! + # TransactionsRoot is the keccak256 hash of the root of the trie of transactions in this block. + transactionsRoot: Bytes32! + # TransactionCount is the number of transactions in this block. if + # transactions are not available for this block, this field will be null. + transactionCount: Int + # StateRoot is the keccak256 hash of the state trie after this block was processed. + stateRoot: Bytes32! + # ReceiptsRoot is the keccak256 hash of the trie of transaction receipts in this block. + receiptsRoot: Bytes32! + # Miner is the account that mined this block. + miner(block: Long): Account! + # ExtraData is an arbitrary data field supplied by the miner. + extraData: Bytes! + # GasLimit is the maximum amount of gas that was available to transactions in this block. + gasLimit: Long! + # GasUsed is the amount of gas that was used executing transactions in this block. + gasUsed: Long! + # Timestamp is the unix timestamp at which this block was mined. + timestamp: BigInt! + # LogsBloom is a bloom filter that can be used to check if a block may + # contain log entries matching a filter. + logsBloom: Bytes! + # MixHash is the hash that was used as an input to the PoW process. + mixHash: Bytes32! + # Difficulty is a measure of the difficulty of mining this block. + difficulty: BigInt! + # TotalDifficulty is the sum of all difficulty values up to and including + # this block. + totalDifficulty: BigInt! + # OmmerCount is the number of ommers (AKA uncles) associated with this + # block. If ommers are unavailable, this field will be null. + ommerCount: Int + # Ommers is a list of ommer (AKA uncle) blocks associated with this block. + # If ommers are unavailable, this field will be null. Depending on your + # node, the transactions, transactionAt, transactionCount, ommers, + # ommerCount and ommerAt fields may not be available on any ommer blocks. + ommers: [Block] + # OmmerAt returns the ommer (AKA uncle) at the specified index. If ommers + # are unavailable, or the index is out of bounds, this field will be null. + ommerAt(index: Int!): Block + # OmmerHash is the keccak256 hash of all the ommers (AKA uncles) + # associated with this block. + ommerHash: Bytes32! + # Transactions is a list of transactions associated with this block. If + # transactions are unavailable for this block, this field will be null. + transactions: [Transaction!] + # TransactionAt returns the transaction at the specified index. If + # transactions are unavailable for this block, or if the index is out of + # bounds, this field will be null. + transactionAt(index: Int!): Transaction + # Logs returns a filtered set of logs from this block. + logs(filter: BlockFilterCriteria!): [Log!]! + # Account fetches an Ethereum account at the current block's state. + account(address: Address!): Account + # Call executes a local call operation at the current block's state. + call(data: CallData!): CallResult + # EstimateGas estimates the amount of gas that will be required for + # successful execution of a transaction at the current block's state. + estimateGas(data: CallData!): Long! +} + +# CallData represents the data associated with a local contract call. +# All fields are optional. +input CallData { + # From is the address making the call. + from: Address + # To is the address the call is sent to. + to: Address + # Gas is the amount of gas sent with the call. + gas: Long + # GasPrice is the price, in wei, offered for each unit of gas. + gasPrice: BigInt + # Value is the value, in wei, sent along with the call. + value: BigInt + # Data is the data sent to the callee. + data: Bytes +} + +# CallResult is the result of a local call operation. +type CallResult { + # Data is the return data of the called contract. + data: Bytes! + # GasUsed is the amount of gas used by the call, after any refunds. + gasUsed: Long! + # Status is the result of the call - 1 for success or 0 for failure. + status: Long! +} + +# FilterCriteria encapsulates log filter criteria for searching log entries. +input FilterCriteria { + # FromBlock is the block at which to start searching, inclusive. Defaults + # to the latest block if not supplied. + fromBlock: Long + # ToBlock is the block at which to stop searching, inclusive. Defaults + # to the latest block if not supplied. + toBlock: Long + # Addresses is a list of addresses that are of interest. If this list is + # empty, results will not be filtered by address. + addresses: [Address!] + # Topics list restricts matches to particular event topics. Each event has a list + # of topics. Topics matches a prefix of that list. An empty element array matches any + # topic. Non-empty elements represent an alternative that matches any of the + # contained topics. + # + # Examples: + # - [] or nil matches any topic list + # - [[A]] matches topic A in first position + # - [[], [B]] matches any topic in first position, B in second position + # - [[A], [B]] matches topic A in first position, B in second position + # - [[A, B]], [C, D]] matches topic (A OR B) in first position, (C OR D) in second position + topics: [[Bytes32!]!] +} + +# SyncState contains the current synchronisation state of the client. +type SyncState{ + # StartingBlock is the block number at which synchronisation started. + startingBlock: Long! + # CurrentBlock is the point at which synchronisation has presently reached. + currentBlock: Long! + # HighestBlock is the latest known block number. + highestBlock: Long! + # PulledStates is the number of state entries fetched so far, or null + # if this is not known or not relevant. + pulledStates: Long + # KnownStates is the number of states the node knows of so far, or null + # if this is not known or not relevant. + knownStates: Long +} + +# Pending represents the current pending state. +type Pending { + # TransactionCount is the number of transactions in the pending state. + transactionCount: Int! + # Transactions is a list of transactions in the current pending state. + transactions: [Transaction!] + # Account fetches an Ethereum account for the pending state. + account(address: Address!): Account + # Call executes a local call operation for the pending state. + call(data: CallData!): CallResult + # EstimateGas estimates the amount of gas that will be required for + # successful execution of a transaction for the pending state. + estimateGas(data: CallData!): Long! +} + +type Query { + # Block fetches an Ethereum block by number or by hash. If neither is + # supplied, the most recent known block is returned. + block(number: Long, hash: Bytes32): Block + # Blocks returns all the blocks between two numbers, inclusive. If + # to is not supplied, it defaults to the most recent known block. + blocks(from: Long!, to: Long): [Block!]! + # Pending returns the current pending state. + pending: Pending! + # Transaction returns a transaction specified by its hash. + transaction(hash: Bytes32!): Transaction + # Logs returns log entries matching the provided filter. + logs(filter: FilterCriteria!): [Log!]! + # GasPrice returns the node's estimate of a gas price sufficient to + # ensure a transaction is mined in a timely fashion. + gasPrice: BigInt! + # ProtocolVersion returns the current wire protocol version number. + protocolVersion: Int! + # Syncing returns information on the current synchronisation state. + syncing: SyncState +} + +type Mutation { + # SendRawTransaction sends an RLP-encoded transaction to the network. + sendRawTransaction(data: Bytes!): Bytes32! +} +``` + +Nodes MAY offer a superset of this schema, by adding new fields or types. Experimental or client-specific fields MUST be prefixed with '_client_' (eg, '_geth_' or '_parity_'). Unprefixed fields MUST be specified in a new EIP that extends this one. + +## Rationale +Ethereum nodes have been moving away from providing read-write functionality such as transaction and message signing, and from other services such as code compilation, in favor of a more 'unix-like' approach where each task is performed by a dedicated process. We have thus specified a core set of types and fields that reflects this trend, leaving out functionality that is presently, or intended to be, deprecated: + + - `eth_compile*` calls are deprecated, and hence not provided here. + - `eth_accounts`, `eth_sign`, and `eth_sendTransaction` are considered by many to be deprecated, and are not provided here; callers should use local accounts or a separate signing daemon instead. + +Further, two areas of the current API interface have been omitted for simplicity in this initial standard, with the intention that they will be defined in a later EIP: + + - Filters will require use of GraphQL subscriptions, and require careful consideration around the desire for nodes without local per-caller state. + - Mining functionality is less-used and benefits less from reimplementation in GraphQL, and should be specified in a separate EIP. + +## Backwards Compatibility + +This schema implements the bulk of the current read-only functionality provided by the JSON-RPC node interface. Existing RPC calls can be mapped to GraphQL queries as follows: + +| RPC | Status | Description | +| --- | ------ | ----------- | +| eth_blockNumber | IMPLEMENTED | `{ block { number } }` | +| eth_call | IMPLEMENTED | `{ call(data: { to: "0x...", data: "0x..." }) { data status gasUsed } }` | +| eth_estimateGas | IMPLEMENTED | `{ estimateGas(data: { to: "0x...", data: "0x..." }) }` | +| eth_gasPrice | IMPLEMENTED | `{ gasPrice }` | +| eth_getBalance | IMPLEMENTED | `{ account(address: "0x...") { balance } }` | +| eth_getBlockByHash | IMPLEMENTED | `{ block(hash: "0x...") { ... } }` | +| eth_getBlockByNumber | IMPLEMENTED | `{ block(number: 123) { ... } }` | +| eth_getBlockTransactionCountByHash | IMPLEMENTED | `{ block(hash: "0x...") { transactionCount } }` | +| eth_getBlockTransactionCountByNumber | IMPLEMENTED | `{ block(number: x) { transactionCounnt } }` | +| eth_getCode | IMPLEMENTED | `{ account(address: "0x...") { code } }` | +| eth_getLogs | IMPLEMENTED | `{ logs(filter: { ... }) { ... } }` or `{ block(...) { logs(filter: { ... }) { ... } } }` | +| eth_getStorageAt | IMPLEMENTED | `{ account(address: "0x...") { storage(slot: "0x...") } }` | +| eth_getTransactionByBlockHashAndIndex | IMPLEMENTED | `{ block(hash: "0x...") { transactionAt(index: x) { ... } } }` | +| eth_getTransactionByBlockNumberAndIndex | IMPLEMENTED | `{ block(number: n) { transactionAt(index: x) { ... } } }` | +| eth_getTransactionByHash | IMPLEMENTED | `{ transaction(hash: "0x...") { ... } }` | +| eth_getTransactionCount | IMPLEMENTED | `{ account(address: "0x...") { transactionCount } }` | +| eth_getTransactionReceipt | IMPLEMENTED | `{ transaction(hash: "0x...") { ... } }` | +| eth_getUncleByBlockHashAndIndex | IMPLEMENTED | `{ block(hash: "0x...") { ommerAt(index: x) { ... } } }` | +| eth_getUncleByBlockNumberAndIndex | IMPLEMENTED | `{ block(number: n) { ommerAt(index: x) { ... } } }` | +| eth_getUncleCountByBlockHash | IMPLEMENTED | `{ block(hash: "0x...") { ommerCount } }` | +| eth_getUncleCountByBlockNumber | IMPLEMENTED | `{ block(number: x) { ommerCount } }` | +| eth_protocolVersion | IMPLEMENTED | `{ protocolVersion }` | +| eth_sendRawTransaction | IMPLEMENTED | `mutation { sendRawTransaction(data: data) }` | +| eth_syncing | IMPLEMENTED | `{ syncing { ... } }` | +| eth_getCompilers | NOT IMPLEMENTED | Compiler functionality is deprecated in JSON-RPC. | +| eth_compileLLL | NOT IMPLEMENTED | Compiler functionality is deprecated in JSON-RPC. | +| eth_compileSolidity | NOT IMPLEMENTED | Compiler functionality is deprecated in JSON-RPC. | +| eth_compileSerpent | NOT IMPLEMENTED | Compiler functionality is deprecated in JSON-RPC. | +| eth_newFilter | NOT IMPLEMENTED | Filter functionality may be specified in a future EIP. | +| eth_newBlockFilter | NOT IMPLEMENTED | Filter functionality may be specified in a future EIP. | +| eth_newPendingTransactionFilter | NOT IMPLEMENTED | Filter functionality may be specified in a future EIP. | +| eth_uninstallFilter | NOT IMPLEMENTED | Filter functionality may be specified in a future EIP. | +| eth_getFilterChanges | NOT IMPLEMENTED | Filter functionality may be specified in a future EIP. | +| eth_getFilterLogs | NOT IMPLEMENTED | Filter functionality may be specified in a future EIP. | +| eth_accounts | NOT IMPLEMENTED | Accounts functionality is not part of the core node API. | +| eth_sign | NOT IMPLEMENTED | Accounts functionality is not part of the core node API. | +| eth_sendTransaction | NOT IMPLEMENTED | Accounts functionality is not part of the core node API. | +| eth_coinbase | NOT IMPLEMENTED | Mining functionality to be defined separately. | +| eth_getWork | NOT IMPLEMENTED | Mining functionality to be defined separately. | +| eth_hashRate | NOT IMPLEMENTED | Mining functionality to be defined separately. | +| eth_mining | NOT IMPLEMENTED | Mining functionality to be defined separately. | +| eth_submitHashrate | NOT IMPLEMENTED | Mining functionality to be defined separately. | +| eth_submitWork | NOT IMPLEMENTED | Mining functionality to be defined separately. | + +For specific reasoning behind omitted functionality, see the Rationale section. + +## Test Cases +TBD. + +## Implementation + +- Implemented and released in [Go-ethereum 1.9.0](https://github.com/ethereum/go-ethereum/releases/tag/v1.9.0) +- Implemented and released in [Pantheon 1.1.1](https://github.com/PegaSysEng/pantheon/blob/master/CHANGELOG.md#111) +- Work in progress in [Trinity](https://github.com/ethereum/trinity/issues/302) +- Work in progress in [Parity](https://github.com/paritytech/parity-ethereum/issues/10933) + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1775.md b/EIPS/eip-1775.md new file mode 100644 index 0000000..3ee41df --- /dev/null +++ b/EIPS/eip-1775.md @@ -0,0 +1,196 @@ +--- +eip: 1775 +title: App Keys, application specific wallet accounts +author: Vincent Eli (@Bunjin), Dan Finlay (@DanFinlay) +discussions-to: https://ethereum-magicians.org/t/eip-erc-app-keys-application-specific-wallet-accounts/2742 +status: Stagnant +type: Standards Track +category: ERC +created: 2019-02-20 +--- + +## Simple Summary + +Among others cryptographic applications, scalability and privacy solutions for ethereum blockchain require that an user performs a significant amount of signing operations. It may also require her to watch some state and be ready to sign data automatically (e.g. sign a state or contest a withdraw). The way wallets currently implement accounts poses several obstacles to the development of a complete web3.0 experience both in terms of UX, security and privacy. + +This proposal describes a standard and api for a new type of wallet accounts that are derived specifically for a each given application. We propose to call them `app keys`. They allow to isolate the accounts used for each application, thus potentially increasing privacy. They also allow to give more control to the applications developers over account management and signing delegation. For these app keys, wallets can have a more permissive level of security (e.g. not requesting user's confirmation) while keeping main accounts secure. Finally wallets can also implement a different behavior such as allowing to sign transactions without broadcasting them. + +This new accounts type can allow to significantly improve UX and permit new designs for applications of the crypto permissionned web. + +## Abstract +In a wallet, an user often holds most of her funds in her main accounts. These accounts require a significant level of security and should not be delegated in any way, this significantly impacts the design of cryptographic applications if a user has to manually confirm every action. Also often an user uses the same accounts across apps, which is a privacy and potentially also a security issue. + +We introduce here a new account type, app keys, which permits signing delegation and accounts isolation across applications for privacy and security. + +In this EIP, we provide a proposal on how to uniquely identify and authenticate each application, how to derive a master account (or app key) unique for the domain from an user private key (her root private key or any other private key of an account derived or not from her root one). This EIP aims at becoming a standard on how to derive keys specific to each application that can be regenerated from scratch without further input from the user if she restores her wallet and uses again the application for which this key was derived. +These app keys can then be endowed a different set of permissions (through the requestPermission model introduced in [EIP-2255](./eip-2255.md)). This will potentially allow an user to partly trust some apps to perform some crypto operations on their behalf without compromising any security with respect to her main accounts. + +## Motivation +Wallets developers have agreed on an HD derivation path for ethereum accounts using BIP32, BIP44, SLIP44, [(see the discussion here)](https://github.com/ethereum/EIPs/issues/84). Web3 wallets have implemented in a roughly similar way the rpc eth api. [EIP-1102](./eip-1102.md) introduced privacy through non automatic opt-in of a wallet account into an app increasing privacy. + +However several limitations remain in order to allow for proper design and UX for crypto permissioned apps. + +Most of GUI based current wallets don't allow to: +* being able to automatically and effortlessly use different keys / accounts for each apps, +* being able to sign some app's action without prompting the user with the same level of security as sending funds from their main accounts, +* being able to use throwable keys to improve anonymity, +* effortlessly signing transactions for an app without broadcasting these while still being able to perform other transaction signing as usual from their main accounts, +* All this while being fully restorable using the user's mnemonic or hardware wallet and the HD Path determined uniquely by the app's ens name. + +We try to overcome these limitations by introducing a new account's type, app keys, made to be used along side the existing main accounts. + +These new app keys can permit to give more power and flexibility to the crypto apps developers. This can allow to improve a lot the UX of crypto dapps and to create new designs that were not possible before leveraging the ability to create and handle many accounts, to presign messages and broadcast them later. These features were not compatible with the level of security we were requesting for main accounts that hold most of an user's funds. + + +## Specification + +### Applications + +An app is a website (or other) that would like to request from a wallet to access a cryptographic key specifically derived for this usage. It can be any form of cryptography/identity relying application, Ethereum based but not only. + +Once connected to a wallet, an application can request to access an account derived exclusively for that application using the following algorithm. + +### Private App Key generation algorithm + +We now propose an algorithm to generate application keys that: +- are uniquely defined, with respect to the account that the user selected to generate these keys, +- and thus can be isolated when changing the user account, allowing persona management (see next section), +- are specific to each application, +- can be fully restored from the user master seed mnemonic and the applications' names. + +#### Using different accounts as personas + +We allow the user to span a different set of application keys by changing the account selected to generate each key. Thus from the same master seed mnemonic, an user can use each of her account index to generate an alternative set of application keys. One can describe this as using different personas. +This would allow potentially an user to fully isolate her interaction with a given app across personas. One can use this for instance to create a personal and business profile for a given's domain both backup up from the same mnemonic, using 2 different accounts to generate these. The app or domain, will not be aware that it is the same person and mnemonic behind both. +If an application interacts with several main accounts of an user, one of these accounts, a master account can be used as persona and the others as auxiliary accounts. + +This EIP is agnostic about the way one generates the private keys used to span different app keys spaces. However for compatibility purposes and for clean disambiguation between personas and cryptocurrency accounts, a new EIP, distinct from this one but to be used alongside, will be proposed soon introducing clean persona generation and management. + +#### Applications' Unique Identifiers + +Each application is uniquely defined and authenticated by its origin, a domain string. It can be a Domain Name Service (DNS) name or, in the future, an Ethereum Name Service (ENS) name or IPFS hash. + +For Ipfs or swam origins, but we could probably use the ipfs or swarm addresses as origin or we could require those to be pointed at through an ENS entry and use the ENS address as origin, although this would mean that the content it refers to could change. It would thus allow for different security and updatibility models. + +We will probably require for protocol prefixes when using an ENS domain to point to an IPFS address: +`ens://ipfs.snap.eth` + + +#### Private App Key generation algorithm + +Using the domain name of an application, we generate a private key for each application (and per main account) : + +`const appKeyPrivKey = keccak256(privKey + originString)` + +where `+` is concatenation, `privKey` is the private key of the user's account selected to span the application key and `originString` represents the origin url from which the permission call to access the application key is originated from. + +This is exposed as an RPC method to allow any domain to request its own app key associated with the current requested account (if available): + +``` +const appKey = await provider.send({ + method: 'wallet_getAppKeyForAccount', + params: [address1] +}); +``` + +See here for an implementation: +https://github.com/MetaMask/eth-simple-keyring/blob/master/index.js#L169 + +#### App keys and Hierarchical Deterministic keys + +The app keys generated using the algorithm described in the previous section will not be BIP32 compliant. Therefore apps will not be able to create several app keys or use non-hardening and extended public keys techniques directly. They get a single private key (per origin, per persona). +Yet they can use this as initial entropy to span a new HD tree and generate addresses that can be either hardened or not. Thus we should not be losing use cases. + +## Rationale + +### Sharing application keys across domains: +While this does not explicit cover cases of sharing these app keys between pages on its own, this need can be met by composition: + +Since a domain would get a unique key per persona, and because domains can intercommunicate, one domain (app) could request another domain (signer) to perform its cryptographic operation on some data, with its appKey as a seed, potentially allowing new signing strategies to be added as easily as new websites. + +This could also pass it to domains that are loading specific signing strategies. This may sound dangerous at first, but if a domain represents a static hash of a trusted cryptographic function implementation, it could be as safe as calling any audited internal dependency. + +### Privacy and the funding trail + +If all an application needs to do with its keys is to sign messages and it does not require funding, then this EIP allows for privacy through the use of distinct keys for each application with a simple deterministic standard compatible across wallets. + +However if these application keys require funding, there can be trail and the use of app keys would not fully solve the privacy problem there. + +Mixers or anonymous ways of funding an ethereum address (ring signatures) along with this proposal would guarantee privacy. + +Even if privacy is not solved fully without this anonymous funding method, we still need a way to easily create and restore different accounts/addresses for each application + +## Backwards Compatibility +From a wallet point of view, there does not seem to be compatibility issues since these are separate accounts from those that were used previously by wallets and they are supposed to be used along-side in synergy. + +However, for applications that associated in some way their users to their main accounts may want to reflect on if and how they would like to leverage the power offered by `app keys` to migrate to them and leverage on the new app designs they permit. + +## Implementation + +Here is an early implementation of app keys for standard (non HW) MetaMask accounts. +https://github.com/MetaMask/eth-simple-keyring/blob/6d12bd9d73adcccbe0b0c7e32a99d279085e2934/index.js#L139-L152 + +See here for a fork of MetaMask that implements app keys along side plugins: +https://github.com/MetaMask/metamask-snaps-beta +https://github.com/MetaMask/metamask-snaps-beta/wiki/Plugin-API + +## Example use cases + +* signing transactions without broadcasting them +https://github.com/MetaMask/metamask-extension/issues/3475 + +* token contract +https://github.com/ethereum/EIPs/issues/85 + +* default account for dapps +https://ethereum-magicians.org/t/default-accounts-for-dapps/904 + +* non wallet/crypto accounts +[EIP1581: Non-wallet usage of keys derived from BIP32 trees](./eip-1581.md) + +* state channel application + +* privacy solution + +* non custodian cross cryptocurrency exchange... + +## Acknowledgements +MetaMask team, Christian Lundkvist, Counterfactual team, Liam Horne, Erik Bryn, Richard Moore, Jeff Coleman. + + +## References + +### HD and mnemonics +#### BIPs +* [BIP32: Hierarchical Deterministic Wallets:](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) + +* [BIP39: Mnemonic code for generating deterministic keys:](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki) + +* [SLIP44: Registered coin types for BIP44](https://github.com/satoshilabs/slips/blob/master/slip-0044.md) + + +#### Derivation path for eth +* [Issue 84](https://github.com/ethereum/EIPs/issues/84) + +* [Issue 85](https://github.com/ethereum/EIPs/issues/85) + +* [EIP600 Ethereum purpose allocation for Deterministic Wallets](./eip-600.md) + + +* [EIP601 Ethereum hierarchy for deterministic wallets](./eip-601.md) + + +### Previous proposals and discussions related to app keys +* [Meta: we should value privacy more](https://ethereum-magicians.org/t/meta-we-should-value-privacy-more/2475) + +* [EIP1102: Opt-in account exposure](./eip-1102.md) + +* [EIP1581: Non-wallet usage of keys derived from BIP-32 trees](./eip-1581.md) + +* [EIP1581: discussion](https://ethereum-magicians.org/t/non-wallet-usage-of-keys-derived-from-bip-32-trees/1817/4) + +* [SLIP13: Authentication using deterministic hierarchy](https://github.com/satoshilabs/slips/blob/master/slip-0013.md) + + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1803.md b/EIPS/eip-1803.md new file mode 100644 index 0000000..2d0e50c --- /dev/null +++ b/EIPS/eip-1803.md @@ -0,0 +1,42 @@ +--- +eip: 1803 +title: Rename opcodes for clarity +author: Alex Beregszaszi (@axic) +discussions-to: https://ethereum-magicians.org/t/eip-1803-rename-opcodes-for-clarity/3345 +type: Standards Track +category: Interface +status: Stagnant +created: 2017-07-28 +requires: 141 +--- + +## Abstract + +Rename the `BALANCE`, `SHA3`, `NUMBER`, `GASLIMIT`, `GAS` and `INVALID` opcodes to reflect their true meaning. + +## Specification + +Rename the opcodes as follows: +- `BALANCE` (`0x31`) to `EXTBALANCE` to be in line with `EXTCODESIZE`, `EXTCODECOPY` and `EXTCODEHASH` +- `SHA3` (`0x20`) to `KECCAK256` +- `NUMBER` (`0x43`) to `BLOCKNUMBER` +- `GASLIMIT` (`0x45`) to `BLOCKGASLIMIT` to avoid confusion with the gas limit of the transaction +- `GAS` (`0x5a`) to `GASLEFT` to be clear what it refers to +- `INVALID` (`0xfe`) to `ABORT` to clearly articulate when someone refers this opcode as opposed to "any invalid opcode" + +## Backwards Compatibility + +This has no effect on any code. It can influence what mnemonics assemblers will use. + +## Implementation + +Not applicable. + +## References + +[EIP-6](./eip-6.md) previously renamed `SUICIDE` (`0xff`) to `SELFDESTRUCT`. +Renaming `SHA3` was previously proposed by [EIP-59](https://github.com/ethereum/EIPs/issues/59). + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-181.md b/EIPS/eip-181.md new file mode 100644 index 0000000..3c7a231 --- /dev/null +++ b/EIPS/eip-181.md @@ -0,0 +1,209 @@ +--- +eip: 181 +title: ENS support for reverse resolution of Ethereum addresses +author: Nick Johnson +status: Final +type: Standards Track +category: ERC +created: 2016-12-01 +--- + +# Abstract +This EIP specifies a TLD, registrar, and resolver interface for reverse resolution of Ethereum addresses using ENS. This permits associating a human-readable name with any Ethereum blockchain address. Resolvers can be certain that the reverse record was published by the owner of the Ethereum address in question. + +# Motivation +While name services are mostly used for forward resolution - going from human-readable identifiers to machine-readable ones - there are many use-cases in which reverse resolution is useful as well: + + - Applications that allow users to monitor accounts benefit from showing the name of an account instead of its address, even if it was originally added by address. + - Attaching metadata such as descriptive information to an address allows retrieving this information regardless of how the address was originally discovered. + - Anyone can configure a name to resolve to an address, regardless of ownership of that address. Reverse records allow the owner of an address to claim a name as authoritative for that address. + +# Specification +Reverse ENS records are stored in the ENS hierarchy in the same fashion as regular records, under a reserved domain, `addr.reverse`. To generate the ENS name for a given account's reverse records, convert the account to hexadecimal representation in lower-case, and append `addr.reverse`. For instance, the ENS registry's address at `0x112234455c3a32fd11230c42e7bccd4a84e02010` has any reverse records stored at `112234455c3a32fd11230c42e7bccd4a84e02010.addr.reverse`. + +Note that this means that contracts wanting to do dynamic reverse resolution of addresses will need to perform hex encoding in the contract. + +## Registrar +The owner of the `addr.reverse` domain will be a registrar that permits the caller to take ownership of +the reverse record for their own address. It provides the following methods: + +### function claim(address owner) returns (bytes32 node) + +When called by account `x`, instructs the ENS registry to transfer ownership of the name `hex(x) + '.addr.reverse'` to the provided address, and return the namehash of the ENS record thus transferred. + +Allowing the caller to specify an owner other than themselves for the relevant node facilitates contracts that need accurate reverse ENS entries delegating this to their creators with a minimum of code inside their constructor: + + reverseRegistrar.claim(msg.sender) + +### function claimWithResolver(address owner, address resolver) returns (bytes32 node) + +When called by account `x`, instructs the ENS registry to set the resolver of the name `hex(x) + '.addr.reverse'` to the specified resolver, then transfer ownership of the name to the provided address, and return the namehash of the ENS record thus transferred. This method facilitates setting up a custom resolver and owner in fewer transactions than would be required if calling `claim`. + +### function setName(string name) returns (bytes32 node) + +When called by account `x`, sets the resolver for the name `hex(x) + '.addr.reverse'` to a default resolver, and sets the name record on that name to the specified name. This method facilitates setting up simple reverse records for users in a single transaction. + +## Resolver interface +A new resolver interface is defined, consisting of the following method: + + function name(bytes32 node) constant returns (string); + +Resolvers that implement this interface must return a valid ENS name for the requested node, or the empty string if no name is defined for the requested node. + +The interface ID of this interface is 0x691f3431. + +Future EIPs may specify more record types appropriate to reverse ENS records. + +# Appendix 1: Registrar implementation + +This registrar, written in Solidity, implements the specifications outlined above. + + pragma solidity ^0.4.10; + + import "./AbstractENS.sol"; + + contract Resolver { + function setName(bytes32 node, string name) public; + } + + /** + * @dev Provides a default implementation of a resolver for reverse records, + * which permits only the owner to update it. + */ + contract DefaultReverseResolver is Resolver { + AbstractENS public ens; + mapping(bytes32=>string) public name; + + /** + * @dev Constructor + * @param ensAddr The address of the ENS registry. + */ + function DefaultReverseResolver(AbstractENS ensAddr) { + ens = ensAddr; + } + + /** + * @dev Only permits calls by the reverse registrar. + * @param node The node permission is required for. + */ + modifier owner_only(bytes32 node) { + require(msg.sender == ens.owner(node)); + _; + } + + /** + * @dev Sets the name for a node. + * @param node The node to update. + * @param _name The name to set. + */ + function setName(bytes32 node, string _name) public owner_only(node) { + name[node] = _name; + } + } + + contract ReverseRegistrar { + // namehash('addr.reverse') + bytes32 constant ADDR_REVERSE_NODE = 0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2; + + AbstractENS public ens; + Resolver public defaultResolver; + + /** + * @dev Constructor + * @param ensAddr The address of the ENS registry. + * @param resolverAddr The address of the default reverse resolver. + */ + function ReverseRegistrar(AbstractENS ensAddr, Resolver resolverAddr) { + ens = ensAddr; + defaultResolver = resolverAddr; + } + + /** + * @dev Transfers ownership of the reverse ENS record associated with the + * calling account. + * @param owner The address to set as the owner of the reverse record in ENS. + * @return The ENS node hash of the reverse record. + */ + function claim(address owner) returns (bytes32 node) { + return claimWithResolver(owner, 0); + } + + /** + * @dev Transfers ownership of the reverse ENS record associated with the + * calling account. + * @param owner The address to set as the owner of the reverse record in ENS. + * @param resolver The address of the resolver to set; 0 to leave unchanged. + * @return The ENS node hash of the reverse record. + */ + function claimWithResolver(address owner, address resolver) returns (bytes32 node) { + var label = sha3HexAddress(msg.sender); + node = sha3(ADDR_REVERSE_NODE, label); + var currentOwner = ens.owner(node); + + // Update the resolver if required + if(resolver != 0 && resolver != ens.resolver(node)) { + // Transfer the name to us first if it's not already + if(currentOwner != address(this)) { + ens.setSubnodeOwner(ADDR_REVERSE_NODE, label, this); + currentOwner = address(this); + } + ens.setResolver(node, resolver); + } + + // Update the owner if required + if(currentOwner != owner) { + ens.setSubnodeOwner(ADDR_REVERSE_NODE, label, owner); + } + + return node; + } + + /** + * @dev Sets the `name()` record for the reverse ENS record associated with + * the calling account. First updates the resolver to the default reverse + * resolver if necessary. + * @param name The name to set for this address. + * @return The ENS node hash of the reverse record. + */ + function setName(string name) returns (bytes32 node) { + node = claimWithResolver(this, defaultResolver); + defaultResolver.setName(node, name); + return node; + } + + /** + * @dev Returns the node hash for a given account's reverse records. + * @param addr The address to hash + * @return The ENS node hash. + */ + function node(address addr) constant returns (bytes32 ret) { + return sha3(ADDR_REVERSE_NODE, sha3HexAddress(addr)); + } + + /** + * @dev An optimised function to compute the sha3 of the lower-case + * hexadecimal representation of an Ethereum address. + * @param addr The address to hash + * @return The SHA3 hash of the lower-case hexadecimal encoding of the + * input address. + */ + function sha3HexAddress(address addr) private returns (bytes32 ret) { + addr; ret; // Stop warning us about unused variables + assembly { + let lookup := 0x3031323334353637383961626364656600000000000000000000000000000000 + let i := 40 + loop: + i := sub(i, 1) + mstore8(i, byte(and(addr, 0xf), lookup)) + addr := div(addr, 0x10) + i := sub(i, 1) + mstore8(i, byte(and(addr, 0xf), lookup)) + addr := div(addr, 0x10) + jumpi(loop, i) + ret := sha3(0, 40) + } + } + } + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1812.md b/EIPS/eip-1812.md new file mode 100644 index 0000000..8ff5f07 --- /dev/null +++ b/EIPS/eip-1812.md @@ -0,0 +1,442 @@ +--- +eip: 1812 +title: Ethereum Verifiable Claims +author: Pelle Braendgaard (@pelle) +discussions-to: https://ethereum-magicians.org/t/erc-1812-ethereum-verifiable-claims/2814 +status: Stagnant +type: Standards Track +category: ERC +created: 2019-03-03 +requires: 712 +--- + +# Ethereum Verifiable Claims + +## Simple Summary + +Reusable Verifiable Claims using [EIP 712 Signed Typed Data](./eip-712.md). + +## Abstract +A new method for Off-Chain Verifiable Claims built on [EIP-712](./eip-712.md). These Claims can be issued by any user with a EIP 712 compatible web3 provider. Claims can be stored off chain and verified on-chain by Solidity Smart Contracts, State Channel Implementations or off-chain libraries. + +## Motivation +Reusable Off-Chain Verifiable Claims provide an important piece of integrating smart contracts with real world organizational requirements such as meeting regulatory requirements such as KYC, GDPR, Accredited Investor rules etc. + +[ERC-735](https://github.com/ethereum/EIPs/issues/735) and [ERC-780](https://github.com/ethereum/EIPs/issues/780) provide methods of making claims that live on chain. This is useful for some particular use cases, where some claim about an address must be verified on chain. + +In most cases though it is both dangerous and in some cases illegal (according to EU GDPR rules for example) to record Identity Claims containing Personal Identifying Information (PII) on an immutable public database such as the Ethereum blockchain. + +The W3C [Verifiable Claims Data Model and Representations](https://www.w3.org/TR/verifiable-claims-data-model/) as well as uPorts [Verification Message Spec](https://developer.uport.me/messages/verification) are proposed off-chain solutions. + +While built on industry standards such as [JSON-LD](https://json-ld.org) and [JWT](https://jwt.io) neither of them are easy to integrate with the Ethereum ecosystem. + +[EIP-712](./eip-712.md) introduces a new method of signing off chain Identity data. This provides both a data format based on Solidity ABI encoding that can easily be parsed on-chain an a new JSON-RPC call that is easily supported by existing Ethereum wallets and Web3 clients. + +This format allows reusable off-chain Verifiable Claims to be cheaply issued to users, who can present them when needed. + +## Prior Art +Verified Identity Claims such as those proposed by [uPort](https://developer.uport.me/messages/verification) and [W3C Verifiable Claims Working Group](https://www.w3.org/2017/vc/WG/) form an important part of building up reusable identity claims. + +[ERC-735](https://github.com/ethereum/EIPs/issues/735) and [ERC-780](https://github.com/ethereum/EIPs/issues/780) provide on-chain storage and lookups of Verifiable Claims. + +## Specification +### Claims +Claims can be generalized like this: + +> Issuer makes the claim that Subject is something or has some attribute and value. + +Claims should be deterministic, in that the same claim signed multiple times by the same signer. + +### Claims data structure +Each claim should be typed based on its specific use case, which EIP 712 lets us do effortlessly. But there are 3 minimal attributes required of the claims structure. + +* `subject` the subject of the claim as an `address` (who the claim is about) +* `validFrom` the time in seconds encoded as a `uint256` of start of validity of claim. In most cases this would be the time of issuance, but some claims may be valid in the future or past. +* `validTo` the time in seconds encoded as a `uint256` of when the validity of the claim expires. If you intend for the claim not to expire use `0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff`. + +The basic minimal claim data structure as a Solidity struct: + +```solidity +struct [CLAIM TYPE] { + address subject; + uint256 validFrom; + uint256 validTo; +} +``` + +The CLAIM TYPE is the actual name of the claim. While not required, in most cases use the taxonomy developed by [schema.org](https://schema.org/docs/full.html) which is also commonly used in other Verifiable Claims formats. + +Example claim that issuer knows a subject: + +```solidity +struct Know { + address subject; + uint256 validFrom; + uint256 validTo; +} +``` + +### Presenting a Verifiable Claim +#### Verifying Contract +When defining Verifiable Claims formats a Verifying Contract should be created with a public `verify()` view function. This makes it very easy for other smart contracts to verify a claim correctly. + +It also provides a convenient interface for web3 and state channel apps to verify claims securely. + +```solidity +function verifyIssuer(Know memory claim, uint8 v, bytes32 r, bytes32 s) public returns (address) { + bytes32 digest = keccak256( + abi.encodePacked( + "\x19\x01", + DOMAIN_SEPARATOR, + hash(claim) + ) + ); + require( + (claim.validFrom >= block.timestamp) && (block.timestamp < claim.validTo) +, "invalid issuance timestamps"); + return ecrecover(digest, v, r, s); +} +``` + +#### Calling a SmartContract function +Verifiable Claims can be presented to a solidity function call as it’s struct together with the `v`, `r` and `s` signature components. + +```solidity +function vouch(Know memory claim, uint8 v, bytes32 r, bytes32 s) public returns (bool) { + address issuer = verifier.verifyIssuer(claim, v, r, s); + require(issuer !== '0x0'); + knows[issuer][claim.subject] = block.number; + return true; +} +``` + +#### Embedding a Verifiable Claim in another Signed Typed Data structure +The Claim struct should be embedded in another struct together with the `v`, `r` and `s` signature parameters. + +```solidity +struct Know { + address subject; + uint256 validFrom; + uint256 validTo; +} + +struct VerifiableReference { + Know delegate; + uint8 v; + bytes32 r; + bytes32 s; +} + +struct Introduction { + address recipient; + VerifiableReference issuer; +} +``` + +Each Verifiable Claim should be individually verified together with the parent Signed Typed Data structure. + +Verifiable Claims issued to different EIP 712 Domains can be embedded within each other. + +#### State Channels +This proposal will not show how to use Eth Verifiable Claims as part of a specific State Channel method. + +Any State Channel based on EIP712 should be able to include the embeddable Verifiable Claims as part of its protocol. This could be useful for exchanging private Identity Claims between the parties for regulatory reasons, while ultimately not posting them to the blockchain on conclusion of a channel. + +### Key Delegation +In most simple cases the issuer of a Claim is the signer of the data. There are cases however where signing should be delegated to an intermediary key. + +KeyDelegation can be used to implement off chain signing for smart contract based addresses, server side key rotation as well as employee permissions in complex business use cases. + +#### ERC1056 Signing Delegation + +[ERC-1056](./eip-1056.md) provides a method for addresses to assign delegate signers. One of the primary use cases for this is that a smart contract can allow a key pair to sign on its behalf for a certain period. It also allows server based issuance tools to institute key rotation. + +To support this an additional `issuer` attribute can be added to the Claim Type struct. In this case the verification code should lookup the EthereumDIDRegistry to see if the signer of the data is an allowed signing delegate for the `issuer` + +The following is the minimal struct for a Claim containing an issuer: + +```solidity +struct [CLAIM TYPE] { + address subject; + address issuer; + uint256 validFrom; + uint256 validTo; +} +``` + +If the `issuer` is specified in the struct In addition to performing the standard ERC712 verification the verification code MUST also verify that the signing address is a valid `veriKey` delegate for the address specified in the issuer. + +```solidity +registry.validDelegate(issuer, 'veriKey', recoveredAddress) +``` + + +#### Embedded Delegation Proof +There may be applications, in particularly where organizations want to allow delegates to issue claims about specific domains and types. + +For this purpose instead of the `issuer` we allow a special claim to be embedded following this same format: + +```solidity +struct Delegate { + address issuer; + address subject; + uint256 validFrom; + uint256 validTo; +} + +struct VerifiableDelegate { + Delegate delegate; + uint8 v; + bytes32 r; + bytes32 s; +} + + +struct [CLAIM TYPE] { + address subject; + VerifiedDelegate issuer; + uint256 validFrom; + uint256 validTo; +} +``` + +Delegates should be created for specific EIP 712 Domains and not be reused across Domains. + +Implementers of new EIP 712 Domains can add further data to the `Delegate` struct to allow finer grained application specific rules to it. + +### Claim Types +#### Binary Claims +A Binary claim is something that doesn’t have a particular value. It either is issued or not. + +Examples: +* subject is a Person +* subject is my owner (eg. Linking an ethereum account to an owner identity) + +Example: + +```solidity +struct Person { + address issuer; + address subject; + uint256 validFrom; + uint256 validTo; +} +``` + +This is exactly the same as the minimal claim above with the CLAIM TYPE set to [Person](https://schema.org/Person). + +### Value Claims +Value claims can be used to make a claim about the subject containing a specific readable value. + +**WARNING**: Be very careful about using Value Claims as part of Smart Contract transactions. Identity Claims containing values could be a GDPR violation for the business or developer encouraging a user to post it to a public blockchain. + +Examples: +* subject’s name is Alice +* subjects average account balance is 1234555 + +Each value should use the `value` field to indicate the value. + +A Name Claim + +```solidity +struct Name { + address issuer; + address subject; + string name; + uint256 validFrom; + uint256 validTo; +} +``` + +Average Balance + +```solidity +struct AverageBalance { + address issuer; + address subject; + uint256 value; + uint256 validFrom; + uint256 validTo; +} +``` + +### Hashed Claims +Hashed claims can be used to make a claim about the subject containing the hash of a claim value. Hashes should use ethereum standard `keccak256` hashing function. + +**WARNING**: Be very careful about using Hashed Claims as part of Smart Contract transactions. Identity Claims containing hashes of known values could be a GDPR violation for the business or developer encouraging a user to post it to a public blockchain. + +Examples: +- [ ] hash of subject’s name is `keccak256(“Alice Torres”)` +- [ ] hash of subject’s email is `keccak256(“alice@example.com”)` + +Each value should use the `keccak256 ` field to indicate the hashed value. Question. The choice of using this name is that we can easily add support for future algorithms as well as maybe zkSnark proofs. + +A Name Claim + +```solidity +struct Name { + address issuer; + address subject; + bytes32 keccak256; + uint256 validFrom; + uint256 validTo; +} +``` + +Email Claim + +```solidity +struct Email { + address issuer; + address subject; + bytes32 keccak256; + uint256 validFrom; + uint256 validTo; +} +``` + +### EIP 712 Domain +The EIP 712 Domain specifies what kind of message that is to be signed and is used to differentiate between signed data types. The content MUST contain the following: + +```solidity +{ + name: "EIP1???Claim", + version: 1, + chainId: 1, // for mainnet + verifyingContract: 0x // TBD + salt: ... +} +``` + +#### Full Combined format for EIP 712 signing: + +Following the EIP 712 standard we can combine the Claim Type with the EIP 712 Domain and the claim itself (in the `message`) attribute. + +Eg: +```solidity + { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Email": [ + { + "name": "subject", + "type": "address" + }, + { + "name": "keccak256", + "type": "bytes32" + }, + { + "name": "validFrom", + "type": "uint256" + }, + { + "name": "validTo", + "type": "uint256" + } + ] + }, + "primaryType": "Email", + "domain": { + "name": "EIP1??? Claim", + "version": "1", + "chainId": 1, + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" + }, + "message": { + "subject": "0x5792e817336f41de1d8f54feab4bc200624a1d9d", + "value": "9c8465d9ae0b0bc167dee7f62880034f59313100a638dcc86a901956ea52e280", + "validFrom": "0x0000000000000000000000000000000000000000000000000001644b74c2a0", + "validTo": "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + } + } +``` + + +### Revocation +Both Issuers and Subjects should be allowed to revoke Verifiable Claims. Revocations can be handled through a simple on-chain registry. + +The ultimate rules of who should be able to revoke a claim is determined by the Verifying contract. + +The `digest` used for revocation is the EIP712 Signed Typed Data digest. + +```solidity +contract RevocationRegistry { + mapping (bytes32 => mapping (address => uint)) public revocations; + + function revoke(bytes32 digest) public returns (bool) { + revocations[digest][msg.sender] = block.number; + return true; + } + + function revoked(address party, bytes32 digest) public view returns (bool) { + return revocations[digest][party] > 0; + } +} +``` + +A verifying contract can query the Revocation Registry as such: + +```solidity +bytes32 digest = keccak256( + abi.encodePacked( + "\x19\x01", + DOMAIN_SEPARATOR, + hash(claim) + ) +); +require(valid(claim.validFrom, claim.validTo), "invalid issuance timestamps"); +address issuer = ecrecover(digest, v, r, s); +require(!revocations.revoked(issuer, digest), "claim was revoked by issuer"); +require(!revocations.revoked(claim.subject, digest), "claim was revoked by subject"); +``` + +### Creation of Verifiable Claims Domains + +Creating specific is Verifiable Claims Domains is out of the scope of this EIP. The Example Code has a few examples. + +EIP’s or another process could be used to standardize specific important Domains that are universally useful across the Ethereum world. + +## Rationale +Signed Typed Data provides a strong foundation for Verifiable Claims that can be used in many different kinds of applications built on both Layer 1 and Layer 2 of Ethereum. + +### Rationale for using not using a single EIP 712 Domain +EIP712 supports complex types and domains in itself, that we believe are perfect building blocks for building Verifiable Claims for specific purposes. + +The Type and Domain of a Claim is itself an important part of a claim and ensures that Verifiable Claims are used for the specific purposes required and not misused. + +EIP712 Domains also allow rapid experimentation, allowing taxonomies to be built up by the community. + +## Test Cases +There is a repo with a few example verifiers and consuming smart contracts written in Solidity: + +**Example Verifiers** +* [Verifier for very simple IdVerification Verifiable Claims containing minimal Personal Data](https://github.com/uport-project/eip712-claims-experiments/blob/master/contracts/IdentityClaimsVerifier.sol) +* [Verifier for OwnershipProofs signed by a users wallet](https://github.com/uport-project/eip712-claims-experiments/blob/master/contracts/OwnershipProofVerifier.sol) + +**Example Smart Contracts** +* [KYCCoin.sol](https://github.com/uport-project/eip712-claims-experiments/blob/master/contracts/KYCCoin.sol) - Example Token allows reusable IdVerification claims issued by trusted verifiers and users to whitelist their own addresses using OwnershipProofs +* [ConsortiumAgreement.sol](https://github.com/uport-project/eip712-claims-experiments/blob/master/contracts/ConsortiumAgreements.sol) - Example Consortium Agreement smart contract. Consortium Members can issue Delegated Claims to employees or servers to interact on their behalf. + +**Shared Registries** +* [RevocationRegistry.sol](https://github.com/uport-project/eip712-claims-experiments/blob/master/contracts/RevocationRegistry.sol) + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1820.md b/EIPS/eip-1820.md new file mode 100644 index 0000000..67db306 --- /dev/null +++ b/EIPS/eip-1820.md @@ -0,0 +1,926 @@ +--- +eip: 1820 +title: Pseudo-introspection Registry Contract +author: Jordi Baylina , Jacques Dafflon +discussions-to: https://github.com/ethereum/EIPs/pull/1820 +status: Final +type: Standards Track +category: ERC +requires: 165, 214 +created: 2019-03-04 +--- + +> :information_source: **[ERC-1820] has superseded [ERC-820].** :information_source: +> [ERC-1820] fixes the incompatibility in the [ERC-165] logic which was introduced by the Solidity 0.5 update. +> Have a look at the [official announcement][erc1820-annoucement], and the comments about the [bug][erc820-bug] and the [fix][erc820-fix]. +> Apart from this fix, [ERC-1820] is functionally equivalent to [ERC-820]. +> +> :warning: [ERC-1820] MUST be used in lieu of [ERC-820]. :warning: + +## Simple Summary + +This standard defines a universal registry smart contract where any address (contract or regular account) can register which interface it supports and which smart contract is responsible for its implementation. + +This standard keeps backward compatibility with [ERC-165]. + +## Abstract + +This standard defines a registry where smart contracts and regular accounts can publish which functionality they implement---either directly or through a proxy contract. + +Anyone can query this registry to ask if a specific address implements a given interface and which smart contract handles its implementation. + +This registry MAY be deployed on any chain and shares the same address on all chains. + +Interfaces with zeroes (`0`) as the last 28 bytes are considered [ERC-165] interfaces, +and this registry SHALL forward the call to the contract to see if it implements the interface. + +This contract also acts as an [ERC-165] cache to reduce gas consumption. + +## Motivation + +There have been different approaches to define pseudo-introspection in Ethereum. +The first is [ERC-165] which has the limitation that it cannot be used by regular accounts. +The second attempt is [ERC-672] which uses reverse [ENS]. Using reverse [ENS] has two issues. +First, it is unnecessarily complicated, and second, [ENS] is still a centralized contract controlled by a multisig. +This multisig theoretically would be able to modify the system. + +This standard is much simpler than [ERC-672], and it is *fully* decentralized. + +This standard also provides a *unique* address for all chains. +Thus solving the problem of resolving the correct registry address for different chains. + +## Specification + +### [ERC-1820] Registry Smart Contract + +> This is an exact copy of the code of the [ERC1820 registry smart contract]. + +``` solidity +/* ERC1820 Pseudo-introspection Registry Contract + * This standard defines a universal registry smart contract where any address (contract or regular account) can + * register which interface it supports and which smart contract is responsible for its implementation. + * + * Written in 2019 by Jordi Baylina and Jacques Dafflon + * + * To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to + * this software to the public domain worldwide. This software is distributed without any warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication along with this software. If not, see + * . + * + * ███████╗██████╗ ██████╗ ██╗ █████╗ ██████╗ ██████╗ + * ██╔════╝██╔══██╗██╔════╝███║██╔══██╗╚════██╗██╔═████╗ + * █████╗ ██████╔╝██║ ╚██║╚█████╔╝ █████╔╝██║██╔██║ + * ██╔══╝ ██╔══██╗██║ ██║██╔══██╗██╔═══╝ ████╔╝██║ + * ███████╗██║ ██║╚██████╗ ██║╚█████╔╝███████╗╚██████╔╝ + * ╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚════╝ ╚══════╝ ╚═════╝ + * + * ██████╗ ███████╗ ██████╗ ██╗███████╗████████╗██████╗ ██╗ ██╗ + * ██╔══██╗██╔════╝██╔════╝ ██║██╔════╝╚══██╔══╝██╔══██╗╚██╗ ██╔╝ + * ██████╔╝█████╗ ██║ ███╗██║███████╗ ██║ ██████╔╝ ╚████╔╝ + * ██╔══██╗██╔══╝ ██║ ██║██║╚════██║ ██║ ██╔══██╗ ╚██╔╝ + * ██║ ██║███████╗╚██████╔╝██║███████║ ██║ ██║ ██║ ██║ + * ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ + * + */ +pragma solidity 0.5.3; +// IV is value needed to have a vanity address starting with '0x1820'. +// IV: 53759 + +/// @dev The interface a contract MUST implement if it is the implementer of +/// some (other) interface for any address other than itself. +interface ERC1820ImplementerInterface { + /// @notice Indicates whether the contract implements the interface 'interfaceHash' for the address 'addr' or not. + /// @param interfaceHash keccak256 hash of the name of the interface + /// @param addr Address for which the contract will implement the interface + /// @return ERC1820_ACCEPT_MAGIC only if the contract implements 'interfaceHash' for the address 'addr'. + function canImplementInterfaceForAddress(bytes32 interfaceHash, address addr) external view returns(bytes32); +} + + +/// @title ERC1820 Pseudo-introspection Registry Contract +/// @author Jordi Baylina and Jacques Dafflon +/// @notice This contract is the official implementation of the ERC1820 Registry. +/// @notice For more details, see https://eips.ethereum.org/EIPS/eip-1820 +contract ERC1820Registry { + /// @notice ERC165 Invalid ID. + bytes4 constant internal INVALID_ID = 0xffffffff; + /// @notice Method ID for the ERC165 supportsInterface method (= `bytes4(keccak256('supportsInterface(bytes4)'))`). + bytes4 constant internal ERC165ID = 0x01ffc9a7; + /// @notice Magic value which is returned if a contract implements an interface on behalf of some other address. + bytes32 constant internal ERC1820_ACCEPT_MAGIC = keccak256(abi.encodePacked("ERC1820_ACCEPT_MAGIC")); + + /// @notice mapping from addresses and interface hashes to their implementers. + mapping(address => mapping(bytes32 => address)) internal interfaces; + /// @notice mapping from addresses to their manager. + mapping(address => address) internal managers; + /// @notice flag for each address and erc165 interface to indicate if it is cached. + mapping(address => mapping(bytes4 => bool)) internal erc165Cached; + + /// @notice Indicates a contract is the 'implementer' of 'interfaceHash' for 'addr'. + event InterfaceImplementerSet(address indexed addr, bytes32 indexed interfaceHash, address indexed implementer); + /// @notice Indicates 'newManager' is the address of the new manager for 'addr'. + event ManagerChanged(address indexed addr, address indexed newManager); + + /// @notice Query if an address implements an interface and through which contract. + /// @param _addr Address being queried for the implementer of an interface. + /// (If '_addr' is the zero address then 'msg.sender' is assumed.) + /// @param _interfaceHash Keccak256 hash of the name of the interface as a string. + /// E.g., 'web3.utils.keccak256("ERC777TokensRecipient")' for the 'ERC777TokensRecipient' interface. + /// @return The address of the contract which implements the interface '_interfaceHash' for '_addr' + /// or '0' if '_addr' did not register an implementer for this interface. + function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) external view returns (address) { + address addr = _addr == address(0) ? msg.sender : _addr; + if (isERC165Interface(_interfaceHash)) { + bytes4 erc165InterfaceHash = bytes4(_interfaceHash); + return implementsERC165Interface(addr, erc165InterfaceHash) ? addr : address(0); + } + return interfaces[addr][_interfaceHash]; + } + + /// @notice Sets the contract which implements a specific interface for an address. + /// Only the manager defined for that address can set it. + /// (Each address is the manager for itself until it sets a new manager.) + /// @param _addr Address for which to set the interface. + /// (If '_addr' is the zero address then 'msg.sender' is assumed.) + /// @param _interfaceHash Keccak256 hash of the name of the interface as a string. + /// E.g., 'web3.utils.keccak256("ERC777TokensRecipient")' for the 'ERC777TokensRecipient' interface. + /// @param _implementer Contract address implementing '_interfaceHash' for '_addr'. + function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external { + address addr = _addr == address(0) ? msg.sender : _addr; + require(getManager(addr) == msg.sender, "Not the manager"); + + require(!isERC165Interface(_interfaceHash), "Must not be an ERC165 hash"); + if (_implementer != address(0) && _implementer != msg.sender) { + require( + ERC1820ImplementerInterface(_implementer) + .canImplementInterfaceForAddress(_interfaceHash, addr) == ERC1820_ACCEPT_MAGIC, + "Does not implement the interface" + ); + } + interfaces[addr][_interfaceHash] = _implementer; + emit InterfaceImplementerSet(addr, _interfaceHash, _implementer); + } + + /// @notice Sets '_newManager' as manager for '_addr'. + /// The new manager will be able to call 'setInterfaceImplementer' for '_addr'. + /// @param _addr Address for which to set the new manager. + /// @param _newManager Address of the new manager for 'addr'. (Pass '0x0' to reset the manager to '_addr'.) + function setManager(address _addr, address _newManager) external { + require(getManager(_addr) == msg.sender, "Not the manager"); + managers[_addr] = _newManager == _addr ? address(0) : _newManager; + emit ManagerChanged(_addr, _newManager); + } + + /// @notice Get the manager of an address. + /// @param _addr Address for which to return the manager. + /// @return Address of the manager for a given address. + function getManager(address _addr) public view returns(address) { + // By default the manager of an address is the same address + if (managers[_addr] == address(0)) { + return _addr; + } else { + return managers[_addr]; + } + } + + /// @notice Compute the keccak256 hash of an interface given its name. + /// @param _interfaceName Name of the interface. + /// @return The keccak256 hash of an interface name. + function interfaceHash(string calldata _interfaceName) external pure returns(bytes32) { + return keccak256(abi.encodePacked(_interfaceName)); + } + + /* --- ERC165 Related Functions --- */ + /* --- Developed in collaboration with William Entriken. --- */ + + /// @notice Updates the cache with whether the contract implements an ERC165 interface or not. + /// @param _contract Address of the contract for which to update the cache. + /// @param _interfaceId ERC165 interface for which to update the cache. + function updateERC165Cache(address _contract, bytes4 _interfaceId) external { + interfaces[_contract][_interfaceId] = implementsERC165InterfaceNoCache( + _contract, _interfaceId) ? _contract : address(0); + erc165Cached[_contract][_interfaceId] = true; + } + + /// @notice Checks whether a contract implements an ERC165 interface or not. + // If the result is not cached a direct lookup on the contract address is performed. + // If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling + // 'updateERC165Cache' with the contract address. + /// @param _contract Address of the contract to check. + /// @param _interfaceId ERC165 interface to check. + /// @return True if '_contract' implements '_interfaceId', false otherwise. + function implementsERC165Interface(address _contract, bytes4 _interfaceId) public view returns (bool) { + if (!erc165Cached[_contract][_interfaceId]) { + return implementsERC165InterfaceNoCache(_contract, _interfaceId); + } + return interfaces[_contract][_interfaceId] == _contract; + } + + /// @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache. + /// @param _contract Address of the contract to check. + /// @param _interfaceId ERC165 interface to check. + /// @return True if '_contract' implements '_interfaceId', false otherwise. + function implementsERC165InterfaceNoCache(address _contract, bytes4 _interfaceId) public view returns (bool) { + uint256 success; + uint256 result; + + (success, result) = noThrowCall(_contract, ERC165ID); + if (success == 0 || result == 0) { + return false; + } + + (success, result) = noThrowCall(_contract, INVALID_ID); + if (success == 0 || result != 0) { + return false; + } + + (success, result) = noThrowCall(_contract, _interfaceId); + if (success == 1 && result == 1) { + return true; + } + return false; + } + + /// @notice Checks whether the hash is a ERC165 interface (ending with 28 zeroes) or not. + /// @param _interfaceHash The hash to check. + /// @return True if '_interfaceHash' is an ERC165 interface (ending with 28 zeroes), false otherwise. + function isERC165Interface(bytes32 _interfaceHash) internal pure returns (bool) { + return _interfaceHash & 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0; + } + + /// @dev Make a call on a contract without throwing if the function does not exist. + function noThrowCall(address _contract, bytes4 _interfaceId) + internal view returns (uint256 success, uint256 result) + { + bytes4 erc165ID = ERC165ID; + + assembly { + let x := mload(0x40) // Find empty storage location using "free memory pointer" + mstore(x, erc165ID) // Place signature at beginning of empty storage + mstore(add(x, 0x04), _interfaceId) // Place first argument directly next to signature + + success := staticcall( + 30000, // 30k gas + _contract, // To addr + x, // Inputs are stored at location x + 0x24, // Inputs are 36 (4 + 32) bytes long + x, // Store output over input (saves space) + 0x20 // Outputs are 32 bytes long + ) + + result := mload(x) // Load the result + } + } +} + +``` + +### Deployment Transaction + +Below is the raw transaction which MUST be used to deploy the smart contract on any chain. + +``` +0xf90a388085174876e800830c35008080b909e5608060405234801561001057600080fd5b506109c5806100206000396000f3fe608060405234801561001057600080fd5b50600436106100a5576000357c010000000000000000000000000000000000000000000000000000000090048063a41e7d5111610078578063a41e7d51146101d4578063aabbb8ca1461020a578063b705676514610236578063f712f3e814610280576100a5565b806329965a1d146100aa5780633d584063146100e25780635df8122f1461012457806365ba36c114610152575b600080fd5b6100e0600480360360608110156100c057600080fd5b50600160a060020a038135811691602081013591604090910135166102b6565b005b610108600480360360208110156100f857600080fd5b5035600160a060020a0316610570565b60408051600160a060020a039092168252519081900360200190f35b6100e06004803603604081101561013a57600080fd5b50600160a060020a03813581169160200135166105bc565b6101c26004803603602081101561016857600080fd5b81019060208101813564010000000081111561018357600080fd5b82018360208201111561019557600080fd5b803590602001918460018302840111640100000000831117156101b757600080fd5b5090925090506106b3565b60408051918252519081900360200190f35b6100e0600480360360408110156101ea57600080fd5b508035600160a060020a03169060200135600160e060020a0319166106ee565b6101086004803603604081101561022057600080fd5b50600160a060020a038135169060200135610778565b61026c6004803603604081101561024c57600080fd5b508035600160a060020a03169060200135600160e060020a0319166107ef565b604080519115158252519081900360200190f35b61026c6004803603604081101561029657600080fd5b508035600160a060020a03169060200135600160e060020a0319166108aa565b6000600160a060020a038416156102cd57836102cf565b335b9050336102db82610570565b600160a060020a031614610339576040805160e560020a62461bcd02815260206004820152600f60248201527f4e6f7420746865206d616e616765720000000000000000000000000000000000604482015290519081900360640190fd5b6103428361092a565b15610397576040805160e560020a62461bcd02815260206004820152601a60248201527f4d757374206e6f7420626520616e204552433136352068617368000000000000604482015290519081900360640190fd5b600160a060020a038216158015906103b85750600160a060020a0382163314155b156104ff5760405160200180807f455243313832305f4143434550545f4d4147494300000000000000000000000081525060140190506040516020818303038152906040528051906020012082600160a060020a031663249cb3fa85846040518363ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004018083815260200182600160a060020a0316600160a060020a031681526020019250505060206040518083038186803b15801561047e57600080fd5b505afa158015610492573d6000803e3d6000fd5b505050506040513d60208110156104a857600080fd5b5051146104ff576040805160e560020a62461bcd02815260206004820181905260248201527f446f6573206e6f7420696d706c656d656e742074686520696e74657266616365604482015290519081900360640190fd5b600160a060020a03818116600081815260208181526040808320888452909152808220805473ffffffffffffffffffffffffffffffffffffffff19169487169485179055518692917f93baa6efbd2244243bfee6ce4cfdd1d04fc4c0e9a786abd3a41313bd352db15391a450505050565b600160a060020a03818116600090815260016020526040812054909116151561059a5750806105b7565b50600160a060020a03808216600090815260016020526040902054165b919050565b336105c683610570565b600160a060020a031614610624576040805160e560020a62461bcd02815260206004820152600f60248201527f4e6f7420746865206d616e616765720000000000000000000000000000000000604482015290519081900360640190fd5b81600160a060020a031681600160a060020a0316146106435780610646565b60005b600160a060020a03838116600081815260016020526040808220805473ffffffffffffffffffffffffffffffffffffffff19169585169590951790945592519184169290917f605c2dbf762e5f7d60a546d42e7205dcb1b011ebc62a61736a57c9089d3a43509190a35050565b600082826040516020018083838082843780830192505050925050506040516020818303038152906040528051906020012090505b92915050565b6106f882826107ef565b610703576000610705565b815b600160a060020a03928316600081815260208181526040808320600160e060020a031996909616808452958252808320805473ffffffffffffffffffffffffffffffffffffffff19169590971694909417909555908152600284528181209281529190925220805460ff19166001179055565b600080600160a060020a038416156107905783610792565b335b905061079d8361092a565b156107c357826107ad82826108aa565b6107b85760006107ba565b815b925050506106e8565b600160a060020a0390811660009081526020818152604080832086845290915290205416905092915050565b6000808061081d857f01ffc9a70000000000000000000000000000000000000000000000000000000061094c565b909250905081158061082d575080155b1561083d576000925050506106e8565b61084f85600160e060020a031961094c565b909250905081158061086057508015155b15610870576000925050506106e8565b61087a858561094c565b909250905060018214801561088f5750806001145b1561089f576001925050506106e8565b506000949350505050565b600160a060020a0382166000908152600260209081526040808320600160e060020a03198516845290915281205460ff1615156108f2576108eb83836107ef565b90506106e8565b50600160a060020a03808316600081815260208181526040808320600160e060020a0319871684529091529020549091161492915050565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff161590565b6040517f01ffc9a7000000000000000000000000000000000000000000000000000000008082526004820183905260009182919060208160248189617530fa90519096909550935050505056fea165627a7a72305820377f4a2d4301ede9949f163f319021a6e9c687c292a5e2b2c4734c126b524e6c00291ba01820182018201820182018201820182018201820182018201820182018201820a01820182018201820182018201820182018201820182018201820182018201820 +``` + +The strings of `1820`'s at the end of the transaction are the `r` and `s` of the signature. +From this deterministic pattern (generated by a human), anyone can deduce that no one knows the private key for the deployment account. + +### Deployment Method + +This contract is going to be deployed using the keyless deployment method---also known as [Nick]'s method---which relies on a single-use address. +(See [Nick's article] for more details). This method works as follows: + +1. Generate a transaction which deploys the contract from a new random account. + - This transaction MUST NOT use [EIP-155] in order to work on any chain. + - This transaction MUST have a relatively high gas price to be deployed on any chain. In this case, it is going to be 100 Gwei. + +2. Set the `v`, `r`, `s` of the transaction signature to the following values: + + ``` + v: 27, + r: 0x1820182018201820182018201820182018201820182018201820182018201820' + s: 0x1820182018201820182018201820182018201820182018201820182018201820' + ``` + + Those `r` and `s` values---made of a repeating pattern of `1820`'s---are predictable "random numbers" generated deterministically by a human. + +3. We recover the sender of this transaction, i.e., the single-use deployment account. + + > Thus we obtain an account that can broadcast that transaction, but we also have the warranty that nobody knows the private key of that account. + +4. Send exactly 0.08 ether to this single-use deployment account. + +5. Broadcast the deployment transaction. + +This operation can be done on any chain, guaranteeing that the contract address is always the same and nobody can use that address with a different contract. + + +### Single-use Registry Deployment Account + +``` +0xa990077c3205cbDf861e17Fa532eeB069cE9fF96 +``` + +This account is generated by reverse engineering it from its signature for the transaction. +This way no one knows the private key, but it is known that it is the valid signer of the deployment transaction. + +> To deploy the registry, 0.08 ether MUST be sent to this account *first*. + +### Registry Contract Address + +``` +0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24 +``` + +The contract has the address above for every chain on which it is deployed. + +
+Raw metadata of ./contracts/ERC1820Registry.sol +
+{
+        "compiler": {
+          "version": "0.5.3+commit.10d17f24"
+        },
+        "language": "Solidity",
+        "output": {
+          "abi": [
+            {
+              "constant": false,
+              "inputs": [
+                {
+                  "name": "_addr",
+                  "type": "address"
+                },
+                {
+                  "name": "_interfaceHash",
+                  "type": "bytes32"
+                },
+                {
+                  "name": "_implementer",
+                  "type": "address"
+                }
+              ],
+              "name": "setInterfaceImplementer",
+              "outputs": [],
+              "payable": false,
+              "stateMutability": "nonpayable",
+              "type": "function"
+            },
+            {
+              "constant": true,
+              "inputs": [
+                {
+                  "name": "_addr",
+                  "type": "address"
+                }
+              ],
+              "name": "getManager",
+              "outputs": [
+                {
+                  "name": "",
+                  "type": "address"
+                }
+              ],
+              "payable": false,
+              "stateMutability": "view",
+              "type": "function"
+            },
+            {
+              "constant": false,
+              "inputs": [
+                {
+                  "name": "_addr",
+                  "type": "address"
+                },
+                {
+                  "name": "_newManager",
+                  "type": "address"
+                }
+              ],
+              "name": "setManager",
+              "outputs": [],
+              "payable": false,
+              "stateMutability": "nonpayable",
+              "type": "function"
+            },
+            {
+              "constant": true,
+              "inputs": [
+                {
+                  "name": "_interfaceName",
+                  "type": "string"
+                }
+              ],
+              "name": "interfaceHash",
+              "outputs": [
+                {
+                  "name": "",
+                  "type": "bytes32"
+                }
+              ],
+              "payable": false,
+              "stateMutability": "pure",
+              "type": "function"
+            },
+            {
+              "constant": false,
+              "inputs": [
+                {
+                  "name": "_contract",
+                  "type": "address"
+                },
+                {
+                  "name": "_interfaceId",
+                  "type": "bytes4"
+                }
+              ],
+              "name": "updateERC165Cache",
+              "outputs": [],
+              "payable": false,
+              "stateMutability": "nonpayable",
+              "type": "function"
+            },
+            {
+              "constant": true,
+              "inputs": [
+                {
+                  "name": "_addr",
+                  "type": "address"
+                },
+                {
+                  "name": "_interfaceHash",
+                  "type": "bytes32"
+                }
+              ],
+              "name": "getInterfaceImplementer",
+              "outputs": [
+                {
+                  "name": "",
+                  "type": "address"
+                }
+              ],
+              "payable": false,
+              "stateMutability": "view",
+              "type": "function"
+            },
+            {
+              "constant": true,
+              "inputs": [
+                {
+                  "name": "_contract",
+                  "type": "address"
+                },
+                {
+                  "name": "_interfaceId",
+                  "type": "bytes4"
+                }
+              ],
+              "name": "implementsERC165InterfaceNoCache",
+              "outputs": [
+                {
+                  "name": "",
+                  "type": "bool"
+                }
+              ],
+              "payable": false,
+              "stateMutability": "view",
+              "type": "function"
+            },
+            {
+              "constant": true,
+              "inputs": [
+                {
+                  "name": "_contract",
+                  "type": "address"
+                },
+                {
+                  "name": "_interfaceId",
+                  "type": "bytes4"
+                }
+              ],
+              "name": "implementsERC165Interface",
+              "outputs": [
+                {
+                  "name": "",
+                  "type": "bool"
+                }
+              ],
+              "payable": false,
+              "stateMutability": "view",
+              "type": "function"
+            },
+            {
+              "anonymous": false,
+              "inputs": [
+                {
+                  "indexed": true,
+                  "name": "addr",
+                  "type": "address"
+                },
+                {
+                  "indexed": true,
+                  "name": "interfaceHash",
+                  "type": "bytes32"
+                },
+                {
+                  "indexed": true,
+                  "name": "implementer",
+                  "type": "address"
+                }
+              ],
+              "name": "InterfaceImplementerSet",
+              "type": "event"
+            },
+            {
+              "anonymous": false,
+              "inputs": [
+                {
+                  "indexed": true,
+                  "name": "addr",
+                  "type": "address"
+                },
+                {
+                  "indexed": true,
+                  "name": "newManager",
+                  "type": "address"
+                }
+              ],
+              "name": "ManagerChanged",
+              "type": "event"
+            }
+          ],
+          "devdoc": {
+            "author": "Jordi Baylina and Jacques Dafflon",
+            "methods": {
+              "getInterfaceImplementer(address,bytes32)": {
+                "params": {
+                  "_addr": "Address being queried for the implementer of an interface. (If '_addr' is the zero address then 'msg.sender' is assumed.)",
+                  "_interfaceHash": "Keccak256 hash of the name of the interface as a string. E.g., 'web3.utils.keccak256(\"ERC777TokensRecipient\")' for the 'ERC777TokensRecipient' interface."
+                },
+                "return": "The address of the contract which implements the interface '_interfaceHash' for '_addr' or '0' if '_addr' did not register an implementer for this interface."
+              },
+              "getManager(address)": {
+                "params": {
+                  "_addr": "Address for which to return the manager."
+                },
+                "return": "Address of the manager for a given address."
+              },
+              "implementsERC165Interface(address,bytes4)": {
+                "params": {
+                  "_contract": "Address of the contract to check.",
+                  "_interfaceId": "ERC165 interface to check."
+                },
+                "return": "True if '_contract' implements '_interfaceId', false otherwise."
+              },
+              "implementsERC165InterfaceNoCache(address,bytes4)": {
+                "params": {
+                  "_contract": "Address of the contract to check.",
+                  "_interfaceId": "ERC165 interface to check."
+                },
+                "return": "True if '_contract' implements '_interfaceId', false otherwise."
+              },
+              "interfaceHash(string)": {
+                "params": {
+                  "_interfaceName": "Name of the interface."
+                },
+                "return": "The keccak256 hash of an interface name."
+              },
+              "setInterfaceImplementer(address,bytes32,address)": {
+                "params": {
+                  "_addr": "Address for which to set the interface. (If '_addr' is the zero address then 'msg.sender' is assumed.)",
+                  "_implementer": "Contract address implementing '_interfaceHash' for '_addr'.",
+                  "_interfaceHash": "Keccak256 hash of the name of the interface as a string. E.g., 'web3.utils.keccak256(\"ERC777TokensRecipient\")' for the 'ERC777TokensRecipient' interface."
+                }
+              },
+              "setManager(address,address)": {
+                "params": {
+                  "_addr": "Address for which to set the new manager.",
+                  "_newManager": "Address of the new manager for 'addr'. (Pass '0x0' to reset the manager to '_addr'.)"
+                }
+              },
+              "updateERC165Cache(address,bytes4)": {
+                "params": {
+                  "_contract": "Address of the contract for which to update the cache.",
+                  "_interfaceId": "ERC165 interface for which to update the cache."
+                }
+              }
+            },
+            "title": "ERC1820 Pseudo-introspection Registry Contract"
+          },
+          "userdoc": {
+            "methods": {
+              "getInterfaceImplementer(address,bytes32)": {
+                "notice": "Query if an address implements an interface and through which contract."
+              },
+              "getManager(address)": {
+                "notice": "Get the manager of an address."
+              },
+              "implementsERC165InterfaceNoCache(address,bytes4)": {
+                "notice": "Checks whether a contract implements an ERC165 interface or not without using nor updating the cache."
+              },
+              "interfaceHash(string)": {
+                "notice": "Compute the keccak256 hash of an interface given its name."
+              },
+              "setInterfaceImplementer(address,bytes32,address)": {
+                "notice": "Sets the contract which implements a specific interface for an address. Only the manager defined for that address can set it. (Each address is the manager for itself until it sets a new manager.)"
+              },
+              "setManager(address,address)": {
+                "notice": "Sets '_newManager' as manager for '_addr'. The new manager will be able to call 'setInterfaceImplementer' for '_addr'."
+              },
+              "updateERC165Cache(address,bytes4)": {
+                "notice": "Updates the cache with whether the contract implements an ERC165 interface or not."
+              }
+            },
+            "notice": "This contract is the official implementation of the ERC1820 Registry.For more details, see https://eips.ethereum.org/EIPS/eip-1820"
+          }
+        },
+        "settings": {
+          "compilationTarget": {
+            "./contracts/ERC1820Registry.sol": "ERC1820Registry"
+          },
+          "evmVersion": "byzantium",
+          "libraries": {},
+          "optimizer": {
+            "enabled": true,
+            "runs": 200
+          },
+          "remappings": []
+        },
+        "sources": {
+          "./contracts/ERC1820Registry.sol": {
+            "content": "/* ERC1820 Pseudo-introspection Registry Contract\n * This standard defines a universal registry smart contract where any address (contract or regular account) can\n * register which interface it supports and which smart contract is responsible for its implementation.\n *\n * Written in 2019 by Jordi Baylina and Jacques Dafflon\n *\n * To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to\n * this software to the public domain worldwide. This software is distributed without any warranty.\n *\n * You should have received a copy of the CC0 Public Domain Dedication along with this software. If not, see\n * .\n *\n *    ███████╗██████╗  ██████╗ ██╗ █████╗ ██████╗  ██████╗\n *    ██╔════╝██╔══██╗██╔════╝███║██╔══██╗╚════██╗██╔═████╗\n *    █████╗  ██████╔╝██║     ╚██║╚█████╔╝ █████╔╝██║██╔██║\n *    ██╔══╝  ██╔══██╗██║      ██║██╔══██╗██╔═══╝ ████╔╝██║\n *    ███████╗██║  ██║╚██████╗ ██║╚█████╔╝███████╗╚██████╔╝\n *    ╚══════╝╚═╝  ╚═╝ ╚═════╝ ╚═╝ ╚════╝ ╚══════╝ ╚═════╝\n *\n *    ██████╗ ███████╗ ██████╗ ██╗███████╗████████╗██████╗ ██╗   ██╗\n *    ██╔══██╗██╔════╝██╔════╝ ██║██╔════╝╚══██╔══╝██╔══██╗╚██╗ ██╔╝\n *    ██████╔╝█████╗  ██║  ███╗██║███████╗   ██║   ██████╔╝ ╚████╔╝\n *    ██╔══██╗██╔══╝  ██║   ██║██║╚════██║   ██║   ██╔══██╗  ╚██╔╝\n *    ██║  ██║███████╗╚██████╔╝██║███████║   ██║   ██║  ██║   ██║\n *    ╚═╝  ╚═╝╚══════╝ ╚═════╝ ╚═╝╚══════╝   ╚═╝   ╚═╝  ╚═╝   ╚═╝\n *\n */\npragma solidity 0.5.3;\n// IV is value needed to have a vanity address starting with '0x1820'.\n// IV: 53759\n\n/// @dev The interface a contract MUST implement if it is the implementer of\n/// some (other) interface for any address other than itself.\ninterface ERC1820ImplementerInterface {\n    /// @notice Indicates whether the contract implements the interface 'interfaceHash' for the address 'addr' or not.\n    /// @param interfaceHash keccak256 hash of the name of the interface\n    /// @param addr Address for which the contract will implement the interface\n    /// @return ERC1820_ACCEPT_MAGIC only if the contract implements 'interfaceHash' for the address 'addr'.\n    function canImplementInterfaceForAddress(bytes32 interfaceHash, address addr) external view returns(bytes32);\n}\n\n\n/// @title ERC1820 Pseudo-introspection Registry Contract\n/// @author Jordi Baylina and Jacques Dafflon\n/// @notice This contract is the official implementation of the ERC1820 Registry.\n/// @notice For more details, see https://eips.ethereum.org/EIPS/eip-1820\ncontract ERC1820Registry {\n    /// @notice ERC165 Invalid ID.\n    bytes4 constant internal INVALID_ID = 0xffffffff;\n    /// @notice Method ID for the ERC165 supportsInterface method (= `bytes4(keccak256('supportsInterface(bytes4)'))`).\n    bytes4 constant internal ERC165ID = 0x01ffc9a7;\n    /// @notice Magic value which is returned if a contract implements an interface on behalf of some other address.\n    bytes32 constant internal ERC1820_ACCEPT_MAGIC = keccak256(abi.encodePacked(\"ERC1820_ACCEPT_MAGIC\"));\n\n    /// @notice mapping from addresses and interface hashes to their implementers.\n    mapping(address => mapping(bytes32 => address)) internal interfaces;\n    /// @notice mapping from addresses to their manager.\n    mapping(address => address) internal managers;\n    /// @notice flag for each address and erc165 interface to indicate if it is cached.\n    mapping(address => mapping(bytes4 => bool)) internal erc165Cached;\n\n    /// @notice Indicates a contract is the 'implementer' of 'interfaceHash' for 'addr'.\n    event InterfaceImplementerSet(address indexed addr, bytes32 indexed interfaceHash, address indexed implementer);\n    /// @notice Indicates 'newManager' is the address of the new manager for 'addr'.\n    event ManagerChanged(address indexed addr, address indexed newManager);\n\n    /// @notice Query if an address implements an interface and through which contract.\n    /// @param _addr Address being queried for the implementer of an interface.\n    /// (If '_addr' is the zero address then 'msg.sender' is assumed.)\n    /// @param _interfaceHash Keccak256 hash of the name of the interface as a string.\n    /// E.g., 'web3.utils.keccak256(\"ERC777TokensRecipient\")' for the 'ERC777TokensRecipient' interface.\n    /// @return The address of the contract which implements the interface '_interfaceHash' for '_addr'\n    /// or '0' if '_addr' did not register an implementer for this interface.\n    function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) external view returns (address) {\n        address addr = _addr == address(0) ? msg.sender : _addr;\n        if (isERC165Interface(_interfaceHash)) {\n            bytes4 erc165InterfaceHash = bytes4(_interfaceHash);\n            return implementsERC165Interface(addr, erc165InterfaceHash) ? addr : address(0);\n        }\n        return interfaces[addr][_interfaceHash];\n    }\n\n    /// @notice Sets the contract which implements a specific interface for an address.\n    /// Only the manager defined for that address can set it.\n    /// (Each address is the manager for itself until it sets a new manager.)\n    /// @param _addr Address for which to set the interface.\n    /// (If '_addr' is the zero address then 'msg.sender' is assumed.)\n    /// @param _interfaceHash Keccak256 hash of the name of the interface as a string.\n    /// E.g., 'web3.utils.keccak256(\"ERC777TokensRecipient\")' for the 'ERC777TokensRecipient' interface.\n    /// @param _implementer Contract address implementing '_interfaceHash' for '_addr'.\n    function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external {\n        address addr = _addr == address(0) ? msg.sender : _addr;\n        require(getManager(addr) == msg.sender, \"Not the manager\");\n\n        require(!isERC165Interface(_interfaceHash), \"Must not be an ERC165 hash\");\n        if (_implementer != address(0) && _implementer != msg.sender) {\n            require(\n                ERC1820ImplementerInterface(_implementer)\n                    .canImplementInterfaceForAddress(_interfaceHash, addr) == ERC1820_ACCEPT_MAGIC,\n                \"Does not implement the interface\"\n            );\n        }\n        interfaces[addr][_interfaceHash] = _implementer;\n        emit InterfaceImplementerSet(addr, _interfaceHash, _implementer);\n    }\n\n    /// @notice Sets '_newManager' as manager for '_addr'.\n    /// The new manager will be able to call 'setInterfaceImplementer' for '_addr'.\n    /// @param _addr Address for which to set the new manager.\n    /// @param _newManager Address of the new manager for 'addr'. (Pass '0x0' to reset the manager to '_addr'.)\n    function setManager(address _addr, address _newManager) external {\n        require(getManager(_addr) == msg.sender, \"Not the manager\");\n        managers[_addr] = _newManager == _addr ? address(0) : _newManager;\n        emit ManagerChanged(_addr, _newManager);\n    }\n\n    /// @notice Get the manager of an address.\n    /// @param _addr Address for which to return the manager.\n    /// @return Address of the manager for a given address.\n    function getManager(address _addr) public view returns(address) {\n        // By default the manager of an address is the same address\n        if (managers[_addr] == address(0)) {\n            return _addr;\n        } else {\n            return managers[_addr];\n        }\n    }\n\n    /// @notice Compute the keccak256 hash of an interface given its name.\n    /// @param _interfaceName Name of the interface.\n    /// @return The keccak256 hash of an interface name.\n    function interfaceHash(string calldata _interfaceName) external pure returns(bytes32) {\n        return keccak256(abi.encodePacked(_interfaceName));\n    }\n\n    /* --- ERC165 Related Functions --- */\n    /* --- Developed in collaboration with William Entriken. --- */\n\n    /// @notice Updates the cache with whether the contract implements an ERC165 interface or not.\n    /// @param _contract Address of the contract for which to update the cache.\n    /// @param _interfaceId ERC165 interface for which to update the cache.\n    function updateERC165Cache(address _contract, bytes4 _interfaceId) external {\n        interfaces[_contract][_interfaceId] = implementsERC165InterfaceNoCache(\n            _contract, _interfaceId) ? _contract : address(0);\n        erc165Cached[_contract][_interfaceId] = true;\n    }\n\n    /// @notice Checks whether a contract implements an ERC165 interface or not.\n    //  If the result is not cached a direct lookup on the contract address is performed.\n    //  If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling\n    //  'updateERC165Cache' with the contract address.\n    /// @param _contract Address of the contract to check.\n    /// @param _interfaceId ERC165 interface to check.\n    /// @return True if '_contract' implements '_interfaceId', false otherwise.\n    function implementsERC165Interface(address _contract, bytes4 _interfaceId) public view returns (bool) {\n        if (!erc165Cached[_contract][_interfaceId]) {\n            return implementsERC165InterfaceNoCache(_contract, _interfaceId);\n        }\n        return interfaces[_contract][_interfaceId] == _contract;\n    }\n\n    /// @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache.\n    /// @param _contract Address of the contract to check.\n    /// @param _interfaceId ERC165 interface to check.\n    /// @return True if '_contract' implements '_interfaceId', false otherwise.\n    function implementsERC165InterfaceNoCache(address _contract, bytes4 _interfaceId) public view returns (bool) {\n        uint256 success;\n        uint256 result;\n\n        (success, result) = noThrowCall(_contract, ERC165ID);\n        if (success == 0 || result == 0) {\n            return false;\n        }\n\n        (success, result) = noThrowCall(_contract, INVALID_ID);\n        if (success == 0 || result != 0) {\n            return false;\n        }\n\n        (success, result) = noThrowCall(_contract, _interfaceId);\n        if (success == 1 && result == 1) {\n            return true;\n        }\n        return false;\n    }\n\n    /// @notice Checks whether the hash is a ERC165 interface (ending with 28 zeroes) or not.\n    /// @param _interfaceHash The hash to check.\n    /// @return True if '_interfaceHash' is an ERC165 interface (ending with 28 zeroes), false otherwise.\n    function isERC165Interface(bytes32 _interfaceHash) internal pure returns (bool) {\n        return _interfaceHash & 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0;\n    }\n\n    /// @dev Make a call on a contract without throwing if the function does not exist.\n    function noThrowCall(address _contract, bytes4 _interfaceId)\n        internal view returns (uint256 success, uint256 result)\n    {\n        bytes4 erc165ID = ERC165ID;\n\n        assembly {\n            let x := mload(0x40)               // Find empty storage location using \"free memory pointer\"\n            mstore(x, erc165ID)                // Place signature at beginning of empty storage\n            mstore(add(x, 0x04), _interfaceId) // Place first argument directly next to signature\n\n            success := staticcall(\n                30000,                         // 30k gas\n                _contract,                     // To addr\n                x,                             // Inputs are stored at location x\n                0x24,                          // Inputs are 36 (4 + 32) bytes long\n                x,                             // Store output over input (saves space)\n                0x20                           // Outputs are 32 bytes long\n            )\n\n            result := mload(x)                 // Load the result\n        }\n    }\n}\n",
+            "keccak256": "0x64025ecebddb6e126a5075c1fd6c01de2840492668e2909cef7157040a9d1945"
+          }
+        },
+        "version": 1
+      }
+
+
+ +### Interface Name + +Any interface name is hashed using `keccak256` and sent to `getInterfaceImplementer()`. + +If the interface is part of a standard, it is best practice to explicitly state the interface name and link to this published [ERC-1820] such that other people don't have to come here to look up these rules. + +For convenience, the registry provides a function to compute the hash on-chain: + +``` solidity +function interfaceHash(string _interfaceName) public pure returns(bytes32) +``` + +Compute the keccak256 hash of an interface given its name. + +> **identifier:** `65ba36c1` +> **parameters** +> `_interfaceName`: Name of the interface. +> **returns:** The `keccak256` hash of an interface name. + +#### **Approved ERCs** + +If the interface is part of an approved ERC, it MUST be named `ERC###XXXXX` where `###` is the number of the ERC and XXXXX should be the name of the interface in CamelCase. +The meaning of this interface SHOULD be defined in the specified ERC. + +Examples: + +- `keccak256("ERC20Token")` +- `keccak256("ERC777Token")` +- `keccak256("ERC777TokensSender")` +- `keccak256("ERC777TokensRecipient")` + +#### **[ERC-165] Compatible Interfaces** + +> The compatibility with [ERC-165], including the [ERC165 Cache], has been designed and developed with [William Entriken]. + +Any interface where the last 28 bytes are zeroes (`0`) SHALL be considered an [ERC-165] interface. + +**[ERC-165] Lookup** + +Anyone can explicitly check if a contract implements an [ERC-165] interface using the registry by calling one of the two functions below: + +``` solidity +function implementsERC165Interface(address _contract, bytes4 _interfaceId) public view returns (bool) +``` + +Checks whether a contract implements an [ERC-165] interface or not. + +If the result is not cached a direct lookup on the contract address is performed. + +*NOTE*: If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling `updateERC165Cache` with the contract address. +(See [ERC165 Cache] for more details.) + +> **identifier:** `f712f3e8` +> **parameters** +> `_contract`: Address of the contract to check. +> `_interfaceId`: [ERC-165] interface to check. +> **returns:** `true` if `_contract` implements `_interfaceId`, `false` otherwise. + +``` solidity +function implementsERC165InterfaceNoCache(address _contract, bytes4 _interfaceId) public view returns (bool) +``` + +Checks whether a contract implements an [ERC-165] interface or not without using nor updating the cache. + +> **identifier:** `b7056765` +> **parameters** +> `_contract`: Address of the contract to check. +> `_interfaceId`: [ERC-165] interface to check. +> **returns:** `true` if `_contract` implements `_interfaceId`, false otherwise. + +**[ERC-165] Cache** + +Whether a contract implements an [ERC-165] interface or not can be cached manually to save gas. + +If a contract dynamically changes its interface and relies on the [ERC-165] cache of the [ERC-1820] registry, the cache MUST be updated manually---there is no automatic cache invalidation or cache update. +Ideally the contract SHOULD automatically update the cache when changing its interface. +However anyone MAY update the cache on the contract's behalf. + +The cache update MUST be done using the `updateERC165Cache` function: + +``` solidity +function updateERC165Cache(address _contract, bytes4 _interfaceId) external +``` + +> **identifier:** `a41e7d51` +> **parameters** +> `_contract`: Address of the contract for which to update the cache. +> `_interfaceId`: [ERC-165] interface for which to update the cache. + +#### **Private User-defined Interfaces** + +This scheme is extensible. +You MAY make up your own interface name and raise awareness to get other people to implement it and then check for those implementations. +Have fun but please, you MUST not conflict with the reserved designations above. + +### Set An Interface For An Address + +For any address to set a contract as the interface implementation, it must call the following function of the [ERC-1820] registry: + +``` solidity +function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external +``` + +Sets the contract which implements a specific interface for an address. + +Only the `manager` defined for that address can set it. +(Each address is the manager for itself, see the [manager] section for more details.) + +*NOTE*: If `_addr` and `_implementer` are two different addresses, then: + +- The `_implementer` MUST implement the `ERC1820ImplementerInterface` (detailed below). +- Calling `canImplementInterfaceForAddress` on `_implementer` with the given `_addr` and `_interfaceHash` MUST return the `ERC1820_ACCEPT_MAGIC` value. + +*NOTE*: The `_interfaceHash` MUST NOT be an [ERC-165] interface---it MUST NOT end with 28 zeroes (`0`). + +*NOTE*: The `_addr` MAY be `0`, then `msg.sender` is assumed. +This default value simplifies interactions via multisigs where the data of the transaction to sign is constant regardless of the address of the multisig instance. + +> **identifier:** `29965a1d` +> **parameters** +> `_addr`: Address for which to set the interface. (If `_addr` is the zero address then `msg.sender` is assumed.) +> `_interfaceHash`: Keccak256 hash of the name of the interface as a string, for example `web3.utils.keccak256('ERC777TokensRecipient')` for the ERC777TokensRecipient interface. +> `_implementer`: Contract implementing `_interfaceHash` for `_addr`. + +### Get An Implementation Of An Interface For An Address + +Anyone MAY query the [ERC-1820] Registry to obtain the address of a contract implementing an interface on behalf of some address using the `getInterfaceImplementer` function. + +``` solidity +function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) external view returns (address) +``` + +Query if an address implements an interface and through which contract. + +*NOTE*: If the last 28 bytes of the `_interfaceHash` are zeroes (`0`), then the first 4 bytes are considered an [ERC-165] interface and the registry SHALL forward the call to the contract at `_addr` to see if it implements the [ERC-165] interface (the first 4 bytes of `_interfaceHash`). +The registry SHALL also cache [ERC-165] queries to reduce gas consumption. Anyone MAY call the `erc165UpdateCache` function to update whether a contract implements an interface or not. + +*NOTE*: The `_addr` MAY be `0`, then `msg.sender` is assumed. +This default value is consistent with the behavior of the `setInterfaceImplementer` function and simplifies interactions via multisigs where the data of the transaction to sign is constant regardless of the address of the multisig instance. + +> **identifier:** `aabbb8ca` +> **parameters** +> `_addr`: Address being queried for the implementer of an interface. (If `_addr` is the zero address then `msg.sender` is assumed.) +> `_interfaceHash`: keccak256 hash of the name of the interface as a string. E.g. `web3.utils.keccak256('ERC777Token')` +> **returns:** The address of the contract which implements the interface `_interfaceHash` for `_addr` or `0` if `_addr` did not register an implementer for this interface. + + +### Interface Implementation (`ERC1820ImplementerInterface`) + +``` solidity +interface ERC1820ImplementerInterface { + /// @notice Indicates whether the contract implements the interface `interfaceHash` for the address `addr` or not. + /// @param interfaceHash keccak256 hash of the name of the interface + /// @param addr Address for which the contract will implement the interface + /// @return ERC1820_ACCEPT_MAGIC only if the contract implements `interfaceHash` for the address `addr`. + function canImplementInterfaceForAddress(bytes32 interfaceHash, address addr) external view returns(bytes32); +} +``` + +Any contract being registered as the implementation of an interface for a given address MUST implement said interface. +In addition if it implements an interface on behalf of a different address, the contract MUST implement the `ERC1820ImplementerInterface` shown above. + +``` solidity +function canImplementInterfaceForAddress(bytes32 interfaceHash, address addr) external view returns(bytes32) +``` + +Indicates whether a contract implements an interface (`interfaceHash`) for a given address (`addr`). + +If a contract implements the interface (`interfaceHash`) for a given address (`addr`), it MUST return `ERC1820_ACCEPT_MAGIC` when called with the `addr` and the `interfaceHash`. +If it does not implement the `interfaceHash` for a given address (`addr`), it MUST NOT return `ERC1820_ACCEPT_MAGIC`. + +> **identifier:** `f0083250` +> **parameters** +> `interfaceHash`: Hash of the interface which is implemented +> `addr`: Address for which the interface is implemented +> **returns:** `ERC1820_ACCEPT_MAGIC` only if the contract implements `ìnterfaceHash` for the address `addr`. + +The special value `ERC1820_ACCEPT_MAGIC` is defined as the `keccka256` hash of the string `"ERC1820_ACCEPT_MAGIC"`. + +``` solidity +bytes32 constant internal ERC1820_ACCEPT_MAGIC = keccak256(abi.encodePacked("ERC1820_ACCEPT_MAGIC")); +``` + +> The reason to return `ERC1820_ACCEPT_MAGIC` instead of a boolean is to prevent cases where a contract fails to implement the `canImplementInterfaceForAddress` but implements a fallback function which does not throw. In this case, since `canImplementInterfaceForAddress` does not exist, the fallback function is called instead, executed without throwing and returns `1`. Thus making it appear as if `canImplementInterfaceForAddress` returned `true`. + +### Manager + +The manager of an address (regular account or a contract) is the only entity allowed to register implementations of interfaces for the address. +By default, any address is its own manager. + +The manager can transfer its role to another address by calling `setManager` on the registry contract with the address for which to transfer the manager and the address of the new manager. + +**`setManager` Function** + +``` solidity +function setManager(address _addr, address _newManager) external +``` + +Sets `_newManager` as manager for `_addr`. + +The new manager will be able to call `setInterfaceImplementer` for `_addr`. + +If `_newManager` is `0x0`, the manager is reset to `_addr` itself as the manager. + +> **identifier:** `5df8122f` +> **parameters** +> `_addr`: Address for which to set the new manager. +> `_newManager`: The address of the new manager for `_addr`. (Pass `0x0` to reset the manager to `_addr`.) + +**`getManager` Function** + +``` solidity +function getManager(address _addr) public view returns(address) +``` + +Get the manager of an address. + +> **identifier:** `3d584063` +> **parameters** +> `_addr`: Address for which to return the manager. +> **returns:** Address of the manager for a given address. + +## Rationale + +This standards offers a way for any type of address (externally owned and contracts) to implement an interface and potentially delegate the implementation of the interface to a proxy contract. +This delegation to a proxy contract is necessary for externally owned accounts and useful to avoid redeploying existing contracts such as multisigs and DAOs. + +The registry can also act as a [ERC-165] cache in order to save gas when looking up if a contract implements a specific [ERC-165] interface. +This cache is intentionally kept simple, without automatic cache update or invalidation. +Anyone can easily and safely update the cache for any interface and any contract by calling the `updateERC165Cache` function. + +The registry is deployed using a keyless deployment method relying on a single-use deployment address to ensure no one controls the registry, thereby ensuring trust. + +## Backward Compatibility + +This standard is backward compatible with [ERC-165], as both methods MAY be implemented without conflicting with each other. + +## Test Cases + +Please check the [0xjac/ERC1820] repository for the full test suite. + +## Implementation + +The implementation is available in the repo: [0xjac/ERC1820]. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). + +[EIP-155]: ./eip-155.md +[ERC-165]: ./eip-165.md +[ERC-672]: https://github.com/ethereum/EIPs/issues/672 +[ERC-820]: ./eip-820.md +[ERC-1820]: ./eip-1820.md +[ERC1820 registry smart contract]: https://github.com/0xjac/ERC1820/blob/master/contracts/ERC1820Registry.sol +[erc1820-annoucement]: https://github.com/ethereum/EIPs/issues/820#issuecomment-464109166 +[erc820-bug]: https://github.com/ethereum/EIPs/issues/820#issuecomment-452465748 +[erc820-fix]: https://github.com/ethereum/EIPs/issues/820#issuecomment-454021564 +[manager]: #manager +[lookup]: #get-an-implementation-of-an-interface-for-an-address +[ERC165 Cache]: #erc165-cache +[Nick's article]: https://medium.com/@weka/how-to-send-ether-to-11-440-people-187e332566b7 +[0xjac/ERC1820]: https://github.com/0xjac/ERC1820 +[Nick]: https://github.com/Arachnid/ +[William Entriken]: https://github.com/fulldecent +[ENS]: https://ens.domains/ diff --git a/EIPS/eip-1822.md b/EIPS/eip-1822.md new file mode 100644 index 0000000..9289266 --- /dev/null +++ b/EIPS/eip-1822.md @@ -0,0 +1,349 @@ +--- +eip: 1822 +title: Universal Upgradeable Proxy Standard (UUPS) +author: Gabriel Barros , Patrick Gallagher +discussions-to: https://ethereum-magicians.org/t/eip-1822-universal-upgradeable-proxy-standard-uups +status: Stagnant +type: Standards Track +category: ERC +created: 2019-03-04 +--- + +## Table of contents + + + +- [Table of contents](#table-of-contents) +- [Simple Summary](#simple-summary) +- [Abstract](#abstract) +- [Motivation](#motivation) +- [Terminology](#terminology) +- [Specification](#specification) + - [Proxy Contract](#proxy-contract) + - [Functions](#functions) + - [`fallback`](#fallback) + - [`constructor`](#constructor) + - [Proxiable Contract](#proxiable-contract) + - [Functions](#functions-1) + - [`proxiable`](#proxiable) + - [`updateCodeAddress`](#updatecodeaddress) +- [Pitfalls when using a proxy](#pitfalls-when-using-a-proxy) + - [Separating Variables from Logic](#separating-variables-from-logic) + - [Restricting dangerous functions](#restricting-dangerous-functions) +- [Examples](#examples) + - [Owned](#owned) + - [ERC-20 Token](#erc-20-token) + - [Proxy Contract](#proxy-contract-1) + - [Token Logic Contract](#token-logic-contract) +- [References](#references) +- [Copyright](#copyright) + + +## Simple Summary + +Standard upgradeable proxy contract. + +## Abstract + +The following describes a standard for proxy contracts which is universally compatible with all contracts, and does not create incompatibility between the proxy and business-logic contracts. This is achieved by utilizing a unique storage position in the proxy contract to store the Logic Contract's address. A compatibility check ensures successful upgrades. Upgrading can be performed unlimited times, or as determined by custom logic. In addition, a method for selecting from multiple constructors is provided, which does not inhibit the ability to verify bytecode. + +## Motivation + +- Improve upon existing proxy implementations to improve developer experience for deploying and maintaining Proxy and Logic Contracts. + +- Standardize and improve the methods for verifying the bytecode used by the Proxy Contract. + +## Terminology + +- `delegatecall()` - Function in contract **A** which allows an external contract **B** (delegating) to modify **A**'s storage (see diagram below, [Solidity docs](https://solidity.readthedocs.io/en/v0.5.3/introduction-to-smart-contracts.html#delegatecall-callcode-and-libraries)) +- **Proxy Contract** - The contract **A** which stores data, but uses the logic of external contract **B** by way of `delegatecall()`. +- **Logic Contract** - The contract **B** which contains the logic used by Proxy Contract **A** +- **Proxiable Contract** - Inherited in Logic Contract **B** to provide the upgrade functionality + +

diagram

+ +## Specification + +The Proxy Contract proposed here should be deployed _as is_, and used as a drop-in replacement for any existing methods of lifecycle management of contracts. In addition to the Proxy Contract, we propose the Proxiable Contract interface/base which establishes a pattern for the upgrade which does not interfere with existing business rules. The logic for allowing upgrades can be implemented as needed. + +### Proxy Contract + +#### Functions + +##### `fallback` + +The proposed fallback function follows the common pattern seen in other Proxy Contract implementations such as [Zeppelin][1] or [Gnosis][2]. + +However, rather than forcing use of a variable, the address of the Logic Contract is stored at the defined storage position `keccak256("PROXIABLE")`. This eliminates the possibility of collision between variables in the Proxy and Logic Contracts, thus providing "universal" compatibility with any Logic Contract. + +```javascript +function() external payable { + assembly { // solium-disable-line + let contractLogic := sload(0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7) + calldatacopy(0x0, 0x0, calldatasize) + let success := delegatecall(sub(gas, 10000), contractLogic, 0x0, calldatasize, 0, 0) + let retSz := returndatasize + returndatacopy(0, 0, retSz) + switch success + case 0 { + revert(0, retSz) + } + default { + return(0, retSz) + } + } +} +``` + +#### `constructor` + +The proposed constructor accepts any number of arguments of any type, and thus is compatible with any Logic Contract constructor function. + +In addition, the arbitrary nature of the Proxy Contract's constructor provides the ability to select from one or more constructor functions available in the Logic Contract source code (e.g., `constructor1`, `constructor2`, ... etc. ). Note that if multiple constructors are included in the Logic Contract, a check should be included to prohibit calling a constructor again post-initialization. + +It's worth noting that the added functionality of supporting multiple constructors does not inhibit verification of the Proxy Contract's bytecode, since the initialization tx call data (input) can be decoded by first using the Proxy Contract ABI, and then using the Logic Contract ABI. + +The contract below shows the proposed implementation of the Proxy Contract. + +```javascript +contract Proxy { + // Code position in storage is keccak256("PROXIABLE") = "0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7" + constructor(bytes memory constructData, address contractLogic) public { + // save the code address + assembly { // solium-disable-line + sstore(0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7, contractLogic) + } + (bool success, bytes memory _ ) = contractLogic.delegatecall(constructData); // solium-disable-line + require(success, "Construction failed"); + } + + function() external payable { + assembly { // solium-disable-line + let contractLogic := sload(0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7) + calldatacopy(0x0, 0x0, calldatasize) + let success := delegatecall(sub(gas, 10000), contractLogic, 0x0, calldatasize, 0, 0) + let retSz := returndatasize + returndatacopy(0, 0, retSz) + switch success + case 0 { + revert(0, retSz) + } + default { + return(0, retSz) + } + } + } +} +``` + +### Proxiable Contract + +The Proxiable Contract is included in the Logic Contract, and provides the functions needed to perform an upgrade. The compatibility check `proxiable` prevents irreparable updates during an upgrade. + +> :warning: Warning: `updateCodeAddress` and `proxiable` must be present in the Logic Contract. Failure to include these may prevent upgrades, and could allow the Proxy Contract to become entirely unusable. See below [Restricting dangerous functions](#restricting-dangerous-functions) + +#### Functions + +##### `proxiable` + +Compatibility check to ensure the new Logic Contract implements the Universal Upgradeable Proxy Standard. Note that in order to support future implementations, the `bytes32` comparison could be changed e.g., `keccak256("PROXIABLE-ERC1822-v1")`. + +##### `updateCodeAddress` + +Stores the Logic Contract's address at storage `keccak256("PROXIABLE")` in the Proxy Contract. + +The contract below shows the proposed implementation of the Proxiable Contract. + +```javascript +contract Proxiable { + // Code position in storage is keccak256("PROXIABLE") = "0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7" + + function updateCodeAddress(address newAddress) internal { + require( + bytes32(0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7) == Proxiable(newAddress).proxiableUUID(), + "Not compatible" + ); + assembly { // solium-disable-line + sstore(0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7, newAddress) + } + } + function proxiableUUID() public pure returns (bytes32) { + return 0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7; + } +} +``` + +## Pitfalls when using a proxy + +The following common best practices should be employed for all Logic Contracts when using a proxy contract. + +### Separating Variables from Logic + +Careful consideration should be made when designing a new Logic Contract to prevent incompatibility with the existing storage of the Proxy Contract after an upgrade. Specifically, the order in which variables are instantiated in the new contract should not be modified, and any new variables should be added after all existing variables from the previous Logic Contract + +To facilitate this practice, we recommend utilizing a single "base" contract which holds all variables, and which is inherited in subsequent logic contract(s). This practice greatly reduces the chances of accidentally reordering variables or overwriting them in storage. + +### Restricting dangerous functions + +The compatibility check in the Proxiable Contract is a safety mechanism to prevent upgrading to a Logic Contract which does not implement the Universal Upgradeable Proxy Standard. However, as occurred in the parity wallet hack, it is still possible to perform irreparable damage to the Logic Contract itself. + +In order to prevent damage to the Logic Contract, we recommend restricting permissions for any potentially damaging functions to `onlyOwner`, and giving away ownership of the Logic Contract immediately upon deployment to a null address (e.g., address(1)). Potentially damaging functions include native functions such as `SELFDESTRUCT`, as well functions whose code may originate externally such as `CALLCODE`, and `delegatecall()`. In the [ERC-20 Token](#erc-20-token) example below, a `LibraryLock` contract is used to prevent destruction of the logic contract. + +## Examples + +### Owned + +In this example, we show the standard ownership example, and restrict the `updateCodeAddress` to only the owner. + +```javascript +contract Owned is Proxiable { + // ensures no one can manipulate this contract once it is deployed + address public owner = address(1); + + function constructor1() public{ + // ensures this can be called only once per *proxy* contract deployed + require(owner == address(0)); + owner = msg.sender; + } + + function updateCode(address newCode) onlyOwner public { + updateCodeAddress(newCode); + } + + modifier onlyOwner() { + require(msg.sender == owner, "Only owner is allowed to perform this action"); + _; + } +} +``` + +### ERC-20 Token + +#### Proxy Contract + +```javascript +pragma solidity ^0.5.1; + +contract Proxy { + // Code position in storage is keccak256("PROXIABLE") = "0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7" + constructor(bytes memory constructData, address contractLogic) public { + // save the code address + assembly { // solium-disable-line + sstore(0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7, contractLogic) + } + (bool success, bytes memory _ ) = contractLogic.delegatecall(constructData); // solium-disable-line + require(success, "Construction failed"); + } + + function() external payable { + assembly { // solium-disable-line + let contractLogic := sload(0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7) + calldatacopy(0x0, 0x0, calldatasize) + let success := delegatecall(sub(gas, 10000), contractLogic, 0x0, calldatasize, 0, 0) + let retSz := returndatasize + returndatacopy(0, 0, retSz) + switch success + case 0 { + revert(0, retSz) + } + default { + return(0, retSz) + } + } + } +} +``` + +#### Token Logic Contract + +``` javascript + +contract Proxiable { + // Code position in storage is keccak256("PROXIABLE") = "0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7" + + function updateCodeAddress(address newAddress) internal { + require( + bytes32(0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7) == Proxiable(newAddress).proxiableUUID(), + "Not compatible" + ); + assembly { // solium-disable-line + sstore(0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7, newAddress) + } + } + function proxiableUUID() public pure returns (bytes32) { + return 0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7; + } +} + + +contract Owned { + + address owner; + + function setOwner(address _owner) internal { + owner = _owner; + } + modifier onlyOwner() { + require(msg.sender == owner, "Only owner is allowed to perform this action"); + _; + } +} + +contract LibraryLockDataLayout { + bool public initialized = false; +} + +contract LibraryLock is LibraryLockDataLayout { + // Ensures no one can manipulate the Logic Contract once it is deployed. + // PARITY WALLET HACK PREVENTION + + modifier delegatedOnly() { + require(initialized == true, "The library is locked. No direct 'call' is allowed"); + _; + } + function initialize() internal { + initialized = true; + } +} + +contract ERC20DataLayout is LibraryLockDataLayout { + uint256 public totalSupply; + mapping(address=>uint256) public tokens; +} + +contract ERC20 { + // ... + function transfer(address to, uint256 amount) public { + require(tokens[msg.sender] >= amount, "Not enough funds for transfer"); + tokens[to] += amount; + tokens[msg.sender] -= amount; + } +} + +contract MyToken is ERC20DataLayout, ERC20, Owned, Proxiable, LibraryLock { + + function constructor1(uint256 _initialSupply) public { + totalSupply = _initialSupply; + tokens[msg.sender] = _initialSupply; + initialize(); + setOwner(msg.sender); + } + function updateCode(address newCode) public onlyOwner delegatedOnly { + updateCodeAddress(newCode); + } + function transfer(address to, uint256 amount) public delegatedOnly { + ERC20.transfer(to, amount); + } +} +``` + +## References + +- ["Escape-hatch" proxy Medium Post](https://medium.com/terminaldotco/escape-hatch-proxy-efb681de108d) + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). + +[1]: https://github.com/maraoz/solidity-proxy/blob/master/contracts/Dispatcher.sol +[2]: https://blog.gnosis.pm/solidity-delegateproxy-contracts-e09957d0f201 diff --git a/EIPS/eip-1829.md b/EIPS/eip-1829.md new file mode 100644 index 0000000..8a2a27a --- /dev/null +++ b/EIPS/eip-1829.md @@ -0,0 +1,150 @@ +--- +eip: 1829 +title: Precompile for Elliptic Curve Linear Combinations +author: Remco Bloemen +discussions-to: https://ethereum-magicians.org/t/ewasm-precompile-for-general-elliptic-curve-math/2581 +status: Stagnant +type: Standards Track +category: Core +created: 2019-03-06 +--- + +# Precompile for Elliptic Curve Linear Combinations + +## Simple Summary + +Currently the EVM only supports *secp256k1* in a limited way through `ecrecover` and *altbn128* through two pre-compiles. There are draft proposals to add more curves. There are many more elliptic curve that have useful application for integration with existing systems or newly developed curves for zero-knowledge proofs. + +This EIP adds a precompile that allows whole classes of curves to be used. + +## Abstract + +A precompile that takes a curve and computes a linear combination of curve points. + +## Motivation + +## Specification + +Given integers `m, α` and `β`, scalars `s_i`, and curve points `A_i` construct the elliptic curve + +``` +y² = x³ + α ⋅ x + β mod m +``` + +and compute the following + +``` +C = s₀ ⋅ A₀ + s₁ ⋅ A₁ + ⋯ + s_n ⋅ A_n +``` + +aka *linear combination*, *inner product*, *multi-multiplication* or even *multi-exponentiation*. + +``` +(Cx, Cy) := ecmul(m, α, β, s0, Ax0, As0, s1, Ax1, As1, ...) +``` + +### Gas cost + +``` +BASE_GAS = ... +ADD_GAS = ... +MUL_GAS = ... +``` + +The total gas cost is `BASE_GAS` plus `ADD_GAS` for each `s_i` that is `1` and `MUL_GAS` for each `s_i > 1` (`s_i = 0` is free). + +### Encoding of points + +Encode as `(x, y')` where `s` indicates whether `y` or `-y` is to be taken. It follows SEC 1 v 1.9 2.3.4, except uncompressed points (`y' = 0x04`) are not supported. + +| `y'` | `(x, y)` | +|--------|-----| +| `0x00` | Point at infinity | +| `0x02` | Solution with `y` even | +| `0x03` | Solution with `y` odd | + +Conversion from affine coordinates to compressed coordinates is trivial: `y' = 0x02 | (y & 0x01)`. + +### Special cases + +**Coordinate recovery.** Set `s₀ = 1`. The output will be the recovered coordinates of `A₀`. + +**On-curve checking.** Do coordinate recovery and compare `y` coordinate. + +**Addition.** Set `s₀ = s₁ = 1`, the output will be `A₀ + A₁`. + +**Doubling.** Set `s₀ = 2`. The output will be `2 ⋅ A₀`. (Note: under current gas model this may be more costly than self-addition!) + +**Scalar multiplication.** Set only `s₀` and `A₀`. + +**Modular square root.** Set `α = s₀ = A = 0` the output will have `Cy² = β mod m`. + +### Edge cases + +* Non-prime moduli or too small modulus +* Field elements larger than modulus +* Curve has singular points (`4 α³ + 27 β² = 0`) +* Invalid sign bytes +* x coordinate not on curve +* Returning the point at infinity +* (Please add if you spot more) + +## Rationale + +**Generic Field and Curve.** Many important optimizations are independent of the field and curve used. Some missed specific optimizations are: + +* Reductions specific to the binary structure of the field prime. +* Precomputation of Montgomery factors. +* Precomputation of multiples of certain popular points like the generator. +* Special point addition/doubling [formulas][formulas] for `α = -3`, `α = -1`, `α = 0`, `β = 0`. + + +[formulas]: https://www.hyperelliptic.org/EFD/g1p/auto-shortw.html + +TODO: The special cases for `α` and `β` might be worth implementing and offered a gas discount. + +**Compressed Coordinates.** Compressed coordinates allow contract to work with only `x` coordinates and sign bytes. It also prevents errors around points not being on-curve. Conversion to compressed coordinates is trivial. + +**Linear Combination.** We could instead have a simple multiply `C = r ⋅ A`. In this case we would need a separate pre-compile for addition. In addition, a linear combination allows for optimizations that like Shamir's trick that are not available in a single scalar multiplication. ECDSA requires `s₀ ⋅ A₀ + s₁ ⋅ A₁` and would benefit from this. + +The BN254 (aka alt_bn8) multiplication operation introduced by the [EIP-196][EIP-196] precompile only handles a single scalar multiplication. The missed performance is such that for two or more points it is cheaper to use EVM, as practically demonstrated by [Weierstrudel][ws]. + +[EIP-196]: ./eip-196.md +[ws]: https://medium.com/aztec-protocol/huffing-for-crypto-with-weierstrudel-9c9568c06901 + +**Variable Time Math.** When called during a transaction, there is no assumption of privacy and no mitigations for side-channel attacks are necessary. + +**Prime Fields.** This EIP is for fields of large characteristic. It does not cover Binary fields and other fields of non-prime characteristic. + +**256-bit modulus.** This EIP is for field moduli less than `2^{256}`. This covers many of the popular curves while still having all parameters fit in a single EVM word. + +TODO: Consider a double-word version. 512 bits would cover all known curves except E-521. In particular it will cover the NIST P-384 curve used by the Estonian e-Identity and the BLS12-381 curve used by [ZCash Sappling][sappling]. + +[sappling]: https://z.cash/blog/new-snark-curve/ + +**Short Weierstrass Curves.** This EIP is for fields specified in short Weierstrass form. While any curve can be converted to short Weierstrass form through a [substitution of variables][cov], this misses out on the performance advantages of those specific forms. + +[cov]: https://safecurves.cr.yp.to/equation.html + +## Backwards Compatibility + +## Test Cases + +## Implementation + +There will be a reference implementation in Rust based on the existing libraries (in particular those by ZCash and The Matter Inc.). + +The reference implementation will be production grade and compile to a native library with a C api and a webassembly version. Node developers are encouraged to use the reference implementation and can use either the rust library, the native C bindings or the webassembly module. Node developers can of course always decide to implement their own. + +## References + +This EIP overlaps in scope with + +* [EIP-196](./eip-196.md): ecadd, ecmul for altbn128 +* [EIP issue 603](https://github.com/ethereum/EIPs/issues/603): ecadd, ecmul for SECP256k1. +* [EIP-665](./eip-665.md): ECDSA verify for ED25519. +* [EIP-1108](./eip-1108.md): Optimize ecadd and ecmul for altbn128. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). + diff --git a/EIPS/eip-1844.md b/EIPS/eip-1844.md new file mode 100644 index 0000000..a80e696 --- /dev/null +++ b/EIPS/eip-1844.md @@ -0,0 +1,64 @@ +--- +eip: 1844 +title: ENS Interface Discovery +author: Nick Johnson (@arachnid) +discussions-to: https://ethereum-magicians.org/t/ens-interface-discovery/2924 +status: Stagnant +type: Standards Track +category: ERC +created: 2019-03-15 +requires: 137, 165 +--- + +## Simple Summary +Defines a method of associating contract interfaces with ENS names and addresses, and of discovering those interfaces. + +## Abstract +This EIP specifies a method for exposing interfaces associated with an ENS name or an address (typically a contract address) and allowing applications to discover those interfaces and interact with them. Interfaces can be implemented either by the target contract (if any) or by any other contract. + +## Motivation +EIP 165 supports interface discovery - determining if the contract at a given address supports a requested interface. However, in many cases it's useful to be able to discover functionality associated with a name or an address that is implemented by other contracts. + +For example, a token contract may not itself provide any kind of 'atomic swap' functionality, but there may be associated contracts that do. With ENS interface discovery, the token contract can expose this metadata, informing applications where they can find that functionality. + +## Specification +A new profile for ENS resolvers is defined, consisting of the following method: + +```solidity +function interfaceImplementer(bytes32 node, bytes4 interfaceID) external view returns (address); +``` + +The EIP-165 interface ID of this interface is `0xb8f2bbb4`. + +Given an ENS name hash `node` and an EIP-165 `interfaceID`, this function returns the address of an appropriate implementer of that interface. If there is no interface matching that interface ID for that node, 0 is returned. + +The address returned by `interfaceImplementer` MUST refer to a smart contract. + +The smart contract at the returned address SHOULD implement EIP-165. + +Resolvers implementing this interface MAY utilise a fallback strategy: If no matching interface was explicitly provided by the user, query the contract returned by `addr()`, returning its address if the requested interface is supported by that contract, and 0 otherwise. If they do this, they MUST ensure they return 0, rather than reverting, if the target contract reverts. + +This field may be used with both forward resolution and reverse resolution. + +## Rationale + +A naive approach to this problem would involve adding this method directly to the target contract. However, doing this has several shortcomings: + + 1. Each contract must maintain its own list of interface implementations. + 2. Modifying this list requires access controls, which the contract may not have previously required. + 3. Support for this must be designed in when the contract is written, and cannot be retrofitted afterwards. + 4. Only one canonical list of interfaces can be supported. + +Using ENS resolvers instead mitigates these shortcomings, making it possible for anyone to associate interfaces with a name, even for contracts not previously built with this in mind. + +## Backwards Compatibility +There are no backwards compatibility issues. + +## Test Cases +TBD + +## Implementation +The PublicResolver in the [ensdomains/resolvers](https://github.com/ensdomains/resolvers/) repository implements this interface. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1872.md b/EIPS/eip-1872.md new file mode 100644 index 0000000..7fcab2a --- /dev/null +++ b/EIPS/eip-1872.md @@ -0,0 +1,216 @@ +--- +eip: 1872 +title: Ethereum Network Upgrade Windows +author: Danno Ferrin (@shemnon) +discussions-to: https://ethereum-magicians.org/t/eip-1872-ethereum-network-upgrade-windows/2993 +status: Stagnant +type: Meta +created: 2018-03-25 +--- + +## Simple Summary + +A proposal to define a limited number of annual time windows in which network +upgrades (aka hard forks) should be performed within. Policies for scheduling +network upgrades outside these windows are also described. + +## Abstract + +Four different weeks, spaced roughly evenly throughout the year, are targeted +for network upgrades to be launched. Regular network upgrades should announce +their intention to launch in a particular window early in their process and +choose a block number four to six weeks prior to that window. If a network +upgrade is cancelled then it would be rescheduled for the next window. Not all +windows will be used. Priority upgrades outside the roadmap may be scheduled in +the third week of any month, but such use is discouraged. Critical upgrades are +scheduled as needed. + +## Motivation + +The aim of this EIP is to provide some level of regularity and predictability to +the Ethereum network upgrade/hard fork process. This will allow service +providers such as exchanges and node operators a predictable framework to +schedule activities around. This also provides a framework to regularize the +delivery of network upgrades. + +## Specification + +Scheduling is defined for three categories of network upgrades. First are +`Roadmap` network upgrades that include deliberate protocol improvements. Next +are `Priority` network updates, where there are technical reasons that +necessitate a prompt protocol change but these reasons do not present a systemic +risk to the protocol or the ecosystem. Finally, `Critical` network upgrades are +to address issues that present a systemic risk to the protocol or the ecosystem. + +### Roadmap Network Upgrades + +Roadmap network upgrades are network upgrades that are deliberate and measured +to improve the protocol and ecosystem. Historical examples are Homestead, +Byzantium, and Constantinople. + +Roadmap network upgrades should be scheduled in one of four windows: the week +with the third Wednesday in January, April, July, and October. When initiating a +network upgrade or early in the planning process a particular window should be +targeted. + +> **Note to reviewers:** The months and week chosen are to provide an initial +> recommendation and are easily modifiable prior to final call. They thread the +> needle between many third quarter and fourth quarter holidays. + +Implementors are expected to have software for a Roadmap network upgrade ready +two to four weeks prior to the upgrade. Hence a block number for the network +upgrade should be chosen four to six weeks prior to the network upgrade window. +Scheduling details such as whether this choice is made prior to or after testnet +deployment are out of scope of this EIP. + +Depending on the release cadence of Roadmap network upgrades some windows will +not be used. For example if a six month release cadence is chosen a roadmap +upgrade would not occur in adjacent upgrade windows. Hence for a six month +cadence if a roadmap upgrade occurred in April then the July window would not be +used for network upgrades. + +If a planned roadmap upgrade needs to be rescheduled then strong consideration +should be given to rescheduling the upgrade for the next window in three months +time. For the case of a six month cadence this may cause releases to be in +adjacent release windows. For a three month cadence the next network upgrade +would be merged with the current upgrade or the next network upgrade would be +delayed. + +To be compatible with the scheduled release windows the cadence of the Roadmap +Network Upgrades should be a multiple of three months. Whether it is three, six, +nine, or more month cadence is out of scope of this EIP. + +### Priority Network Upgrades + +Priority network upgrades are reserved for upgrades that require more urgency +than a roadmap network upgrade yet do not present a systemic risk to the network +or the ecosystem. To date there have been no examples of a priority upgrade. +Possible examples may include roadmap upgrades that need to occur in multiple +upgrades or for security risks that have a existing mitigation in place that +would be better served by a network upgrade. Another possible reason may be to +defuse the difficulty bomb due to postponed roadmap upgrades. + +Priority network upgrades are best launched in unused roadmap launch windows, +namely the third week of January, April, July, and October. If necessary they +may be launched in the third week of any month, but strong consideration and +preference should be given to unused roadmap launch windows. + +Priority network upgrades should be announced and a block chosen far enough in +advance so major clients implementors can release software with the needed block +number in a timely fashion. These releases should occur at least a week before +the launch window. Hence priority launch windows should be chosen two to four +weeks in advance. + +### Critical Network Upgrades + +Critical network upgrades are network upgrades that are designed to address +systemic risks to the protocol or to the ecosystem. Historical examples include +Dao Fork, Tangerine Whistle, and Spurious Dragon. + +This EIP provides neither guidance nor restrictions to the development and +deployment of these emergency hard forks. These upgrades are typically launched +promptly after a solution to the systemic risk is agreed upon between the client +implementors. + +It is recommended that such upgrades perform the minimum amount of changes +needed to address the issues that caused the need for the Critical network +upgrade and that other changes be integrated into subsequent Priority and +Roadmap network upgrades. + +### Network Upgrade Block Number Choice + +When choosing an activation block the number can be used to communicate the role +of a particular network in the Ethereum Ecosystem. Networks that serve as a +value store or are otherwise production grade have different stability concerns +than networks that serve as technology demonstration or are explicitly +designated for testing. + +To date all Mainnet activation blocks have ended in three or more zeros, +including Critical Network Upgrades. Ropsten and Kovan initially started with +three zeros but switched to palindromic numbers. Rinkeby has always had +palindromic activation blocks. Goerli has yet to perform a network upgrade. + +To continue this pattern network upgrade activation block numbers for mainnet +deployments and production grade networks should chose a number whose base 10 +representation ends with three or more zeros. + +For testnet and testing or development grades network operators are encouraged +to choose a block activation number that is a palindrome in base 10. + +Block numbers for Roadmap and Priority network upgrades should be chosen so that +it is forecast to occur relatively close to Wednesday at 12:00 UTC+0 during the +launch window. This should result in an actual block production occurring +sometime between Monday and Friday of the chosen week. + +## Rationale + +The rationale for defining launch windows is to give business running Ethereum +infrastructure a predictable schedule for when upgrades may or may not occur. +Knowing when a upgrade is not going to occur gives the businesses a clear time +frame within which to perform internal upgrades free from external changes. It +also provides a timetable for developers and IT professionals to schedule time +off against. + +## Backwards Compatibility + +Except for the specific launch windows the previous network upgrades would have +complied with these policies. Homestead, Byzantium, and Constantinople would +have been Roadmap Network Upgrades. There were no Priority Network Upgrades, +although Spurious Dragon would have been a good candidate. Dao Fork was a +Critical Network Upgrade in response to TheDao. Tangerine Whistle and Spurious +Dragon were critical upgrades in response to the Shanghai Spam Attacks. +Constantinople Fix (as it is termed in the reference tests) was in response to +the EIP-1283 security issues. + +If this policy were in place prior to Constantinople then the initial 2018 +launch would likely have been bumped to the next window after the Ropsten +testnet consensus failures. The EIP-1283 issues likely would have resulted in an +out of window upgrade because of the impact of the difficulty bomb. + + + + +## Implementation + +The windows in this EIP are expected to start after the Istanbul Network +upgrade, which is the next planned Roadmap upgrade. Istanbul is currently slated +for mainnet launch on 2019-10-16, which is compatible with the schedule in this +EIP. + +The Roadmap Upgrade windows starting with Istanbul would be as follows: + +| Block Target | Launch Week Range | +| ------------ | ------------------------ | +| 2019-10-16 | 2019-10-14 to 2019-10-18 | +| 2020-01-15 | 2020-01-13 to 2020-01-17 | +| 2020-04-15 | 2020-04-13 to 2020-04-17 | +| 2020-07-15 | 2020-07-13 to 2020-07-17 | +| 2020-10-21 | 2020-10-19 to 2020-10-23 | +| 2021-01-20 | 2021-01-18 to 2021-01-22 | +| 2021-04-21 | 2021-04-19 to 2021-04-23 | +| 2021-07-21 | 2021-07-19 to 2021-07-23 | +| 2021-10-20 | 2021-10-18 to 2021-10-22 | +| 2022-01-19 | 2022-01-17 to 2022-01-21 | +| 2022-04-20 | 2022-04-18 to 2022-04-22 | +| 2022-07-20 | 2022-07-18 to 2022-07-22 | +| 2022-10-19 | 2022-10-17 to 2022-10-21 | + +The Priority windows through next year, excluding Roadmap windows, are as +follows: + +| Block Target | Launch Week Range | +| ------------ | ------------------------ | +| 2019-11-20 | 2019-11-18 to 2019-11-22 | +| 2019-12-18 | 2019-12-16 to 2019-12-20 | +| 2020-02-19 | 2020-02-17 to 2020-02-21 | +| 2020-03-18 | 2020-03-16 to 2020-03-20 | +| 2020-05-20 | 2020-05-18 to 2020-05-22 | +| 2020-06-17 | 2020-06-15 to 2020-06-19 | +| 2020-08-19 | 2020-08-18 to 2020-08-21 | +| 2020-09-16 | 2020-09-14 to 2020-09-18 | +| 2020-11-18 | 2020-11-16 to 2020-11-20 | +| 2020-12-16 | 2020-12-14 to 2020-12-18 | + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1884.md b/EIPS/eip-1884.md new file mode 100644 index 0000000..3698c20 --- /dev/null +++ b/EIPS/eip-1884.md @@ -0,0 +1,160 @@ +--- +eip: 1884 +title: Repricing for trie-size-dependent opcodes +author: Martin Holst Swende (@holiman) +type: Standards Track +category: Core +discussions-to: https://ethereum-magicians.org/t/opcode-repricing/3024 +status: Final +created: 2019-03-28 +requires: 150, 1052 +--- + + +## Simple Summary + +This EIP proposes repricing certain opcodes, to obtain a good balance between gas expenditure and resource consumption. + +## Abstract + +The growth of the Ethereum state has caused certain opcodes to be more resource-intensive at this point than +they were previously. This EIP proposes to raise the `gasCost` for those opcodes. + +## Motivation + +An imbalance between the price of an operation and the resource consumption (CPU time, memory etc) +has several drawbacks: + +- It could be used for attacks, by filling blocks with underpriced operations which causes excessive block processing time. +- Underpriced opcodes cause a skewed block gas limit, where sometimes blocks finish quickly but other blocks with similar gas use finish slowly. + +If operations are well-balanced, we can maximise the block gaslimit and have a more stable processing time. + +## Specification + +At block `N`, + +- The `SLOAD` (`0x54`) operation changes from `200` to `800` gas, +- The `BALANCE` (`0x31`) operation changes from `400` to `700` gas, +- The `EXTCODEHASH` (`0x3F`) operation changes from `400` to `700` gas, +- A new opcode, `SELFBALANCE` is introduced at `0x47`. + - `SELFBALANCE` pops `0` arguments off the stack, + - `SELFBALANCE` pushes the `balance` of the current address to the stack, + - `SELFBALANCE` is priced as `GasFastStep`, at `5` gas. + +## Rationale + +Here are two charts, taken from a full sync using Geth. The execution time was measured for every opcode, and aggregated for 10K blocks. These bar charts show the top 25 'heavy' opcodes in the ranges 5M to 6M and 6M to 7M: + +![bars1](../assets/eip-1884/run3.total-bars-5.png) +![bars2](../assets/eip-1884/run3.total-bars-6.png) + +Note: It can also be seen that the `SLOAD` moves towards the top position. The `GASPRICE` (`0x3a`) opcode has position one which I believe can be optimized away within the client -- which is not the case with `SLOAD`/`BALANCE`. + +Here is another chart, showing a full sync with Geth. It represents the blocks `0` to `5.7M`, and highlights what the block processing time is spent on. + +![geth](../assets/eip-1884/geth_processing.png) + +It can be seen that `storage_reads` and `account_reads` are the two most significant factors contributing to the block processing time. + +### `SLOAD` + +`SLOAD` was repriced at [EIP-150][eip-150], from `50` to `200`. +The following graph shows a go-ethereum full sync, where each data point represents + 10K blocks. During those 10K blocks, the execution time for the opcode was aggregated. + +![graph](../assets/eip-1884/SLOAD-run3.png) + +It can be seen that the repricing at [EIP-150][eip-150] caused a steep drop, from around `67` to `23`. +Around block `5M`, it started reaching pre-[EIP-150][eip-150] levels, and at block `7M` +it was averaging on around `150` - more than double pre-eip-150 levels. + +Increasing the cost of `SLOAD` by `4` would bring it back down to around `40`. +It is to be expected that it will rise again in the future, and may need future repricing, unless +state clearing efforts are implemented before that happens. + +### `BALANCE` + +`BALANCE` (a.k.a `EXTBALANCE`) is an operation which fetches data from the state trie. It was repriced at [EIP-150][eip-150] from `20` to `400`. + +![graph](../assets/eip-1884/BALANCE-run3.png) + +It is comparable to `EXTCODESIZE` and `EXTCODEHASH`, which are priced at `700` already. + +It has a built-in high variance, since it is often used for checking the balance of `this`, +which is a inherently cheap operation, however, it can be used to lookup the balance of arbitrary account which often require trie (disk) access. + +In hindsight, it might have been a better choice to have two +opcodes: `EXTBALANCE(address)` and `SELFBALANCE`, and have two different prices. + +* This EIP proposes to extend the current opcode set. + * Unfortunately, the opcode span `0x3X` is already full, hence the suggestion to place `SELFBALANCE` in the `0x4X` range. + * As for why it is priced at `5` (`GasFastStep`) instead of `2` (`GasQuickStep`), like other similar operations: the EVM execution engine still needs a lookup into the (cached) trie, and `balance`, unlike `gasPrice` or `timeStamp`, is not constant during the execution, so it has a bit more inherent overhead. + + +### `EXTCODEHASH` + +`EXTCODEHASH` was introduced in Constantinople, with [EIP-1052](./eip-1052.md). It was priced at `400` with the reasoning: + +> The gas cost is the same as the gas cost for the `BALANCE` opcode because the execution of the `EXTCODEHASH` requires the same account lookup as in `BALANCE`. + +Ergo, if we increase `BALANCE`, we should also increase `EXTCODEHASH` + + +## Backwards Compatibility + +The changes require a hardfork. The changes have the following consequences: + +- Certain calls will become more expensive. +- Default-functions which access the storage and may in some cases require more than`2300` gas (the minimum gas that is always available in calls). +- Contracts that assume a certain fixed gas cost for calls (or internal sections) may cease to function. + - A fixed gas cost is specified in [ERC-165](./eip-165.md) and implementations of this interface do use the affected opcodes. + - The ERC-165 method `supportsInterface` must return a `bool` and use at most `30,000` gas. + - The two example implementations from the EIP were, at the time of writing + 1. `586` gas for any input, and + 2. `236` gas, but increases linearly with a higher number of supported interfaces + - It is unlikely that any ERC-165 `supportsInterface` implementation will go above `30.000` gas. That would require that the second variant is used, and thirty:ish interfaces are supported. + - However, these operations have already been repriced earlier, so there is a historical precedent that 'the gascost for these operations may change', which should have prevented such fixed-gas-cost assumptions from being implemented. + +I expect that certain patterns will be less used, for example the use of multiple modifiers which `SLOAD`s the same opcode will be merged into one. It may also lead to less `log` operations containing `SLOAD`ed values that are not strictly necessary. + +## Test Cases + +Testcases that should be implemented: +- Test that `selfbalance == balance(address)`, +- Test that `balance(this)` costs as before, +- Test that `selfbalance` does not pop from stack +- Gascost verification of `SLOAD`, `EXTCODEHASH` and `SELFBALANCE` +- Verify that `SELFBALANCE` is invalid before Istanbul + +Some testcases have been implemented as statetests at https://github.com/holiman/IstanbulTests/tree/master/GeneralStateTests + +## Implementation + +This EIP has not yet been implemented in any client. +Both these opcodes have been repriced before, and the client internals for managing reprices are already in place. + +### `SELFBALANCE` + +This is the implementation for the new opcode in go-ethereum: + +```golang + +func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(interpreter.intPool.get().Set(interpreter.evm.StateDB.GetBalance(contract.Address()) + return nil, nil +} + +``` + +## Security considerations + +- See backwards compatibility section. +- There are no special edgecases regarding `SELFBALANCE`, if we define it as `BALANCE` with `address` instead of popping an address from the stack -- since `BALANCE` is already well-defined. +- It should be investigated if Solidity contains any hardcoded expectations on the gas cost of these operations. +- In many cases, a recipient of `ether` from a `CALL` will want to issue a `LOG`. The `LOG` operation costs `375` plus `375` per topic. If the `LOG` also wants to do an `SLOAD`, this change may make some such transfers fail. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). + +[eip-150]: ./eip-150.md diff --git a/EIPS/eip-1890.md b/EIPS/eip-1890.md new file mode 100644 index 0000000..8c3a38b --- /dev/null +++ b/EIPS/eip-1890.md @@ -0,0 +1,53 @@ +--- +eip: 1890 +title: Commitment to Sustainable Ecosystem Funding +author: Gregory Markou , Kevin Owocki , Lane Rettig +discussions-to: https://t.me/joinchat/DwEd_xahL5hHvzNYH2RnQA +status: Withdrawn +type: Standards Track +category: Core +created: 2019-03-31 +--- + +# Commitment to Sustainable Ecosystem Funding + +## Simple Summary + +Ethereum currently provides a block reward to proof of work miners every block, but it does not capture any block rewards for ecosystem funding. This EIP adds a simple mechanism for capturing a portion of block rewards for ecosystem funding as a credible commitment to doing so in future, but it does not actually capture any such rewards. + +## Abstract + +A mechanism that allows specification of two parameters, a beneficiary address and a per-block reward denominated in wei, that allows a portion of block rewards to be captured for the purpose of ecosystem funding. Both values are set to zero. + +## Motivation + +In order for Ethereum to succeed, it needs talented, motivated researchers and developers to continue to develop and maintain the platform. Those talented researchers and developers deserve to be paid fairly for their work. At present there is no mechanism in the Ethereum ecosystem that rewards R&D teams fairly for their work on the platform. + +We recognize that, while technically trivial, the real challenge in inflation-based funding is social: how to fairly capture, govern, and distribute block rewards. It will take time to work out the answer to these questions. For this reason, this EIP only seeks to make a credible commitment on the part of core developers to securing the funding they need to keep Ethereum alive and healthy by adding a mechanism to do so, but the actual amount of rewards captured remains at zero, i.e., there is no change at present to Ethereum’s economics. Raising the amount captured above zero would require a future EIP. + +## Specification + +Two new constants are introduced: BENEFICIARY_ADDRESS, an Address, and DEVFUND_BLOCK_REWARD, an amount denominated in wei. Both are set to zero. + +Beginning with block ISTANBUL_BLOCK_HEIGHT, DEVFUND_BLOCK_REWARD wei is added to the balance of BENEFICIARY_ADDRESS at each block. + +We may optionally add another constant, DECAY_FACTOR, which specifies a linear or exponenential decay factor that reduces the reward at every block > ISTANBUL_BLOCK_HEIGHT until it decays to zero. For simplicity, it has been omitted from this proposal. + +## Rationale + +We believe that the technical design of this EIP is straightforward. The social rationale is explained in [this article](https://medium.com/gitcoin/funding-open-source-in-the-blockchain-era-8ded753bf05f). + +## Backwards Compatibility + +This EIP has no impact on backwards compatibility. + +## Test Cases + +This EIP makes no changes to existing state transitions. Existing consensus tests should be sufficient. + +## Implementation + +Reference implementations are included for the Trinity, go-ethereum, and parity clients. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1895.md b/EIPS/eip-1895.md new file mode 100644 index 0000000..5c52083 --- /dev/null +++ b/EIPS/eip-1895.md @@ -0,0 +1,160 @@ +--- +eip: 1895 +title: Support for an Elliptic Curve Cycle +author: Alexandre Belling +discussions-to: https://ethresear.ch/t/reducing-the-verification-cost-of-a-snark-through-hierarchical-aggregation/5128 +status: Stagnant +type: Standards Track +category: Core +created: 2018-03-31 +--- + +## Simple Summary + +The EVM currently supports elliptic curves operations for curve *alt-bn128* thanks to precompiles `ecadd` and `ecmul` and `ecpairing`. The classes MNT4 and 6 contain cycles of curves. Those cycles enable doing operations on one curve inside a SNARK on the other curve (and reversely). This EIP suggests adding support for those curves. + +## Abstract + +Adds supports for the following operations through precompiles: + +* `ecadd` on MNT4 +* `ecmul` on MNT4 +* `ecpairing` on MNT4 + +## Motivation + +Elliptic curve is the basic block of recursive SNARKs (ie: verifying a SNARK inside a SNARK) and this addresses the issue of scalable zero-knowledge. More generally this addresses partly the scalability issue as SNARKs verification are constant time in the size of the circuit being verified. + +More concretely, today if the EVM has to deal with 1000s of SNARK verification it would take around 1.5 billion gas and would be impractical for Ethereum. Recursive SNARKs for instance make it possible to aggregate multiple proofs into a single one that can be verified like any other SNARK. It results in a massive cost reduction for the verification. + +However, this is impossible using *alt-bn128* and in my knowledge, the only family of pairing-friendly curves known to produce cycles are MNT4 and MNT6. A complete characterization of the cycles existing between those two families is proposed in [On cycles of pairing-friendly elliptic curves +](https://arxiv.org/pdf/1803.02067.pdf) + +## Specification + +### The curve + +The proposed cycle has been introduced in [Scalable Zero Knowledge via Cycles of Elliptic Curves](https://eprint.iacr.org/2014/595.pdf). + +### MNT4 definition + +The groups `G_1` and `G_2` are cyclic groups of prime order : + +```. +q = 475922286169261325753349249653048451545124878552823515553267735739164647307408490559963137 +``` + +`G_1` is defined over the field `F_p` of prime order : + +```. +p = 475922286169261325753349249653048451545124879242694725395555128576210262817955800483758081 +``` + +with generator P: + +```. +P = ( + 60760244141852568949126569781626075788424196370144486719385562369396875346601926534016838, + 363732850702582978263902770815145784459747722357071843971107674179038674942891694705904306 +) +``` + +Both p and q can be written in 298 bits. + +The group G_1 is defined on the curve defined by the equation `Y² = X³ + aX + b` where: + +```. + a = 2 + b = 423894536526684178289416011533888240029318103673896002803341544124054745019340795360841685 +``` + +The twisted group G_2 is defined over the field `F_p^2 = F_p / <>` + +The twisted group G_2 is defined on the curve defined by the equation `Y² = X² + aX + b` where : + +```. + a = 34 + i * 0 + b = 0 + i * 67372828414711144619833451280373307321534573815811166723479321465776723059456513877937430 +``` + +G_2 generator is generated by : + +```. + P2 = ( + 438374926219350099854919100077809681842783509163790991847867546339851681564223481322252708 + + i * 37620953615500480110935514360923278605464476459712393277679280819942849043649216370485641, + 37437409008528968268352521034936931842973546441370663118543015118291998305624025037512482 + + i * 424621479598893882672393190337420680597584695892317197646113820787463109735345923009077489 + ) +``` + +### The operations and gas cost + +The following operations and their gas cost would be implemented + +```. +MNT_X_ADD = <> +MNT_X_MUL = <> +MNT_X_PAIRING = <> +``` + +Where `X` is either 4. + +### Encoding + +The curves points P(X, Y) over F_p are represented in their compressed form C(X, Y): + +```. + C = X | s +``` + +where `s` represents `Y` as follow: + +```. + | `s'` | `Y` | + |--------|--------------------------| + | `0x00` | Point at infinity | + | `0x02` | Solution with `y` even | + | `0x03` | Solution with `y` odd | +``` + +Compression operation from affine coordinate is trivial: + +```. + s = 0x02 | (s & 0x01) +``` + +In the EVM the compressed form allows us to represents curve points with 2 uint256 instead of 3. + +### Edge cases + +* Several acceptable representations for the point at infinity + +## Rationale + +The curve has 80 bits of security (whereas MNT6 has 120 bits) which might not be considered enough for critical security level, (for instance transferring several billions), but enough for others. If it turns out this is not enough security for adoption, there is another option : another cycle is being used by Coda but is defined over a 753 bits sized field which might also be prohibitively low (no reference to this curve from Coda's publications found). + +Independently of the cycle chosen, the groups and field elements are represented with integers larger than 256 bits (even for the 80 bits of security), therefore it might be necessary to also add support for larger field size operations. + +We currently don't know more efficient pairing-friendly cycles and don't know if there are. It might be possible to circumvent this problem though by relaxing the constraint that all the curves of the cycle must be pairing friendly). If we had a cycle with only one pairing friendly curve we would still be able to compose proofs by alternating between SNARKs and any other general purpose zero-knowledge cryptosystems. + +Assuming we find a convenient cycle, we don't need to implement support for all the curves it contains, only one. The best choice would be the fastest one as the overall security of the recursive snark do not depends on which curve the verification is made. + +Proper benchmarks will be done in order to make this choice and to price the operations in gas. + +## Test Cases + +## References + +* *Eli-Ben-Sasson, Alessandro Chiesa, Eran Tromer, Madars Virza, [BCTV14], April 28, 2015, Scalable Zero Knowledge via Cycles of Elliptic Curves : https://eprint.iacr.org/2014/595.pdf* +* *Alessandro Chiesa, Lynn Chua, Matthew Weidner, [CCW18], November 5, 2018, On cycles of pairing-friendly elliptic curves : https://arxiv.org/pdf/1803.02067.pdf* + +## Implementation + +* [go-boojum](https://github.com/AlexandreBelling/go-boojum) : A PoC demo of an application of recursive SNARKs +* [libff](https://github.com/scipr-lab/libff) : a C++ library for finite fields and elliptic curves +* [coda](https://github.com/CodaProtocol/coda) : a new cryptocurrency protocol with a lightweight, constant sized blockchain. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1898.md b/EIPS/eip-1898.md new file mode 100644 index 0000000..0bb8bc4 --- /dev/null +++ b/EIPS/eip-1898.md @@ -0,0 +1,95 @@ +--- +eip: 1898 +title: Add `blockHash` to defaultBlock methods +description: Add `blockHash` option to JSON-RPC methods that currently support defaultBlock parameter. +author: Charles Cooper (@charles-cooper) +discussions-to: https://ethereum-magicians.org/t/eip-1898-add-blockhash-option-to-json-rpc-methods-that-currently-support-defaultblock-parameter/11757 +status: Review +type: Standards Track +category: Interface +created: 2019-04-01 +requires: 234 +--- + +## Abstract + +For JSON-RPC methods which currently accept a default block parameter, additionally allow the parameter to be a block hash. + +This EIP can be considered a generalization of [EIP-234](./eip-234.md). It would enable clients to unambiguously specify the block they want to query for certain JSON-RPC methods, even if the block is not in the canonical chain. This allows clients to maintain a coherent picture of blockchain state that they are interested in, even in the presence of reorgs, without requiring that the node maintain a persistent connection with the client or store any client-specific state. + +## Specification + +The following JSON-RPC methods are affected: + +- `eth_getBalance` +- `eth_getStorageAt` +- `eth_getTransactionCount` +- `eth_getCode` +- `eth_call` +- `eth_getProof` + +The following options, quoted from the Ethereum JSON-RPC spec, are currently possible for the defaultBlock parameter: + +> - HEX String - an integer block number +> - String "earliest" for the earliest/genesis block +> - String "latest" - for the latest canonical block +> - String "pending" - for the pending state/transactions +> - String "safe" - for the most recent safe block +> - String "finalized" - for the most recent finalized block + +Since there is no way to clearly distinguish between a DATA parameter and a QUANTITY parameter, this EIP proposes a new scheme for the block parameter. The following option is additionally allowed: + +- OBJECT + - `blockNumber`: QUANTITY - a block number + - `blockHash`: DATA - a block hash + +If the block is not found, the callee SHOULD raise a JSON-RPC error (the recommended error code is `-32001: Resource not found`). + +If the tag is `blockHash`, an additional boolean field may be supplied to the block parameter, `requireCanonical`, which defaults to `false` and defines whether the block must be a canonical block according to the callee. If `requireCanonical` is `false`, the callee should raise a JSON-RPC error only if the block is not found (as described above). If `requireCanonical` is `true`, the callee SHOULD additionally raise a JSON-RPC error if the block is not in the canonical chain (the recommended error code is `-32000: Invalid input` and in any case should be different than the error code for the block not found case so that the caller can distinguish the cases). The block-not-found check SHOULD take precedence over the block-is-canonical check, so that if the block is not found the callee raises block-not-found rather than block-not-canonical. + +To maintain backwards compatibility, the block number MAY be specified either as a hex string or using the new block parameter scheme. In other words, the following are equivalent for the default block parameter: + +- `"earliest"` +- `"0x0"` +- `{ "blockNumber": "0x0" }` +- `{ "blockHash": "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" }` (hash of the genesis block on the Ethereum main chain) +- `{ "blockHash": "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3", "requireCanonical": true }` +- `{ "blockHash": "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3", "requireCanonical": false }` + +## Rationale + +Currently, the state-querying JSON-RPC methods specified above have no option to unambiguously specify which block to query the state for. This can cause issues for applications which need to make multiple calls to the RPC. For instance, a wallet which just executed a transfer may want to display the balances of both the sender and recipient. If there is a re-org in between when the balance of the sender is queried via `eth_getBalance` and when the balance of the recipient is queried, the balances may not reconcile. As a slightly more complicated example, the UI for a decentralized exchange (which hosts orders on-chain) may walk a list of orders by calling `eth_call` for each of them to get the order data. Another type of use case is where an application needs to make a decision based on multiple pieces of state, e.g. a payout predicated on simultaneous ownership of two NFTs. + +In order to ensure that the state is coherent (i.e., `eth_call` was called with exactly the same block for every call), the application may currently use one of several strategies: + +- Decide on a block number to use (e.g., the latest block number known to the application). After each `eth_call` using that block number, call `eth_getBlockByNumber`, also with that block number. If the block hash does not match the known hash for that block number, rollback the current activity and retry from the beginning. This adds `O(n)` invocations as baseline overhead and another `O(n)` invocations for every retry needed. Moreover, there is no way to detect the (unlikely but possible) case that the relevant block was reorged out before `eth_call`, and then reorged back in before `eth_getBlockByNumber`. +- Rely on logs, which *can* be queried unambiguously thanks to the `blockHash` parameter. However, this requires semantic support from the smart contract; if the smart contract does not emit appropriate events, the client will not be able to reconstruct the specific state it is interested in. +- Rely on non-standard extensions like `parity_subscribe`. This requires a persistent connection between the client and node (via IPC or websockets), increases coupling between the client and the node, and cannot handle use cases where there are dependencies between invocations of `eth_call`, for example, walking a linked list. + +Allowing `eth_call` and friends to unambiguously specify the block to be queried give the application developer a robust and intuitive way to solve these problems. Multiple sequential queries will query the same state, enabling the application developer to not worry about inconsistencies in their view of the blockchain state. + +## Backwards Compatibility + +Backwards compatible. + +## Test Cases + +- `eth_getStorageAt [ "0x
", { "blockNumber": "0x0" }` -> return storage at given address in genesis block +- `eth_getStorageAt [ "0x
", { "blockHash": "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" }` -> return storage at given address in genesis block +- `eth_getStorageAt [ "0x
", { "blockHash": "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3", "requireCanonical": false }` -> return storage at given address in genesis block +- `eth_getStorageAt [ "0x
", { "blockHash": "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3", "requireCanonical": true }` -> return storage at given address in genesis block +- `eth_getStorageAt [ "0x
", { "blockHash": "0x" }` -> raise block-not-found error +- `eth_getStorageAt [ "0x
", { "blockHash": "0x", "requireCanonical": false }` -> raise block-not-found error +- `eth_getStorageAt [ "0x
", { "blockHash": "0x", "requireCanonical": true }` -> raise block-not-found error +- `eth_getStorageAt [ "0x
", { "blockHash": "0x" }` -> return storage at given address in specified block +- `eth_getStorageAt [ "0x
", { "blockHash": "0x", "requireCanonical": false }` -> return storage at given address in specified block +- `eth_getStorageAt [ "0x
", { "blockHash": "0x", "requireCanonical": true }` -> raise block-not-canonical error + +## Security Considerations + +None + + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-190.md b/EIPS/eip-190.md new file mode 100644 index 0000000..bf90970 --- /dev/null +++ b/EIPS/eip-190.md @@ -0,0 +1,96 @@ +--- +eip: 190 +title: Ethereum Smart Contract Packaging Standard +author: Piper Merriam (@pipermerriam), Tim Coulter (@tcoulter), Denis Erfurt (@mhhf), RJ Catalano (@VoR0220), Iuri Matias (@iurimatias) +status: Final +type: Standards Track +category: ERC +created: 2017-01-10 +--- + +# Abstract + +This ERC proposes a specification for Ethereum smart contract packages. + +The specification was collaboratively developed by the following Ethereum development framework maintainers. + +* Tim Coulter (Truffle) +* Denis Erfurt (Dapple) +* Piper Merriam (Populus) +* RJ Catalano (Eris PM) +* Iuri Matias (Embark) + +# Motivation + +Packaging is a core piece of modern software development which is missing from the Ethereum ecosystem. The lack of packaging limits the ability for developers to reuse code which negatively affects productivity and security. + +A key example of this is the ERC20 standard. There are a few well audited reusable token contracts available but most developers end up writing their own because of the difficulty in finding and reusing existing code. + +A packaging standard should have the following positive effects on the ecosystem: + +* Greater overall productivity caused by the ability to reuse existing code. +* Increased security caused by the ability to reuse existing well audited implementations of common patterns (ERC20, crowdfunding, etc). + +Smart contract packaging should also have a direct positive effect on the end user. Wallet software will be able to consume a released package and generate an interface for interacting with any deployed contracts included within that package. With the advent of [ENS](./eip-137.md) all of the pieces will be in place for a wallet to take a human readable name and present the user with an interface for interacting with the underlying application. + + +# Specification + +The full specification for this standard is maintained separately in the repository [epm/epm-spec](https://github.com/ethpm/epm-spec). + +This EIP refers to the `1.0.0` version of the specification: [https://github.com/ethpm/epm-spec/tree/v1.0.0](https://github.com/ethpm/epm-spec/tree/v1.0.0) + +The specification contains details for a single document referred to as a *"Release Lockfile"*. + +* Release Lockfile Specification: [https://github.com/ethpm/epm-spec/blob/v1.0.0/release-lockfile.spec.md](https://github.com/ethpm/epm-spec/blob/v1.0.0/release-lockfile.spec.md). +* JSON Schema for Release Lockfile: [https://github.com/ethpm/epm-spec/blob/v1.0.0/spec/release-lockfile.spec.json](https://github.com/ethpm/epm-spec/blob/v1.0.0/spec/release-lockfile.spec.json) + +> These documents have not been inlined into this ERC to ensure that there is a single source of truth for the specification. + + +# Use Cases + +This specification covers the following types of smart contract packages. + +1. Packages with contracts intended to be used as base contract such as the common `owned` pattern. +2. Packages with contracts that are ready to use as-is such as an ERC20 token contract. +3. Packages with deployed contracts such as libraries or services. + +Full explanations and examples of these use cases can be found in the [`README.md`](https://github.com/ethpm/epm-spec/blob/v1.0.0/README.md#use-cases) from the `epm/epm-spec` repository. + + +# Package Managers + +The *Release Lockfile* is intended for consumption by package management software. Specific care was made to ensure that all of the following functionality can be implemented by package managers. + + +## Deterministic builds + +Ensures that a package will always resolve to the same set of dependencies and source files. Both source files and dependencies are content addressed to ensure that the referenced resources cannot change. + + +## Bytecode verification + +Contains the appropriate information for a package manager to inspect a deployed contract and verify that its bytecode matches the bytecode that results from compilation and linking of the package source code. + + +## Multi-chain deploys + +Supports deployments across multiple chains, allowing a package to define addresses on both the public mainnet and testnet. + + +## Trusted packages + +Allows for packages which exclude source code or other elements which would be needed for verification of the contract bytecode. This allows for minimalistic packages to be created for special situations where the package manager will not be performing verification. + + +# Framework support and integration + +Support for ERC190 is either implemented or in progress for the following: + +* [Truffle](https://truffleframework.com/) +* [Populus](https://populus.readthedocs.io/en/latest/) +* [Dapple](https://dapple.readthedocs.io/en/master/) +* [Eris PM](https://github.com/eris-ltd/eris-cli) +* [Embark](https://github.com/iurimatias/embark-framework) +* [Browser Solidity](https://github.com/ethereum/remix-ide/issues/386) diff --git a/EIPS/eip-1900.md b/EIPS/eip-1900.md new file mode 100644 index 0000000..a11b794 --- /dev/null +++ b/EIPS/eip-1900.md @@ -0,0 +1,276 @@ +--- +eip: 1900 +title: dType - Decentralized Type System for EVM +author: Loredana Cirstea (@loredanacirstea), Christian Tzurcanu (@ctzurcanu) +discussions-to: https://github.com/ethereum/EIPs/issues/1882 +status: Stagnant +type: Standards Track +category: ERC +created: 2019-03-28 +--- + +## Simple Summary + +The EVM and related languages such as Solidity need consensus on an extensible Type System in order to further evolve into the Singleton Operating System (The World Computer). + +## Abstract + +We are proposing a decentralized Type System for Ethereum, to introduce data definition (and therefore ABI) consistency. This ERC focuses on defining an on-chain Type Registry (named `dType`) and a common interface for creating types, based on `struct`s. + + +## Motivation + +In order to build a network of interoperable protocols on Ethereum, we need data standardization, to ensure a smooth flow of on-chain information. Off-chain, the Type Registry will allow a better analysis of blockchain data (e.g. for blockchain explorers) and creation of smart contract development tools for easily using existing types in a new smart contract. + +However, this is only the first phase. As defined in this document and in the future proposals that will be based on this one, we are proposing something more: a decentralized Type System with Data Storage - [ERC-2158](https://github.com/ethereum/EIPs/pull/2158). In addition, developers can create libraries of `pure` functions that know how to interact and modify the data entries - [dType Functions Extension](https://github.com/ethereum/EIPs/issues/1921). This will effectively create the base for a general functional programming system on Ethereum, where developers can use previously created building blocks. + +To summarize: + +* We would like to have a good decentralized medium for integrating all Ethereum data, and relationships between the different types of data. Also, a way to address the behavior related to each data type. +* Functional programming becomes easier. Functions like `map`, `reduce`, `filter`, are implemented by each type library. +* Solidity development tools could be transparently extended to include the created types (For example in IDEs like Remix). At a later point, the EVM itself can have precompiled support for these types. +* The system can be easily extended to types pertaining to other languages. (With type definitions in the source (Swarm stored source code in the respective language)) +* The dType database should be part of the System Registry for the Operating System of The World Computer + + +## Specification + +The Type Registry can have a governance protocol for its CRUD operations. However, this, and other permission guards are not covered in this proposal. + +### Type Definition and Metadata + +The dType registry should support the registration of Solidity's elementary and complex types. In addition, it should also support contract events definitions. In this EIP, the focus will be on describing the minimal on-chain type definition and metadata needed for registering Solidity user-defined types. + +#### Type Definition: TypeLibrary + +A type definition consists of a type library containing: +- the nominal `struct` used to define the type +- additional functions: + - `isInstanceOf`: checks whether a given variable is an instance of the defined type. Additional rules can be defined for each type fields, e.g. having a specific range for a `uint16 amount`. + - provide HOFs such as `map`, `filter`, `reduce` + - `structureBytes` and `destructureBytes`: provide type structuring and destructuring. This can be useful for low-level calls or assembly code, when importing contract interfaces is not an efficient option. It can also be used for type checking. + +A simple example is: + +```solidity +pragma solidity ^0.5.0; +pragma experimental ABIEncoderV2; + +library myBalanceLib { + + struct myBalance { + string accountName; + uint256 amount; + } + + function structureBytes(bytes memory data) pure public returns(myBalance memory balance) + + function destructureBytes(myBalance memory balance) pure public returns(bytes memory data) + + function isInstanceOf(myBalance memory balance) pure public returns(bool isInstance) + + function map( + address callbackAddr, + bytes4 callbackSig, + myBalance[] memory balanceArr + ) + view + internal + returns (myBalance[] memory result) +} +``` + +Types can also use existing types in their composition. However, this will always result in a directed acyclic graph. + +```solidity +library myTokenLib { + using myBalanceLib for myBalanceLib.myBalance; + + struct myToken { + address token; + myBalanceLib.myBalance; + } +} +``` + +#### Type Metadata: dType Registry + +Type metadata will be registered on-chain, in the dType registry contract. This consists of: +- `name` - the type's name, as it would be used in Solidity; it can be stored as a `string` or encoded as `bytes`. The name can have a human-readable part and a version number. +- `typeChoice` - used for storing additional ABI data that differentiate how types are handled on and off chain. It is defined as an `enum` with the following options: `BaseType`, `PayableFunction`, `StateFunction`, `ViewFunction`, `PureFunction`, `Event` +- `contractAddress` - the Ethereum `address` of the `TypeRootContract`. For this proposal, we can consider the Type Library address as the `TypeRootContract`. Future EIPs will make it more flexible and propose additional TypeStorage contracts that will modify the scope of `contractAddress` - [ERC-2158](https://github.com/ethereum/EIPs/pull/2158). +- `source` - a `bytes32` Swarm hash where the source code of the type library and contracts can be found; in future EIPs, where dType will be extended to support other languages (e.g. JavaScript, Rust), the file identified by the Swarm hash will contain the type definitions in that language. +- `types` - metadata for subtypes: the first depth level internal components. This is an array of objects (`structs`), with the following fields: + - `name` - the subtype name, of type `string`, similar to the above `name` definition + - `label` - the subtype label + - `dimensions` - `string[]` used for storing array dimensions. E.g.: + - `[]` -> `TypeA` + - `[""]` -> `TypeA[]` + - `["2"]` -> `TypeA[2]` + - `["",""]` -> `TypeA[][]` + - `["2","3"]` -> `TypeA[2][3]` + +Examples of metadata, for simple, value types: +```javascript +{ + "contractAddress": "0x0000000000000000000000000000000000000000", + "typeChoice": 0, + "source": "0x0000000000000000000000000000000000000000000000000000000000000000", + "name": "uint256", + "types": [] +} + +{ + "contractAddress": "0x0000000000000000000000000000000000000000", + "typeChoice": 0, + "source": "0x0000000000000000000000000000000000000000000000000000000000000000", + "name": "string", + "types": [] +} +``` + +Composed types can be defined as: +```javascript +{ + "contractAddress": "0x105631C6CdDBa84D12Fa916f0045B1F97eC9C268", + "typeChoice": 0, + "source": , + "name": "myBalance", + "types": [ + {"name": "string", "label": "accountName", dimensions: []}, + {"name": "uint256", "label": "amount", dimensions: []} + ] +} +``` + +Composed types can be further composed: +```javascript +{ + "contractAddress": "0x91E3737f15e9b182EdD44D45d943cF248b3a3BF9", + "typeChoice": 0, + "source": , + "name": "myToken", + "types": [ + {"name": "address", "label": "token", dimensions: []}, + {"name": "myBalance", "label": "balance", dimensions: []} + ] +} +``` + +`myToken` type will have the final data format: `(address,(string,uint256))` and a labeled format: `(address token, (string accountName, uint256 amount))`. + +##### dType Registry Data Structures and Interface + +To store this metadata, the dType registry will have the following data structures: + +```solidity +enum TypeChoices { + BaseType, + PayableFunction, + StateFunction, + ViewFunction, + PureFunction, + Event +} + +struct dTypes { + string name; + string label; + string[] dimensions; +} + +struct dType { + TypeChoices typeChoice; + address contractAddress; + bytes32 source; + string name; + dTypes[] types; +} + +``` + +For storage, we propose a pattern which isolates the type metadata from additional storage-specific data and allows CRUD operations on records. + +```solidity +// key: identifier +mapping(bytes32 => Type) public typeStruct; + +// array of identifiers +bytes32[] public typeIndex; + +struct Type { + dType data; + uint256 index; +} +``` + +Note that we are proposing to define the type's primary identifier, `identifier`, as `keccak256(abi.encodePacked(name))`. If the system is extended to other programming languages, we can define `identifier` as `keccak256(abi.encodePacked(language, name))`. +Initially, single word English names can be disallowed, avoiding name squatting. + + +The dType registry interface is: + +```solidity +import './dTypeLib.sol'; +interface dType { + event LogNew(bytes32 indexed identifier, uint256 indexed index); + event LogUpdate(bytes32 indexed identifier, uint256 indexed index); + event LogRemove(bytes32 indexed identifier, uint256 indexed index); + + function insert(dTypeLib.dType calldata data) external returns (bytes32 identifier); + + function remove(bytes32 identifier) external returns(uint256 index); + + function count() external view returns(uint256 counter); + + function getTypeIdentifier(string memory name) pure external returns (bytes32 identifier); + + function getByIdentifier(bytes32 identifier) view external returns(dTypeLib.dType memory dtype); + + function get(string memory name) view external returns(dTypeLib.dType memory dtype); + + function isRegistered(bytes32 identifier) view external returns(bool registered); +} +``` + +**Notes:** + +To ensure backward compatibility, we suggest that updating types should not be supported. + +The `remove` function can also be removed from the interface, to ensure immutability. One reason for keeping it would be clearing up storage for types that are not in use or have been made obsolete. However, this can have undesired effects and should be accompanied by a solid permissions system, testing and governance process. This part will be updated when enough feedback has been received. + +## Rationale + +The Type Registry must store the minimum amount of information for rebuilding the type ABI definition. This allows us to: +* support on-chain interoperability +* decode blockchain side effects off-chain (useful for block explorers) +* allow off-chain tools to cache and search through the collection (e.g. editor plugin for writing typed smart contracts) + +There is one advantage that has become clear with the emergence of global operating systems, like Ethereum: we can have a global type system through which the system’s parts can interoperate. Projects should agree on standardizing types and a type registry, continuously working on improving them, instead of creating encapsulated projects, each with their own types. + +The effort of having consensus on new types being added or removing unused ones is left to the governance system. + +After the basis of such a system is specified, we can move forward to building a static type checking system at compile time, based on the type definitions and rules stored in the dType registry. + +The Type Library must express the behavior strictly pertinent to its defined type. Additional behavior, required by various project's business logic can be added later, through libraries containing functions that handle the respective type. These can also be registered in dType, but will be detailed in a future ERC. + +This is an approach that will separate definitions from stored data and behavior, allowing for easier and more secure fine-grained upgrades. + +## Backwards Compatibility + +This proposal does not affect extant Ethereum standards or implementations. It uses the present experimental version of ABIEncoderV2. + +## Test Cases + +Will be added. + +## Implementation + +An in-work implementation can be found at https://github.com/pipeos-one/dType/tree/master/contracts/contracts. +This proposal will be updated with an appropriate implementation when consensus is reached on the specifications. + +A video demo of the current implementation (a more extended version of this proposal) can be seen at https://youtu.be/pcqi4yWBDuQ. + + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1901.md b/EIPS/eip-1901.md new file mode 100644 index 0000000..ad873a4 --- /dev/null +++ b/EIPS/eip-1901.md @@ -0,0 +1,105 @@ +--- +eip: 1901 +title: Add OpenRPC Service Discovery To JSON-RPC Services +author: Shane Jonas (@shanejonas), Zachary Belford (@belfordz) +discussions-to: https://github.com/ethereum/EIPs/issues/1902 +status: Stagnant +type: Standards Track +category: Interface +created: 2019-02-25 +--- + +## Abstract +### What is this? + +This is a proposal to add [OpenRPC](https://github.com/open-rpc/spec) support to existing and future JSON-RPC services by adding the method [`rpc.discover`](https://github.com/open-rpc/spec#service-discovery-method) to the projects [JSON-RPC](https://www.jsonrpc.org/specification) APIs, enabling automation and tooling. + +The OpenRPC Document and generated Documentation that specifies all the methods an EVM-based blockchain should implement can be found [here](https://github.com/etclabscore/ethereum-json-rpc-specification). + +This was first proposed [here as an ECIP](https://github.com/etclabscore/ECIPs/blob/master/ECIPs/ECIP-1053.md), but the benefits of this kind of tooling is apparent across Bitcoin, Ethereum Classic, Ethereum and other JSON-RPC accessible blockchains. + +## Motivation + +Although [EIP-1474](./eip-1474.md) outlines a JSON-RPC specification. Ethereum still lacks a machine-readable JSON-RPC Specification that can be used as the industry standard for tooling. This proposal attempts to standardize such a specification in a way that is versionable, and both human and machine readable. + +Ethereum clients can expose RPC endpoints with different method signatures and cause compatibility issues between clients. + +Developers need a reliable developer experience, and an industry standard way to describe Ethereum JSON-RPC 2.0 APIs. + +## Specification + +### What is OpenRPC? + +The [OpenRPC](https://github.com/open-rpc/spec) Specification defines a standard, programming language-agnostic interface description for [JSON-RPC 2.0](https://www.jsonrpc.org/specification) APIs, which allows both humans and computers to discover and understand the capabilities of a service without requiring access to source code, additional documentation, or inspection of network traffic. When properly defined via OpenRPC, a consumer can understand and interact with the remote service with a minimal amount of implementation logic, and share these logic patterns across use cases. Similar to what interface descriptions have done for lower-level programming, the OpenRPC Specification removes guesswork in calling a service. + +##### Structure + +This is the structure of an OpenRPC Document: + +![openrpc-spec-structure](../assets/eip-1901/OpenRPC_structure.png) + +JSON-RPC APIs can support the OpenRPC specification by implementing a service discovery method that will return the [OpenRPC document](https://github.com/open-rpc/spec#openrpc-document) for the JSON-RPC API. The method MUST be named `rpc.discover`. The `rpc.` prefix is a reserved method prefix for [JSON-RPC 2.0 Specification](https://www.jsonrpc.org/specification) system extensions. + +### Use Case + +This is the vision for the use case of OpenRPC and how it would relate to a client implementation like multi-geth: + +![MultGethRpc-usecase](../assets/eip-1901/multi-geth-use-case.png) + +## Rationale + +### Why would we do this? +Services need to figure out how to talk to each other. If we really want to build the next generation of automation, then having up to date libraries, documented APIs, and modern tools are going to provide easy discovery, on-boarding, and enable end user and developer interaction. + +Use cases for machine-readable [JSON-RPC 2.0](https://www.jsonrpc.org/specification) API definition documents include, but are not limited to: + +- A common vocabulary and document will keep developers, testers, architects, and technical writers all in sync. +- Server stubs/skeletons generated in many languages +- Clients generated in many languages +- Mock Server generated in many languages +- Tests generated in many languages +- Documentation Generation + +### Alternative + +[OpenRPC](https://github.com/open-rpc/spec) documents just describe [JSON-RPC](https://www.jsonrpc.org/specification) APIs services, and are represented in JSON format. These documents may be produced and served statically OR generated dynamically from an application and returned via the [`rpc.discover`](https://github.com/open-rpc/spec#service-discovery-method) method. This gives projects and communities the opportunity to adopt tools, documentation, and clients outlined in the [etclabscore/ethereum-json-rpc-specification](./eip-1474.md) before the [`rpc.discover`](https://github.com/open-rpc/spec#service-discovery-method) method is implemented for a particular client. + +## Implementation + +- [Multi-Geth OpenRPC Discovery](https://github.com/multi-geth/multi-geth#openrpc-discovery) + +### Tooling + +#### Interactive Documentation Playground + +You can view the interactive documentation [here](https://playground.open-rpc.org/?schemaUrl=https://raw.githubusercontent.com/etclabscore/ethereum-json-rpc-specification/master/openrpc.json). + +**OR** + +Using `rpc.discover` via multi-geth, the playground can discover and display the documentation for the Ethereum JSON-RPC API: + +![eth-playground-openrpc](../assets/eip-1901/eth-playground-openrpc.gif) + + +#### Generated Client + +The [clients](https://github.com/etclabscore/ethereum-json-rpc-specification#clients) are generated from the OpenRPC Document openrpc.json outlined in this EIP, and can be used as an alternative to web3.js or ethers.js but for various languages: + +![eth-generated-client-openrpc](../assets/eip-1901/eth-generated-client-openrpc.gif) + +#### Mock Server + +The [OpenRPC mock server](https://github.com/open-rpc/mock-server) provides a mock server for any given OpenRPC Document which allows testing without booting up a real network. + +![inspector-mock-server-openrpc](../assets/eip-1901/inspector-mock-server-openrpc.png) + +## Resources + +- [Multi-Geth OpenRPC Discovery](https://github.com/multi-geth/multi-geth#openrpc-discovery) +- [EDCON 2019 talk on OpenRPC and The Future of JSON-RPC Tooling](https://www.youtube.com/watch?v=UgSPMZ9FQ4Q) +- [etclabscore/ethereum-json-rpc-specification](https://github.com/etclabscore/ethereum-json-rpc-specification) +- [open-rpc.org](https://open-rpc.org) + +## Copyright + + Copyright and related rights waived via [CC0](../LICENSE.md). \ No newline at end of file diff --git a/EIPS/eip-191.md b/EIPS/eip-191.md new file mode 100644 index 0000000..9a166d6 --- /dev/null +++ b/EIPS/eip-191.md @@ -0,0 +1,108 @@ +--- +eip: 191 +title: Signed Data Standard +author: Martin Holst Swende (@holiman), Nick Johnson +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` (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> . +``` + +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> +``` + +The version `0x00` has `` 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)> +``` + +The version `0x45` (E) has `` 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). diff --git a/EIPS/eip-1921.md b/EIPS/eip-1921.md new file mode 100644 index 0000000..9a67131 --- /dev/null +++ b/EIPS/eip-1921.md @@ -0,0 +1,141 @@ +--- +eip: 1921 +title: dType Functions Extension +author: Loredana Cirstea (@loredanacirstea), Christian Tzurcanu (@ctzurcanu) +discussions-to: https://github.com/ethereum/EIPs/issues/1921 +status: Stagnant +type: Standards Track +category: ERC +created: 2019-04-06 +requires: 1900 +--- + +## Simple Summary +In the context of dType, the Decentralized Type System described in [EIP-1900](./eip-1900.md), we are proposing to add support for registering functions (with a preference for `pure` and `view`) in the dType Registry. + +## Abstract + +This proposal is part of a series of EIPs focused on expanding the concept of a Decentralized Type System, as explained in [EIP-1900](./eip-1900.md). +The current EIP specifies the data definitions and interfaces needed to support registering individual smart contract functions, as entries in the dType Registry. + +## Motivation + +In order to evolve the EVM into a Singleton Operating System, we need a way to register, find and address contract functions that we want to run in an automated way. +This implies having access to all the data needed to run the function inside the EVM. + +Aside from the above motivation, there are also near future benefits for this proposal. Having a globally available, non-custodial functions registry, will democratize the development of tools, such as those targeting: blockchain data analysis (e.g. block explorers), smart contract IDEs, security analysis of smart contracts. + +Registering new smart contract functions can be done through the same consensus mechanism as [EIP-1900](./eip-1900.md) mentions, in order to avoid burdening the chain state with redundant or improper records. + + +## Specification + +This specification targets `pure` and `view` functions. + +For each function, we can store: +* `name` - type `string` unique function name, as defined in EIP-1900; required +* `types` - the type data and label of each input, as defined in EIP-1900; required +* `outputs` - the type data and label of each output; required +* `contractAddress` - type `address` - smart contract where the function resides, as defined in EIP-1900; optional for interfaces +* `source` - type `bytes32` - reference to an external file containing the function source code, as defined in EIP-1900; optional + +Therefore, this proposal adds `outputs` to the EIP-1900 type registration definition. + +An example of a function registration object for the dType registry is: + +``` +{ + "name": "setStaked", + "types": [ + {"name": "TypeA", "label": "typeA", "relation":0, "dimensions":[]} + ], + "typeChoice": 4, + "contractAddress":
, + "source": , + "outputs": [ + {"name": "TypeB", "label": "typeB", "relation":0, "dimensions":[]} + ] +} +``` + +The above object will be passed to `.insert({...})` + +An additional `setOutputs` function is proposed for the dType registry: + +``` +function setOutputs( + bytes32 identifier, + dTypes[] memory outputs +) + public +``` + +- `identifier` - type `bytes32`, the type's identifier, as defined in EIP-1900 +- `outputs` - type `dTypes`, as defined in EIP-1900 + +### Implementation Suggestions + + +In the dType registry implementation, `outputs` can be stored in a `mapping`: + +``` +mapping(bytes32 => dTypes[]) public outputs; +``` + +## Rationale + + +The suggestion to treat each `pure` or `view` function as a separate entity instead of having a contract-based approach allows us to: +* have a global context of readily available functions +* scale designs through functional programming patterns rather than contract-encapsulated logic (which can be successfully used to scale development efforts independently) +* bidirectionally connect functions with the types they use, making automation easier +* cherry-pick functions from already deployed contracts if the other contract functions do not pass community consensus +* have scope-restricted improvements - instead of redeploying entire contracts, we can just redeploy the new function versions that we want to be added to the registry +* enable fine-grained auditing of individual functions, for the common good +* enable testing directly on a production chain, without state side-effects + +The proposal to store the minimum ABI information on-chain, for each function, allows us to: +* enable on-chain automation (e.g. function chaining and composition) +* be backward compatible in case the function signature format changes (e.g. from `bytes4` to `bytes32`): multiple signature calculation functions can be registered with dType. Examples: + +``` +function getSignatureBytes4(bytes32 identifier) + view + public + returns (bytes4 signature) + +function getSignatureBytes32(bytes32 identifier) + view + public + returns (bytes32 signature) +``` + +- `identifier` - the type's identifier, as defined in EIP-1900 +- `signature` - the function's signature + + +Concerns about this design might be: +* redundancy of storing `contractAddress` for each function that is part of the same contract + +We think that state/storage cost will be compensated through DRYness across the chain, due to reusing types and functions that have already been registered and are now easy to find. Other state/storage cost calculations will be added once the specification and implementation are closer to be finalized. + + +Note that the input and output types are based on types that have already been registered. This lowers the amount of ABI information needed to be stored for each function and enables developers to aggregate and find functions that use the same types for their I/O. This can be a powerful tool for interoperability and smart contract composition. + + +## Backwards Compatibility + +This proposal does not affect extant Ethereum standards or implementations. Registering functions for existing contract deployments should be fully supported. + +## Test Cases + +Will be added. + + +## Implementation + +In-work implementation examples can be found at https://github.com/pipeos-one/dType. +This proposal will be updated with an appropriate implementation when consensus is reached on the specifications. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1922.md b/EIPS/eip-1922.md new file mode 100644 index 0000000..2ab2ed7 --- /dev/null +++ b/EIPS/eip-1922.md @@ -0,0 +1,207 @@ +--- +eip: 1922 +title: zk-SNARK Verifier Standard +author: Michael Connor , Chaitanya Konda , Duncan Westland +discussions-to: https://github.com/ethereum/EIPs/issues/1922 +type: Standards Track +category: ERC +status: Stagnant +created: 2018-09-14 +requires: 165, 196, 197 +--- + +## Simple Summary + +A standard interface for "Verifier" contracts which verify zk-SNARKs. + +## Abstract +The following standard allows for the implementation of a standard contract API for the verification of zk-SNARKs ("Zero-Knowledge Succinct Non-Interactive Arguments of Knowledge"), also known as "proofs", "arguments", or "commitments". + +This standard provides basic functionality to load all necessary parameters for the verification of any zk-SNARK into a verifier contract, so that the proof may ultimately return a `true` or `false` response; corresponding to whether it has been verified or not verified. + +## Motivation +zk-SNARKs are a promising area of interest for the Ethereum community. Key applications of zk-SNARKs include: +- Private transactions +- Private computations +- Improved transaction scaling through proofs of "bundled" transactions + +A standard interface for verifying all zk-SNARKs will allow applications to more easily implement private transactions, private contracts, and scaling solutions; and to extract and interpret the limited information which gets emitted during zk-SNARK verifications. + +This standard was initially proposed by EY, and was inspired in particular by the requirements of businesses wishing to keep their agreements, transactions, and supply chain activities confidential—all whilst still benefiting from the commonly cited strengths of blockchains and smart contracts. + +:warning: TODO: Explain the benefits to and perspective of a consumer of information. I.e. the thing that interfaces with the standard verifier. + +## Specification +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. + +Terminology in this specification is used consistently with libsnark, as provided in that project's README. + +* Adhering Contract — A Verifier contract which adheres to this specification. +* Arithmetic circuit: An abstraction of logical statements into addition and multiplication gates. +* Public Inputs: often denoted as a vector 'x' in zk-SNARKs literature, and denoted `inputs` in this interface. An arithmetic circuit can be thought of as taking two parameters; the Public Inputs, 'x', and a secret 'witness', 'w'. This interface standardises functions which can load the `inputs` into an Adhering Contract. +* Proof: A 'prover' who wants to 'prove' knowledge of some secret witness 'w' (which satisfies an arithmetic circuit), generates a `proof` from: the circuit's Proving Key; their secret witness 'w'; and its corresponding Public Inputs 'x'. Together, a pair `(proof, inputs)` of satisfying `inputs` and their corresponding `proof` forms a zk-SNARK. +* Verification Key: A 'trusted setup' calculation creates both a public 'Proving Key' and a public 'Verification Key' from an arithmetic circuit. This interface does not provide a method for loading a Verification Key onto the blockchain. An Adhering Contract SHALL be able to accept arguments of knowledge (`(proof, inputs)` pairs) for at least one Verification Key. We shall call such Verification Keys 'in-scope' Verification Keys. An Adhering Contract MUST be able to interpret unambiguously a unique `verificationKeyId` for each of its 'in-scope' Verification Keys. + +**Every ERC-XXXX compliant verifier contract must implement the `ERCXXXX` and `ERC165` interfaces** (subject to "caveats" below): + + +```solidity +pragma solidity ^0.5.6; + +/// @title EIP-XXXX zk-SNARK Verifier Standard +/// @dev See https://github.com/EYBlockchain/zksnark-verifier-standard +/// Note: the ERC-165 identifier for this interface is 0xXXXXXXXX. +/// ⚠️ TODO: Calculate interface identifier +interface EIPXXXX /* is ERC165 */ { + /// @notice Checks the arguments of Proof, through elliptic curve + /// pairing functions. + /// @dev + /// MUST return `true` if Proof passes all checks (i.e. the Proof is + /// valid). + /// MUST return `false` if the Proof does not pass all checks (i.e. if the + /// Proof is invalid). + /// @param proof A zk-SNARK. + /// @param inputs Public inputs which accompany Proof. + /// @param verificationKeyId A unique identifier (known to this verifier + /// contract) for the Verification Key to which Proof corresponds. + /// @return result The result of the verification calculation. True + /// if Proof is valid; false otherwise. + function verify(uint256[] calldata proof, uint256[] calldata inputs, bytes32 verificationKeyId) external returns (bool result); +} +``` +### Interface +``` solidity +interface ERC165 { + /// @notice Query if a contract implements an interface + /// @param interfaceID The interface identifier, as specified in ERC-165 + /// @dev Interface identification is specified in ERC-165. This function + /// uses less than 30,000 gas. + /// @return `true` if the contract implements `interfaceID` and + /// `interfaceID` is not 0xffffffff, `false` otherwise + function supportsInterface(bytes4 interfaceID) external view returns (bool); +} +``` + +## Rationale + +### Taxonomy + +⚠️ TODO: Add a specific reference to libsnark here, explaining the choice of variable names. + +:warning: TODO: Explain how _C_ may not necessarily be a satisfiable arithmetic circuit of logical statements. As current, this is a limitation to certain kinds of SNARKS. Whereas the source references also mention polynomials, and other applications. + +_C_ — A satisfiable arithmetic circuit abstraction of logical statements. + +_lambda​_ - A random number, generated at the 'setup' phase - commonly referred to as 'toxic waste', because knowledge of _lambda​_ would allow an untrustworthy party to create 'false' proofs which would verify as 'true'. _lambda​_ must be destroyed. + +_pk​_ - The proving key for a particular circuit _C​_. + +_vk_ - The verification key for a particular circuit _C_. + +Both _pk​_ and _vk​_ are generated as a pair by some function _G​_: +_(pk, vk) = G(lambda, C)​_ + +Note: _C_ can be represented unambiguously by either of _pk_ or _vk_. In zk-SNARK constructions, _vk_ is much smaller in size than _pk_, so as to enable succinct verification on-chain. Hence, _vk_ is the representative of _C_ that is 'known' to the contract. Therefore, we can identify each circuit uniquely through some `verificationKeyId`, where `verificationKeyId` serves as a more succinct mapping to _vk_. + +_w_ - A 'private witness' string. A private argument to the circuit _C_ known only to the prover, which, when combined with the `inputs` argument _x_, comprises an argument of knowledge which satisfies the circuit _C_. + +_x_ or `inputs` - A vector of 'Public Inputs'. A public argument to the circuit _C_ which, when combined with the private witness string _w_, comprises an argument of knowledge which satisfies the circuit _C_. + +_pi_ or `proof` - an encoded vector of values which represents the 'prover's' 'argument of knowledge' of values _w_ and _x_ which satisfy the circuit _C_. +_pi = P(pk, x, w)_. + +The ultimate purpose of a Verifier contract, as specified in this EIP, is to verify a proof (of the form _pi​_) through some verification function _V​_. + +_V(vk, x, pi) = 1_, if there exists a _w_ s.t. _C(x,w)=1_. +_V(vk, x, pi) = 0_, otherwise. + +The `verify()` function of this specification serves the purpose of _V​_; returning either `true` (the proof has been verified to satisfy the arithmetic circuit) or `false` (the proof has not been verified). + +### Functions + +#### `verify` +The `verify` function forms the crux this standard. The parameters are intended to be as generic as possible, to allow for verification of any zk-SNARK: + +- `proof` + Specified as `uint256[]`. + `uint256` is the most appropriate type for elliptic curve operations over a finite field. Indeed, this type is used in the predominant 'Pairing library' implementation of zk-SNARKs by Christian Reitweissner. + A one-dimensional dynamic array has been chosen for several reasons: + - Dynamic: There are several possible methods for producing a zk-SNARK proof, including PGHR13, G16, GM17, and future methods might be developed in future. Although each method may produce differently sized proof objects, a dynamic array allows for these differing sizes. + - Array: An array has been chosen over a 'struct' object, because it is currently easier to pass dynamic arrays between functions in Solidity. Any proof 'struct' can be 'flattened' to an array and passed to the `verify` function. Interpretation of that flattened array is the responsibility of the implemented body of the function. Example implementations demonstrate that this can be achieved. + - One-dimensional: A one-dimensional array has been chosen over multi-dimensional array, because it is currently easier to work with one-dimensional arrays in Solidity. Any proof can be 'flattened' to a one-dimensional array and passed to the `verify` function. Interpretation of that flattened array is the responsibility of the implemented body of the Adhering Contract. Example implementations demonstrate that this can be achieved. + +- `inputs` + Specified as `uint256[]`. + `uint256` is the most appropriate type for elliptic curve operations over a finite field. Indeed, this type is used in the predominant 'Pairing library' implementation of zk-SNARKs by Christian Reitweissner. + The number of inputs will vary in size, depending on the number of 'public inputs' of the arithmetic circuit being verified against. In a similar vein to the `proof` parameter, a one-dimensional dynamic array is general enough to cope with any set of inputs to a zk-SNARK. + +- `verificationKeyId` + A verification key (referencing a particular arithmetic circuit) only needs to be stored on-chain once. Any proof (relating to the underlying arithmetic circuit) can then be verified against that verification key. Given this, it would be unnecessary (from a 'gas cost' point of view) to pass a duplicate of the full verification key to the `verify` function every time a new `(proof, inputs)` pair is passed in. We do however need to tell the Adhering Verifier Contract which verification key corresponds to the `(proof, inputs)` pair being passed in. A `verificationKeyId` serves this purpose - it uniquely represents a verification key as a `bytes32` id. A method for uniquely assigning a `verificationKeyId` to a verification key is the responsibility of the implemented body of the Adhering Contract. + + +## Backwards Compatibility +- At the time this EIP was first proposed, there was one implementation on the Ethereum main net - deployed by [EY](https://www.ey.com). This was compiled with Solidity 0.4.24 for compatibility with [Truffle](https://github.com/trufflesuite/truffle) but otherwise compatible with this standard, which is presented at the latest current version of Solidity. +- Dr Christian Reitwiessner's excellent [example](https://gist.github.com/chriseth/f9be9d9391efc5beb9704255a8e2989d) of a Verifier contract and elliptic curve pairing library has been instrumental in the Ethereum community's experimentation and development of zk-SNARK protocols. Many of the naming conventions of this EIP have been kept consistent with his example. +- Existing zk-SNARK compilers such as [ZoKrates](https://github.com/Zokrates/ZoKrates), which produce 'Verifier.sol' contracts, do not currently produce Verifier contracts which adhere to this EIP specification. + - :warning: TODO: Provide a converter contract or technique which allows ZoKrates verifier.sol contracts to adhere with this EIP. + + +## Test Cases + +Truffle tests of example implementations are included in the test case repository. + +⚠️ TODO: Reference specific test cases because there are many currently in the repository. + + +## Implementations +Detailed example implementations and Truffle tests of these example implementations are included in this repository. + +:warning: TODO: Update referenced verifier implementations so that they are ready-to-deploy or reference deployed versions of those implementations. At current, the referenced code specifically states "DO NOT USE THIS IN PRODUCTION". + +:warning: TODO: Provide reference to an implementation which interrogates a standard verifier contract that implements this standard. + + +## References + +:warning: TODO: Update references and confirm that each reference is cited (parenthetical documentation not necessary) in the text. + +**Standards** + +1. ERC-20 Token Standard. ./eip-20.md + +1. ERC-165 Standard Interface Detection. ./eip-165.md +1. ERC-173 Contract Ownership Standard (DRAFT). ./eip-173.md +1. ERC-196 Precompiled contracts for addition and scalar multiplication on the elliptic curve alt_bn128. ./eip-196.md +1. ERC-197 Precompiled contracts for optimal ate pairing check on the elliptic curve alt_bn128. ./eip-197.md +1. Ethereum Name Service (ENS). https://ens.domains +1. RFC 2119 Key words for use in RFCs to Indicate Requirement Levels. https://www.ietf.org/rfc/rfc2119.txt + +##### Educational material: zk-SNARKs +1. Zcash. What are zk-SNARKs? https://z.cash/technology/zksnarks.html +1. Vitalik Buterin. zk-SNARKs: Under the Hood. https://medium.com/@VitalikButerin/zk-snarks-under-the-hood-b33151a013f6 +1. Christian Reitweissner. zk-SNARKs in a Nutshell. https://blog.ethereum.org/2016/12/05/zksnarks-in-a-nutshell/ +1. Ben-Sasson, Chiesa, Tromer, et. al. Succinct Non-Interactive Zero Knowledge for a von Neumann Architecture. https://eprint.iacr.org/2013/879.pdf + +##### Notable applications of zk-SNARKs + 1. EY. Implementation of a business agreement through Token Commitment transactions on the Ethereum mainnet. https://github.com/EYBlockchain/ZKPChallenge + 1. Zcash. https://z.cash + 1. Zcash. How Transactions Between Shielded Addresses Work. https://blog.z.cash/zcash-private-transactions/ + +##### Notable projects relating to zk-SNARKs + 1. libsnark: A C++ Library for zk-SNARKs ("project README)". https://github.com/scipr-lab/libsnark + 1. ZoKrates: Scalable Privacy-Preserving Off-Chain Computations. https://www.ise.tu-berlin.de/fileadmin/fg308/publications/2018/2018_eberhardt_ZoKrates.pdf + 1. ZoKrates Project Repository. https://github.com/JacobEberhardt/ZoKrates + 1. Joseph Stockermans. zkSNARKs: Driver's Ed. https://github.com/jstoxrocky/zksnarks_example + 1. Christian Reitweissner - snarktest.solidity. https://gist.github.com/chriseth/f9be9d9391efc5beb9704255a8e2989d + +##### Notable 'alternatives' to zk-SNARKs - areas of ongoing zero-knowledge proof research + 1. Vitalik Buterin. STARKs. https://vitalik.ca/general/2017/11/09/starks_part_1.html + 1. Bu ̈nz, Bootle, Boneh, et. al. Bulletproofs. https://eprint.iacr.org/2017/1066.pdf + 1. Range Proofs. https://www.cosic.esat.kuleuven.be/ecrypt/provpriv2012/abstracts/canard.pdf + 1. Apple. Secure Enclaves. https://developer.apple.com/documentation/security/certificate_key_and_trust_services/keys/storing_keys_in_the_secure_enclave + 1. Intel Software Guard Extensions. https://software.intel.com/en-us/sgx + + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1923.md b/EIPS/eip-1923.md new file mode 100644 index 0000000..2865c7d --- /dev/null +++ b/EIPS/eip-1923.md @@ -0,0 +1,164 @@ +--- +eip: 1923 +title: zk-SNARK Verifier Registry Standard +author: Michael Connor , Chaitanya Konda , Duncan Westland +discussions-to: https://github.com/ethereum/EIPs/issues/1923 +type: Standards Track +category: ERC +status: Stagnant +created: 2018-12-22 +requires: 165, 196, 197 +--- + +## Simple Summary + + +A standard interface for a "Verifier Registry"'" contract, through which all zk-SNARK verification activity can be registered. + +## Abstract +The following standard allows for the implementation of a standard contract API for the registration of zk-SNARKs ("Zero-Knowledge Succinct Non-Interactive Arguments of Knowledge"), also known as "proofs", "arguments", or "commitments". + +TODO: Which functionality is exposed in this standard interface? + +## Motivation +zk-SNARKs are a promising area of interest for the Ethereum community. Key applications of zk-SNARKs include: +- Private transactions +- Private computations +- Ethereum scaling through proofs of 'bundled' transactions + +A standard interface for registering all zk-SNARKs will allow applications to more easily implement private transactions, private contracts, and scaling solutions; and to extract and interpret the limited information which gets emitted during zk-SNARK verifications. + +:warning: TODO: Explain the motivation for standardizing a registry, other than simply standardizing the verifier interactions. + +⚠️ TODO: Explain the benefits to and perspective of a consumer of information. I.e. the thing that interfaces with the standard verifier registry. + +## Specification +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. + + +```solidity +pragma solidity ^0.5.6; + +/// @title EIP-XXXX zk-SNARK Verifier Registry Standard +/// @dev See https://github.com/EYBlockchain/zksnark-verifier-standard +/// Note: the ERC-165 identifier for this interface is 0xXXXXXXXXX. +/// ⚠️ TODO: Set the interface identifier +interface EIP-XXXX /* is ERC165 */ { + + event NewProofSubmitted(bytes32 indexed _proofId, uint256[] _proof, uint64[] _inputs); + + event NewVkRegistered(bytes32 indexed _vkId); + + event NewVerifierContractRegistered(address indexed _contractAddress); + + event NewAttestation(bytes32 indexed _proofId, address indexed _verifier, bool indexed _result); + + + function getVk(bytes32 _vkId) external returns (uint256[] memory); + + function registerVerifierContract(address _verifierContract) external returns (bool); + + function registerVk(uint256[] calldata _vk, address[] calldata _verifierContracts) external returns (bytes32); + + function submitProof(uint256[] calldata _proof, uint64[] calldata _inputs, bytes32 _vkId) external returns (bytes32); + + function submitProof(uint256[] calldata _proof, uint64[] calldata _inputs, bytes32 _vkId, address _verifierContract) external returns (bytes32); + + function submitProofAndVerify(uint256[] calldata _proof, uint64[] calldata _inputs, bytes32 _vkId, address _verifierContract) external returns (bytes32); + + function attestProof(bytes32 _proofId, bytes32 _vkId, bool _result) external; + + function attestProofs(bytes32[] calldata _proofIds, bytes32[] calldata _vkIds, bool[] calldata _results) external; + + function challengeAttestation(bytes32 _proofId, uint256[] calldata _proof, uint64[] calldata _inputs, address _verifierContract) external; + + function createNewVkId(uint256[] calldata _vk) external pure returns (bytes32); + + function createNewProofId(uint256[] calldata _proof, uint64[] calldata _inputs) external pure returns (bytes32); + +} +``` +### Interface +``` solidity +interface ERC165 { + /// @notice Query if a contract implements an interface + /// @param interfaceID The interface identifier, as specified in ERC-165 + /// @dev Interface identification is specified in ERC-165. This function + /// uses less than 30,000 gas. + /// @return `true` if the contract implements `interfaceID` and + /// `interfaceID` is not 0xffffffff, `false` otherwise + function supportsInterface(bytes4 interfaceID) external view returns (bool); +} +``` + +## Rationale + +⚠️ TODO: Add Rationale section. + +### Backwards Compatibility + +⚠️ TODO: Add Backwards Compatibility section. + +### Test Cases + +Truffle tests of example implementations are included in this Repo. + +⚠️ TODO: Reference specific test cases because there are many currently in the repository. + + +## Implementations +Detailed example implementations and Truffle tests of these example implementations are included in this Repo. + +⚠️ TODO: Update referenced verifier registry implementations so that they are ready-to-deploy or reference deployed versions of those implementations. At current, the referenced code specifically states "DO NOT USE THIS IN PRODUCTION". + +⚠️ TODO: Provide reference to an implementation which interrogates a standard verifier registry contract that implements this standard. + + +## References + +⚠️ TODO: Update references and confirm that each reference is cited (parenthetical documentation not necessary) in the text. + +**Standards** + +1. ERC-20 Token Standard. ./eip-20.md + +1. ERC-165 Standard Interface Detection. ./eip-165.md +2. ERC-173 Contract Ownership Standard (DRAFT). ./eip-173.md +3. ERC-196 Precompiled contracts for addition and scalar multiplication on the elliptic curve alt_bn128. ./eip-196.md +4. ERC-197 Precompiled contracts for optimal ate pairing check on the elliptic curve alt_bn128. ./eip-197.md +5. Ethereum Name Service (ENS). https://ens.domains +6. RFC 2119 Key words for use in RFCs to Indicate Requirement Levels. https://www.ietf.org/rfc/rfc2119.txt + +##### Educational material: zk-SNARKs + +1. Zcash. What are zk-SNARKs? https://z.cash/technology/zksnarks.html +2. Vitalik Buterin. zk-SNARKs: Under the Hood. https://medium.com/@VitalikButerin/zk-snarks-under-the-hood-b33151a013f6 +3. Christian Reitweissner. zk-SNARKs in a Nutshell. https://blog.ethereum.org/2016/12/05/zksnarks-in-a-nutshell/ +4. Ben-Sasson, Chiesa, Tromer, et. al. Succinct Non-Interactive Zero Knowledge for a von Neumann Architecture. https://eprint.iacr.org/2013/879.pdf + +##### Notable applications of zk-SNARKs + +1. EY. Implementation of a business agreement through Token Commitment transactions on the Ethereum mainnet. https://github.com/EYBlockchain/ZKPChallenge +2. Zcash. https://z.cash +3. Zcash. How Transactions Between Shielded Addresses Work. https://blog.z.cash/zcash-private-transactions/ + +##### Notable projects relating to zk-SNARKs + +1. libsnark: A C++ Library for zk-SNARKs ("project README)". https://github.com/scipr-lab/libsnark +2. ZoKrates: Scalable Privacy-Preserving Off-Chain Computations. https://www.ise.tu-berlin.de/fileadmin/fg308/publications/2018/2018_eberhardt_ZoKrates.pdf +3. ZoKrates Project Repository. https://github.com/JacobEberhardt/ZoKrates +4. Joseph Stockermans. zkSNARKs: Driver's Ed. https://github.com/jstoxrocky/zksnarks_example +5. Christian Reitweissner - snarktest.solidity. https://gist.github.com/chriseth/f9be9d9391efc5beb9704255a8e2989d + +##### Notable 'alternatives' to zk-SNARKs - areas of ongoing zero-knowledge proof research + +1. Vitalik Buterin. STARKs. https://vitalik.ca/general/2017/11/09/starks_part_1.html +2. Bu ̈nz, Bootle, Boneh, et. al. Bulletproofs. https://eprint.iacr.org/2017/1066.pdf +3. Range Proofs. https://www.cosic.esat.kuleuven.be/ecrypt/provpriv2012/abstracts/canard.pdf +4. Apple. Secure Enclaves. https://developer.apple.com/documentation/security/certificate_key_and_trust_services/keys/storing_keys_in_the_secure_enclave +5. Intel Software Guard Extensions. https://software.intel.com/en-us/sgx + + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1930.md b/EIPS/eip-1930.md new file mode 100644 index 0000000..a4aa444 --- /dev/null +++ b/EIPS/eip-1930.md @@ -0,0 +1,150 @@ +--- +eip: 1930 +title: CALLs with strict gas semantic. Revert if not enough gas available. +author: Ronan Sandford (@wighawag) +type: Standards Track +discussions-to: https://github.com/ethereum/EIPs/issues/1930 +category: Core +status: Stagnant +created: 2019-04-10 +--- + +## Simple Summary + +Add the ability for smart contract to execute calls with a specific amount of gas. If this is not possible the execution should revert. + +## Abstract + +The current CALL, DELEGATE_CALL, STATIC_CALL opcode do not enforce the gas being sent, they simply consider the gas value as a maximum. This pose serious problem for applications that require the call to be executed with a precise amount of gas. + +This is for example the case for meta-transaction where the contract needs to ensure the call is executed exactly as the signing user intended. + +But this is also the case for common use cases, like checking "on-chain" if a smart contract support a specific interface (via [EIP-165](./eip-165.md) for example). + +The solution presented here is to add new call semantic that enforce the amount of gas specified : the call either proceed with the exact amount of gas or do not get executed and the current call revert. + + +### Specification + +There are 2 possibilities + +a) one is to add opcode variant that have a stricter gas semantic + +b) The other is to consider a specific gas value range (one that have never been used before) to have strict gas semantic, while leaving other values as before + +Here are the details description + +#### option a) + +- add a new variant of the CALL opcode where the gas specified is enforced so that if the gas left at the point of call is not enough to give the specified gas to the destination, the current call revert +- add a new variant of the DELEGATE_CALL opcode where the gas specified is enforced so that if the gas left at the point of call is not enough to give the specified gas to the destination, the current call revert +- add a new variant of the STATIC_CALL opcode where the gas specified is enforced so that if the gas left at the point of call is not enough to give the specified gas to the destination, the current call revert + +##### Rational for a) +This solution has the merit to avoid any possibility of old contract be affected by the change. On the other hand it introduce 3 new opcodes. With EIP-1702, we could render the old opcode obsolete though. + +#### option b) + +For all opcode that allow to pass gas to another contract, do the following: +- If the most significant bit is one, consider the 31 less significant bit as the amount of gas to be given to the receiving contract in the strict sense. SO like a) if the gas left at the point of call is not enough to give the specified gas to the destination, the current call revert. +- If the 2nd most significant bit is zero, consider the whole value to behave like before, that is, it act as a maximum value, and even if not enough gas is present, the gas that can be given is given to the receiving contract + +##### Rational for b) +This solution relies on the fact that no contract would have given any value bigger or equal to 0x8000000000000000000000000000000000000000000000000000000000000000 + +Note that solidity for example do not use value like 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF as it is more expensive than passing the gasLeft. + +Its main benefit though is that it does not require extra opcodes. + +#### strict gas semantic + +To be precise, regarding the strict gas semantic, based on [EIP-150](./eip-150.md), the current call must revert unless G >= I x 64/63 where G is gas left at the point of call (after deducing the cost of the call itself) and I is the gas specified. + +So instead of +``` +availableGas = availableGas - base +gas := availableGas - availableGas/64 +... +if !callCost.IsUint64() || gas < callCost.Uint64() { + return gas, nil +} +``` +see https://github.com/ethereum/go-ethereum/blob/7504dbd6eb3f62371f86b06b03ffd665690951f2/core/vm/gas.go#L41-L48 + +we would have +``` +availableGas = availableGas - base +gas := availableGas - availableGas/64 +if !callCost.IsUint64() || gas < callCost.Uint64() { + return 0, errNotEnoughGas +} +``` + +### Rationale + +Currently the gas specified as part of these opcodes is simply a maximum value. And due to the behavior of [EIP-150](./eip-150.md) it is possible for an external call to be given less gas than intended (less than the gas specified as part of the CALL) while the rest of the current call is given enough to continue and succeed. Indeed since with EIP-150, the external call is given at max ```G - Math.floor(G/64)``` where G is the gasleft() at the point of the CALL, the rest of the current call is given ```Math.floor(G/64)``` which can be plenty enough for the transaction to succeed. For example, when G = 6,400,000 the rest of the transaction will be given 100,000 gas plenty enough in many case to succeed. + +This is an issue for contracts that require external call to only fails if they would fails with enough gas. This requirement is present in smart contract wallet and meta transaction in general, where the one executing the transaction is not the signer of the execution data. Because in such case, the contract needs to ensure the call is executed exactly as the signing user intended. + +But this is also true for simple use case, like checking if a contract implement an interface via EIP-165. Indeed as specified by such EIP, the ```supporstInterface``` method is bounded to use 30,000 gas so that it is theoretically possible to ensure that the throw is not a result of a lack of gas. Unfortunately due to how the different CALL opcodes behave contracts can't simply rely on the gas value specified. They have to ensure by other means that there is enough gas for the call. + +Indeed, if the caller do not ensure that 30,000 gas or more is provided to the callee, the callee might throw because of a lack of gas (and not because it does not support the interface), and the parent call will be given up to 476 gas to continue. This would result in the caller interpreting wrongly that the callee is not implementing the interface in question. + +While such requirement can be enforced by checking the gas left according to EIP-150 and the precise gas required before the call (see solution presented in that [bug report](https://web.solidified.io/contract/5b4769b1e6c0d80014f3ea4e/bug/5c83d86ac2dd6600116381f9) or after the call (see the native meta transaction implementation [here](https://github.com/pixowl/thesandbox-contracts/blob/623f4d4ca10644dcee145bcbd9296579a1543d3d/src/Sand/erc20/ERC20MetaTxExtension.sol#L176), it would be much better if the EVM allowed us to strictly specify how much gas is to be given to the CALL so contract implementations do not need to follow [EIP-150](./eip-150.md) behavior and the current gas pricing so closely. + +This would also allow the behaviour of [EIP-150](./eip-150.md) to be changed without having to affect contract that require this strict gas behaviour. + +As mentioned, such strict gas behaviour is important for smart contract wallet and meta transaction in general. +The issue is actually already a problem in the wild as can be seen in the case of Gnosis safe which did not consider the behavior of EIP-150 and thus fails to check the gas properly, requiring the safe owners to add otherwise unnecessary extra gas to their signed message to avoid the possibility of losing funds. See https://github.com/gnosis/safe-contracts/issues/100 + +As for EIP-165, the issue already exists in the example implementation presented in the EIP. Please see the details of the issue [here](https://github.com/ethereum/EIPs/pull/881#issuecomment-491677748) + +The same issue exists also on OpenZeppelin implementation, a library used by many. It does not for perform any check on gas before calling ```supportsInterface``` with 30,000 gas (see [here](https://github.com/OpenZeppelin/openzeppelin-solidity/blob/fa004a7f5de572b3dbcde1a8a81f9a87e353e799/contracts/introspection/ERC165Checker.sol#L37) and is thus vulnerable to the issue mentioned. + + +While such issue can be prevented today by checking the gas with EIP-150 in mind, a solution at the opcode level is more elegant. + +Indeed, the two possible ways to currently enforce that the correct amount of gas is sent are as follow : + +1) check done before the call + +``` +uint256 gasAvailable = gasleft() - E; +require(gasAvailable - gasAvailable / 64 >= `txGas`, "not enough gas provided") +to.call.gas(txGas)(data); // CALL +``` +where E is the gas required for the operation between the call to ```gasleft()``` and the actual call PLUS the gas cost of the call itself. +While it is possible to simply over estimate ```E``` to prevent call to be executed if not enough gas is provided to the current call it would be better to have the EVM do the precise work itself. As gas pricing continue to evolve, this is important to have a mechanism to ensure a specific amount of gas is passed to the call so such mechanism can be used without having to relies on a specific gas pricing. + + +2) check done after the call: + +``` +to.call.gas(txGas)(data); // CALL +require(gasleft() > txGas / 63, "not enough gas left"); +``` +This solution does not require to compute a ```E``` value and thus do not relies on a specific gas pricing (except for the behaviour of EIP-150) since if the call is given not enough gas and fails for that reason, the condition above will always fail, ensuring the current call will revert. +But this check still pass if the gas given was less AND the external call reverted or succeeded EARLY (so that the gas left after the call > txGas / 63). +This can be an issue if the code executed as part of the CALL is reverting as a result of a check against the gas provided. Like a meta transaction in a meta transaction. + +Similarly to the the previous solution, an EVM mechanism would be much better. + +## Backwards Compatibility + +for specification a) : Backwards compatible as it introduce new opcodes. + +for specification b) : Backwards compatible as it use value range outside of what is used by existing contract (to be verified) + +## Test Cases + +## Implementation + +None fully implemented yet. But see Specifications for an example in geth. + +## References + +1. EIP-150, ./eip-150.md + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1948.md b/EIPS/eip-1948.md new file mode 100644 index 0000000..2b58a1a --- /dev/null +++ b/EIPS/eip-1948.md @@ -0,0 +1,159 @@ +--- +eip: 1948 +title: Non-fungible Data Token +author: Johann Barbie (@johannbarbie), Ben Bollen , pinkiebell (@pinkiebell) +discussions-to: https://ethereum-magicians.org/t/erc-non-fungible-data-token/3139 +status: Stagnant +type: Standards Track +category: ERC +created: 2019-04-18 +requires: 721 +--- + +## Simple Summary + +Some NFT use-cases require to have dynamic data associated with a non-fungible token that can change during its lifetime. Examples for dynamic data: +- cryptokitties that can change color +- intellectual property tokens that encode rights holders +- tokens that store data to transport them across chains + +The existing metadata standard does not suffice as data can only be set at minting time and not modified later. + +## Abstract + +Non-fungible tokens (NFTs) are extended with the ability to store dynamic data. A 32 bytes data field is added and a read function allows to access it. The write function allows to update it, if the caller is the owner of the token. An event is emitted every time the data updates and the previous and new value is emitted in it. + +## Motivation + +The proposal is made to standardize on tokens with dynamic data. Interactions with bridges for side-chains like xDAI or Plasma chains will profit from the ability to use such tokens. Protocols that build on data tokens like [distributed breeding](https://ethresear.ch/t/a-distributed-breeding-function/5264) will be enabled. + +## Specification + +An extension of [ERC-721](./eip-721.md) interface with the following functions and events is suggested: + +``` solidity +pragma solidity ^0.5.2; + +/** + * @dev Interface of the ERC1948 contract. + */ +interface IERC1948 { + + /** + * @dev Emitted when `oldData` is replaced with `newData` in storage of `tokenId`. + * + * Note that `oldData` or `newData` may be empty bytes. + */ + event DataUpdated(uint256 indexed tokenId, bytes32 oldData, bytes32 newData); + + /** + * @dev Reads the data of a specified token. Returns the current data in + * storage of `tokenId`. + * + * @param tokenId The token to read the data off. + * + * @return A bytes32 representing the current data stored in the token. + */ + function readData(uint256 tokenId) external view returns (bytes32); + + /** + * @dev Updates the data of a specified token. Writes `newData` into storage + * of `tokenId`. + * + * @param tokenId The token to write data to. + * @param newData The data to be written to the token. + * + * Emits a `DataUpdated` event. + */ + function writeData(uint256 tokenId, bytes32 newData) external; + +} +``` + +## Rationale + +The suggested data field in the NFT is used either for storing data directly, like a counter or address. If more data is required the implementer should fall back to authenticated data structures, like merkle- or patricia-trees. + +The proposal for this ERC stems from the [distributed breeding proposal](https://ethresear.ch/t/a-distributed-breeding-function/5264) to allow better integration of NFTs across side-chains. [ost.com](https://ost.com/), [Skale](https://skalelabs.com/), [POA](https://poa.network/), and [LeapDAO](https://leapdao.org/) have been part of the discussion. + +## Backwards Compatibility + +🤷‍♂️ No related proposals are known to the author, hence no backwards compatibility to consider. + +## Test Cases + +Simple happy test: + +``` javascript +const ERC1948 = artifacts.require('./ERC1948.sol'); + +contract('ERC1948', (accounts) => { + const firstTokenId = 100; + const empty = '0x0000000000000000000000000000000000000000000000000000000000000000'; + const data = '0x0101010101010101010101010101010101010101010101010101010101010101'; + let dataToken; + + beforeEach(async () => { + dataToken = await ERC1948.new(); + await dataToken.mint(accounts[0], firstTokenId); + }); + + it('should allow to write and read', async () => { + let rsp = await dataToken.readData(firstTokenId); + assert.equal(rsp, empty); + await dataToken.writeData(firstTokenId, data); + rsp = await dataToken.readData(firstTokenId); + assert.equal(rsp, data); + }); + +}); +``` + + +## Implementation + +An example implementation of the interface in solidity would look like this: + +``` solidity +/** + * @dev Implementation of ERC721 token and the `IERC1948` interface. + * + * ERC1948 is a non-fungible token (NFT) extended with the ability to store + * dynamic data. The data is a bytes32 field for each tokenId. If 32 bytes + * do not suffice to store the data, an authenticated data structure (hash or + * merkle tree) shall be used. + */ +contract ERC1948 is IERC1948, ERC721 { + + mapping(uint256 => bytes32) data; + + /** + * @dev See `IERC1948.readData`. + * + * Requirements: + * + * - `tokenId` needs to exist. + */ + function readData(uint256 tokenId) external view returns (bytes32) { + require(_exists(tokenId)); + return data[tokenId]; + } + + /** + * @dev See `IERC1948.writeData`. + * + * Requirements: + * + * - `msg.sender` needs to be owner of `tokenId`. + */ + function writeData(uint256 tokenId, bytes32 newData) external { + require(msg.sender == ownerOf(tokenId)); + emit DataUpdated(tokenId, data[tokenId], newData); + data[tokenId] = newData; + } + +} +``` + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1959.md b/EIPS/eip-1959.md new file mode 100644 index 0000000..bf6d5b2 --- /dev/null +++ b/EIPS/eip-1959.md @@ -0,0 +1,79 @@ +--- +eip: 1959 +title: New Opcode to check if a chainID is part of the history of chainIDs +author: Ronan Sandford (@wighawag) +category: Core +type: Standards Track +discussions-to: https://ethereum-magicians.org/t/eip-1959-valid-chainid-opcode/3170 +status: Stagnant +created: 2019-04-20 +requires: 155 +--- + + +## Simple Summary +To protect off-chain messages from being reused across different chain, a mechanism need to be given to smart contract to only accept messages for that chain. Since a chain can change its chainID, the mechanism should consider old chainID valid. + +## Abstract +This EIP adds an opcode that returns whether the specific number passed in has been a valid chainID (EIP-155 unique identifier) in the history of the chain (including the current chainID). + +## Motivation +[EIP-155](./eip-155.md) proposes to use the chain ID to prevent replay attacks between different chains. It would be a great benefit to have the same possibility inside smart contracts when handling signatures, especially for Layer 2 signature schemes using [EIP-712](./eip-712.md). + +[EIP-1344](./eip-1344.md) is attempting to solve this by giving smart contract access to the tip of the chainID history. This is insufficient as such value is changing. Hence why EIP-1344 describes a contract based solution to work around the problem. It would be better to solve it in a simpler, cheaper and safer manner, removing the potential risk of misuse present in EIP-1344. + +## Specification +Adds a new opcode ```VALID_CHAINID``` at 0x46, which uses 1 stack argument : a 32 bytes value that represent the chainID to test. It will push ```0x1``` onto the stack if the uint256 value is part of the history (since genesis) of chainIDs of that chain, ```0x0``` otherwise. + +The operation costs `G_blockhash` to execute. + +The cost of the operation might need to be adjusted later as the number of chainID in the history of the chain grows. + +Note though that the alternative to keep track of old chainID is to implement a smart contract based caching solution as EIP-1344 proposes comes with an overall higher gas cost. As such the gas cost is simply a necessary cost for the feature. + +## Rationale +The only approach available today is to specify the chain ID at compile time. Using this approach will result in problems after a contentious hardfork as the contract can't accept message signed with a new chainID. + +The approach proposed by EIP-1344 is to give access to the latest chainID. This is in itself not sufficient and pose the opposite of the problem mentioned above since as soon as a hardfork that change the chainID happens, every L2 messages signed as per [EIP-712](./eip-712.md) (with the previous chainID) will fails to be accepted by the contracts after the fork. + +That's why in the rationale of EIP-1344 it is mentioned that users need to implement/use a mechanism to verify the validity of past chainID via a trustless cache implemented via smart contract. + +While this works (except for a temporary gap where the immediately previous chainID is not considered valid), this is actually a required procedure for all contracts that want to accept L2 messages since without it, messages signed before an hardfork that updated the chainID would be rejected. In other words, EIP-1344 expose such risk and it is easy for contract to not consider it by simply checking ```chainID == CHAIN_ID()``` without considering past chainIDs. + +Indeed letting contracts access the latest chainID for L2 message verification is dangerous. The latest chainID is only the tip of the chainID history. As a changing value, the latest chainID is thus not appropriate to ensure the validity of L2 messages. + +Users signing off-chain messages expect their messages to be valid from the time of signing and do not expect these message to be affected by a future hardfork. If the contract use the latest chainID as is for verification, the messages would be invalid as soon as a hardfork that update the chainID happens. For some applications, this will require users to resubmit a new message (think meta transaction), causing them potential loss (or some inconvenience during the hardfork transition), but for some other applications (think state channel) the whole off-chain state become inaccessible, resulting in potentially disastrous situations. + +In other words, we should consider all off-chain messages (with valid chainID) as part of the chain's offchain state. The opcode proposed here, offer smart contracts a simple and safe method to ensure that the offchain state stay valid across fork. + +As for replay protection, the idea of considering all of the off-chain messages signed with valid chainID as part of the chain's offchain-state means that all of these off-chain messages can be reused on the different forks which share a common chainID history (up to where they differ). This is actually an important feature since as mentioned, users expect their signed messages to be valid from the time of signing. From that time onwards these messages should be considered as part of the chain's offchain state. A hardfork should not thus render them invalid. This is similar to how the previous on-chain state is shared between 2 hardforks. + +The wallets will make sure that at any time, a signing message request use the latest chainID of the chain being used. This prevent replay attack onto chain that have different chainID histories (they would not have the same latest chainID). + +Now it is argued in the [EIP1344 discussion](https://ethereum-magicians.org/t/eip-1344-add-chain-id-opcode/1131) that when a contentious hardfork happen and one side of the fork decide to not update its chainID, that side of the chain would be vulnerable to replays since users will keep signing with a chainID that is also valid in the chain that forked. An issue also present in EIP-1344. + +This is simply a natural consequence of using chainID as the only anti-replay information for L2 messages. But this can indeed be an issue if the hardfork is created by a small minority. In that case if the majority ignore the fork and do not update its chainID, then all new message from the majority chain (until they update their chainID) can be replayed on the minority-led hardfork since the majority's current chainID is also part of the minority-led fork's chainID history. + +To fix this, every message could specify the block number representing the time it was signed. The contract could then verify that chainID specified as part of that message was valid at that particular block. + + +While EIP-1344 can't do that accurately as the caching system might leave a gap, this proposal can solve it if it is modified to return the blockNumber at which a chainID become invalid. Unfortunately, this would be easy for contracts to not perform that check. And since it suffice of only one important applications to not follow this procedure to put the minority-led fork at a disadvantage, this would fail to achieve the desired goal of protecting the minority-led fork from replay. + +Since a minority-led fork ignored by the majority means that the majority will not keep track of the messages to be submitted (state channel, ...), if such fork get traction later, this would be at the expense of majority users who were not aware of it. As such this proposal assume that minority-led fork will not get traction later and thus do not require to be protected. + +## Test Cases +TBD + +## Implementation +TBD + +## Backwards Compatibility +This EIP is fully backwards compatible with all chains which implement EIP-155 chain ID domain separator for transaction signing. Existing contract are not affected. + +Similarly to EIP-1344, it might be beneficial to update EIP-712 (still in Draft) to deal with chainID separately from the domain separator. Indeed since chainID is expected to change, if the domain separator include chainID, it would have to be dynamically computed. A caching mechanism could be used by smart contract instead though. + +## References +This was previously suggested as part of [EIP-1344 discussion](https://ethereum-magicians.org/t/eip-1344-add-chain-id-opcode/1131/39). + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). \ No newline at end of file diff --git a/EIPS/eip-196.md b/EIPS/eip-196.md new file mode 100644 index 0000000..6538d19 --- /dev/null +++ b/EIPS/eip-196.md @@ -0,0 +1,105 @@ +--- +eip: 196 +title: Precompiled contracts for addition and scalar multiplication on the elliptic curve alt_bn128 +author: Christian Reitwiessner +type: Standards Track +category: Core +status: Final +created: 2017-02-02 +--- + +## Simple Summary + +Precompiled contracts for elliptic curve operations are required in order to perform zkSNARK verification within the block gas limit. + +## Abstract + +This EIP suggests to add precompiled contracts for addition and scalar multiplication on a specific pairing-friendly elliptic curve. This can in turn be combined with [EIP-197](./eip-197.md) to verify zkSNARKs in Ethereum smart contracts. The general benefit of zkSNARKs for Ethereum is that it will increase the privacy for users (because of the Zero-Knowledge property) and might also be a scalability solution (because of the succinctness and efficient verifiability property). + +## Motivation + +Current smart contract executions on Ethereum are fully transparent, which makes them unsuitable for several use-cases that involve private information like the location, identity or history of past transactions. The technology of zkSNARKs could be a solution to this problem. While the Ethereum Virtual Machine can make use of zkSNARKs in theory, they are currently too expensive +to fit the block gas limit. Because of that, this EIP proposes to specify certain parameters for some elementary primitives that enable zkSNARKs so that they can be implemented more efficiently and the gas cost be reduced. + +Note that while fixing these parameters might look like limiting the use-cases for zkSNARKs, the primitives are so basic that they can be combined in ways that are flexible enough so that it should even be possible to allow future advances in zkSNARK research without the need for a further hard fork. + +## Specification + +If `block.number >= BYZANTIUM_FORK_BLKNUM`, add precompiled contracts for point addition (ADD) and scalar multiplication (MUL) on the elliptic curve "alt_bn128". + +Address of ADD: 0x6 +Address for MUL: 0x7 + +The curve is defined by: +``` +Y^2 = X^3 + 3 +over the field F_p with +p = 21888242871839275222246405745257275088696311157297823662689037894645226208583 +``` + +### Encoding + +Field elements and scalars are encoded as 32 byte big-endian numbers. Curve points are encoded as two field elements `(x, y)`, where the point at infinity is encoded as `(0, 0)`. + +Tuples of objects are encoded as their concatenation. + +For both precompiled contracts, if the input is shorter than expected, it is assumed to be virtually padded with zeros at the end (i.e. compatible with the semantics of the `CALLDATALOAD` opcode). If the input is longer than expected, surplus bytes at the end are ignored. + +The length of the returned data is always as specified (i.e. it is not "unpadded"). + +### Exact semantics + +Invalid input: For both contracts, if any input point does not lie on the curve or any of the field elements (point coordinates) is equal or larger than the field modulus p, the contract fails. The scalar can be any number between `0` and `2**256-1`. + +#### ADD +Input: two curve points `(x, y)`. +Output: curve point `x + y`, where `+` is point addition on the elliptic curve `alt_bn128` specified above. +Fails on invalid input and consumes all gas provided. + +#### MUL +Input: curve point and scalar `(x, s)`. +Output: curve point `s * x`, where `*` is the scalar multiplication on the elliptic curve `alt_bn128` specified above. +Fails on invalid input and consumes all gas. + +### Gas costs + + - Gas cost for ``ECADD``: 500 + - Gas cost for ``ECMUL``: 40000 + +## Rationale + +The specific curve `alt_bn128` was chosen because it is particularly well-suited for zkSNARKs, or, more specifically their verification building block of pairing functions. Furthermore, by choosing this curve, we can use synergy effects with ZCash and re-use some of their components and artifacts. + +The feature of adding curve and field parameters to the inputs was considered but ultimately rejected since it complicates the specification: The gas costs are much harder to determine and it would be possible to call the contracts on something which is not an actual elliptic curve. + +A non-compact point encoding was chosen since it still allows to perform some operations in the smart contract itself (inclusion of the full y coordinate) and two encoded points can be compared for equality (no third projective coordinate). + +## Backwards Compatibility + +As with the introduction of any precompiled contract, contracts that already use the given addresses will change their semantics. Because of that, the addresses are taken from the "reserved range" below 256. + +## Test Cases + +Inputs to test: + + - Curve points which would be valid if the numbers were taken mod p (should fail). + - Both contracts should succeed on empty input. + - Truncated input that results in a valid curve point. + - Points not on curve (but valid otherwise). + - Multiply point with scalar that lies between the order of the group and the field (should succeed). + - Multiply point with scalar that is larger than the field order (should succeed). + +## Implementation + +Implementation of these primitives are available here: + + - [libff](https://github.com/scipr-lab/libff/blob/master/libff/algebra/curves/alt_bn128/alt_bn128_g1.cpp) (C++) + - [bn](https://github.com/zcash/bn/blob/master/src/groups/mod.rs) (Rust) + +In both codebases, a specific group on the curve alt_bn128 is used and is called G1. + + - [Python](https://github.com/ethereum/py_pairing/blob/master/py_ecc/bn128/bn128_curve.py) - probably most self-contained and best readable. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1962.md b/EIPS/eip-1962.md new file mode 100644 index 0000000..5a69e32 --- /dev/null +++ b/EIPS/eip-1962.md @@ -0,0 +1,137 @@ +--- +eip: 1962 +title: EC arithmetic and pairings with runtime definitions +author: Alex Vlasov (@shamatar) +discussions-to: https://ethereum-magicians.org/t/generalised-precompile-for-elliptic-curve-arithmetics-and-pairings-working-group/3208/2 +type: Standards Track +category: Core +status: Stagnant +created: 2019-04-22 +requires: 1109 +--- + +# Simple summary + +This proposal is an extension and formalization of [EIP-1829](./eip-1829.md) with an inclusion of pairings. [EIP-1109](./eip-1109.md) is required due to low cost of some operations compared to the `STATICCALL` opcode (more information in the corresponding section below). + +## Abstract + +This EIP proposes a new precompile to bring cryptographic functionality desired for privacy and scaling solutions. Functionality of such precompile will require the following: + +- Implementation the following operations over elliptic curves in the Weierstrass form with curve parameters such as base field, A, B coefficients defined in runtime: + - Point addition + - Multiplication of a single point over a scalar + - Multiexponentiation +- Implementation pairing operation over elliptic curves from the following "families" with parameters such as base field, extension tower structure, coefficients defined in runtime: + - BLS12 + - BN + - MNT4/6 (Ate pairing) + +Full functionality of the precompile is described below in `Specification` section. + +## Motivation + +- There is a pending proposal to implement base elliptic curve arithmetic is covered by [EIP-1829](./eip-1829.md) and will allow to implement various privacy-preserving protocols with a reasonable gas costs per operation. +- Pairings are an important extension for basic arithmetic and so this new precompile is proposed with the following benefits: + - Extended set of curves will be available to allow Ethereum users to choose their security parameters and required functionality. + - Generic approach of this precompile will allow Ethereum users to experiment with newly found curves of their choice and new constructions constructions without waiting for new forks. + - EC arithmetic is indeed re-implemented in this precompile, but it's strictly required. Most of the pairing-based protocols still need to perform standard EC multiplications or additions and thus such operations must be available on generic set of curves. +- Gas costs - this EIP is designed to estimate gas-cost of performed operation as early as possible during the call and base if solely on specified parameters and operation type. This is a strict requirement for any precompile to allow Ethereum nodes to efficiently reject transactions and operations as early as possible. + +Functionality of this newly proposed precompile is different from [EIP-1829](./eip-1829.md) in the following aspects: +- Operation on arbitrary-length modulus (up to some upper-limit) for a base field and scalar field of the curve +- Pairing operations are introduced +- Different ABI due to variable parameter length + +## Specification + +If `block.number >= XXXXX`, define a set of `10` new precompiles with an addresses `[0x.., 0x.., ...]` and the following functionality. + +- Addition of points on the curve defined over base field +- Multiplication of a point on the curve defined over base field +- Multiexponentiation for `N` pairs of `(scalar, point)` on the curve defined over base field +- Addition of points on the curve defined over quadratic or cubic extension of the base field +- Multiplication of a point on the curve defined over quadratic or cubic extension of the base field +- Multiexponentiation for `N` pairs of `(scalar, point)` on the curve defined over quadratic or cubic extension of the base field +- Pairing operation on the curve of `BLS12` family +- Pairing operation on the curve of `BN` family +- Pairing operation on the curve of `MNT4` family +- Pairing operation on the curve of `MNT6` family + +Due to actuve development of the precompile and a lot of ongoing changes there is a single [source of truth](https://github.com/matter-labs/eip1962/tree/master/documentation). It covers binary interface, gas schedule, integration guide for existing implementations. + +### Possible simplifications + +Due to high complexity of the proposed operations in the aspects of implementation, debugging and evaluation of the factors for gas costs it may be appropriate to either limit the set of curves at the moment of acceptance to some list and then extend it. Another approach (if it's technically possible) would be to have the "whilelist" contract that can be updated without consensus changes (w/o fork). + +In the case of limited set of curve the following set is proposed as a minimal: +- BN254 curve from the current version of Ethereum +- BN curve from DIZK with 2^32 roots of unity +- BLS12-381 +- BLS12-377 from ZEXE with large number of roots of unity +- MNT4/6 cycle from the original [paper](https://eprint.iacr.org/2014/595.pdf). It's not too secure, but may give some freedom for experiments. +- MNT4/6 cycle from Coda if performance allows +- Set of CP generated curves that would allow embedding of BLS12-377 and may be some BN curve that would have large power of two divisor for both base field and scalar field modulus (example of CP curve for BLS12-377 can be found in ZEXE). + +## Rationale + +Only the largest design decisions will be covered: +- While there is no arithmetic over the scalar field (which is modulo size of the main group) of the curve, it's required for gas estimation purposes. +- Multiexponentiation is a separate operation due to large cost saving +- There are no point decompressions due to impossibility to get universal gas estimation of square root operation. For a limited number of "good" cases prices would be too different, so specifying the "worst case" is expensive and inefficient, while introduction of another level if complexity into already complicated gas costs formula is not worth is. + +### This precompile and EIP 1109 + +While there is no strict requirement of EIP 1109 for functionality, here is an example why it would be desired: +- BLS12-381 curve, 381 bit modulus, 255 bit scalar field, no native arithmetic is available in EVM for this +- Point addition would take 5000ns (quite overestimated) +- Point multiplication would take roughly 150000ns +- Crude gas schedule 15 Mgas/second from ECRecover precompile +- Point addition would cost 75 gas, with `STATICCALL` adding another 700 +- Point multiplication would cost 2250 gas +- One should also add the cost of memory allocation that is at least `1 + 1 + 48 + 48 + 48 + 1 + 32 + 2*48 + 2*48 = 371 byte` that is around 12 native Ethereum "words" and will require extra 36 gas (with negligible price for memory extension) + +Based on these quite crude estimations one can see that `STATICCALL` price will dominate the total cost (in case of addition) or bring significant overhead (in case of multiplication operation) in case of calls to this precompile. + + +## Backwards Compatibility + +This change is not backwards compatible and requires hard fork to be activated. + +Functionality of the new precompile itself does not affect any existing functionality of Ethereum or EVM. + +This precompile may serve as a complete replacement of the current set of `ECADD`, `ECMUL` and pairing check precompiles (`0x06`, `0x07`, `0x08`) + +## Test Cases +Test cases are the part of the implementation with a link below. + +## Implementation +There is an ongoing implementation effort [here](https://github.com/matter-labs/eip1829). Right now: +- Non-pairing operations are implemented and tested. +- BLS12 family is completed and tested for BLS12-381 and BLS12-377 curves. +- BN family is completed and tested with BN254 curve. +- Cocks-Pinch method curve is tested for k=6 curve from ZEXE. + +## Preliminary benchmarks + +cp6 in benchmarks is a Cocks-Pinch method curve that embeds BLS12-377. Machine: Core i7, 2.9 GHz. + +Multiexponentiation benchmarks take 100 pairs `(generator, random scalar)` as input. Due to the same "base" it may be not too representative benchmark and will be updated. + +``` +test pairings::bls12::tests::bench_bls12_381_pairing ... bench: 2,348,317 ns/iter (+/- 605,340) +test pairings::cp::tests::bench_cp6_pairing ... bench: 86,328,825 ns/iter (+/- 11,802,073) +test tests::bench_addition_bn254 ... bench: 388 ns/iter (+/- 73) +test tests::bench_doubling_bn254 ... bench: 187 ns/iter (+/- 4) +test tests::bench_field_inverse ... bench: 2,478 ns/iter (+/- 167) +test tests::bench_field_mont_inverse ... bench: 2,356 ns/iter (+/- 51) +test tests::bench_multiplication_bn254 ... bench: 81,744 ns/iter (+/- 6,984) +test tests::bench_multiplication_bn254_into_affine ... bench: 81,925 ns/iter (+/- 3,323) +test tests::bench_multiplication_bn254_into_affine_wnaf ... bench: 74,716 ns/iter (+/- 4,076) +test tests::bench_naive_multiexp_bn254 ... bench: 10,659,911 ns/iter (+/- 559,790) +test tests::bench_peppinger_bn254 ... bench: 2,678,743 ns/iter (+/- 148,914) +test tests::bench_wnaf_multiexp_bn254 ... bench: 9,161,281 ns/iter (+/- 456,137) +``` + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). \ No newline at end of file diff --git a/EIPS/eip-1965.md b/EIPS/eip-1965.md new file mode 100644 index 0000000..6dd8dc0 --- /dev/null +++ b/EIPS/eip-1965.md @@ -0,0 +1,63 @@ +--- +eip: 1965 +title: Method to check if a chainID is valid at a specific block Number +author: Ronan Sandford (@wighawag) +category: Core +type: Standards Track +discussions-to: https://ethereum-magicians.org/t/eip-1965-valid-chainid-for-specific-blocknumber-protect-all-forks/3181 +status: Stagnant +created: 2019-04-20 +requires: 155 +--- + +## Abstract +This EIP adds a precompile that returns whether a specific chainID (EIP-155 unique identifier) is valid at a specific blockNumber. ChainID are assumed to be valid up to the blockNumber at which they get replaced by a new chainID. + +## Motivation +[EIP-155](./eip-155.md) proposes to use the chain ID to prevent the replay of transactions between different chains. It would be a great benefit to have the same possibility inside smart contracts when handling off-chain message signatures, especially for Layer 2 signature schemes using [EIP-712](./eip-712.md). + +[EIP-1344](./eip-1344.md) is attempting to solve this by giving smart contract access to the tip of the chainID history. This is insuficient as such value is changing. Hence why EIP-1344 describes a contract based solution to work around the problem. It would be better to solve it in a simpler, cheaper and safer manner, removing the potential risk of misuse present in EIP-1344. Furthermore EIP-1344 can't protect replay properly for minority-led hardfork as the caching system cannot guarantee accuracy of the blockNumber at which the new chainID has been introduced. + +[EIP-1959](./eip-1959.md) solves the issue of EIP-1344 but do not attempt to protect from minority-led hardfork as mentioned in the rationale. We consider this a mistake, since it remove some freedom to fork. We consider that all fork should be given equal oportunities. And while there will always be issues we can't solve for the majority that ignore a particular fork, **users that decide to use both the minority-fork and the majority-chain should be protected from replay without having to wait for the majority chain to update its chainID.** + +## Specification +Adds a new precompile which uses 2 argument : a 32 bytes value that represent the chainID to test and a 32 bytes value representing the blockNumber at which the chainID is tested. It return 0x1 if the chainID is valid at the specific blockNumber, 0x0 otherwise. Note that chainID are considered valid up to the blockNumber at which they are replaced. So they are valid for every blockNumber past their replacement. + +The operation will costs no more than `G_blockhash` + `G_verylow` to execute. This could be lower as chainID are only introduced during hardfork. + +The cost of the operation might need to be adjusted later as the number of chainID in the history of the chain grows. + +Note though that the alternative to keep track of old chainID is to implement a smart contract based caching solution as EIP-1344 proposes comes with an overall higher gas cost and exhibit issues for minority-led hardfork (see Rationale section below). As such the gas cost is simply a necessary cost for the feature. + +## Rationale + +The rationale at EIP-1959 applies here as well too : + +- An opcode is better than a caching system for past chainID, It is cheaper, safer and do not include gaps. +- Direct access to the latest chainID is dangerous since it make it easy for contract to use it as a replay protection mechanism while preventing otherwise valid old messages to be valid after a fork that change the chainID. This can have disastrous consequences on users. +- all off-chain messaged signed before a fork should be valid across all side of the fork. + +The only difference is that this current proposal propose a solution to protect hardfork led by a minority. + +To summarize there is 2 possible fork scenario : + +1) The majority decide to make an hardfork but a minority disagree with it (ETC is such example). The fork is planned for block X. If the majority is not taking any action to automate the process of assigning a different chainID for both, the minority has plenty of time to plan for a chainID upgrade to happen at that same block X. Now if they do not do it, their users will face the problem that their messages will be replayable on the majority chain (Note that this is not true the other way around as we assume the majority decided to change the chainID). As such there is no reason that they’ll leave it that way. + +2) A minority decide to create an hardfork that the majority disagree with (or simply ignore). Now, the same as above can happen but since we are talking about a minority there is a chance that the majority do not care about the minority. In that case, there would be no incentive for the majority to upgrade the chainID. This means that user of both side of the fork will have the messages meant for the majority chain replayable on the minority-chain (even if this one changed its chainID) unless extra precaution is taken. + +The solution is to add the blockNumber representing the time at which the message was signed and use it as an argument to the opcode proposed here. This way, when the minority forks with a new chainID, the previous chainID become invalid from that time onward. So new messages destinated to the majority chain can't be replayed on the minority fork. + + +## Backwards Compatibility + +EIP-712 is still in draft but would need to be updated to include the blockNumber as part of the values that wallets need to verify for the protection of their users. + +Since chainID and blockNumber will vary, they should not be part of the domain separator (meant to be generated once) but another part of the message. + +While the pair could be optional for contract that do not care about replays or have other ways to prevent them, if chainID is present, the blockNumber must be present too. And if any of them is present, wallet need to ensure that the chainID is indeed the latest one of the chain being used, while the blockNumber is the latest one at the point of signing. During fork transition, the wallet can use the blockNumber to know which chainID to use. + +## References +This was previously suggested as part of [EIP1959 discussion](https://ethereum-magicians.org/t/eip-1959-valid-chainid-opcode/3170). + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1967.md b/EIPS/eip-1967.md new file mode 100644 index 0000000..9816540 --- /dev/null +++ b/EIPS/eip-1967.md @@ -0,0 +1,468 @@ +--- +eip: 1967 +title: Proxy Storage Slots +description: A consistent location where proxies store the address of the logic contract they delegate to, as well as other proxy-specific information. +author: Santiago Palladino (@spalladino), Francisco Giordano (@frangio), Hadrien Croubois (@Amxx) +discussions-to: https://ethereum-magicians.org/t/eip-1967-standard-proxy-storage-slots/3185 +status: Final +type: Standards Track +category: ERC +created: 2019-04-24 +--- + +## Abstract +Delegating **proxy contracts** are widely used for both upgradeability and gas savings. These proxies rely on a **logic contract** (also known as implementation contract or master copy) that is called using `delegatecall`. This allows proxies to keep a persistent state (storage and balance) while the code is delegated to the logic contract. + +To avoid clashes in storage usage between the proxy and logic contract, the address of the logic contract is typically saved in a specific storage slot (for example `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc` in OpenZeppelin contracts) guaranteed to be never allocated by a compiler. This EIP proposes a set of standard slots to store proxy information. This allows clients like block explorers to properly extract and show this information to end users, and logic contracts to optionally act upon it. + +## Motivation +Delegating proxies are widely in use, as a means to both support upgrades and reduce gas costs of deployments. Examples of these proxies are found in OpenZeppelin Contracts, Gnosis, AragonOS, Melonport, Limechain, WindingTree, Decentraland, and many others. + +However, the lack of a common interface for obtaining the logic address for a proxy makes it impossible to build common tools that act upon this information. + +A classic example of this is a block explorer. Here, the end user wants to interact with the underlying logic contract and not the proxy itself. Having a common way to retrieve the logic contract address from a proxy allows a block explorer to show the ABI of the logic contract and not that of the proxy. The explorer checks the storage of the contract at the distinguished slots to determine if it is indeed a proxy, in which case it shows information on both the proxy and the logic contract. As an example, this is how `0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48` is shown on Etherscan: + +![Sample proxy on Etherscan](../assets/eip-1967/Sample-proxy-on-etherscan.png) + +Another example is logic contracts that explicitly act upon the fact that they are being proxied. This allows them to potentially trigger a code update as part of their logic. A common storage slot allows these use cases independently of the specific proxy implementation being used. + +## Specification +Monitoring of proxies is essential to the security of many applications. It is thus essential to have the ability to track changes to the implementation and admin slots. Unfortunately, tracking changes to storage slots is not easy. Consequently, it is recommended that any function that changes any of these slots SHOULD also emit the corresponding event. This includes initialization, from `0x0` to the first non-zero value. + +The proposed storage slots for proxy-specific information are the following. More slots for additional information can be added in subsequent ERCs as needed. + +### Logic contract address + +Storage slot `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc` +(obtained as `bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)`). + +Holds the address of the logic contract that this proxy delegates to. SHOULD be empty if a beacon is used instead. Changes to this slot SHOULD be notified by the event: + +```solidity +event Upgraded(address indexed implementation); +``` + +### Beacon contract address + +Storage slot `0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50` (obtained as `bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)`). + +Holds the address of the beacon contract this proxy relies on (fallback). SHOULD be empty if a logic address is used directly instead, and should only be considered if the logic contract slot is empty. Changes to this slot SHOULD be notified by the event: + +```solidity +event BeaconUpgraded(address indexed beacon); +``` + +Beacons are used for keeping the logic address for multiple proxies in a single location, allowing the upgrade of multiple proxies by modifying a single storage slot. A beacon contract MUST implement the function: + +``` +function implementation() returns (address) +``` + +Beacon based proxy contracts do not use the logic contract slot. Instead, they use the beacon contract slot to store the address of the beacon they are attached to. In order to know the logic contract used by a beacon proxy, a client SHOULD: + +- Read the address of the beacon for the beacon logic storage slot; +- Call the `implementation()` function on the beacon contract. + +The result of the `implementation()` function on the beacon contract SHOULD NOT depend on the caller (`msg.sender`). + + +### Admin address + +Storage slot `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103` +(obtained as `bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1)`). + +Holds the address that is allowed to upgrade the logic contract address for this proxy (optional). Changes to this slot SHOULD be notified by the event: + +```solidity +event AdminChanged(address previousAdmin, address newAdmin); +``` + +## Rationale + +This EIP standardises the **storage slot** for the logic contract address, instead of a public method on the proxy contract. The rationale for this is that proxies should never expose functions to end users that could potentially clash with those of the logic contract. + +Note that a clash may occur even among functions with different names, since the ABI relies on just four bytes for the function selector. This can lead to unexpected errors, or even exploits, where a call to a proxied contract returns a different value than expected, since the proxy intercepts the call and answers with a value of its own. + +From _Malicious backdoors in Ethereum proxies_ by Nomic Labs: + +> Any function in the Proxy contract whose selector matches with one in the implementation contract will be called directly, completely skipping the implementation code. +> +> Because the function selectors use a fixed amount of bytes, there will always be the possibility of a clash. This isn’t an issue for day to day development, given that the Solidity compiler will detect a selector clash within a contract, but this becomes exploitable when selectors are used for cross-contract interaction. Clashes can be abused to create a seemingly well-behaved contract that’s actually concealing a backdoor. + +The fact that proxy public functions are potentially exploitable makes it necessary to standardise the logic contract address in a different way. + +The main requirement for the storage slots chosen is that they must never be picked by the compiler to store any contract state variable. Otherwise, a logic contract could inadvertently overwrite this information on the proxy when writing to a variable of its own. + +Solidity maps variables to storage based on the order in which they were declared, after the contract inheritance chain is linearized: the first variable is assigned the first slot, and so on. The exception is values in dynamic arrays and mappings, which are stored in the hash of the concatenation of the key and the storage slot. The Solidity development team has confirmed that the storage layout is to be preserved among new versions: + +> The layout of state variables in storage is considered to be part of the external interface of Solidity due to the fact that storage pointers can be passed to libraries. This means that any change to the rules outlined in this section is considered a breaking change of the language and due to its critical nature should be considered very carefully before being executed. In the event of such a breaking change, we would want to release a compatibility mode in which the compiler would generate bytecode supporting the old layout. + +Vyper seems to follow the same strategy as Solidity. Note that contracts written in other languages, or directly in assembly, may incur in clashes. + +They are chosen in such a way so they are guaranteed to not clash with state variables allocated by the compiler, since they depend on the hash of a string that does not start with a storage index. Furthermore, a `-1` offset is added so the preimage of the hash cannot be known, further reducing the chances of a possible attack. + +## Reference Implementation + +```solidity +/** + * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an + * implementation address that can be changed. This address is stored in storage in the location specified by + * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the + * implementation behind the proxy. + */ +contract ERC1967Proxy is Proxy, ERC1967Upgrade { + /** + * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`. + * + * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded + * function call, and allows initializing the storage of the proxy like a Solidity constructor. + */ + constructor(address _logic, bytes memory _data) payable { + assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1)); + _upgradeToAndCall(_logic, _data, false); + } + + /** + * @dev Returns the current implementation address. + */ + function _implementation() internal view virtual override returns (address impl) { + return ERC1967Upgrade._getImplementation(); + } +} + +/** + * @dev This abstract contract provides getters and event emitting update functions for + * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots. + */ +abstract contract ERC1967Upgrade { + // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1 + bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143; + + /** + * @dev Storage slot with the address of the current implementation. + * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is + * validated in the constructor. + */ + bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; + + /** + * @dev Emitted when the implementation is upgraded. + */ + event Upgraded(address indexed implementation); + + /** + * @dev Returns the current implementation address. + */ + function _getImplementation() internal view returns (address) { + return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value; + } + + /** + * @dev Stores a new address in the EIP1967 implementation slot. + */ + function _setImplementation(address newImplementation) private { + require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract"); + StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; + } + + /** + * @dev Perform implementation upgrade + * + * Emits an {Upgraded} event. + */ + function _upgradeTo(address newImplementation) internal { + _setImplementation(newImplementation); + emit Upgraded(newImplementation); + } + + /** + * @dev Perform implementation upgrade with additional setup call. + * + * Emits an {Upgraded} event. + */ + function _upgradeToAndCall( + address newImplementation, + bytes memory data, + bool forceCall + ) internal { + _upgradeTo(newImplementation); + if (data.length > 0 || forceCall) { + Address.functionDelegateCall(newImplementation, data); + } + } + + /** + * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call. + * + * Emits an {Upgraded} event. + */ + function _upgradeToAndCallSecure( + address newImplementation, + bytes memory data, + bool forceCall + ) internal { + address oldImplementation = _getImplementation(); + + // Initial upgrade and setup call + _setImplementation(newImplementation); + if (data.length > 0 || forceCall) { + Address.functionDelegateCall(newImplementation, data); + } + + // Perform rollback test if not already in progress + StorageSlot.BooleanSlot storage rollbackTesting = StorageSlot.getBooleanSlot(_ROLLBACK_SLOT); + if (!rollbackTesting.value) { + // Trigger rollback using upgradeTo from the new implementation + rollbackTesting.value = true; + Address.functionDelegateCall( + newImplementation, + abi.encodeWithSignature("upgradeTo(address)", oldImplementation) + ); + rollbackTesting.value = false; + // Check rollback was effective + require(oldImplementation == _getImplementation(), "ERC1967Upgrade: upgrade breaks further upgrades"); + // Finally reset to the new implementation and log the upgrade + _upgradeTo(newImplementation); + } + } + + /** + * @dev Storage slot with the admin of the contract. + * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is + * validated in the constructor. + */ + bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; + + /** + * @dev Emitted when the admin account has changed. + */ + event AdminChanged(address previousAdmin, address newAdmin); + + /** + * @dev Returns the current admin. + */ + function _getAdmin() internal view returns (address) { + return StorageSlot.getAddressSlot(_ADMIN_SLOT).value; + } + + /** + * @dev Stores a new address in the EIP1967 admin slot. + */ + function _setAdmin(address newAdmin) private { + require(newAdmin != address(0), "ERC1967: new admin is the zero address"); + StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin; + } + + /** + * @dev Changes the admin of the proxy. + * + * Emits an {AdminChanged} event. + */ + function _changeAdmin(address newAdmin) internal { + emit AdminChanged(_getAdmin(), newAdmin); + _setAdmin(newAdmin); + } + + /** + * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy. + * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor. + */ + bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50; + + /** + * @dev Emitted when the beacon is upgraded. + */ + event BeaconUpgraded(address indexed beacon); + + /** + * @dev Returns the current beacon. + */ + function _getBeacon() internal view returns (address) { + return StorageSlot.getAddressSlot(_BEACON_SLOT).value; + } + + /** + * @dev Stores a new beacon in the EIP1967 beacon slot. + */ + function _setBeacon(address newBeacon) private { + require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract"); + require( + Address.isContract(IBeacon(newBeacon).implementation()), + "ERC1967: beacon implementation is not a contract" + ); + StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon; + } + + /** + * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does + * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that). + * + * Emits a {BeaconUpgraded} event. + */ + function _upgradeBeaconToAndCall( + address newBeacon, + bytes memory data, + bool forceCall + ) internal { + _setBeacon(newBeacon); + emit BeaconUpgraded(newBeacon); + if (data.length > 0 || forceCall) { + Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data); + } + } +} + +/** + * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM + * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to + * be specified by overriding the virtual {_implementation} function. + * + * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a + * different contract through the {_delegate} function. + * + * The success and return data of the delegated call will be returned back to the caller of the proxy. + */ +abstract contract Proxy { + /** + * @dev Delegates the current call to `implementation`. + * + * This function does not return to its internal call site, it will return directly to the external caller. + */ + function _delegate(address implementation) internal virtual { + assembly { + // Copy msg.data. We take full control of memory in this inline assembly + // block because it will not return to Solidity code. We overwrite the + // Solidity scratch pad at memory position 0. + calldatacopy(0, 0, calldatasize()) + + // Call the implementation. + // out and outsize are 0 because we don't know the size yet. + let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0) + + // Copy the returned data. + returndatacopy(0, 0, returndatasize()) + + switch result + // delegatecall returns 0 on error. + case 0 { + revert(0, returndatasize()) + } + default { + return(0, returndatasize()) + } + } + } + + /** + * @dev This is a virtual function that should be overridden so it returns the address to which the fallback function + * and {_fallback} should delegate. + */ + function _implementation() internal view virtual returns (address); + + /** + * @dev Delegates the current call to the address returned by `_implementation()`. + * + * This function does not return to its internal call site, it will return directly to the external caller. + */ + function _fallback() internal virtual { + _beforeFallback(); + _delegate(_implementation()); + } + + /** + * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other + * function in the contract matches the call data. + */ + fallback() external payable virtual { + _fallback(); + } + + /** + * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data + * is empty. + */ + receive() external payable virtual { + _fallback(); + } + + /** + * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback` + * call, or as part of the Solidity `fallback` or `receive` functions. + * + * If overridden should call `super._beforeFallback()`. + */ + function _beforeFallback() internal virtual {} +} + +/** + * @dev Library for reading and writing primitive types to specific storage slots. + * + * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts. + * This library helps with reading and writing to such slots without the need for inline assembly. + * + * The functions in this library return Slot structs that contain a `value` member that can be used to read or write. + */ +library StorageSlot { + struct AddressSlot { + address value; + } + + struct BooleanSlot { + bool value; + } + + struct Bytes32Slot { + bytes32 value; + } + + struct Uint256Slot { + uint256 value; + } + + /** + * @dev Returns an `AddressSlot` with member `value` located at `slot`. + */ + function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) { + assembly { + r.slot := slot + } + } + + /** + * @dev Returns an `BooleanSlot` with member `value` located at `slot`. + */ + function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) { + assembly { + r.slot := slot + } + } + + /** + * @dev Returns an `Bytes32Slot` with member `value` located at `slot`. + */ + function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) { + assembly { + r.slot := slot + } + } + + /** + * @dev Returns an `Uint256Slot` with member `value` located at `slot`. + */ + function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) { + assembly { + r.slot := slot + } + } +} +``` + +## Security Considerations + +This ERC relies on the fact that the chosen storage slots are **not** to be allocated by the solidity compiler. This guarantees that an implementation contract will not accidentally overwrite any of the information required for the proxy to operate. As such, locations with a high slot number were chosen to avoid clashes with the slots allocated by the compiler. Also, locations with no known preimage were picked, to ensure that a write to mapping with a maliciously crafted key could not overwrite it. + +Logic contracts that intend to modify proxy-specific information must do so deliberately (as is the case with UUPS) by writing to the specific storage slot. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-197.md b/EIPS/eip-197.md new file mode 100644 index 0000000..9f9b3d3 --- /dev/null +++ b/EIPS/eip-197.md @@ -0,0 +1,140 @@ +--- +eip: 197 +title: Precompiled contracts for optimal ate pairing check on the elliptic curve alt_bn128 +author: Vitalik Buterin , Christian Reitwiessner +type: Standards Track +category: Core +status: Final +created: 2017-02-06 +--- + +## Simple Summary + +Precompiled contracts for elliptic curve pairing operations are required in order to perform zkSNARK verification within the block gas limit. + +## Abstract + +This EIP suggests to add precompiled contracts for a pairing function on a specific pairing-friendly elliptic curve. This can in turn be combined with [EIP-196](./eip-196.md) to verify zkSNARKs in Ethereum smart contracts. The general benefit of zkSNARKs for Ethereum is that it will increase the privacy for users (because of the Zero-Knowledge property) and might also be a scalability solution (because of the succinctness and efficient verifiability property). + +## Motivation + +Current smart contract executions on Ethereum are fully transparent, which makes them unsuitable for several use-cases that involve private information like the location, identity or history of past transactions. The technology of zkSNARKs could be a solution to this problem. While the Ethereum Virtual Machine can make use of zkSNARKs in theory, they are currently too expensive +to fit the block gas limit. Because of that, this EIP proposes to specify certain parameters for some elementary primitives that enable zkSNARKs so that they can be implemented more efficiently and the gas cost be reduced. + +Note that fixing these parameters will in no way limit the use-cases for zkSNARKs, it will even allow for incorporating some advances in zkSNARK research without the need for a further hard fork. + +Pairing functions can be used to perform a limited form of multiplicatively homomorphic operations, which are necessary for current zkSNARKs. This precompile can be used to run such computations within the block gas limit. This precompiled contract only specifies a certain check, and not an evaluation of a pairing function. The reason is that the codomain of a pairing function is a rather complex field which could provide encoding problems and all known uses of pairing function in zkSNARKs only require the specified check. + +## Specification + +For blocks where `block.number >= BYZANTIUM_FORK_BLKNUM`, add a precompiled contracts for a bilinear function on groups on the elliptic curve "alt_bn128". We will define the precompiled contract in terms of a discrete logarithm. The discrete logarithm is of course assumed to be hard to compute, but we will give an equivalent specification that makes use of elliptic curve pairing functions which can be efficiently computed below. + +Address: 0x8 + +For a cyclic group `G` (written additively) of prime order `q` let `log_P: G -> F_q` be the discrete logarithm on this group with respect to a generator `P`, i.e. `log_P(x)` is the smallest non-negative integer `n` such that `n * P = x`. + +The precompiled contract is defined as follows, where the two groups `G_1` and `G_2` are defined by their generators `P_1` and `P_2` below. Both generators have the same prime order `q`. + +``` +Input: (a1, b1, a2, b2, ..., ak, bk) from (G_1 x G_2)^k +Output: If the length of the input is incorrect or any of the inputs are not elements of + the respective group or are not encoded correctly, the call fails. + Otherwise, return one if + log_P1(a1) * log_P2(b1) + ... + log_P1(ak) * log_P2(bk) = 0 + (in F_q) and zero else. +``` + +Note that `k` is determined from the length of the input. Following the section on the encoding below, +`k` is the length of the input divided by `192`. If the input length is not a multiple of `192`, +the call fails. Empty input is valid and results in returning one. + +In order to check that an input is an element of `G_1`, verifying the encoding of the coordinates and checking that they satisfy the curve equation (or is the encoding of infinity) is sufficient. For `G_2`, in addition to that, the order of the element has to be checked to be equal to the group order `q = 21888242871839275222246405745257275088548364400416034343698204186575808495617`. + +### Definition of the groups + +The groups `G_1` and `G_2` are cyclic groups of prime order `q = 21888242871839275222246405745257275088548364400416034343698204186575808495617`. + +The group `G_1` is defined on the curve `Y^2 = X^3 + 3` over the field `F_p` with `p = 21888242871839275222246405745257275088696311157297823662689037894645226208583` with generator `P1 = (1, 2)`. + +The group `G_2` is defined on the curve `Y^2 = X^3 + 3/(i+9)` over a different field `F_p^2 = F_p[i] / (i^2 + 1)` (p is the same as above) with generator +``` +P2 = ( + 11559732032986387107991004021392285783925812861821192530917403151452391805634 * i + + 10857046999023057135944570762232829481370756359578518086990519993285655852781, + 4082367875863433681332203403145435568316851327593401208105741076214120093531 * i + + 8495653923123431417604973247489272438418190587263600148770280649306958101930 +) +``` + +Note that `G_2` is the only group of order `q` of that elliptic curve over the field `F_p^2`. Any other generator of order `q` instead of `P2` would define the same `G_2`. However, the concrete value of `P2` is useful for skeptical readers who doubt the existence of a group of order `q`. They can be instructed to compare the concrete values of `q * P2` and `P2`. + + +### Encoding + +Elements of `F_p` are encoded as 32 byte big-endian numbers. An encoding value of `p` or larger is invalid. + +Elements `a * i + b` of `F_p^2` are encoded as two elements of `F_p`, `(a, b)`. + +Elliptic curve points are encoded as a Jacobian pair `(X, Y)` where the point at infinity is encoded as `(0, 0)`. + +Note that the number `k` is derived from the input length. + +The length of the returned data is always exactly 32 bytes and encoded as a 32 byte big-endian number. + +### Gas costs + +The gas costs of the precompiled contract are `80 000 * k + 100 000`, where `k` is the number of +points or, equivalently, the length of the input divided by 192. + +## Rationale + +The specific curve `alt_bn128` was chosen because it is particularly well-suited for zkSNARKs, or, more specifically their verification building block of pairing functions. Furthermore, by choosing this curve, we can use synergy effects with ZCash and re-use some of their components and artifacts. + +The feature of adding curve and field parameters to the inputs was considered but ultimately rejected since it complicates the specification; the gas costs are much harder to determine and it would be possible to call the contracts on something which is not an actual elliptic curve or does not admit an efficient pairing implementation. + +A non-compact point encoding was chosen since it still allows to perform some operations in the smart contract itself (inclusion of the full y coordinate) and two encoded points can be compared for equality (no third projective coordinate). + +The encoding of field elements in `F_p^2` was chosen in this order to be in line with the big endian encoding of the elements themselves. + +## Backwards Compatibility + +As with the introduction of any precompiled contract, contracts that already use the given addresses will change their semantics. Because of that, the addresses are taken from the "reserved range" below 256. + +## Test Cases + +To be written. + +## Implementation + +The precompiled contract can be implemented using elliptic curve pairing functions, more specifically, an optimal ate pairing on the alt_bn128 curve, which can be implemented efficiently. In order to see that, first note that a pairing function `e: G_1 x G_2 -> G_T` fulfills the following properties (`G_1` and `G_2` are written additively, `G_T` is written multiplicatively): + +(1) `e(m * P1, n * P2) = e(P1, P2)^(m * n)` +(2) `e` is non-degenerate + +Now observe that +``` +log_P1(a1) * log_P2(b1) + ... + log_P1(ak) * log_P2(bk) = 0 (in F_q) +``` +if and only if +``` +e(P1, P2)^(log_P1(a1) * log_P2(b1) + ... + log_P1(ak) * log_P2(bk)) = 1 (in G_T) +``` + +Furthermore, the left hand side of this equation is equal to +``` +e(log_P1(a1) * P1, log_P2(b1) * P2) * ... * e(log_P1(ak) * P1, log_P2(bk) * P2) += e(a1, b1) * ... * e(ak, bk) +``` + +And thus, the precompiled contract can be implemented by verifying that +`e(a1, b1) * ... * e(ak, bk) = 1` + +Implementations are available here: + + - [libff](https://github.com/scipr-lab/libff/blob/master/libff/algebra/curves/alt_bn128/alt_bn128_g1.hpp) (C++) + - [bn](https://github.com/zcash/bn/blob/master/src/groups/mod.rs) (Rust) + - [Python](https://github.com/ethereum/py_pairing/blob/master/py_ecc/bn128/bn128_pairing.py) + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1973.md b/EIPS/eip-1973.md new file mode 100644 index 0000000..d3c94b9 --- /dev/null +++ b/EIPS/eip-1973.md @@ -0,0 +1,270 @@ +--- +eip: 1973 +title: Scalable Rewards +author: Lee Raj (@lerajk), Qin Jian (@qinjian) +type: Standards Track +category: ERC +status: Stagnant +created: 2019-04-01 +--- + +## Simple Summary + + A mintable token rewards interface that mints 'n' tokens per block which are distributed equally among the 'm' participants in the DAPP's ecosystem. + +## Abstract + + The mintable token rewards interface allows DApps to build a token economy where token rewards are distributed equally among the active participants. The tokens are minted based on per block basis that are configurable (E.g. 10.2356 tokens per block, 0.1 token per block, 1350 tokens per block) and the mint function can be initiated by any active participant. The token rewards distributed to each participant is dependent on the number of participants in the network. At the beginning, when the network has low volume, the tokens rewards per participant is high but as the network scales the token rewards decreases dynamically. + + + ## Motivation + +Distributing tokens through a push system to a large amount of participants fails due to block gas limit. As the number of participants in the network grow to tens of thousands, keeping track of the iterable registry of participants and their corresponding rewards in a push system becomes unmanagable. E.g. Looping through 5000 addresses to distribute 0.0000001 reward tokens is highly inefficient. Furthermore, the gas fees in these transactions are high and needs to be undertaken by the DApp developer or the respective company, leading to centralization concerns. + +A pull system is required to keep the application completely decentralized and to avoid the block gas limit problem. However, no standard solution has been proposed to distribute scalable rewards to tens of thousands participants with a pull system. This is what we propose with this EIP through concepts like TPP, round mask, participant mask. + +## Specification + +### Definitions + + `token amount per participant in the ecosytem or TPP (token per participant)`: TPP = (token amount to mint / total active participants) + + `roundMask`: the cumulative snapshot of TPP over time for the token contract. E.g. transactionOne = 10 tokens are minted with 100 available participants (TPP = 10 / 100) , transactionTwo = 12 tokens are minted with 95 participants (TPP = 12 / 95 ) + + roundMask = (10/100) + (12/95) + + `participantMask`: is used to keep track of a `msg.sender` (participant) rewards over time. When a `msg.sender` joins or leaves the ecosystem, the player mask is updated + + participantMask = previous roundMask OR (current roundMask - TPP) + + `rewards for msg.sender`: roundMask - participantMask + + E.g. Let's assume a total of 6 transactions (smart contract triggers or functions calls) are in place with 10 existing participants (denominator) and 20 tokens (numerator) are minted per transaction. At 2nd transaction, the 11th participant joins the network and exits before 5th transaction, the 11th participant's balance is as follows: + + ``` + t1 roundMask = (20/10) + t2 roundMask = (20/10) + (20/11) + t3 roundMask = (20/10) + (20/11) + (20/11) + t4 roundMask = (20/10) + (20/11) + (20/11) + (20/11) + t5 roundMask = (20/10) + (20/11) + (20/11) + (20/11)+ (20/10) + t6 roundMask = (20/10) + (20/11) + (20/11) + (20/11)+ (20/10) + (20/10) + ``` + + Total tokens released in 6 transactions = 60 tokens + + As the participant joins at t2 and leaves before t5, the participant deserves the rewards between t2 and t4. When the participant joins at t2, the 'participantMask = (20/10)', when the participant leaves before t5, the cumulative deserved reward tokens are : + + rewards for msg.sender: `[t4 roundMask = (20/10) + (20/11)+ (20/11) + (20/11)] - [participantMask = (20/10)] = [rewards = (20/11)+ (20/11) + (20/11)]` + + When the same participant joins the ecosystem at a later point (t27 or t35), a new 'participantMask' is given that is used to calculate the new deserved reward tokens when the participant exits. This process continues dynamically for each participant. + + `tokensPerBlock`: the amount of tokens that will be released per block + + `blockFreezeInterval`: the number of blocks that need to pass until the next mint. E.g. if set to 50 and 'n' tokens were minted at block 'b', the next 'n' tokens won't be minted until 'b + 50' blocks have passed + + `lastMintedBlockNumber`: the block number on which last 'n' tokens were minted + + `totalParticipants` : the total number of participants in the DApp network + + `tokencontractAddress` : the contract address to which tokens will be minted, default is address(this) + +```solidity + +pragma solidity ^0.5.2; + +import "openzeppelin-solidity/contracts/token/ERC20/ERC20Mintable.sol"; +import "openzeppelin-solidity/contracts/token/ERC20/ERC20Detailed.sol"; + +contract Rewards is ERC20Mintable, ERC20Detailed { + +using SafeMath for uint256; + +uint256 public roundMask; +uint256 public lastMintedBlockNumber; +uint256 public totalParticipants = 0; +uint256 public tokensPerBlock; +uint256 public blockFreezeInterval; +address public tokencontractAddress = address(this); +mapping(address => uint256) public participantMask; + +/** + * @dev constructor, initializes variables. + * @param _tokensPerBlock The amount of token that will be released per block, entered in wei format (E.g. 1000000000000000000) + * @param _blockFreezeInterval The amount of blocks that need to pass (E.g. 1, 10, 100) before more tokens are brought into the ecosystem. + */ + constructor(uint256 _tokensPerBlock, uint256 _blockFreezeInterval) public ERC20Detailed("Simple Token", "SIM", 18){ +lastMintedBlockNumber = block.number; +tokensPerBlock = _tokensPerBlock; +blockFreezeInterval = _blockFreezeInterval; +} + +/** + * @dev Modifier to check if msg.sender is whitelisted as a minter. + */ +modifier isAuthorized() { +require(isMinter(msg.sender)); +_; +} + +/** + * @dev Function to add participants in the network. + * @param _minter The address that will be able to mint tokens. + * @return A boolean that indicates if the operation was successful. + */ +function addMinters(address _minter) external returns (bool) { +_addMinter(_minter); +totalParticipants = totalParticipants.add(1); +updateParticipantMask(_minter); +return true; +} + + +/** + * @dev Function to remove participants in the network. + * @param _minter The address that will be unable to mint tokens. + * @return A boolean that indicates if the operation was successful. + */ +function removeMinters(address _minter) external returns (bool) { +totalParticipants = totalParticipants.sub(1); +_removeMinter(_minter); +return true; +} + + +/** + * @dev Function to introduce new tokens in the network. + * @return A boolean that indicates if the operation was successful. + */ +function trigger() external isAuthorized returns (bool) { +bool res = readyToMint(); +if(res == false) { +return false; +} else { +mintTokens(); +return true; +} +} + +/** + * @dev Function to withdraw rewarded tokens by a participant. + * @return A boolean that indicates if the operation was successful. + */ +function withdraw() external isAuthorized returns (bool) { +uint256 amount = calculateRewards(); +require(amount >0); +ERC20(tokencontractAddress).transfer(msg.sender, amount); +} + +/** + * @dev Function to check if new tokens are ready to be minted. + * @return A boolean that indicates if the operation was successful. + */ +function readyToMint() public view returns (bool) { +uint256 currentBlockNumber = block.number; +uint256 lastBlockNumber = lastMintedBlockNumber; +if(currentBlockNumber > lastBlockNumber + blockFreezeInterval) { +return true; +} else { +return false; +} +} + +/** + * @dev Function to calculate current rewards for a participant. + * @return A uint that returns the calculated rewards amount. + */ +function calculateRewards() private returns (uint256) { +uint256 playerMask = participantMask[msg.sender]; +uint256 rewards = roundMask.sub(playerMask); +updateParticipantMask(msg.sender); +return rewards; +} + +/** + * @dev Function to mint new tokens into the economy. + * @return A boolean that indicates if the operation was successful. + */ +function mintTokens() private returns (bool) { +uint256 currentBlockNumber = block.number; +uint256 tokenReleaseAmount = (currentBlockNumber.sub(lastMintedBlockNumber)).mul(tokensPerBlock); +lastMintedBlockNumber = currentBlockNumber; +mint(tokencontractAddress, tokenReleaseAmount); +calculateTPP(tokenReleaseAmount); +return true; +} + + /** +* @dev Function to calculate TPP (token amount per participant). +* @return A boolean that indicates if the operation was successful. +*/ +function calculateTPP(uint256 tokens) private returns (bool) { +uint256 tpp = tokens.div(totalParticipants); +updateRoundMask(tpp); +return true; +} + + /** +* @dev Function to update round mask. +* @return A boolean that indicates if the operation was successful. +*/ +function updateRoundMask(uint256 tpp) private returns (bool) { +roundMask = roundMask.add(tpp); +return true; +} + + /** +* @dev Function to update participant mask (store the previous round mask) +* @return A boolean that indicates if the operation was successful. +*/ +function updateParticipantMask(address participant) private returns (bool) { +uint256 previousRoundMask = roundMask; +participantMask[participant] = previousRoundMask; +return true; +} + +} +``` + +## Rationale + +Currently, there is no standard for a scalable reward distribution mechanism. In order to create a sustainable cryptoeconomic environment within DAPPs, incentives play a large role. However, without a scalable way to distribute rewards to tens of thousands of participants, most DAPPs lack a good incentive structure. The ones with a sustainable cryptoeconomic environment depend heavily on centralized servers or a group of selective nodes to trigger the smart contracts. But, in order to keep an application truly decentralized, the reward distribution mechanism must depend on the active participants itself and scale as the number of participants grow. This is what this EIP intends to accomplish. + +## Backwards Compatibility + +Not Applicable. + +## Test Cases + +WIP, will be added. + +## Implementation + +WIP, a proper implementation will be added later.A sample example is below: + +`etherscan rewards contract` : https://ropsten.etherscan.io/address/0x8b0abfc541ab7558857816a67e186221adf887bc#tokentxns + +`Step 1` : deploy Rewards contract with the following parameters_tokensPerBlock = 1e18, _blockFreezeInterval = 1 + +`Step 2` : add Alice(0x123) and Bob(0x456) as minters, addMinters(address _minter) + +`Step 3` : call trigger() from Alice / Bob's account. 65 blocks are passed, hence 65 SIM tokens are minted. The RM is 32500000000000000000 + +`Step 4` : Alice withdraws and receives 32.5 SIM tokens (65 tokens / 2 participants) and her PM = 32500000000000000000 + +`Step 5` : add Satoshi(0x321) and Vitalik(0x654) as minters, addMinters(address _minter) + +`Step 6` : call trigger() from Alice / Bob's / Satoshi / Vitalik account. 101 blocks are passed, hence 101 SIM tokens are minted. The RM is 57750000000000000000 + +`Step 7` : Alice withdraws and receives 25.25 SIM tokens (101 tokens / 4 participants) and her PM = 57750000000000000000 + +`Step 8` : Bob withdraws and receives 57.75 SIM tokens ((65 tokens / 2 participants) + (101 tokens / 4 participants)). Bob's PM = 57750000000000000000 + +## Copyright + +Copyright and related rights waived via CC0. + +## References + +1. Scalable Reward Distribution on the Ethereum Blockchain by Bogdan Batog, Lucian Boca and Nick Johnson + +2. Fomo3d DApp, https://fomo3d.hostedwiki.co/ diff --git a/EIPS/eip-198.md b/EIPS/eip-198.md new file mode 100644 index 0000000..5c8f066 --- /dev/null +++ b/EIPS/eip-198.md @@ -0,0 +1,104 @@ +--- +eip: 198 +title: Big integer modular exponentiation +author: Vitalik Buterin (@vbuterin) +status: Final +type: Standards Track +category: Core +created: 2017-01-30 +--- + +# Parameters + +* `GQUADDIVISOR: 20` + +# Specification + +At address 0x00......05, add a precompile that expects input in the following format: + + + +Where every length is a 32-byte left-padded integer representing the number of bytes to be taken up by the next value. Call data is assumed to be infinitely right-padded with zero bytes, and excess data is ignored. Consumes `floor(mult_complexity(max(length_of_MODULUS, length_of_BASE)) * max(ADJUSTED_EXPONENT_LENGTH, 1) / GQUADDIVISOR)` gas, and if there is enough gas, returns an output `(BASE**EXPONENT) % MODULUS` as a byte array with the same length as the modulus. + +`ADJUSTED_EXPONENT_LENGTH` is defined as follows. + +* If `length_of_EXPONENT <= 32`, and all bits in `EXPONENT` are 0, return 0 +* If `length_of_EXPONENT <= 32`, then return the index of the highest bit in `EXPONENT` (eg. 1 -> 0, 2 -> 1, 3 -> 1, 255 -> 7, 256 -> 8). +* If `length_of_EXPONENT > 32`, then return `8 * (length_of_EXPONENT - 32)` plus the index of the highest bit in the first 32 bytes of `EXPONENT` (eg. if `EXPONENT = \x00\x00\x01\x00.....\x00`, with one hundred bytes, then the result is 8 * (100 - 32) + 253 = 797). If all of the first 32 bytes of `EXPONENT` are zero, return exactly `8 * (length_of_EXPONENT - 32)`. + +`mult_complexity` is a function intended to approximate the difficulty of Karatsuba multiplication (used in all major bigint libraries) and is defined as follows. + +``` +def mult_complexity(x): + if x <= 64: return x ** 2 + elif x <= 1024: return x ** 2 // 4 + 96 * x - 3072 + else: return x ** 2 // 16 + 480 * x - 199680 +``` + +For example, the input data: + + 0000000000000000000000000000000000000000000000000000000000000001 + 0000000000000000000000000000000000000000000000000000000000000020 + 0000000000000000000000000000000000000000000000000000000000000020 + 03 + fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e + fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f + +Represents the exponent `3**(2**256 - 2**32 - 978) % (2**256 - 2**32 - 977)`. By Fermat's little theorem, this equals 1, so the result is: + + 0000000000000000000000000000000000000000000000000000000000000001 + +Returned as 32 bytes because the modulus length was 32 bytes. The `ADJUSTED_EXPONENT_LENGTH` would be 255, and the gas cost would be `mult_complexity(32) * 255 / 20 = 13056` gas (note that this is ~8 times the cost of using the EXP opcode to compute a 32-byte exponent). A 4096-bit RSA exponentiation would cost `mult_complexity(512) * 4095 / 100 = 22853376` gas in the worst case, though RSA verification in practice usually uses an exponent of 3 or 65537, which would reduce the gas consumption to 5580 or 89292, respectively. + +This input data: + + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000020 + 0000000000000000000000000000000000000000000000000000000000000020 + fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e + fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f + +Would be parsed as a base of 0, exponent of `2**256 - 2**32 - 978` and modulus of `2**256 - 2**32 - 977`, and so would return 0. Notice how if the length_of_BASE is 0, then it does not interpret _any_ data as the base, instead immediately interpreting the next 32 bytes as EXPONENT. + +This input data: + + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000020 + ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe + fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd + +Would parse a base length of 0, an exponent length of 32, and a modulus length of `2**256 - 1`, where the base is empty, the exponent is `2**256 - 2` and the modulus is `(2**256 - 3) * 256**(2**256 - 33)` (yes, that's a really big number). It would then immediately fail, as it's not possible to provide enough gas to make that computation. + +This input data: + + 0000000000000000000000000000000000000000000000000000000000000001 + 0000000000000000000000000000000000000000000000000000000000000002 + 0000000000000000000000000000000000000000000000000000000000000020 + 03 + ffff + 8000000000000000000000000000000000000000000000000000000000000000 + 07 + +Would parse as a base of 3, an exponent of 65535, and a modulus of `2**255`, and it would ignore the remaining 0x07 byte. + +This input data: + + 0000000000000000000000000000000000000000000000000000000000000001 + 0000000000000000000000000000000000000000000000000000000000000002 + 0000000000000000000000000000000000000000000000000000000000000020 + 03 + ffff + 80 + +Would also parse as a base of 3, an exponent of 65535 and a modulus of `2**255`, as it attempts to grab 32 bytes for the modulus starting from 0x80 - but there is no further data, so it right-pads it with 31 zero bytes. + +# Rationale + +This allows for efficient RSA verification inside of the EVM, as well as other forms of number theory-based cryptography. Note that adding precompiles for addition and subtraction is not required, as the in-EVM algorithm is efficient enough, and multiplication can be done through this precompile via `a * b = ((a + b)**2 - (a - b)**2) / 4`. + +The bit-based exponent calculation is done specifically to fairly charge for the often-used exponents of 2 (for multiplication) and 3 and 65537 (for RSA verification). + +# Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-1985.md b/EIPS/eip-1985.md new file mode 100644 index 0000000..9bb02f1 --- /dev/null +++ b/EIPS/eip-1985.md @@ -0,0 +1,138 @@ +--- +eip: 1985 +title: Sane limits for certain EVM parameters +author: Alex Beregszaszi (@axic), Paweł Bylica (@chfast) +discussions-to: https://ethereum-magicians.org/t/eip-1985-sane-limits-for-certain-evm-parameters/3224 +status: Stagnant +type: Standards Track +category: Core +created: 2018-08-01 +--- + +## Abstract + +Introduce an explicit value range for certain EVM parameters +(such as gas limit, block number, block timestamp, size field when returning/copying data within EVM). +Some of these already have an implicit value range due to various (practical) reasons. + +## Motivation + +Having such an explicit value range can help in creating compatible client implementations, +in certain cases it can also offer minor speed improvements, +and can reduce the effort needed to create consensus critical test cases +by eliminating unrealistic edge cases. + +## Specification + +If `block.number >= {FORK_BLOCK}`, the following value ranges are introduced. +They restrict the results (i.e. values pushed to the stack) of the instructions listed below. + +1. *gas*, *gas limit*, *block gas limit* + is a range between `0` and `0x7fffffffffffffff` (`2**63 - 1`, `9223372036854775807`). + It affects the following instructions: + - `GASLIMIT` (`0x45`), + - `GAS` (`0x5a`). + +2. *block number*, *timestamp* + is a range between `0` and `0x7fffffffffffffff` (`2**63 - 1`, `9223372036854775807`). + It affects the following instructions: + - `TIMESTAMP` (`0x42`), + - `NUMBER` (`0x43`). + +3. *account address* + is a range between `0` and `0xffffffffffffffffffffffffffffffffffffffff` (`2**160 - 1`, `1461501637330902918203684832716283019655932542975`) + i.e. the address occupies the 160 low bits of the 256-bit value and the remaining top 96 bits must be zeros. + It affects the following instructions: + - `ADDRESS` (`0x30`), + - `ORIGIN` (`0x32`), + - `CALLER` (`0x33`), + - `COINBASE` (`0x41`), + - `CREATE` (`0xf0`), + - `CREATE2` (`0xf5`). + +4. *buffer size*, *code size*, *memory size* + is a range between `0` and `0xffffffff` (`2**32 - 1`, `4294967295`). + It affects the following instructions: + - `CALLDATASIZE` (`0x36`), + - `CODESIZE` (`0x38`), + - `EXTCODESIZE` (`0x3b`), + - `RETURNDATASIZE` (`0x3d`), + - `MSIZE` (`0x59`), + - `PC` (`0x58`). + + +## Rationale + +These limits have been: +- proposed by [EVMC] +- implemented partially by certain clients, such as [Aleth], [geth], [Parity] and [ethereumjs] +- allowed by certain test cases in the [Ethereum testing suite] +- and implicitly also allowed by certain assumptions, such as due to gas limits some of these values cannot grow past a certain limit + +Most of the limits proposed in this document have been previously explored and tested in [EVMC]. + +Using the `2**63 - 1` constant to limit some of the ranges: +- allows using signed 64-bit integer type to represent it, + what helps programming languages not having unsigned types, +- makes arithmetic simpler (e.g. checking out-of-gas conditions is simple as `gas_counter < 0`). + +### Timestamp + +The [Yellow Paper] defines the timestamp in block as "A scalar value equal to the reasonable output of Unix’s time() at this block’s inception". +IEEE Std 1003.1-2001 (POSIX.1) leaves that definition implementation defined. + +### Addresses + +The size of addresses is specified in the [Yellow Paper] as 20 bytes. +E.g. the `COINBASE` instruction is specified to return *H*c ∈ 𝔹20 which has 20 bytes. + +### Memory size + +Memory expansion cost is not linear and is determined by the following formula: + cost = cost_per_word * number_of_words + (number_of_words ^ 2 / 512) + +Expanding to over `2^32 - 1` bytes would cost `35184774742016` gas. This number fits into the gas limit imposed above (`2 ^ 63 - 1`) and would cost around 35184 Ether in a transaction to exhaust, with a 1 GWei gas cost, which can be attained on mainnet. + +However, setting the limit `2^32 - 1` is beneficial from a VM design perspective and we believe limiting memory should be done via carefully selecting the block gas limit. + +### Code size + +[EIP-170](./eip-170.md) has implemented a code size limit of 0x6000, however even before that, it was practically impossible to deploy a code blob exceeding `2**32 - 1` bytes in size. + +### Comparing current implementations + +- Timestamp is implemented as a 64-bit value in [Aleth], [geth] and [Parity] +- Block gas limit is implemented as a 64-bit in [Aleth] and [geth] +- Memory, buffer and code sizes are implemented as 64-bit values in [geth] + +## Backwards Compatibility + +All of these limits are already enforced mostly through the block gas limit. Since the out of range case results in a transaction failure, there should not be a change in behaviour. + +## Test Cases + +TBA + +## Implementation + +TBA + +## References + +- [EIP-92](https://github.com/ethereum/EIPs/issues/92) proposed the transaction gas limit to be limited at `2**63 - 1` and had a lengthy discussion about other limits. +- [EIP-106](https://github.com/ethereum/EIPs/issues/106) proposed the block gas limit to be limited at `2**63 - 1`. + +## TODO + +1. Does the gas limit apply to the gas argument for call instructions? + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). + +[EVMC]: https://github.com/ethereum/evmc +[Aleth]: https://github.com/ethereum/aleth +[geth]: https://github.com/ethereum/go-ethereum +[Parity]: https://github.com/paritytech/parity-ethereum +[ethereumjs]: https://github.com/ethereumjs +[Ethereum testing suite]: https://github.com/ethereum/tests +[Yellow Paper]: https://github.com/ethereum/yellowpaper diff --git a/EIPS/eip-1996.md b/EIPS/eip-1996.md new file mode 100644 index 0000000..fffb24a --- /dev/null +++ b/EIPS/eip-1996.md @@ -0,0 +1,294 @@ +--- +eip: 1996 +title: Holdable Token +author: Julio Faura , Fernando Paris , Daniel Lehrner +discussions-to: https://github.com/ethereum/EIPs/issues/2103 +status: Stagnant +type: Standards Track +category: ERC +created: 2019-04-10 +requires: 20 +--- + +## Simple Summary +An extension to the ERC-20 standard token that allows tokens to be put on hold. This guarantees a future transfer and makes the held tokens unavailable for transfer in the mean time. Holds are similar to escrows in that are firm and lead to final settlement. + +## Actors + +#### Operator +An account which has been approved by an account to create holds on its behalf. + +#### Hold issuer +The account, which creates a hold. This can be the account owner itself, or any account, which has been approved as an operator for the account. + +#### Notary +The account which decides if a hold should be executed. + +## Abstract +A hold specifies a payer, a payee, a maximum amount, a notary and an expiration time. When the hold is created, the specified token balance from the payer is put on hold. A held balance cannot be transferred until the hold is either executed or released. The hold can only be executed by the notary, which triggers the transfer of the tokens from the payer to the payee. If a hold is released, either by the notary at any time, or by anyone after the expiration, no transfer is carried out and the amount is available again for the payer. + +A hold can be partially executed, if the execution specifies an amount less than the maximum amount. In this case the specified amount is transferred to the payee and the remaining amount is available again to the payer. + +Holds can be specified to be perpetual. In this case, the hold cannot be released upon expiration, and thus can only be executed by the notary or released by the notary or payee. + +## Motivation + +A hold has to be used in different scenarios where a immediate transfer between accounts is not possible or has to be guaranteed beforehand: + +1. A regulated token may not allow to do a token transfer between accounts without verifying first, that it follows all the regulations. In this case a clearable transfer has to be used. During the clearing process a hold is created to ensure, that the transfer is successful after all checks have passed. If the transfer violates any of the regulations, it is cleared and not further processed. + +1. In certain business situations a payment has to be guaranteed before its services can be used. For example: When checking in a hotel, the hotel will put a hold on the guest's account to ensure that enough balance is available to pay for the room before handing over the keys. + +1. In other occasions a payment has to be guaranteed without knowing the exact amount beforehand. To stay with the hotel example: The hotel can put a hold on the guest's account as a guarantee for any possible extras, like room service. When the guest checks out the hold is partially executed and the remaining amount is available again on the guest's account. + +The ERC-20 `approve` function provides some of the necessary functionality for the use cases above. The main difference to holds, is that `approve` does not ensure a payment, as the approved money is not blocked and can be transferred at any moment. + +## Specification + +```solidity +interface IHoldable /* is ERC-20 */ { + enum HoldStatusCode { + Nonexistent, + Ordered, + Executed, + ReleasedByNotary, + ReleasedByPayee, + ReleasedOnExpiration + } + + function hold(string calldata operationId, address to, address notary, uint256 value, uint256 timeToExpiration) external returns (bool); + function holdFrom(string calldata operationId, address from, address to, address notary, uint256 value, uint256 timeToExpiration) external returns (bool); + function releaseHold(string calldata operationId) external returns (bool); + function executeHold(string calldata operationId, uint256 value) external returns (bool); + function renewHold(string calldata operationId, uint256 timeToExpiration) external returns (bool); + function retrieveHoldData(string calldata operationId) external view returns (address from, address to, address notary, uint256 value, uint256 expiration, HoldStatusCode status); + + function balanceOnHold(address account) external view returns (uint256); + function netBalanceOf(address account) external view returns (uint256); + function totalSupplyOnHold() external view returns (uint256); + + function authorizeHoldOperator(address operator) external returns (bool); + function revokeHoldOperator(address operator) external returns (bool); + function isHoldOperatorFor(address operator, address from) external view returns (bool); + + event HoldCreated(address indexed holdIssuer, string operationId, address from, address to, address indexed notary, uint256 value, uint256 expiration); + event HoldExecuted(address indexed holdIssuer, string operationId, address indexed notary, uint256 heldValue, uint256 transferredValue); + event HoldReleased(address indexed holdIssuer, string operationId, HoldStatusCode status); + event HoldRenewed(address indexed holdIssuer, string operationId, uint256 oldExpiration, uint256 newExpiration); + event AuthorizedHoldOperator(address indexed operator, address indexed account); + event RevokedHoldOperator(address indexed operator, address indexed account); +} +``` + +### Functions + +#### hold + +Creates a hold on behalf of the msg.sender in favor of the payee. It specifies a notary who is responsible to either execute or release the hold. The function must revert if the operation ID has been used before. + +| Parameter | Description | +| ---------|-------------| +| operationId | The unique ID to identify the hold | +| to | The address of the payee, to whom the tokens are to be transferred if executed | +| notary | The address of the notary who is going to determine whether the hold is to be executed or released | +| value | The amount to be transferred. Must be less or equal than the balance of the payer. | +| timeToExpiration | The duration until the hold is expired. If it is '0' the hold must be perpetual. | + +#### holdFrom + +Creates a hold on behalf of the payer in favor of the payee. The `from` account has to approve beforehand, that another account can issue holds on its behalf by calling `approveToHold`. The function must revert if the operation ID has been used before. + +| Parameter | Description | +| ---------|-------------| +| operationId | The unique ID to identify the hold | +| from | The address of the payer, from whom the tokens are to be taken if executed | +| to | The address of the payee, to whom the tokens are to be transferred if executed | +| notary | The address of the notary who is going to determine whether the hold is to be executed or released | +| value | The amount to be transferred. Must be less or equal than the balance of the payer. | +| timeToExpiration | The duration until the hold is expired. If it is '0' the hold must be perpetual. | + +#### releaseHold + +Releases a hold. Release means that the transfer is not executed and the held amount is available again for the payer. Until a hold has expired it can only be released by the notary or the payee. After it has expired it can be released by anyone. + +| Parameter | Description | +| ---------|-------------| +| operationId | The unique ID to identify the hold | + +#### executeHold + +Executes a hold. Execute means that the specified value is transferred from the payer to the payee. If the specified value is less than the hold value the remaining amount is available again to the payer. The implementation must verify that only the notary is able to successfully call the function. + +| Parameter | Description | +| ---------|-------------| +| operationId | The unique ID to identify the hold | +| value | The amount to be transferred. This amount has to be less or equal than the hold value | + +#### renewHold + +Renews a hold. The new expiration time must be the block timestamp plus the given `timeToExpiration`, independently if the hold was perpetual or not before that. Furthermore a hold must be made perpetual if `timeToExpiration` is '0'. The implementation must verify that only the payer or operator are able to successfully call the function. Furthermore the only a hold, which has not yet expired can be successfully renewed. + +| Parameter | Description | +| ---------|-------------| +| operationId | The unique ID to identify the hold | +| timeToExpiration | The new duration until the hold is expired. | + +#### retrieveHoldData + +Retrieves all the information available for a particular hold. + +| Parameter | Description | +| ---------|-------------| +| operationId | The unique ID to identify the hold | + +#### balanceOnHold + +Retrieves how much of the balance is currently held and therefore not available for transfer. + +| Parameter | Description | +| ---------|-------------| +| account | The address which held balance should be returned | + +#### netBalanceOf + +Retrieves the net balance, which is the sum of `balanceOf` and `balanceOnHold`. + +| Parameter | Description | +| ---------|-------------| +| account | The address which net balance should be returned | + +#### totalSupplyOnHold + +Retrieves the total sum of how many tokens are on hold. + +| Parameter | Description | +| ---------|-------------| +| - | - | + +#### authorizeHoldOperator + +Approves an operator to issue holds on behalf of msg.sender. + +| Parameter | Description | +| ---------|-------------| +| operator | The address to be approved as operator of holds | + +#### revokeHoldOperator + +Revokes the approval to issue holds on behalf of msg.sender. + +| Parameter | Description | +| ---------|-------------| +| operator | The address to be revoked as operator of holds | + +#### isHoldOperatorFor + +Retrieves if an operator is approved to create holds on behalf of `from`. + +| Parameter | Description | +| ---------|-------------| +| operator | The address to be a operator of holds | +| from | The address on which the holds would be created | + +#### balanceOf + +The standard implementation of ERC-20 has to be changed in order to deduct the held balance from the ERC-20 balance. + +#### transfer + +The standard implementation of ERC-20 has to be changed in order to deduct the held balance from the ERC-20 balance. Any amount that is held must not be transferred. + +#### transferFrom + +The standard implementation of ERC-20 has to be changed in order to deduct the held balance from the ERC-20 balance. Any amount that is held must not be transferred. + +### Events + +#### HoldCreated + +Emitted when a hold has been created. + +| Parameter | Description | +| ---------|-------------| +| holdIssuer | The address of the hold issuer of the hold | +| operationId | The unique ID to identify the hold | +| from | The address of the payer, from whom the tokens are to be taken if executed | +| to | The address of the payee, to whom the tokens are to be paid if executed | +| notary | The address of the notary who is going to determine whether the hold is to be executed or released | +| value | The amount to be transferred. Must be less or equal than the balance of the payer. | +| expiration | The unix timestamp when the hold is expired | + +#### HoldExecuted + +Emitted when a hold has been executed. + +| Parameter | Description | +| ---------|-------------| +| holdIssuer | The address of the hold issuer of the hold | +| operationId | The unique ID to identify the hold | +| notary | The address of the notary who executed the hold | +| heldValue | The amount which was put on hold during creation | +| transferredValue | The amount which was used for the transfer | + +#### HoldReleased + +Emitted when a hold has been released. + +| Parameter | Description | +| ---------|-------------| +| holdIssuer | The address of the hold issuer of the hold | +| operationId | The unique ID to identify the hold | +| status | Can be one of the following values: `ReleasedByNotary`, `ReleasedByPayee`, `ReleasedOnExpiration` | + +#### HoldRenewed + +Emitted when a hold has been renewed. + +| Parameter | Description | +| ---------|-------------| +| holdIssuer | The address of the hold issuer of the hold | +| operationId | The unique ID to identify the hold | +| oldExpiration | The expiration time before the renewal | +| newExpiration | The expiration time after the renewal | + +#### AuthorizedHoldOperator + +Emitted when an operator has been approved to create holds on behalf of another account. + +| Parameter | Description | +| ---------|-------------| +| operator | The address to be a operator of holds | +| account | Address on which behalf holds will potentially be created | + +#### RevokedHoldOperator + +Emitted when an operator has been revoked from creating holds on behalf of another account. + +| Parameter | Description | +| ---------|-------------| +| operator | The address to be a operator of holds | +| account | Address on which behalf holds could potentially be created | + +## Rationale + +This standards provides a functionality, to guarantee future payments, which is needed for many business cases where transfers have to be guaranteed. + +It goes a step further than the ERC-20 `approve` function by ensuring that the held balance will be available when the transfer is done. Something that can not be done with `approve`, as the approved amount is only a maximum spending amount, but never guaranteed to be available. + +While not requiring it, the naming of the functions `authorizeHoldOperator`, `revokeHoldOperator` and `isHoldOperatorFor` follows the naming convention of [ERC-777](./eip-777.md). + +The `operationId` is a string and not something more gas efficient to allow easy traceability of the hold and allow human readable ids. It is up to the implementer if the string should be stored on-chain or only its hash, as it is enough to identify a hold. + +The `operationId` is a competitive resource. It is recommended, but nor required, that the hold issuers used a unique prefix to avoid collisions. + +## Backwards Compatibility +This EIP is fully backwards compatible as its implementation extends the functionality of ERC-20. + +## Implementation +The GitHub repository [IoBuilders/holdable-token](https://github.com/IoBuilders/holdable-token) contains the reference implementation. + +## Contributors +This proposal has been collaboratively implemented by [adhara.io](https://adhara.io/) and [io.builders](https://io.builders/). + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2.md b/EIPS/eip-2.md new file mode 100644 index 0000000..fd1da48 --- /dev/null +++ b/EIPS/eip-2.md @@ -0,0 +1,66 @@ +--- +eip: 2 +title: Homestead Hard-fork Changes +author: Vitalik Buterin (@vbuterin) +status: Final +type: Standards Track +category: Core +created: 2015-11-15 +--- + +### Meta reference + +[Homestead](./eip-606.md). + +### Parameters + +| FORK_BLKNUM | CHAIN_NAME | +|-----------------|-------------| +| 1,150,000 | Main net | +| 494,000 | Morden | +| 0 | Future testnets | + +# Specification + +If `block.number >= HOMESTEAD_FORK_BLKNUM`, do the following: + +1. The gas cost *for creating contracts via a transaction* is increased from 21,000 to 53,000, i.e. if you send a transaction and the to address is the empty string, the initial gas subtracted is 53,000 plus the gas cost of the tx data, rather than 21,000 as is currently the case. Contract creation from a contract using the `CREATE` opcode is unaffected. +2. All transaction signatures whose s-value is greater than `secp256k1n/2` are now considered invalid. The ECDSA recover precompiled contract remains unchanged and will keep accepting high s-values; this is useful e.g. if a contract recovers old Bitcoin signatures. +3. If contract creation does not have enough gas to pay for the final gas fee for adding the contract code to the state, the contract creation fails (i.e. goes out-of-gas) rather than leaving an empty contract. +4. Change the difficulty adjustment algorithm from the current formula: `block_diff = parent_diff + parent_diff // 2048 * (1 if block_timestamp - parent_timestamp < 13 else -1) + int(2**((block.number // 100000) - 2))` (where the `int(2**((block.number // 100000) - 2))` represents the exponential difficulty adjustment component) to `block_diff = parent_diff + parent_diff // 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99) + int(2**((block.number // 100000) - 2))`, where `//` is the integer division operator, eg. `6 // 2 = 3`, `7 // 2 = 3`, `8 // 2 = 4`. The `minDifficulty` still defines the minimum difficulty allowed and no adjustment may take it below this. + +# Rationale + +Currently, there is an excess incentive to create contracts via transactions, where the cost is 21,000, rather than contracts, where the cost is 32,000. Additionally, with the help of suicide refunds, it is currently possible to make a simple ether value transfer using only 11,664 gas; the code for doing this is as follows: + +```python +from ethereum import tester as t +> from ethereum import utils +> s = t.state() +> c = s.abi_contract('def init():\n suicide(0x47e25df8822538a8596b28c637896b4d143c351e)', endowment=10**15) +> s.block.get_receipts()[-1].gas_used +11664 +> s.block.get_balance(utils.normalize_address(0x47e25df8822538a8596b28c637896b4d143c351e)) +1000000000000000 +``` +This is not a particularly serious problem, but it is nevertheless arguably a bug. + +Allowing transactions with any s value with `0 < s < secp256k1n`, as is currently the case, opens a transaction malleability concern, as one can take any transaction, flip the s value from `s` to `secp256k1n - s`, flip the v value (`27 -> 28`, `28 -> 27`), and the resulting signature would still be valid. This is not a serious security flaw, especially since Ethereum uses addresses and not transaction hashes as the input to an ether value transfer or other transaction, but it nevertheless creates a UI inconvenience as an attacker can cause the transaction that gets confirmed in a block to have a different hash from the transaction that any user sends, interfering with user interfaces that use transaction hashes as tracking IDs. Preventing high s values removes this problem. + +Making contract creation go out-of-gas if there is not enough gas to pay for the final gas fee has the benefits that: +- (i) it creates a more intuitive "success or fail" distinction in the result of a contract creation process, rather than the current "success, fail, or empty contract" trichotomy; +- (ii) makes failures more easily detectable, as unless contract creation fully succeeds then no contract account will be created at all; and +- (iii) makes contract creation safer in the case where there is an endowment, as there is a guarantee that either the entire initiation process happens or the transaction fails and the endowment is refunded. + +The difficulty adjustment change conclusively solves a problem that the Ethereum protocol saw two months ago where an excessive number of miners were mining blocks that contain a timestamp equal to `parent_timestamp + 1`; this skewed the block time distribution, and so the current block time algorithm, which targets a *median* of 13 seconds, continued to target the same median but the mean started increasing. If 51% of miners had started mining blocks in this way, the mean would have increased to infinity. The proposed new formula is roughly based on targeting the mean; one can prove that with the formula in use, an average block time longer than 24 seconds is mathematically impossible in the long term. + +The use of `(block_timestamp - parent_timestamp) // 10` as the main input variable rather than the time difference directly serves to maintain the coarse-grained nature of the algorithm, preventing an excessive incentive to set the timestamp difference to exactly 1 in order to create a block that has slightly higher difficulty and that will thus be guaranteed to beat out any possible forks. The cap of -99 simply serves to ensure that the difficulty does not fall extremely far if two blocks happen to be very far apart in time due to a client security bug or other black-swan issue. + +# Implementation + +This is implemented in Python here: + +1. https://github.com/ethereum/pyethereum/blob/d117c8f3fd93359fc641fd850fa799436f7c43b5/ethereum/processblock.py#L130 +2. https://github.com/ethereum/pyethereum/blob/d117c8f3fd93359fc641fd850fa799436f7c43b5/ethereum/processblock.py#L129 +3. https://github.com/ethereum/pyethereum/blob/d117c8f3fd93359fc641fd850fa799436f7c43b5/ethereum/processblock.py#L304 +4. https://github.com/ethereum/pyethereum/blob/d117c8f3fd93359fc641fd850fa799436f7c43b5/ethereum/blocks.py#L42 diff --git a/EIPS/eip-20.md b/EIPS/eip-20.md new file mode 100644 index 0000000..98600ba --- /dev/null +++ b/EIPS/eip-20.md @@ -0,0 +1,193 @@ +--- +eip: 20 +title: Token Standard +author: Fabian Vogelsteller , Vitalik Buterin +type: Standards Track +category: ERC +status: Final +created: 2015-11-19 +--- + +## Simple Summary + +A standard interface for tokens. + + +## Abstract + +The following standard allows for the implementation of a standard API for tokens within smart contracts. +This standard provides basic functionality to transfer tokens, as well as allow tokens to be approved so they can be spent by another on-chain third party. + + +## Motivation + +A standard interface allows any tokens on Ethereum to be re-used by other applications: from wallets to decentralized exchanges. + + +## Specification + +## Token +### Methods + +**NOTES**: + - The following specifications use syntax from Solidity `0.4.17` (or above) + - Callers MUST handle `false` from `returns (bool success)`. Callers MUST NOT assume that `false` is never returned! + + +#### name + +Returns the name of the token - e.g. `"MyToken"`. + +OPTIONAL - This method can be used to improve usability, +but interfaces and other contracts MUST NOT expect these values to be present. + + +``` js +function name() public view returns (string) +``` + + +#### symbol + +Returns the symbol of the token. E.g. "HIX". + +OPTIONAL - This method can be used to improve usability, +but interfaces and other contracts MUST NOT expect these values to be present. + +``` js +function symbol() public view returns (string) +``` + + + +#### decimals + +Returns the number of decimals the token uses - e.g. `8`, means to divide the token amount by `100000000` to get its user representation. + +OPTIONAL - This method can be used to improve usability, +but interfaces and other contracts MUST NOT expect these values to be present. + +``` js +function decimals() public view returns (uint8) +``` + + +#### totalSupply + +Returns the total token supply. + +``` js +function totalSupply() public view returns (uint256) +``` + + + +#### balanceOf + +Returns the account balance of another account with address `_owner`. + +``` js +function balanceOf(address _owner) public view returns (uint256 balance) +``` + + + +#### transfer + +Transfers `_value` amount of tokens to address `_to`, and MUST fire the `Transfer` event. +The function SHOULD `throw` if the message caller's account balance does not have enough tokens to spend. + +*Note* Transfers of 0 values MUST be treated as normal transfers and fire the `Transfer` event. + +``` js +function transfer(address _to, uint256 _value) public returns (bool success) +``` + + + +#### transferFrom + +Transfers `_value` amount of tokens from address `_from` to address `_to`, and MUST fire the `Transfer` event. + +The `transferFrom` method is used for a withdraw workflow, allowing contracts to transfer tokens on your behalf. +This can be used for example to allow a contract to transfer tokens on your behalf and/or to charge fees in sub-currencies. +The function SHOULD `throw` unless the `_from` account has deliberately authorized the sender of the message via some mechanism. + +*Note* Transfers of 0 values MUST be treated as normal transfers and fire the `Transfer` event. + +``` js +function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) +``` + + + +#### approve + +Allows `_spender` to withdraw from your account multiple times, up to the `_value` amount. If this function is called again it overwrites the current allowance with `_value`. + +**NOTE**: To prevent attack vectors like the one [described here](https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM/) and discussed [here](https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729), +clients SHOULD make sure to create user interfaces in such a way that they set the allowance first to `0` before setting it to another value for the same spender. +THOUGH The contract itself shouldn't enforce it, to allow backwards compatibility with contracts deployed before + +``` js +function approve(address _spender, uint256 _value) public returns (bool success) +``` + + +#### allowance + +Returns the amount which `_spender` is still allowed to withdraw from `_owner`. + +``` js +function allowance(address _owner, address _spender) public view returns (uint256 remaining) +``` + + + +### Events + + +#### Transfer + +MUST trigger when tokens are transferred, including zero value transfers. + +A token contract which creates new tokens SHOULD trigger a Transfer event with the `_from` address set to `0x0` when tokens are created. + +``` js +event Transfer(address indexed _from, address indexed _to, uint256 _value) +``` + + + +#### Approval + +MUST trigger on any successful call to `approve(address _spender, uint256 _value)`. + +``` js +event Approval(address indexed _owner, address indexed _spender, uint256 _value) +``` + + + +## Implementation + +There are already plenty of ERC20-compliant tokens deployed on the Ethereum network. +Different implementations have been written by various teams that have different trade-offs: from gas saving to improved security. + +#### Example implementations are available at +- [OpenZeppelin implementation](https://github.com/OpenZeppelin/openzeppelin-solidity/blob/9b3710465583284b8c4c5d2245749246bb2e0094/contracts/token/ERC20/ERC20.sol) +- [ConsenSys implementation](https://github.com/ConsenSys/Tokens/blob/fdf687c69d998266a95f15216b1955a4965a0a6d/contracts/eip20/EIP20.sol) + + +## History + +Historical links related to this standard: + +- Original proposal from Vitalik Buterin: https://github.com/ethereum/wiki/wiki/Standardized_Contract_APIs/499c882f3ec123537fc2fccd57eaa29e6032fe4a +- Reddit discussion: https://www.reddit.com/r/ethereum/comments/3n8fkn/lets_talk_about_the_coin_standard/ +- Original Issue #20: https://github.com/ethereum/EIPs/issues/20 + + + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2003.md b/EIPS/eip-2003.md new file mode 100644 index 0000000..c96aac0 --- /dev/null +++ b/EIPS/eip-2003.md @@ -0,0 +1,135 @@ +--- +eip: 2003 +title: EVMC modules for implementations of precompiled contracts +author: Paweł Bylica (@chfast), Alex Beregszaszi (@axic) +discussions-to: https://github.com/ethereum/evmc/issues/259 +status: Stagnant +type: Standards Track +category: Interface +created: 2019-05-09 +requires: 1352 +--- + +## Abstract + +[EVMC] specifies a generic API for Ethereum execution engines. +This EIP specifies a way of providing implementations of Ethereum precompiled contracts +using the [EVMC VM API]. + + +## Specification + +For the complete [EVMC] specification visit the [EVMC documentation] first. +This EIP is based on and is compatible with EVMC ABI version 6. + +The EVMC module with implementations of precompiled contracts SHOULD: + +1. Advertise the [`EVMC_CAPABILITY_PRECOMPILES`] capability + in the [`get_capabilities()`] method. + +2. Implement the [`execute()`] method in the following way: + + 1. Validate the incoming execution request requirements: + + 1. The message kind ([`evmc_message::kind`]) is a call ([`EVMC_CALL`]). + + 2. The call destination address ([`evmc_message::destination`]) + is within the range of precompiled contracts defined by [EIP-1352]. + + 3. There is no code provided (the `code` argument is `NULL` and `code_size` argument is `0`). + + If the requirements are not fulfilled, abort execution with the [`EVMC_REJECTED`] status code. + + 2. Check if the call destination address ([`evmc_message::destination`]) + targets existing precompiled contract. + Consider the EVM revision ([`evmc_revision`]) requested by + the `rev` parameter of [`execute()`]. + + If yes, execute as follows: + + 1. Inspect the input data ([`evmc_message::input_data`], [`evmc_message::input_size`]) + and calculate the _gas cost_ of the execution. + + 2. Compute the amount of _gas left_ after execution by + subtracting the _gas cost_ from the call gas limit ([`evmc_message::gas`]). + + 3. If _gas left_ is negative, + abort execution with the [`EVMC_OUT_OF_GAS`] status code. + + 4. Otherwise, + execute the code of the precompiled contract, + return the [`EVMC_SUCCESS`] status code, the output and _gas left_ + ([`evmc_result::output_data`], [`evmc_result::output_size`], [`evmc_result::gas_left`]). + + 3. Otherwise, emulate execution of empty code by returning + the [`EVMC_SUCCESS`] status code + and _gas left_ equal the call gas limit ([`evmc_message::gas`]). + +Precompiled contract implementations are allowed to return two more EVMC error codes: +- [`EVMC_FAILURE`] if the failure was caused due to something other than out of gas (e.g. input validation error) +- [`EVMC_REVERT`] if the precompile doesn't want to forfeit all supplied gas (as of May 2019 no such precompile exists) + +The Client is not required to provide the Host interface ([`evmc_context`] argument of [`execute()`] is set to NULL). +Therefore, the precompiled contracts implementation MUST NOT access the `evmc_context`. + + +## Rationale + +It is very unlikely that any precompile will need to access or modify a contract state. +Not requiring the Client to implement the EVMC Host interface removes the big portion of work +needed for full EVMC integration. + + +## Test Cases + +EVMC provides the [evmc-vmtester] tool for checking compatibility with the EVMC specification. + + +## Implementations + +- [Example of Precompiles VM implementation][example_precompiles_vm.cpp] +- [ewasm precompiles] +- Aleth code for precompiles +- Parity code for precompiles +- [EIP-1962 implemented as an EVMC precompile module](https://github.com/axic/eip1962-evmc) + + +## References + +- [EVMC – Ethereum Client-VM Connector API][EVMC] +- [EVMC documentation] +- [EVMC VM Implementation Guide][EVMC VM API] +- [EIP 1352: Specify restricted address range for precompiles/system contracts][EIP-1352] + + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). + + +[EIP-1352]: ./eip-1352.md +[EVMC]: https://github.com/ethereum/evmc +[EVMC documentation]: https://ethereum.github.io/evmc/ +[EVMC VM API]: https://ethereum.github.io/evmc/vmguide.html +[evmc-vmtester]: https://ethereum.github.io/evmc/vmtester.html +[example_precompiles_vm.cpp]: https://github.com/ethereum/evmc/blob/master/examples/example_precompiles_vm/example_precompiles_vm.cpp +[ewasm precompiles]: https://github.com/ewasm/ewasm-precompiles + +[`EVMC_CALL`]: https://ethereum.github.io/evmc/group__EVMC.html#ggab2fa68a92a6828064a61e46060abc634abcf3ae29d9a88ff70b98374fc665694a +[`EVMC_CAPABILITY_PRECOMPILES`]: https://ethereum.github.io/evmc/group__EVMC.html#gga44f9ecb88cf6422a0072936494fd6ac7a43ea2aa7b099a2d67bc53c118ff3683d +[`EVMC_FAILURE`]: https://ethereum.github.io/evmc/group__EVMC.html#gga4c0be97f333c050ff45321fcaa34d920aed5b2a4afa5a47af732569445920a4a9 +[`EVMC_OUT_OF_GAS`]: https://ethereum.github.io/evmc/group__EVMC.html#gga4c0be97f333c050ff45321fcaa34d920abfc47f75656c996c0b29c0553c00fc18 +[`EVMC_REJECTED`]: https://ethereum.github.io/evmc/group__EVMC.html#gga4c0be97f333c050ff45321fcaa34d920a2f3e0d8777f8d974ead27ae2a6eb2005 +[`EVMC_REVERT`]: https://ethereum.github.io/evmc/group__EVMC.html#gga4c0be97f333c050ff45321fcaa34d920aed708e84d49cc1270e54ec20b0ca0a05 +[`EVMC_SUCCESS`]: https://ethereum.github.io/evmc/group__EVMC.html#gga4c0be97f333c050ff45321fcaa34d920a4bc3069fec2bab2a55355a72b7db68b7 +[`execute()`]: https://ethereum.github.io/evmc/structevmc__instance.html#a0823ebff21f9b0395b157e8c6b14a207 +[`get_capabilities()`]: https://ethereum.github.io/evmc/structevmc__instance.html#ae63b9ca898aa41cbd1e2fe86ca8f4e1c +[`evmc_message::destination`]: https://ethereum.github.io/evmc/structevmc__message.html#a88ecfaa03a85a31c6da36fa043b98cea +[`evmc_message::input_data`]: https://ethereum.github.io/evmc/structevmc__message.html#a1adee3454b105eb29cd659ee0cf65c77 +[`evmc_message::input_size`]: https://ethereum.github.io/evmc/structevmc__message.html#a2cf1deebd0dbbb20f25ecdfa299f4b5d +[`evmc_message::gas`]: https://ethereum.github.io/evmc/structevmc__message.html#ae8deff46588584fa27890e74c82db5e7 +[`evmc_message::kind`]: https://ethereum.github.io/evmc/structevmc__message.html#a691cb93e81d6dfd4fd7e2fa3d06a6bfa +[`evmc_result::gas_left`]: https://ethereum.github.io/evmc/structevmc__result.html#af8478c93dbcc3cb2876037c5a5afd4c0 +[`evmc_result::output_data`]: https://ethereum.github.io/evmc/structevmc__result.html#a61978e85f9d795a7b9695b9cbf1748d6 +[`evmc_result::output_size`]: https://ethereum.github.io/evmc/structevmc__result.html#a93bb7419aff492cdef754421c6d74e26 +[`evmc_revision`]: https://ethereum.github.io/evmc/group__EVMC.html#gae5759b1590071966ccf6a505b52a0ef7 diff --git a/EIPS/eip-2009.md b/EIPS/eip-2009.md new file mode 100644 index 0000000..c628e6b --- /dev/null +++ b/EIPS/eip-2009.md @@ -0,0 +1,300 @@ +--- +eip: 2009 +title: Compliance Service +author: Daniel Lehrner +discussions-to: https://github.com/ethereum/EIPs/issues/2022 +status: Stagnant +type: Standards Track +category: ERC +created: 2019-05-09 +requires: 1066 +--- + +## Simple Summary + +This EIP proposes a service for decentralized compliance checks for regulated tokens. + +## Actors + +#### Operator +An account which has been approved by a token to update the tokens accumulated. + +#### Token +An account, normally a smart contract, which uses the `Compliance Service` to check if the an action can be executed or not. + +#### Token holder +An account which is in possession of tokens and on for which the checks are made. + +## Abstract + +A regulated token needs to comply with several legal requirements, especially [KYC][KYC-Wikipedia] and [AML][AML-Wikipedia]. If the necessary checks have to be made off-chain the token transfer becomes centralized. Further the transfer in this case takes longer to complete as it can not be done in one transaction, but requires a second confirmation step. The goal of this proposal is to make this second step unnecessary by providing a service for compliance checks. + +## Motivation + +Currently there is no proposal on how to accomplish decentralized compliance checks. [ERC-1462][ERC-1462] proposes a basic set of functions to check if `transfer`, `mint` and `burn` are allowed for a user, but not how those checks should be implemented. This EIP proposes a way to implement them fully on-chain while being generic enough to leave the actual implementation of the checks up to the implementers, as these may vary a lot between different tokens. + +The proposed `Compliance Service` supports more than one token. Therefore it could be used by law-makers to maintain the compliance rules of regulated tokens in one smart contract. This smart contract could be used by all of the tokens that fall under this jurisdiction and ensure compliance with the current laws. + +By having a standard for compliance checks third-party developers can use them to verify if token movements for a specific account are allowed and act accordingly. + +## Specification + +```solidity +interface CompliantService { + function checkTransferAllowed(bytes32 tokenId, address from, address to, uint256 value) external view returns (byte); + function checkTransferFromAllowed(bytes32 tokenId, address sender, address from, address to, uint256 value) external view returns (byte); + function checkMintAllowed(bytes32 tokenId, address to, uint256 value) external view returns (byte); + function checkBurnAllowed(bytes32 tokenId, address from, uint256 value) external view returns (byte); + + function updateTransferAccumulated(bytes32 tokenId, address from, address to, uint256 value) external; + function updateMintAccumulated(bytes32 tokenId, address to, uint256 value) external; + function updateBurnAccumulated(bytes32 tokenId, address from, uint256 value) external; + + function addToken(bytes32 tokenId, address token) external; + function replaceToken(bytes32 tokenId, address token) external; + function removeToken(bytes32 tokenId) external; + function isToken(address token) external view returns (bool); + function getTokenId(address token) external view returns (bytes32); + + function authorizeAccumulatedOperator(address operator) external returns (bool); + function revokeAccumulatedOperator(address operator) external returns (bool); + function isAccumulatedOperatorFor(address operator, bytes32 tokenId) external view returns (bool); + + event TokenAdded(bytes32 indexed tokenId, address indexed token); + event TokenReplaced(bytes32 indexed tokenId, address indexed previousAddress, address indexed newAddress); + event TokenRemoved(bytes32 indexed tokenId); + event AuthorizedAccumulatedOperator(address indexed operator, bytes32 indexed tokenId); + event RevokedAccumulatedOperator(address indexed operator, bytes32 indexed tokenId); +} +``` + +### Mandatory checks + +The checks must be verified in their corresponding actions. The action must only be successful if the check return an `Allowed` status code. In any other case the functions must revert. + +### Status codes + +If an action is allowed `0x11` (Allowed) or an issuer-specific code with equivalent but more precise meaning must be returned. If the action is not allowed the status must be `0x10` (Disallowed) or an issuer-specific code with equivalent but more precise meaning. + +### Functions + +#### checkTransferAllowed + +Checks if the `transfer` function is allowed to be executed with the given parameters. + +| Parameter | Description | +| ---------|-------------| +| tokenId | The unique ID which identifies a token | +| from | The address of the payer, from whom the tokens are to be taken if executed | +| to | The address of the payee, to whom the tokens are to be transferred if executed | +| value | The amount to be transferred | + +#### checkTransferFromAllowed + +Checks if the `transferFrom` function is allowed to be executed with the given parameters. + +| Parameter | Description | +| ---------|-------------| +| tokenId | The unique ID which identifies a token | +| sender | The address of the sender, who initiated the transaction | +| from | The address of the payer, from whom the tokens are to be taken if executed | +| to | The address of the payee, to whom the tokens are to be transferred if executed | +| value | The amount to be transferred | + +#### checkMintAllowed + +Checks if the `mint` function is allowed to be executed with the given parameters. + +| Parameter | Description | +| ---------|-------------| +| tokenId | The unique ID which identifies a token | +| to | The address of the payee, to whom the tokens are to be given if executed | +| value | The amount to be minted | + +#### checkBurnAllowed + +Checks if the `burn` function is allowed to be executed with the given parameters. + +| Parameter | Description | +| ---------|-------------| +| tokenId | The unique ID which identifies a token | +| from | The address of the payer, from whom the tokens are to be taken if executed | +| value | The amount to be burned | + +#### updateTransferAccumulated + +Must be called in the same transaction as `transfer` or `transferFrom`. It must revert if the update violates any of the compliance rules. It is up to the implementer which specific logic is executed in the function. + +| Parameter | Description | +| ---------|-------------| +| tokenId | The unique ID which identifies a token | +| from | The address of the payer, from whom the tokens are to be taken if executed | +| to | The address of the payee, to whom the tokens are to be transferred if executed | +| value | The amount to be transferred | + +#### updateMintAccumulated + +Must be called in the same transaction as `mint`. It must revert if the update violates any of the compliance rules. It is up to the implementer which specific logic is executed in the function. + +| Parameter | Description | +| ---------|-------------| +| tokenId | The unique ID which identifies a token | +| to | The address of the payee, to whom the tokens are to be given if executed | +| value | The amount to be minted | + +#### updateBurnAccumulated + +Must be called in the same transaction as `burn`. It must revert if the update violates any of the compliance rules. It is up to the implementer which specific logic is executed in the function. + +| Parameter | Description | +| ---------|-------------| +| tokenId | The unique ID which identifies a token | +| from | The address of the payer, from whom the tokens are to be taken if executed | +| value | The amount to be minted | + +#### addToken + +Adds a token to the service, which allows the token to call the functions to update the accumulated. If an existing token id is used the function must revert. It is up to the implementer if adding a token should be restricted or not. + +| Parameter | Description | +| ---------|-------------| +| tokenId | The unique ID which identifies a token | +| token | The address from which the update functions will be called | + +#### replaceToken + +Replaces the address of a added token with another one. It is up to the implementer if replacing a token should be restricted or not, but a token should be able to replace its own address. + +| Parameter | Description | +| ---------|-------------| +| tokenId | The unique ID which identifies a token | +| token | The address from which the update functions will be called | + +#### removeToken + +Removes a token from the service, which disallows the token to call the functions to update the accumulated. It is up to the implementer if removing a token should be restricted or not. + +| Parameter | Description | +| ---------|-------------| +| tokenId | The unique ID which identifies a token | + +#### isToken + +Returns `true` if the address has been added to the service, `false` if not. + +| Parameter | Description | +| ---------|-------------| +| token | The address which should be checked | + +#### getTokenId + +Returns the token id of a token. If the token has not been added to the service, '0' must be returned. + +| Parameter | Description | +| ---------|-------------| +| token | The address which token id should be returned | + +#### authorizeAccumulatedOperator + +Approves an operator to update accumulated on behalf of the token id of msg.sender. + +| Parameter | Description | +| ---------|-------------| +| operator | The address to be approved as operator of accumulated updates | + +#### revokeAccumulatedOperator + +Revokes the approval to update accumulated on behalf the token id the token id ofof msg.sender. + +| Parameter | Description | +| ---------|-------------| +| operator | The address to be revoked as operator of accumulated updates | + +#### isAccumulatedOperatorFor + +Retrieves if an operator is approved to create holds on behalf of `tokenId`. + +| Parameter | Description | +| ---------|-------------| +| operator | The address which is operator of updating the accumulated | +| tokenId | The unique ID which identifies a token | + +### Events + +#### TokenAdded + +Must be emitted after a token has been added. + +| Parameter | Description | +| ---------|-------------| +| tokenId | The unique ID which identifies a token | +| token | The address from which the update functions will be called | + +#### TokenReplaced + +Must be emitted after the address of a token has been replaced. + +| Parameter | Description | +| ---------|-------------| +| tokenId | The unique ID which identifies a token | +| previousAddress | The previous address which was used before | +| newAddress | The address which will be used from now on | + +#### TokenRemoved + +Must be emitted after the a token has been removed. + +| Parameter | Description | +| ---------|-------------| +| tokenId | The unique ID which identifies a token | + +#### AuthorizedAccumulatedOperator + +Emitted when an operator has been approved to update the accumulated on behalf of a token. + +| Parameter | Description | +| ---------|-------------| +| operator | The address which is operator of updating the accumulated | +| tokenId | Token id on which behalf updates of the accumulated will potentially be made | + +#### RevokedHoldOperator + +Emitted when an operator has been revoked from updating the accumulated on behalf of a token. + +| Parameter | Description | +| ---------|-------------| +| operator | The address which was operator of updating the accumulated | +| tokenId | Token id on which behalf updates of the accumulated could be made | + +## Rationale + +The usage of a token id instead of the address has been chosen to give tokens the possibility to update their smart contracts and keeping all their associated accumulated. If the address would be used, a migration process would needed to be done after a smart contract update. + +No event is emitted after updating the accumulated as those are always associated with a `transfer`, `mint` or `burn` of a token which already emits an event of itself. + +While not requiring it, the naming of the functions `checkTransferAllowed`, `checkTransferFromAllowed`, `checkMintAllowed` and `checkBurnAllowed` was adopted from [ERC-1462][ERC-1462]. + +While not requiring it, the naming of the functions `authorizeAccumulatedOperator`, `revokeAccumulatedOperator` and `isAccumulatedOperatorFor` follows the naming convention of [ERC-777][ERC-777]. + +Localization is not part of this EIP, but [ERC-1066][ERC-1066] and [ERC-1444][ERC-1444] can be used together to achieve it. + +## Backwards Compatibility + +As the EIP is not using any existing EIP there are no backwards compatibilities to take into consideration. + +## Implementation + +The GitHub repository [IoBuilders/compliance-service](https://github.com/IoBuilders/compliance-service) contains the work in progress implementation. + +## Contributors +This proposal has been collaboratively implemented by [adhara.io](https://adhara.io/) and [io.builders](https://io.builders/). + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). + +[KYC-Wikipedia]: https://en.wikipedia.org/wiki/Know_your_customer +[AML-Wikipedia]: https://en.wikipedia.org/wiki/Money_laundering#Anti-money_laundering +[ERC-777]: ./eip-777.md +[ERC-1066]: ./eip-1066.md +[ERC-1444]: ./eip-1444.md +[ERC-1462]: ./eip-1462.md diff --git a/EIPS/eip-2014.md b/EIPS/eip-2014.md new file mode 100644 index 0000000..3eacb6b --- /dev/null +++ b/EIPS/eip-2014.md @@ -0,0 +1,97 @@ +--- +eip: 2014 +title: Extended State Oracle +author: Alex Beregszaszi (@axic) +discussions-to: https://ethereum-magicians.org/t/eip-2014-extended-state-oracle/3301 +status: Stagnant +type: Standards Track +category: Core +created: 2019-05-10 +requires: 140 +--- + +## Simple Summary + +## Abstract + +Introduce a new system contract with an extensible interface following the [Contract ABI Encoding] to access extended data sets, such as chain identifiers, block hashes, etc. + +This allows Ethereum contract languages to interact with this contract as if it were a regular contract and not needing any language support. + +## Motivation + +Over the past couple of years several proposals were made to extend the EVM with more data. Some examples include extended access to block hashes ([EIP-210]) and chain identifiers ([EIP-1344]). + +Adding them as EVM opcodes seems to be using the scarce opcode space for relatively less frequently used features, while adding them as precompiles is perceived as more complicated due to an interface +needs to be defined and agreed on for every case. + +This proposal tries to solve both issues with defining an extensible standard interface. + +## Specification + +A new system contract ("precompile") is introduced at address `0x0000000000000000000000000000000000000009` called ESO (Extended State Oracle). + +It can be queried using `CALL` or `STATICCALL` and follows the [Contract ABI Encoding] for the inputs and outputs. Using elementary types in the ABI encoding is encouraged to keep complexity low. + +In the future it could be possible to extend ESO to have a state and accept transactions from a system address to store the passed data -- similarly to what [EIP-210] proposed. + +Proposals wanting to introduce more data to the state, which is not part of blocks or transactions, should aim to extend the ESO. + +At this time it is not proposed to make the ESO into a contract existing in the state, but to include it as a precompile and leave the implementation details to the client. +In the future if it is sufficiently extended and a need arises to have a state, it would make sense to move it from being a precompile and have actual code. + +### Chain identifier + +Initially, a feature to read the current chain identifier is introduced: `getCurrentChainId()` returns the current chain identifier as a `uint64` encoded value. +It should be a non-payable function, which means sending any value would revert the transaction as described in [EIP-140]. +This has been proposed as [EIP-1344]. + +The contract ABI JSON is the following: +```json +[ + { + "constant": true, + "inputs": [], + "name": "getCurrentChainId", + "outputs": [ + { + "name": "", + "type": "uint64" + } + ], + "payable": false, + "stateMutability": "pure", + "type": "function" + } +] +``` + +This will be translated into sending the bytes `5cf0e8a4` to the ESO and returning the bytes `0000000000000000000000000000000000000000000000000000000000000001` for Ethereum mainnet. + +**Note:** It should be possible to introduce another interface checking the validity of a chain identifier in the chain history or for a given block (see [EIP-1959] and [EIP-1965]). + +## Rationale + +TBA + +## Backwards Compatibility + +TBA + +## Test Cases + +TBA + +## Implementation + +TBA + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). + +[Contract ABI Encoding]: https://solidity.readthedocs.io/en/latest/abi-spec.html +[EIP-140]: ./eip-140.md +[EIP-210]: ./eip-210.md +[EIP-1344]: ./eip-1344.md +[EIP-1959]: https://github.com/ethereum/EIPs/pull/1959 +[EIP-1965]: https://github.com/ethereum/EIPs/pull/1965 diff --git a/EIPS/eip-2015.md b/EIPS/eip-2015.md new file mode 100644 index 0000000..7bb6248 --- /dev/null +++ b/EIPS/eip-2015.md @@ -0,0 +1,108 @@ +--- +eip: 2015 +title: Wallet Update Ethereum Chain RPC Method (`wallet_updateEthereumChain`) +author: Pedro Gomes (@pedrouid), Erik Marks (@rekmarks) +discussions-to: https://ethereum-magicians.org/t/eip-2015-wallet-update-chain-json-rpc-method-wallet-updatechain/3274 +status: Stagnant +type: Standards Track +category: Interface +created: 2019-05-12 +requires: 155, 1474 +--- + +## Simple Summary +Wallets can update the active chain when connected to a Dapp but not vice-versa, with `wallet_updateEthereumChain` the Dapp will be able to request this change from the Wallet. + +## Abstract +Dapp can request the Wallet to switch chains by providing the minimal parameters of `chainId`, `chainName`, `rpcUrl`, `nativeCurrency` and `blockExplorerUrl`. The Wallet will display a UI element to inform the user of this change. + +## Motivation +Wallet and Dapp communication rely on the present provider that acts as middleware between the two. Using JSON-RPC methods, the Dapp is able to access not only the active accounts but also the active chain. With [EIP-1102](./eip-1102.md) we introduced the ability for Dapps to request access to the active accounts and the Wallet is able to provide a simple UI to inform the user of this action however the same is not currently possible for switching chains. The current pattern is to display some UI to request the user to switch chains within the Dapp, however this could be easily improved by triggering a UI from the Wallet side that can be approved or rejected by the user instead. + +## Specification +The JSON RPC method will be part of `wallet_` namespaced methods which aim to improve the UX and interoperability between Dapps and Wallets. + +### Required Parameters +- chainId (string): the id of the chain compliant with EIP-155 +- chainName (string): the name of the chain to update +- rpcUrl (string): the url endpoint for RPC requests for this chain +- nativeCurrency (Object): includes three fields for `name` (string), `symbol` (string) and `decimals` (number) +- blockExplorerUrl (string): the url endpoint for a block explorer web site for the chain. + +### Best Practices +- The Wallet should display a UI view similar to a [EIP-1102](./eip-1102.md) informing the user that the currently connected Dapp wants to switch to the specified chain. +- the Wallet should default the rpcUrl to any existing endpoints matching a chainId known previously to the wallet, otherwise it will use the provided rpcUrl as a fallback. +- the Wallet should call the rpcUrl with `net_version` and `eth_chainId` to verify the provided chainId and networkId match the responses from the rpcUrl +- the Wallet should change all nativeCurrency symbols to the provided parameter + +### Example 1 +A JSON-RPC request from a Dapp to switch the Ethereum Goerli chain would be as follows: +```json +{ + "id":1, + "jsonrpc": "2.0", + "method": "wallet_updateChain", + "params": [ + { + "chainId": 0x5, + "chainName": "Goerli", + "rpcUrl": "https://goerli.infura.io/v3/406405f9c65348f99d0d5c27104b2213", + "nativeCurrency": { + "name": "Goerli ETH", + "symbol": "gorETH" + }, + "blockExplorerUrl": "https://goerli.etherscan.io" + } + ] +} +``` + +### Example 2 +A JSON-RPC request from a Dapp to switch the POA Network's xDAI chain would be as follows: +```json +{ + "id":1, + "jsonrpc": "2.0", + "method": "wallet_updateChain", + "params": [ + { + "chainId": "0x5", + "chainName": "Goerli", + "rpcUrl": "https://goerli.infura.io/v3/406405f9c65348f99d0d5c27104b2213", + "nativeCurrency": { + "name": "Goerli ETH", + "symbol": "gorETH" + }, + "blockExplorerUrl": "https://goerli.etherscan.io" + } + ] +} +``` + +### Responses + +A success response: + +```json +{ + "id": 1, + "jsonrpc": "2.0", + "result": true +} +``` + +A failure response: + +```json +{ + "id": 1, + "jsonrpc": "2.0", + "error": { + "code": 4001, + "message": "The user rejected the request." + } +} +``` + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2018.md b/EIPS/eip-2018.md new file mode 100644 index 0000000..9959a34 --- /dev/null +++ b/EIPS/eip-2018.md @@ -0,0 +1,261 @@ +--- +eip: 2018 +title: Clearable Token +author: Julio Faura , Fernando Paris , Daniel Lehrner +discussions-to: https://github.com/ethereum/EIPs/issues/2104 +status: Stagnant +type: Standards Track +category: ERC +created: 2019-04-30 +requires: 1996 +--- + +## Simple Summary + +> "In banking and finance, clearing denotes all activities from the time a commitment is made for a transaction until it is settled." [[1]][Clearing-Wikipedia] + +## Actors + +#### Clearing Agent + +An account which processes, executes or rejects a clearable transfer. + +#### Operator +An account which has been approved by an account to order clearable transfers on its behalf. + +#### Orderer +The account which orders a clearable transfer. This can be the account owner itself, or any account, which has been approved as an operator for the account. + +## Abstract + +The clearing process turns the promise of a transfer into the actual movement of money from one account to another. A clearing agent decides if the transfer can be executed or not. The amount which should be transferred is not deducted from the balance of the payer, but neither is it available for another transfer and therefore ensures, that the execution of the transfer will be successful when it is executed. + +## Motivation + +A regulated token needs to comply with all the legal requirements, especially [KYC][KYC-Wikipedia] and [AML][AML-Wikipedia]. Some of these checks may not be able to be done on-chain and therefore a transfer may not be completed in one step. Currently there is no EIP to make such off-chain checks possible. This proposal allows a user to order a transfer, which can be checked by a clearing agent off-chain. Depending on the result of it, the clearing agent will either execute or cancel the transfer. To provide more information why a transfer is cancelled, the clearing agent can add a reason why it is not executed. + +## Specification + +```solidity +interface ClearableToken /* is ERC-1996 */ { + enum ClearableTransferStatusCode { Nonexistent, Ordered, InProcess, Executed, Rejected, Cancelled } + + function orderTransfer(string calldata operationId, address to, uint256 value) external returns (bool); + function orderTransferFrom(string calldata operationId, address from, address to, uint256 value) external returns (bool); + function cancelTransfer(string calldata operationId) external returns (bool); + function processClearableTransfer(string calldata operationId) external returns (bool); + function executeClearableTransfer(string calldata operationId) external returns (bool); + function rejectClearableTransfer(string calldata operationId, string calldata reason) external returns (bool); + function retrieveClearableTransferData(string calldata operationId) external view returns (address from, address to, uint256 value, ClearableTransferStatusCode status); + + function authorizeClearableTransferOperator(address operator) external returns (bool); + function revokeClearableTransferOperator(address operator) external returns (bool); + function isClearableTransferOperatorFor(address operator, address from) external view returns (bool); + + event ClearableTransferOrdered(address indexed orderer, string operationId, address indexed from, address indexed to, uint256 value); + event ClearableTransferInProcess(address indexed orderer, string operationId); + event ClearableTransferExecuted(address indexed orderer, string operationId); + event ClearableTransferRejected(address indexed orderer, string operationId, string reason); + event ClearableTransferCancelled(address indexed orderer, string operationId); + event AuthorizedClearableTransferOperator(address indexed operator, address indexed account); + event RevokedClearableTransferOperator(address indexed operator, address indexed account); +} +``` + +### Functions + +#### orderTransfer + +Orders a clearable transfer on behalf of the msg.sender in favor of `to`. A clearing agent is responsible to either execute or reject the transfer. The function must revert if the operation ID has been used before. + +| Parameter | Description | +| ---------|-------------| +| operationId | The unique ID to identify the clearable transfer | +| to | The address of the payee, to whom the tokens are to be paid if executed | +| value | The amount to be transferred. Must be less or equal than the balance of the payer. | + +#### orderTransferFrom + +Orders a clearable transfer on behalf of the payer in favor of the `to`. A clearing agent is responsible to either execute or reject the transfer. The function must revert if the operation ID has been used before. + +| Parameter | Description | +| ---------|-------------| +| operationId | The unique ID to identify the clearable transfer | +| from | The address of the payer, from whom the tokens are to be taken if executed | +| to | The address of the payee, to whom the tokens are to be paid if executed | +| value | The amount to be transferred. Must be less or equal than the balance of the payer. | + +#### cancelTransfer + +Cancels the order of a clearable transfer. Only the orderer can cancel their own orders. It must not be successful as soon as the transfer is in status `InProcess`. + +| Parameter | Description | +| ---------|-------------| +| operationId | The unique ID to identify the clearable transfer | + +#### processClearableTransfer + +Sets a clearable transfer to status `InProcess`. Only a clearing agent can successfully execute this action. This status is optional, but without it the orderer can cancel the transfer at any time. + +| Parameter | Description | +| ---------|-------------| +| operationId | The unique ID to identify the clearable transfer | + +#### executeClearableTransfer + +Executes a clearable transfer, which means that the tokens are transferred from the payer to the payee. Only a clearing agent can successfully execute this action. + +| Parameter | Description | +| ---------|-------------| +| operationId | The unique ID to identify the clearable transfer | + +#### rejectClearableTransfer + +Rejects a clearable transfer, which means that the amount that is held is available again to the payer and no transfer is done. Only a clearing agent can successfully execute this action. + +| Parameter | Description | +| ---------|-------------| +| operationId | The unique ID to identify the clearable transfer | +| reason | A reason given by the clearing agent why the transfer has been rejected | + +#### retrieveClearableTransferData + +Retrieves all the information available for a particular clearable transfer. + +| Parameter | Description | +| ---------|-------------| +| operationId | The unique ID to identify the clearable transfer | + +#### authorizeClearableTransferOperator + +Approves an operator to order transfers on behalf of msg.sender. + +| Parameter | Description | +| ---------|-------------| +| operator | The address to be approved as operator of clearable transfers | + +#### revokeClearableTransferOperator + +Revokes the approval to order transfers on behalf of msg.sender. + +| Parameter | Description | +| ---------|-------------| +| operator | The address to be revoked as operator of clearable transfers | + +#### isClearableTransferOperatorFor + +Returns if an operator is approved to order transfers on behalf of `from`. + +| Parameter | Description | +| ---------|-------------| +| operator | The address to be an operator of clearable transfers | +| from | The address on which the holds would be created | + +#### transfer + +It is up to the implementer of the EIP if the `transfer` function of ERC-20 should always revert or is allowed under certain circumstances. + +#### transferFrom + +It is up to the implementer of the EIP if the `transferFrom` function of ERC-20 should always revert or is allowed under certain circumstances. + + +### Events + +#### ClearableTransferOrdered + +Must be emitted when a clearable transfer is ordered. + +| Parameter | Description | +| ---------|-------------| +| orderer | The address of the orderer of the transfer | +| operationId | The unique ID to identify the clearable transfer | +| from | The address of the payer, from whom the tokens are to be taken if executed | +| to | The address of the payee, to whom the tokens are to be paid if executed | +| value | The amount to be transferred if executed | + +#### ClearableTransferInProcess + +Must be emitted when a clearable transfer is put in status `ÌnProcess`. + +| Parameter | Description | +| ---------|-------------| +| orderer | The address of the orderer of the transfer | +| operationId | The unique ID to identify the clearable transfer | + +#### ClearableTransferExecuted + +Must be emitted when a clearable transfer is executed. + +| Parameter | Description | +| ---------|-------------| +| orderer | The address of the orderer of the transfer | +| operationId | The unique ID to identify the clearable transfer | + +#### ClearableTransferRejected + +Must be emitted when a clearable transfer is rejected. + +| Parameter | Description | +| ---------|-------------| +| orderer | The address of the orderer of the transfer | +| operationId | The unique ID to identify the clearable transfer | +| reason | A reason given by the clearing agent why the transfer has been rejected | + +#### ClearableTransferCancelled + +Must be emitted when a clearable transfer is cancelled by its orderer. + +| Parameter | Description | +| ---------|-------------| +| orderer | The address of the orderer of the transfer | +| operationId | The unique ID to identify the clearable transfer | + +#### AuthorizedClearableTransferOperator + +Emitted when an operator has been approved to order transfers on behalf of another account. + +| Parameter | Description | +| ---------|-------------| +| operator | The address which has been approved as operator of clearable transfers | +| account | Address on which behalf transfers will potentially be ordered | + +#### RevokedClearableTransferOperator + +Emitted when an operator has been revoked from ordering transfers on behalf of another account. + +| Parameter | Description | +| ---------|-------------| +| operator | The address which has been revoked as operator of clearable transfers | +| account | Address on which behalf transfers could potentially be ordered | + +## Rationale + +This EIP uses [EIP-1996][EIP-1996] to hold the money after a transfer is ordered. A clearing agent, whose implementation is not part of this proposal, acts as a predefined notary to decide if the transfer complies with the rules of the token or not. + +The `operationId` is a string and not something more gas efficient to allow easy traceability of the hold and allow human readable ids. It is up to the implementer if the string should be stored on-chain or only its hash, as it is enough to identify a hold. + +The `operationId` is a competitive resource. It is recommended, but not required, that the hold issuers used a unique prefix to avoid collisions. + +While not requiring it, the naming of the functions `authorizeClearableTransferOperator`, `revokeClearableTransferOperator` and `isClearableTransferOperatorFor` follows the naming convention of [ERC-777](./eip-777.md). + +## Backwards Compatibility + +This EIP is fully backwards compatible as its implementation extends the functionality of [EIP-1996][EIP-1996]. + +## Implementation + +The GitHub repository [IoBuilders/clearable-token](https://github.com/IoBuilders/clearable-token) contains the reference implementation. + +## Contributors +This proposal has been collaboratively implemented by [adhara.io](https://adhara.io/) and [io.builders](https://io.builders/). + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). + +[1] https://en.wikipedia.org/wiki/Clearing_(finance) + +[Clearing-Wikipedia]: https://en.wikipedia.org/wiki/Clearing_(finance) +[KYC-Wikipedia]: https://en.wikipedia.org/wiki/Know_your_customer +[AML-Wikipedia]: https://en.wikipedia.org/wiki/Money_laundering#Anti-money_laundering +[EIP-1996]: ./eip-1996.md diff --git a/EIPS/eip-2019.md b/EIPS/eip-2019.md new file mode 100644 index 0000000..a59cc4f --- /dev/null +++ b/EIPS/eip-2019.md @@ -0,0 +1,255 @@ +--- +eip: 2019 +title: Fundable Token +author: Fernando Paris , Julio Faura , Daniel Lehrner +discussions-to: https://github.com/ethereum/EIPs/issues/2105 +status: Stagnant +type: Standards Track +category: ERC +created: 2019-05-10 +requires: 20 +--- + +## Simple Summary +An extension to the [ERC-20] standard token that allows Token wallet owners to request a wallet to be funded, by calling the smart contract and attaching a fund instruction string. + +## Actors + +#### Token Wallet Owners +The person or company who owns the wallet, and will order a token fund request into the wallet. + +#### Token contract owner / agent +The entity, company responsible/owner of the token contract, and token issuing/minting. This actor is in charge of trying to fulfill all fund request(s), reading the fund instruction(s), and correlate the private payment details. + +#### Orderer +An actor who is enabled to initiate funding orders on behalf of a token wallet owner. + +## Abstract +Token wallet owners (or approved addresses) can order tokenization requests through blockchain. This is done by calling the ```orderFund``` or ```orderFundFrom``` methods, which initiate the workflow for the token contract operator to either honor or reject the fund request. In this case, fund instructions are provided when submitting the request, which are used by the operator to determine the source of the funds to be debited in order to do fund the token wallet (through minting). + +In general, it is not advisable to place explicit routing instructions for debiting funds on a verbatim basis on the blockchain, and it is advised to use a private communication alternatives, such as private channels, encrypted storage or similar, to do so (external to the blockchain ledger). Another (less desirable) possibility is to place these instructions on the instructions field in encrypted form. + +## Motivation +Nowadays most of the token issuing/funding request, based on any fiat based payment method need a previous centralized transaction, to be able to get the desired tokens issued on requester's wallet. +In the aim of trying to bring all the needed steps into decentralization, exposing all the needed steps of token lifecycle and payment transactions, a funding request can allow wallet owner to initiate the funding request via blockchain. +Key benefits: + +* Funding and payment traceability is enhanced bringing the initiation into the ledger. All payment stat +s can be stored on chain. +* Almost all money/token lifecycle is covered via a decentralized approach, complemented with private communications which is common use in the ecosystem. + +## Specification + +```solidity +interface IFundable /* is ERC-20 */ { + enum FundStatusCode { + Nonexistent, + Ordered, + InProcess, + Executed, + Rejected, + Cancelled + } + function authorizeFundOperator(address orderer) external returns (bool); + function revokeFundOperator(address orderer) external returns (bool) ; + function orderFund(string calldata operationId, uint256 value, string calldata instructions) external returns (bool); + function orderFundFrom(string calldata operationId, address walletToFund, uint256 value, string calldata instructions) external returns (bool); + function cancelFund(string calldata operationId) external returns (bool); + function processFund(string calldata operationId) external returns (bool); + function executeFund(string calldata operationId) external returns (bool); + function rejectFund(string calldata operationId, string calldata reason) external returns (bool); + + function isFundOperatorFor(address walletToFund, address orderer) external view returns (bool); + function retrieveFundData(address orderer, string calldata operationId) external view returns (address walletToFund, uint256 value, string memory instructions, FundStatusCode status); + + event FundOrdered(address indexed orderer, string indexed operationId, address indexed , uint256 value, string instructions); + event FundInProcess(address indexed orderer, string indexed operationId); + event FundExecuted(address indexed orderer, string indexed operationId); + event FundRejected(address indexed orderer, string indexed operationId, string reason); + event FundCancelled(address indexed orderer, string indexed operationId); + event FundOperatorAuthorized(address indexed walletToFund, address indexed orderer); + event FundOperatorRevoked(address indexed walletToFund, address indexed orderer); +} +``` + +### Functions + +#### authorizeFundOperator + +Wallet owner, authorizes a given address to be fund orderer. + +| Parameter | Description | +| ---------|-------------| +| orderer | The address of the orderer. + +#### revokeFundOperator + +Wallet owner, revokes a given address to be fund orderer. + +| Parameter | Description | +| ---------|-------------| +| orderer | The address of the orderer. + +#### orderFund + +Creates a fund request, that will be processed by the token operator. The function must revert if the operation ID has been used before. + +| Parameter | Description | +| ---------|-------------| +| operationId | The unique ID to identify the request | +| value | The amount to be funded. | +| instruction | A string including the payment instruction. | + +#### orderFundFrom + +Creates a fund request, on behalf of a wallet owner, that will be processed by the token operator. The function must revert if the operation ID has been used before. + +| Parameter | Description | +| ---------|-------------| +| operationId |The unique ID to identify the request | +| walletToFund | The wallet to be funded on behalf. +| value | The amount to be funded. | +| instruction | A string including the payment instruction. | + +#### cancelFund + +Cancels a funding request. + +| Parameter | Description | +| ---------|-------------| +| operationId | The unique ID to identify the request that is going to be cancelled. This can only be done by token holder, or the fund initiator. | + +#### processFund + +Marks a funding request as on process. After the status is on process, order cannot be cancelled. + +| Parameter | Description | +| ---------|-------------| +| operationId | The unique ID to identify the request is in process. + +#### executeFund + +Issues the amount of tokens and marks a funding request as executed. + +| Parameter | Description | +| ---------|-------------| +| operationId | The unique ID to identify the request that has been executed. + +#### rejectFund + +Rejects a given operation with a reason. + +| Parameter | Description | +| ---------|-------------| +| operationId | The unique ID to identify the request that has been executed. +| reason | The specific reason that explains why the fund request was rejected. EIP 1066 codes can be used | + +#### isFundOperatorFor + +Checks that given player is allowed to order fund requests, for a given wallet. + +| Parameter | Description | +| ---------|-------------| +| walletToFund | The wallet to be funded, and checked for approval permission. +| orderer | The address of the orderer, to be checked for approval permission. + +#### retrieveFundData + +Retrieves all the fund request data. Only operator, tokenHolder, and orderer can get the given operation data. + +| Parameter | Description | +| ---------|-------------| +| operationId | The unique ID to identify the fund order. + +### Events + +#### FundOrdered + +Emitted when an token wallet owner orders a funding request. + +| Parameter | Description | +| ---------|-------------| +| operationId | The unique ID to identify the request | +| walletToFund | The wallet that the player is allowed to start funding requests | +| value | The amount to be funded. | +| instruction | A string including the payment instruction. | + +#### FundInProcess + +Emitted when an operator starts a funding request after validating the instruction, and the operation is marked as in process. + +| Parameter | Description | +| ---------|-------------| +| orderer | The address of the fund request orderer. | +| operationId | The unique ID to identify the fund. | + +#### FundExecuted + +Emitted when an operator has executed a funding request. + +| Parameter | Description | +| ---------|-------------| +| orderer | The address of the fund request orderer. | +| operationId | The unique ID to identify the fund. | + +#### FundRejected + +Emitted when an operator has rejected a funding request. + +| Parameter | Description | +| ---------|-------------| +| orderer | The address of the fund request orderer. | +| operationId | The unique ID to identify the fund. | +| reason | The specific reason that explains why the fund request was rejected. EIP 1066 codes can be used | + +#### FundCancelled + +Emitted when a token holder, orderer, has cancelled a funding request. This can only be done if the operator hasn't put the funding order in process. + +| Parameter | Description | +| ---------|-------------| +| orderer | The address of the fund request orderer. | +| operationId | The unique ID to identify the fund. | + +#### FundOperatorAuthorized + +Emitted when a given player, operator, company or a given persona, has been approved to start fund request for a given token holder. + +| Parameter | Description | +| ---------|-------------| +| walletToFund | The wallet that the player is allowed to start funding requests | +| orderer | The address that allows the the player to start requests. | + +#### FundOperatorRevoked + +Emitted when a given player has been revoked initiate funding requests. + +| Parameter | Description | +| ---------|-------------| +| walletToFund | The wallet that the player is allowed to start funding requests | +| orderer | The address that allows the the player to start requests. | + +## Rationale +This standards provides a functionality to allow token holders to start funding requests in a decentralized way. + +It's important to highlight that the token operator, need to process all funding request, updating the fund status based on the linked payment that will be done. + +Funding instruction format is open. ISO payment standard like is a good start point, + +The `operationId` is a string and not something more gas efficient to allow easy traceability of the hold and allow human readable ids. It is up to the implementer if the string should be stored on-chain or only its hash, as it is enough to identify a hold. + +The `operationId` is a competitive resource. It is recommended, but not required, that the hold issuers used a unique prefix to avoid collisions. + +## Backwards Compatibility +This EIP is fully backwards compatible as its implementation extends the functionality of [ERC-20]. + +## Implementation +The GitHub repository [IoBuilders/fundable-token](https://github.com/IoBuilders/fundable-token) contains the work in progress implementation. + +## Contributors +This proposal has been collaboratively implemented by [adhara.io](https://adhara.io/) and [io.builders](https://io.builders/). + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). + +[ERC-20]: ./eip-20.md diff --git a/EIPS/eip-2020.md b/EIPS/eip-2020.md new file mode 100644 index 0000000..ddf3419 --- /dev/null +++ b/EIPS/eip-2020.md @@ -0,0 +1,233 @@ +--- +eip: 2020 +title: E-Money Standard Token +author: Julio Faura , Fernando Paris , Daniel Lehrner +discussions-to: https://github.com/ethereum/EIPs/issues/2407 +status: Stagnant +type: Standards Track +category: ERC +created: 2019-05-10 +requires: 20, 1066, 1996, 2009, 2018, 2019, 2021 +--- + +## Simple Summary + +The E-Money Standard Token aims to enable the issuance of regulated electronic money on blockchain networks, and its practical usage in real financial applications. + +## Actors + +#### Operator +An account, which has been approved by an account to perform an action on the behalf of another account. + +## Abstract + +Financial institutions work today with electronic systems, which hold account balances in databases on core banking systems. In order for an institution to be allowed to maintain records of client balances segregated and available for clients, such institution must be regulated under a known legal framework and must possess a license to do so. Maintaining a license under regulatory supervision entails ensuring compliance (i.e. performing KYC on all clients and ensuring good AML practices before allowing transactions) and demonstrating technical and operational solvency through periodic audits, so clients depositing funds with the institution can rest assured that their money is safe. + +## Motivation + +There are only a number of potential regulatory license frameworks that allow institutions to issue and hold money balances for customers (be it retail corporate or institutional types). The most important and practical ones are three: +* **Electronic money entities**: these are legally regulated vehicles that are mostly used today for cash and payments services, instead of more complex financial services. For example prepaid cards or online payment systems such as PayPal run on such schemes. In most jurisdictions, electronic money balances are required to be 100% backed by assets, which often entails holding cash on an omnibus account at a bank with 100% of the funds issued to clients in the electronic money ledger. +* **Banking licenses**: these include commercial and investment banks, which segregate client funds using current and other type of accounts implemented on core banking systems. Banks can create money by lending to clients, so bank money can be backed by promises to pay and other illiquid assets. +* **Central banks**: central banks hold balances for banks in RTGS systems, similar to core banking systems but with much more restricted yet critical functionality. Central banks create money by lending it to banks, which pledge their assets to central banks as a lender of last resort for an official interest rate. + +Regulations for all these types of electronic money are local, i.e. only valid for each jurisdiction and not valid in others. Regulations can vary as well dramatically in different jurisdictions — for example there are places with no electronic money frameworks, on everything has to be done through banking licenses or directly with a central bank. But in all cases compliance with existing regulation needs to ensured, in particular: +* **Know Your Customer (KYC)**: the institution needs to identify the client before providing them with the possibility of depositing money or transact. In different jurisdictions and for different types of licenses there are different levels of balance and activity that can be allowed for different levels of KYC. For example, low KYC requirements with little checks or even no checks at all can usually be acceptable in many jurisdictions if cashin balances are kept low (i.e. hundreds of dollars) +* **Anti Money Laundering (AML)**: the institution needs to perform checks of parties transacting with its clients, typically checking against black lists and doing sanction screening, most notably in the context of international transactions + +Beyond cash, financial instruments such as equities or bonds are also registered in electronic systems in most cases, although all these systems and the bank accounting systems are only connected through rudimentary messaging means, which leads to the need for reconciliations and manual management in many cases. Cash systems to provide settlement of transactions in the capital markets are not well-connected to the transactional systems, and often entail delays and settlement risk. + +The E-Money Standard Token builds on Ethereum standards currently in use such as [ERC-20], but it extends them to provide few key additional pieces of functionality, needed in the regulated financial world: +* **Compliance**: E-Money Standard Token implements a set of methods to check in advance whether user-initiated transactions can be done from a compliance point of view. Implementations must `require` that these methods return a positive answer before executing the transaction. +* **Clearing**: In addition to the standard [ERC-20] `transfer` method, E-Money Standard Token provides a way to submit transfers that need to be cleared by the token issuing authority off-chain. These transfers are then executed in two steps: + 1. transfers are ordered + 1. after clearing them, transfers are executed or rejected by the operator of the token contract +* **Holds**: token balances can be put on hold, which will make the held amount unavailable for further use until the hold is resolved (i.e. either executed or released). Holds have a payer, a payee, and a notary who is in charge of resolving the hold. Holds also implement expiration periods, after which anyone can release the hold Holds are similar to escrows in that are firm and lead to final settlement. Holds can also be used to implement collateralization. +* **Funding requests**: users can request for a wallet to be funded by calling the smart contract and attaching a debit instruction string. The tokenizer reads this request, interprets the debit instructions, and triggers a transfer in the bank ledger to initiate the tokenization process. +* **Payouts**: users can request payouts by calling the smart contract and attaching a payment instruction string. The (de)tokenizer reads this request, interprets the payment instructions, and triggers the transfer of funds (typically from the omnibus account) into the destination account, if possible. Note that a redemption request is a special type of payout in which the destination (bank) account for the payout is the bank account linked to the token wallet. + +The E-Money Standard Token is thus different from other tokens commonly referred to as "stable coins" in that it is designed to be issued, burnt and made available to users in a compliant manner (i.e. with full KYC and AML compliance) through a licensed vehicle (an electronic money entity, a bank, or a central bank), and in that it provides the additional functionality described above, so it can be used by other smart contracts implementing more complex financial applications such as interbank payments, supply chain finance instruments, or the creation of E-Money Standard Token denominated bonds and equities with automatic delivery-vs-payment. + +## Specification + +```solidity +interface EMoneyToken /* is ERC-1996, ERC-2018, ERC-2019, ERC-2021 */ { + function currency() external view returns (string memory); + function version() external pure returns (string memory); + + function availableFunds(address account) external view returns (uint256); + + function checkTransferAllowed(address from, address to, uint256 value) external view returns (byte status); + function checkApproveAllowed(address from, address spender, uint256 value) external view returns (byte status); + + function checkHoldAllowed(address from, address to, address notary, uint256 value) external view returns (byte status); + function checkAuthorizeHoldOperatorAllowed(address operator, address from) external view returns (byte status); + + function checkOrderTransferAllowed(address from, address to, uint256 value) external view returns (byte status); + function checkAuthorizeClearableTransferOperatorAllowed(address operator, address from) external view returns (byte status); + + function checkOrderFundAllowed(address to, address operator, uint256 value) external view returns (byte status); + function checkAuthorizeFundOperatorAllowed(address operator, address to) external view returns (byte status); + + function checkOrderPayoutAllowed(address from, address operator, uint256 value) external view returns (byte status); + function checkAuthorizePayoutOperatorAllowed(address operator, address from) external view returns (byte status); +} +``` + +### Mandatory checks + +The checks must be verified in their corresponding actions. The action must only be successful if the check return an `Allowed` status code. In any other case the functions must revert. + +### Status codes + +If an action is allowed `0x11` (Allowed), or an issuer-specific code with equivalent but more precise meaning must be returned. If the action is not allowed the status must be `0x10` (Disallowed), or an issuer-specific code with equivalent but more precise meaning. + +### Functions + +#### currency + +Returns the currency that backs the token. The value must be a code defined in [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217). + +| Parameter | Description | +| ---------|-------------| +| - | - | + +#### version + +Returns the current version of the smart contract. The format of the version is up to the implementer of the EIP. + +| Parameter | Description | +| ---------|-------------| +| - | - | + +#### availableFunds + +Returns the total net funds of an account. Taking into consideration the outright balance and the held balances. + +| Parameter | Description | +| ---------|-------------| +| account | The account which available funds should be returned | + +#### checkTransferAllowed + +Checks if the `transfer` or `transferFrom` function is allowed to be executed with the given parameters. + +| Parameter | Description | +| ---------|-------------| +| from | The address of the payer, from whom the tokens are to be taken if executed | +| to | The address of the payee, to whom the tokens are to be transferred if executed | +| value | The amount to be transferred | + +#### checkApproveAllowed + +Checks if the `approve` function is allowed to be executed with the given parameters. + +| Parameter | Description | +| ---------|-------------| +| from | The address of the payer, from whom the tokens are to be taken if executed | +| spender | The address of the spender, which potentially can initiate transfers on behalf of `from` | +| value | The maximum amount to be transferred | + +#### checkHoldAllowed + +Checks if the `hold` function is allowed to be executed with the given parameters. + +| Parameter | Description | +| ---------|-------------| +| from | The address of the payer, from whom the tokens are to be taken if executed | +| to | The address of the payee, to whom the tokens are to be transferred if executed | +| notary | The address of the notary who is going to determine whether the hold is to be executed or released | +| value | The amount to be transferred. Must be less or equal than the balance of the payer | + +#### checkAuthorizeHoldOperatorAllowed + +Checks if the `checkAuthorizeHoldOperatorAllowed` function is allowed to be executed with the given parameters. + +| Parameter | Description | +| ---------|-------------| +| operator | The address to be approved as operator of clearable transfers | +| from | The address on which behalf holds could potentially be issued | + +#### checkOrderTransferAllowed + +Checks if the `orderTransfer` function is allowed to be executed with the given parameters. + +| Parameter | Description | +| ---------|-------------| +| from | The address of the payer, from whom the tokens are to be taken if executed | +| to | The address of the payee, to whom the tokens are to be paid if executed | +| value | The amount to be transferred. Must be less or equal than the balance of the payer | + +#### checkAuthorizeClearableTransferOperatorAllowed + +Checks if the `authorizeClearableTransferOperator` function is allowed to be executed with the given parameters. + +| Parameter | Description | +| ---------|-------------| +| operator | The address to be approved as operator of clearable transfers | +| from | The address on which behalf clearable transfers could potentially be ordered | + +#### checkOrderFundAllowed + +Checks if the `orderFund` function is allowed to be executed with the given parameters. + +| Parameter | Description | +| ---------|-------------| +| to | The address to which the tokens are to be given if executed | +| operator | The address of the requester, which initiates the funding order | +| value | The amount to be funded | + +#### checkAuthorizeFundOperatorAllowed + +Checks if the `authorizeFundOperator` function is allowed to be executed with the given parameters. + +| Parameter | Description | +| ---------|-------------| +| operator | The address to be approved as operator of ordering funding | +| to | The address which the tokens are to be given if executed | + +#### checkOrderPayoutAllowed + +Checks if the `orderPayout` function is allowed to be executed with the given parameters. + +| Parameter | Description | +| ---------|-------------| +| from | The address from whom the tokens are to be taken if executed | +| operator | The address of the requester, which initiates the payout request | +| value | The amount to be paid out | + +#### checkAuthorizePayoutOperatorAllowed + +Checks if the `authorizePayoutOperator` function is allowed to be executed with the given parameters. + +| Parameter | Description | +| ---------|-------------| +| operator | The address to be approved as operator of ordering payouts | +| from | The address from which the tokens are to be taken if executed | + +## Rationale + +This EIP unifies [ERC-1996][ERC-1996], [ERC-2018][ERC-2018], [ERC-2019][ERC-2019] and [ERC-2021][ERC-2021] and adds the checks for the compliance on top of it. By this way the separate EIPs are otherwise independent of each other, and the E-Money Standard Token offers a solution for all necessary functionality of regulated electronic money. + +While not requiring it, the naming of the check functions was adopted from [ERC-1462][ERC-1462]. + +## Backwards Compatibility + +This EIP is fully backwards compatible as its implementation extends the functionality of [ERC-1996][ERC-1996], [ERC-2018][ERC-2018], [ERC-2019][ERC-2019], [ERC-2021][ERC-2021] and [ERC-1066][ERC-1066]. + +## Implementation + +The GitHub repository [IoBuilders/em-token](https://github.com/IoBuilders/em-token) contains the work in progress implementation. + +## Contributors +This proposal has been collaboratively implemented by [adhara.io](https://adhara.io/) and [io.builders](https://io.builders/). + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). + +[ERC-20]: ./eip-20.md +[ERC-1066]: ./eip-1066.md +[ERC-1462]: ./eip-1462.md +[ERC-1996]: ./eip-1996.md +[ERC-2018]: ./eip-2018.md +[ERC-2019]: ./eip-2019.md +[ERC-2021]: ./eip-2021.md diff --git a/EIPS/eip-2021.md b/EIPS/eip-2021.md new file mode 100644 index 0000000..8464686 --- /dev/null +++ b/EIPS/eip-2021.md @@ -0,0 +1,290 @@ +--- +eip: 2021 +title: Payoutable Token +author: Fernando Paris , Julio Faura , Daniel Lehrner +discussions-to: https://github.com/ethereum/EIPs/issues/2106 +status: Stagnant +type: Standards Track +category: ERC +created: 2019-05-10 +requires: 20, 1066, 1996 +--- + +## Simple Summary +An extension to the [ERC-20] standard token that allows Token wallet owners to request payout from their wallet, by calling the smart contract and attaching a payout instruction string. + +## Actors + +#### Token Wallet Owners +The person or company who owns the wallet, and will order payout. + +#### Token contract owner / agent +The entity, company responsible/owner of the token contract, and token issuing/minting. This actor is in charge of trying to fulfill all payout request(s), reading the payout instruction(s), and correlate the payout details. + +#### Orderer +An actor who is enabled to initiate payout orders on behalf of a token wallet owner. + +## Abstract +Token wallet owners (or approved addresses) can order payout requests through blockchain. This is done by calling the ```orderPayoutFrom``` or ```orderPayoutFrom``` methods, which initiate the workflow for the token contract operator to either honor or reject the payout request. In this case, payout instructions are provided when submitting the request, which are used by the operator to determine the destination of the funds. + +In general, it is not advisable to place explicit routing instructions for the payouts on a verbatim basis on the blockchain, and it is advised to use a private communication alternatives, such as private channels, encrypted storage or similar, to do so (external to the blockchain ledger). Another (less desirable) possibility is to place these instructions on the instructions field in encrypted form. + +## Motivation +Nowadays most of the token payout requests, need a previous centralized transaction, to be able to define the payout destination to be able to execute the payout (burn transaction). +In the aim of trying to bring all the needed steps into decentralization, exposing all the needed steps of token lifecycle and payment transactions, a payout request can allow wallet owner to initiate the payout order via blockchain. +Key benefits: + +* Payout, burning traceability is enhanced bringing the initiation into the ledger. All payment, payout statuses can be stored on chain. +* Almost all money/token lifecycle is covered via a decentralized approach, complemented with private communications which is common use in the ecosystem. + +In this case, the following movement of tokens are done as the process progresses: + +* Upon launch of the payout request, the appropriate amount of funds are placed on a hold with a predefined notary defined by the platform, and the payout is placed into a ```Ordered``` state +* The operator then can put the payout request ```InProcess```, which prevents the _orderer_ of the payout from being able to cancel the payout request +* After checking the payout is actually possible the operator then executes the hold, which moves the funds to a suspense wallet and places the payout into the ```FundsInSuspense``` state +* The operator then moves the funds offchain (usually from the omnibus account) to the appropriate destination account, then burning the tokens from the suspense wallet and rendering the payout into the ```Executed``` state +* Either before or after placing the request ```InProcess```, the operator can also reject the payout, which returns the funds to the payer and eliminates the hold. The resulting end state of the payout is ```Rejected``` +* When the payout is ```Ordered``` and before the operator places it into the ```InProcess``` state, the orderer of the payout can also cancel it, which frees up the hold and puts the payout into the final ```Cancelled``` state + +## Specification + +```solidity +interface IPayoutable /* is ERC-20 */ { + enum PayoutStatusCode { + Nonexistent, + Ordered, + InProcess, + FundsInSuspense, + Executed, + Rejected, + Cancelled + } + function authorizePayoutOperator(address orderer) external returns (bool); + function revokePayoutOperator(address orderer) external returns (bool); + function orderPayout(string calldata operationId, uint256 value, string calldata instructions) external returns (bool); + function orderPayoutFrom(string calldata operationId, address walletToBePaidOut, uint256 value, string calldata instructions) external returns (bool); + function cancelPayout(string calldata operationId) external returns (bool); + function processPayout(string calldata operationId) external returns (bool); + function putFundsInSuspenseInPayout(string calldata operationId) external returns (bool); + function executePayout(string calldata operationId) external returns (bool); + function rejectPayout(string calldata operationId, string calldata reason) external returns (bool); + + function isPayoutOperatorFor(address walletToDebit, address orderer) external view returns (bool); + function retrievePayoutData(string calldata operationId) external view returns (address walletToDebit, uint256 value, string memory instructions, PayoutStatusCode status); + + event PayoutOrdered(address indexed orderer, string indexed operationId, address indexed walletToDebit, uint256 value, string instructions); + event PayoutInProcess(address indexed orderer, string indexed operationId); + event PayoutFundsInSuspense(address indexed orderer, string indexed operationId); + event PayoutExecuted(address indexed orderer, string indexed operationId); + event PayoutRejected(address indexed orderer, string indexed operationId, string reason); + event PayoutCancelled(address indexed orderer, string indexed operationId); + event PayoutOperatorAuthorized(address indexed walletToBePaidOut, address indexed orderer); + event PayoutOperatorRevoked(address indexed walletToBePaidOut, address indexed orderer); +} +``` + +### Functions + +#### authorizePayoutOperator + +Wallet owner, allows a given address to be payout orderer. + +| Parameter | Description | +| ---------|-------------| +| orderer | The address of the orderer. | + +#### revokePayoutOperator + +Wallet owner, Revokes a given address to be payout orderer. + +| Parameter | Description | +| ---------|-------------| +| orderer | The address of the orderer. | + +#### orderPayout + +Creates a payout request, that will be processed by the token operator. The function must revert if the operation ID has been used before. + +| Parameter | Description | +| ---------|-------------| +| operationId | The unique ID to identify the request | +| value | The amount to be paid out. | +| instruction | A string including the payment instruction. | + +#### orderPayoutFrom + +Creates a payout request, on behalf of a wallet owner, that will be processed by the token operator. The function must revert if the operation ID has been used before. + +| Parameter | Description | +| ---------|-------------| +| operationId |The unique ID to identify the request | +| walletToBePaidOut | The wallet to be paid out on behalf. | +| value | The amount to be paid out. | +| instruction | A string including the payment instruction. | + +#### cancelPayout + +Cancels a payout request. + +| Parameter | Description | +| ---------|-------------| +| operationId | The unique ID to identify the request that is going to be cancelled. This can only be done by token holder, or the payout initiator/orderer. | +| reason | The specific reason that explains why the payout request was rejected. [EIP-1066] codes can be used. | + + +#### processPayout + +Marks a payout request as on process. After the status is on process, order cannot be cancelled. + +| Parameter | Description | +| ---------|-------------| +| operationId | The unique ID to identify that the request is in process. | + +#### putFundsInSuspenseInPayout + +Put a given payout in suspense. Can only be done if it is in process. + +| Parameter | Description | +| ---------|-------------| +| operationId | The unique ID to identify that the request is in process. | + +#### executePayout + +Burn the amount of tokens and marks a payout request as executed. + +| Parameter | Description | +| ---------|-------------| +| operationId | The unique ID to identify the request that has been executed. | + +#### rejectPayout + +Rejects a given operation with a reason. + +| Parameter | Description | +| ---------|-------------| +| operationId | The unique ID to identify the request that has been executed. | +| reason | The specific reason that explains why the payout request was rejected. [EIP-1066] codes can be used | + +#### isApprovedToOrderPayout + +Checks that given player is allowed to order payout requests, for a given wallet. + +| Parameter | Description | +| ---------|-------------| +| walletToBePaidOut | The wallet to be paid out, and checked for approval permission. | +| orderer | The address of the orderer, to be checked for approval permission. | + +#### retrievePayoutData + +Retrieves all the payout request data. Only operator, tokenHolder, and orderer can get the given operation data. + +| Parameter | Description | +| ---------|-------------| +| orderer | The address of the orderer, to correlate the right data. | +| operationId | The unique ID to identify the payout order. | + +### Events + +#### Payout Ordered + +Emitted when an token wallet owner orders a payout request. + +| Parameter | Description | +| ---------|-------------| +| operationId | The unique ID to identify the request | +| walletToBePaidOut | The wallet that is requested to be paid out | +| value | The amount to be funded. | +| instruction | A string including the payment instruction. | + +#### PayoutFundsInSuspense + +Emitted when an operator puts fund in suspense. + +| Parameter | Description | +| ---------|-------------| +| orderer | The address of the payout request orderer. | +| operationId | The unique ID to identify the payout. | + +#### PayoutInProcess + +Emitted when an operator accepts a payout request, and the operation is in process. + +| Parameter | Description | +| ---------|-------------| +| orderer | The address of the payout request orderer. | +| operationId | The unique ID to identify the payout. | + +#### PayoutExecuted + +Emitted when an operator has executed a payout request. + +| Parameter | Description | +| ---------|-------------| +| orderer | The address of the payout request orderer. | +| operationId | The unique ID to identify the payout. | + +#### PayoutRejected + +Emitted when an operator has rejected a payout request. + +| Parameter | Description | +| ---------|-------------| +| orderer | The address of the payout request orderer. | +| operationId | The unique ID to identify the payout. | +| reason | The specific reason that explains why the payout request was rejected. [EIP-1066] codes can be used | + +#### PayoutCancelled + +Emitted when a token holder, orderer, has cancelled a payout request. This can only be done if the operator hasn't put the payout order in process. + +| Parameter | Description | +| ---------|-------------| +| orderer | The address of the payout request orderer. | +| operationId | The unique ID per payout issuer to identify the payout. | + +#### PayoutOperatorAuthorized + +Emitted when a given player, operator, company or a given persona, has been approved to start payout request for a given token holder. + +| Parameter | Description | +| ---------|-------------| +| walletToBePaidOut | The wallet that the player is allowed to start payout requests | +| orderer |The address that allows the the player to start requests. | + +#### PayoutOperatorRevoked + +Emitted when a given player has been revoked initiate payout requests. + +| Parameter | Description | +| ---------|-------------| +| walletToBePaidOut | The wallet that the player is allowed to start payout requests | +| orderer |The address that allows the the player to start requests. | + +## Rationale +This standards provides a functionality to allow token holders to start payout requests in a decentralized way. + +It's important to highlight that the token operator, need to process all payout request, updating the payout status based on the linked payment that will be done. + +Payout instruction format is open. ISO payment standard like is a good start point. + +This EIP uses [EIP-1996] to hold the money after a payout is ordered. The token contract owner or agent, whose implementation is not part of this proposal, acts as a predefined notary to decide if the payout is executed or not. + +The `operationId` is a string and not something more gas efficient to allow easy traceability of the hold and allow human readable ids. It is up to the implementer if the string should be stored on-chain or only its hash, as it is enough to identify a hold. + +The `operationId` is a competitive resource. It is recommended, but not required, that the hold issuers used a unique prefix to avoid collisions. + +## Backwards Compatibility +This EIP is fully backwards compatible as its implementation extends the functionality of [ERC-20] and [ERC-1996]. + +## Implementation +The GitHub repository [IoBuilders/payoutable-token](https://github.com/IoBuilders/payoutable-token) contains the reference implementation. + +## Contributors +This proposal has been collaboratively implemented by [adhara.io](https://adhara.io/) and [io.builders](https://io.builders/). + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). + +[ERC-20]: ./eip-20.md +[EIP-1066]: ./eip-1066.md +[EIP-1996]: ./eip-1996.md diff --git a/EIPS/eip-2025.md b/EIPS/eip-2025.md new file mode 100644 index 0000000..631f602 --- /dev/null +++ b/EIPS/eip-2025.md @@ -0,0 +1,209 @@ +--- +eip: 2025 +title: Block Rewards Proposal for funding Eth1.x +author: James Hancock (@madeoftin) +discussions-to: https://github.com/MadeofTin/EIPs/issues +status: Withdrawn +type: Standards Track +category: Core +created: 2019-04-20 +requires: 1890 +--- + +## Simple Summary + +Add `0.0055 ETH` per block for 18 months (A total of 17050 ETH) as a developer block reward reserved for funding specific Ethereum1.X working groups. The working groups are State rent (750k), Better sync (360k), Finality gadget (360k), Fee market (360k), Testing infrastructure (360k). Governance of the funds will be through a multisig of trusted individuals from the ecosystem including client teams, the foundation, and the community. + +[EIP-1890](./eip-1890.md) proposes a mechanism to capture a portion of block rewards for sustainably funding ongoing network development. That EIP sets values and addresses to zero and so does not actually collect any rewards. This proposal is to explicitly set those values and begin collecting a portion of block rewards for 18 months in order to fund Ethereum 1.X working groups and organization efforts. This funding will be used to repay an initial loan provided by investors in the community funding this work with a small amount of interest. After 18 months the block reward would again reduce to zero. + +## Abstract + +This EIP extends the mechanism established in EIP-1890 to add `0.0055 ETH` to the block reward for a specific distribution period of `3,100,000 BLOCKS`(≈ 18 months). The `RECIPIENT_ADDRESS` is set to a smart contract with hardcoded denominations that distributes incoming ETH to a set of addresses for the purpose of Eth1.X development. The emission schedule would start at the hard fork block number and continue for `3,100,000 BLOCKS` (≈ 18 months) at which point the address and amount would again return to 0. Any further distribution would require a future hard fork. + +## Motivation + +The context for this proposal came from attending the [Core Dev Eth1.X Meeting](https://www.youtube.com/watch?v=Au1Qll-86v0) in Berlin. Development is needed to move Eth1.X forward, and I observed that a lack of funding is the primary barrier to this work. This work can only be effectively conducted by working groups forming around these issues, and these working groups need funding in order to pay dedicated contractors and project managers. This proposal is a plan for funding these groups and supporting their operation. + +## Specification + +Two constants will be introduced: + +- `REWARD_DURATION_IN_BLOCKS`, which specifies the number of blocks after `ISTANBUL_BLOCK_HEIGHT` when the reward collection will terminate (i.e., at block `ISTANBUL_BLOCK_HEIGHT + REWARD_DURATION_IN_BLOCKS`) +- `BENEFICIARY_ADDRESSES`, a list of tuples containing the address and the amount to be transferred to this address per block. These amounts will be determined as the loan is collected from participating organizations and the addresses for repayment will be specified by them. The total of the amounts will sum to 0.0055. At the end of `REWARD_DURATION_IN_BLOCKS` the loan will be completely repaid. + +``` + +REWARD_DURATION_IN_BLOCKS = 3100000 +DEVFUND_BLOCK_REWARD = 0.0055 + + + +BENEFICIARY_ADDRESSES = [ + (
, ), + (
, ) +] +``` + +Beginning with block `ISTANBUL_BLOCK_HEIGHT`, the reward is added to the participating addresses within `BENEFICIARY_ADDRESSES` at each block until the end of the `REWARD_DURATION_IN_BLOCKS`. + +``` +IF (CURRENT_BLOCK - ISTANBUL_BLOCK_HEIGHT <= REWARD_DURATION_IN_BLOCKS) + +FOR BENEFICIARY in BENEFICIARY_ADDRESSES: + BENEFICIARY[0].balance += BENEFICIARY[1] + +``` + +## Rewards Distribution Rationale +``` + Development loan repayment: 0.005 ETH per block: 15500 ETH total + Development loan interest (10% total over the period, simple interest): 0.0005 ETH per block: 1550 ETH total + + Total Block Reward Increase = `0.0055` ETH per block: 17050 ETH Total +``` + +*With a price of Etheruem at $150.00 this will raise approx USD $2,325,000.00 for developing Eth1.X over the next 18 months.* + + +![Block Rewards Distribution](/assets/eip-2025/block_rewards_distribution.png) *Specific Addresses to be determined + +* [FAQ - Why hardcoded values?]( #why-hardcoded-values ) + + +## Rationale + +There has been great public debate concerning EIP-1890, and one of the primary concerns is that it is difficult to evaluate the proposal without more complete information on how funds would be raised, how they would be administered, and how they would be used. There is a need for funding Eth1.x development and it is currently unclear where those funds will come from. This proposal is intended to give a more comprehensive proposal for its funding. In the case that ETH1.x is fully funded before the Istanbul upgrade I will withdraw this EIP. Until that point I intend to continue championing this proposal as a valid funding mechanism for this work. + +### Why a loan? + +The Eth1x initiative needs funding now, not in 18 months. A loan is necessary to complete certain stages of work before the funding mechanism begins to provide funds. A loan would provide this necessary funding today, and the investors willing to front this cost can recoup their contribution + a reward of *a fixed interest rate* for the risk on their loan. This arrangement will make it easier to find investors willing to participate who have sufficient funds. + +### Loan Repayment + +![Loan State Diagram](/assets/eip-2025/loan_state.png) + +There is a risk that the investors lose part of their contribution in the case that this EIP is rejected by the community between the time the funds have been collected and the beginning of the payout schedule. In this case all remaining funds will be returned to the contributors. The interest on the loan is an incentive for investors to participate in spite of this risk. Their downside is limited to the amount of funds spent before this EIP is accepted or rejected, which should be no more than about 5%, while their upside consists of the 10% simple interest paid over the period. + +### Development Loan + + +`Development Loan: 0.005` over 3.1 Million blocks = 15500 ETH + +**Funding Working Groups on 1.X** +* Funding Contractors, Coordinators, and project managers +* Working Groups defined with clear mandates + +Budget + +Working groups + - State rent (750k) + - Better sync (360k) + - finality gadget (360k) + - Fee market (360k) + - testing infrastructure (360k) + + +**ETH1.X Core Dev Gatherings** + +Funding hosting, traveling, and accommodations for necessary in-person gatherings of ETH1.X core developers similar to the Stanford and Berlin ETH1.X Core Dev Meeting held earlier this year. + +At the end of the 18 Months, the whole process would be torn down to prevent any internal tyranny of structurelessness forming within. + + + +* [FAQ - How will the funding of the devs be organized?]( #how-will-funding-the-devs-be-organized) + +## Accountability + +The funds will be transferred into DAI and secured in a multi-sig comprised of members of the community. Representatives from the following groups will receive a key. + + + - EIP Editors + - Geth + - Parity + - ConsenSys/PegaSys (PegaSys) + - The Ethereum Foundation (Hudson Jameson) + - Community + +## Personal Notes and Disclosure +I want to address any concerns about conflicts of interests directly. My participation with Eth1.X currently has been as a volunteer. I am in talks about a possible funded role helping with testing and coordination. If my work for with Eth1.x is funded, I will accept no additional funding collected by the mechanism proposed in this EIP. + +Eth1.X is the now of Ethereum and I would like to see it succeed. This is the intent of my proposal. + +### COI + +Previously I was PM for Tennagraph, a signalling solution for Ethereum. An Aragon grant funded this project and was distributed through Giveth and an AragonDAO. I have not received any funding from the project beyond this grant. All of this is verifiable on-chain. I am stepping down from any paid role on the project to continue as an advisor. I am also stepping down as a moderator for stances as there is a COI moderating stances for EIPs I am working with directly. + +### Disclaimer + +I do not claim to represent the community with my views; any members who wish to join supporting me with this proposal are free to do so. This is as fair of a proposal as I can personally conceive. + +## Backwards Compatibility + +This EIP has no impact on backwards compatibility. + +## Test Cases + +Not Implemented + +## Implementation + +Not Implemented + +## FAQ + +### Why Hardcoded Values? + +Why not us a smart contract with some governance mechanism to allow changing the distribution of funds? Wouldn’t that be more flexible and effective? + +*TLDR: This EIP is not about governance reform* + +First, the payment of the loan will be hardcoded. Once agreed, the terms must be kept to give the lenders confidence in the repayment of the loan. As long as blocks are created the debt will be paid back. This is the essence of a trustless smart contract. + +After the loan, there is the option to allow the amounts (limited to less than .05ETH), and the locations (orgs that receive ecosystem funding) to be changed throughout the emission schedule. It is pretty easy to imagine a smart contract or DAO fulfilling this role. However, there are three classes of options available today we can consider when governing changes. + +* **Give the Keys to the Hands of the Few (Oligarchy)** + +Create a multisig that allows a group of individuals to update the smart contract. The most likely candidates for this are the Core Devs themselves, but it could also be a trusted few from the community/stakeholders. No matter how you slice it, there is a fundamental issue in deciding who gets to decide. There currently is not a trusted/adopted governance mechanism to make these decisions within the Ethereum ecosytem. Also, preventing changing the contract in self interest is difficult without a well-engineered governance system of checks and balances. This EIP does not claim nor aim to solve these issues. + +* **Give the Keys to the Hands of the Many (Plutarchy)** + +Allow ethereum holders with coin votes to update the smart contract. Using holographic consensus could overcome the issue of voter turnout as it scales as participation scales, even to the size of the whole network. This has some benefits as the entire network can participate. However, the problem is that some individuals in the network are over represented -- the wealthy. Without a solution to identity that has been agreed to and implemented by the entire Ethereum Network, there is no way around giving more power in votes to the rich. This EIP does not claim, nor aim to solve these issues. + +* **Use Ethereum Governance as it is Today** + +Criticisms or support aside, there is a system that governs Ethereum today. It is a mix of rough consensus among core devs, miners running nodes, clients implementing changes, and stakeholders adopting those changes. It includes yelling or not yelling on twitter and reddit. It is complicated and I don’t claim to understand it. Even without a clear view of how it works, there is evidence of its existence. This evidence is there are changes that have allowed to be implemented, and changes that have not allowed to be implemented in Ethereum. + +I do not aim to change Ethereum governance. Because this EIP has hardcoded values, it must go through the existing governance process, whatever that is, before it is implemented. It will then continue to operate until the end of the emission schedule. This is intentional. This makes it uncapturable. No party other then the ecosystem itself can modify the contract once it has been deployed. + +This EIP is not about governance reform. + +### Why not allow current client implementors fund this work? (EF, Consensys, Parity, etc...) + +Historically there has been a precedent that the Ethereum Foundation is solely responsible for funding the development of Ethereum. This process has evolved as the development has become more distributed. Aya Miyaguchi observed in a recent [Coindesk article](https://www.coindesk.com/ethereum-foundation-director-sets-new-vision-for-blockchain-non-profit), “it really is not only Ethereum Foundation people who are building [Ethereum]”. Yes, we could rely on the Ethereum Foundation to fund Eth1.X. But, why should we? This is a call for the network to come together and fund its own development. Ethereum _the network_ is not owned by any one organization or group of people. We are lucky to have the EF and I consider this EIP in support of their coordination efforts. + +### How Will Funding the Devs be Organized + +I do not profess to know the best way to organize these funds. There is work already in progress to organize these efforts championed by Alexey Akhunov. The following is a quote from a [recent medium article](https://medium.com/@akhounov/ethereum-1x-as-an-attempt-to-change-the-process-783efa23cf60): + +> “Going from funding a few implementation teams continuously and letting them do 'their stuff' to funding more specific and temporary initiatives requires looking at funding through different lenses. How much 'due diligence' and oversight is too much (in terms of overhead), who can decide whether working groups actually deliver, etc. This is also solvable, and also more on this will come later (not in this post)." + +My suggestion would be to create an Eth1.X core developer DAO using [DaoStack](https://daostack.io/) to coordinate membership and payment of the Core Devs, but ultimately they are capable of determining the system that works best for them. As long as the system is transparent and mature enough to distribute funds when the time comes, this is sufficient for now. + +### Isn't a loan considered a security, or is it? + +I am not a lawyer and will seek further guidance from lawyers in the field on this point in particular. From the research I have done and conversations I have had there is a very good argument that a loan of this nature will not be considered a security by the US Supreme Court. +As the result of [REVES ET AL. v . ERNST YOUNG 1990](https://casetext.com/case/reves-v-ernst-young), the court stated that a home loan, consumer financing, a loan secured by a lien on a small business or some assets of a small business, short term notes, or notes that formalize a debt incurred in the ordinary course of business are not securities. If the note resembles the items listed above (home loans, etc.) then the note will not be deemed a security. The Supreme Court provided four factors to determine if a note sufficiently resembles the types of notes that are not classified as securities. ([source](https://www.invigorlaw.com/loan-subject-securities-regulations/)) + +**Family Resemblance Test** + 1) The intentions of the company and the individual—if the company raised money for general use in a business enterprise, then the note is more likely to be a security; if the individual agreed to the loan primarily for the profit the note was expected to generate, the note is more likely to be a security. + 2) The plan of distribution—the more widely the note is offered, the more likely it is to be found a security. + 3) The expectations of the investing public—if the investors thought they were investing in a business to make a profit on their investment, the note is more likely to be found a security. + 4) Other risk-reducing factor—if the note is collateralized or otherwise less risky than common notes, the note is less likely to be found to be a security. + +The loan is for the specific use of supporting Eth1.X research and development. The distribution will not be widely offered and the note will be collateralized by the network itself, provided in ETH and repaid in ETH. In coordinating the collection of these funds recognise I may be legally liable for some of this work and I will do all of the due dilegence I can, seek legal counsel, and accept any legal repercussions resulting from this work. + +#### + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2026.md b/EIPS/eip-2026.md new file mode 100644 index 0000000..4523184 --- /dev/null +++ b/EIPS/eip-2026.md @@ -0,0 +1,61 @@ +--- +eip: 2026 +title: State Rent H - Fixed Prepayment for accounts +author: Alexey Akhunov (@AlexeyAkhunov) +discussions-to: https://ethereum-magicians.org/t/eip-2026-fixed-rent-prepayment-for-all-accounts-change-h-from-state-rent-v3-proposal/3273 +status: Stagnant +type: Standards Track +category: Core +created: 2019-05-14 +--- + +## Simple Summary +Creation of new accounts (both contracts and non-contracts) requires a fixed one-off rent prepayment. Pre-existed accounts require the same prepayment upon +the first modification. The act of rent prepayment causes the addition of an extra field to accounts, called `rentbalance`. This field becomes part of state. + +## Abstract +This is part of the State Rent roadmap. This particular change introduces a fixed charge for state expansion that comes from adding new accounts to the state. Theoretically, it puts a bound on the number of accounts that can be ever created, because that fixed charge cannot be recycled via mining. + +## Motivation +The penalty is levied to the transaction sender. Rather than raising the gas cost of account creation (that would direct levy towards the miner), this change directs prepayment into the account's special field, `rentbalance`. It addresses several shortcomings of the simple raising of the gas cost: +1. Prepayments cannot be recycled via mining, which puts a theoretical bound on number of accounts in the state (though it is unlikely to ever be reached). +2. It is not possible for miners to circumvent the penalty or to extend such circumventions onto other users (via private fee rebates, for example). +3. This prepayment will be used to cover state rent in the future, and it will allow newly created contracts with 0 endowment not to be evicted in the same block. +4. It makes is possible to refund `rentbalance` upon self-destruction - when contract is self-destructed, both `balance` and `rentbalance` are returned. +5. Prepayments on pre-existing accounts are necessary to prevent hoarding of accounts ahead of this change. + +## Specification +On and after block `H`, every newly created account gets a new field `rentbalance` of type unsigned 256-bit integer. +On and after block `H`, any operation that leads to the creation of a new account, deducts the amount `ACCOUNT_PREPAYMENT` from `tx.origin`. This amount is added to the `rentbalance` field of the created account. +On and after block `H`, any operation that modifies an account that does not yet have `rentbalance` field, deducts the amount `ACCOUNT_PREPAYEMENT` from `tx.origin`. This amount is added to the `rentbalance` field of the modified account. This is a anti-hoarding measure. + +Operations leading to the creations of a new account: +1. Creation of a non-contract account by sending non-zero ETH to an address with no associated account +2. Creation of a non-contract account by the block with `coinbase` pointing to an address with no associated account +3. Creation of a non-contract account by `SELFDESTRUCT` with the argument being an address with no associated account +4. Creation of a contract by transaction without destination but with data. This can result in either converting a non-countract account into a contract account, or creation of a contract account. +5. Creation of a contract by execution of `CREATE` or `CREATE2`. This can result in either converting a non-countract account into a contract account, or creation of a contract account. + +After prepayments are introduced, there can be two reasons for ether to be deducted from `tx.origin`: purchasing and spending gas, and spending gas for prepayments. Gaslimit of a transaction currently plays a role of safety limit, where `gaslimit` * `gasprice` represents the maximum amount of wei the sender (`tx.origin`) authorises the transaction to deduct from its account. +After prepayments are introduced, `gaslimit` * `gasprice` will still represent the maximum amount of wei spend, but it will be used for both gas purchases and prepayments, as necessary. + +## Rationale +Prior to rent prepayments, other alternatives were considered: +1. Simple raising of the gas cost - discussed in the Motivation section. +1. In [first version of State Rent proposal](https://github.com/ledgerwatch/eth_state/blob/master/State_rent.pdf), there was no notion of extra levy upon account creation. It created a slight usability issue, where newly created contracts with 0 endowment would be evicted in the same block (when rent is introduced). It delays the benefits of the State Rent programme until the actual introduction of rent (in second or third hard-fork). +2. In the [second version of State Rent proposal](https://github.com/ledgerwatch/eth_state/blob/master/State_Rent_2.pdf), there was a notion of lock-up. It is very similar to rent prepayment, with the different that lock-up would not be covering future rent payments. + +An alternative approach to limiting the prepayments (instead of the using `gaslimit` * `gasprice` as the limit) is to introduce a new dedicated field `prepaymenlimit` into the transaction. This field would only limit prepayments). Such approach would require changes in the transaction format, as well as changes in the user interface for transaction sender, and having two counters during the transaction execution - one for gas, and one for prepayments. + +## Backwards Compatibility +This change is not backwards compatible and requires hard fork to be activated. +It might have some adverse effects on the existing contracts, due to more gas needed to be allocated for the creation of new accounts. These adverse effects need to analysed in more detail. + +## Test Cases +Tests cases will be generated out of a reference implementation. + +## Implementation +There will be proof of concept implementation to refine and clarify the specification. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2027.md b/EIPS/eip-2027.md new file mode 100644 index 0000000..9faf2ab --- /dev/null +++ b/EIPS/eip-2027.md @@ -0,0 +1,64 @@ +--- +eip: 2027 +title: State Rent C - Net contract size accounting +author: Alexey Akhunov (@AlexeyAkhunov) +discussions-to: https://ethereum-magicians.org/t/eip-2027-net-contract-size-accounting-change-c-from-state-rent-v3-proposal/3275 +status: Stagnant +type: Standards Track +category: Core +created: 2019-05-14 +--- + +## Simple Summary +Ethereum starts counting the number of storage slots filled and emptied in the contracts. Since the number of pre-existing slots is not currently accounted +in the state, effectively, only net change in the number of slots is tracked. In the subsequent change, called *Gross contract size accounting*, the total +number of storage slots starts being tracked. + +## Abstract +This is part of the State Rent roadmap. This particular change introduces initial, net accounting of the number of the contract storage slots. Though not very +useful on its own, it makes it possible to introduce gross accounting of the number of storage slots, which is useful for number of things: +1. Gas cost of operations suchs as `SLOAD` and `SSTORE` will need to be increased to compensate for extra bandwidth consumed by the block proofs. Although in +the beginning the cost would be fixed, it will later be automatically calibrated depending on the size of the contract `SLOAD` and `SSTORE` operate on. +2. Snapshot sync protocols, like *fast sync*, *warp sync*, *firehose*, *red queen*, and perhaps others, will benefit from having the correct size of the +contract storage present in the state (and therefore being provable via Merkle proofs). + +## Motivation +Ethereum currently does not track the number of contract storage slots at all, and producing such number given the downloaded state cannot be done in +constant *O(1)* time. + +## Specification +Each contract (account with `codeHash` field not equal to 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470, which the hash of the empty code) gets a new uint64 field, called `storagesize`. On and after block `C`, the semantics of the operation `SSTORE` (`location`, `value`) changes as follows: +- If previous value of the [`location`] is 0, and value is not 0, *increment* `storagesize` (semantics of *increment* described below) +- If previous value of the [`location`] is not 0, and value is 0, *decrement* `storagesize` (semantics of *decrement* described below) +- As with other state changes, changes of `storagesize` get reverted when the execution frame reverts, i.e. it needs to use the same techniques as storage values, like journalling (in Geth), and substates (in Parity). +Value of `storagesize` is not observable from contracts at this point. + +### Semantics of *increment* `storagesize` +If `storagesize` is not present, `storagesize` = `HUGE_NUMBER` + 1. +If `storagesize` is present, `storagesize` = `storagesize` + 1. + +### Semantics of *decrement* `storagesize` +If `storagesize` is not present, `storagesize` = `HUGE_NUMBER` - 1. +If `storagesize` is present, `storagesize` = `storagesize` - 1. + +### Note of `HUGE_NUMBER` +There is a constant `HUGE_NUMBER`. It needs to be large enough so that no real metrics (contract storage size, number of accounts, number of contracts, total size of code, total size of storage) will never reach that number, and small enough that it fits in an unsigned 64-bit integer. +Current suggestion is to have `HUGE_NUMBER` = 2^63, which is binary representation is the a single bit in a 64-bit number. + +The idea is to make it decidable later whether the storagesize was ever incremented/decremented (presence of the field), and whether it has been converted from net to gross (by value being smaller than `HUGE_NUMBER/2` - because it will not be possible for any contract be larger than 2^62 at the block `C`). + +## Rationale +A mechanism for estimation of contract storage size has been proposed [here](https://medium.com/@akhounov/estimation-approximate-of-the-size-of-contracst-in-ethereum-4642fe92d6fe). But it does have a big drawback of introducing a lot of complexity into the consensus +(in the form of estimation algorithm, which has quite a few edge cases to cater for different sizes of the storage). + +## Backwards Compatibility +This change is not backwards compatible and requires hard fork to be activated. Since the newly introduced field is not observable, this change does not impact any operations of the existing smart contracts. + +## Test Cases +Tests cases will be generated out of a reference implementation. + +## Implementation +There will be proof of concept implementation to refine and clarify the specification. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2028.md b/EIPS/eip-2028.md new file mode 100644 index 0000000..707e63c --- /dev/null +++ b/EIPS/eip-2028.md @@ -0,0 +1,79 @@ +--- +eip: 2028 +title: Transaction data gas cost reduction +author: Alexey Akhunov (@AlexeyAkhunov), Eli Ben Sasson , Tom Brand , Louis Guthmann , Avihu Levy +discussions-to: https://ethereum-magicians.org/t/eip-2028-calldata-gas-cost-reduction/3280 +status: Final +type: Standards Track +category: Core +created: 2019-05-03 +--- + +## Simple Summary +We propose to reduce the gas cost of Calldata (`GTXDATANONZERO`) from its current value of 68 gas per byte to 16 gas per byte, to be backed by mathematical modeling and empirical estimates. The mathematical model is the one used in the works of Sompolinsky and Zohar [1] and Pass, Seeman and Shelat [2], which relates network security to network delay. We shall (1) evaluate the theoretical impact of lower Calldata gas cost on network delay using this model, (2) validate the model empirically, and (3) base the proposed gas cost on our findings. + +## Motivation +There are a couple of main benefits to accepting this proposal and lowering gas cost of Calldata +On-Chain Scalability: Generally speaking, higher bandwidth of Calldata improves scalability, as more data can fit within a single block. +* Layer two scalability: Layer two scaling solutions can improve scalability by moving storage and computation off-chain, but often introduce data transmission instead. + - Proof systems such as STARKs and SNARKs use a single proof that attests to the computational integrity of a large computation, say, one that processes a large batch of transactions. + - Some solutions use fraud proofs which requires a transmission of merkle proofs. + - Moreover, one optional data availability solution to layer two is to place data on the main chain, via Calldata. +* Stateless clients: The same model will be used to determine the price of the state access for the stateless client regime, which will be proposed in the State Rent (from version 4). There, it is expected that the gas cost of state accessing operation will increase roughly proportional to the extra bandwidth required to transmit the “block proofs” as well as extra processing required to verify those block proofs. + +## Specification +The gas per non-zero byte is reduced from 68 to 16. Gas cost of zero bytes is unchanged. + +## Rationale +Roughly speaking, reducing the gas cost of Calldata leads to potentially larger blocks, which increases the network delay associated with data transmission over the network. This is only part of the full network delay, other factors are block processing time (and storage access, as part of it). Increasing network delay affects security by lowering the cost of attacking the network, because at any given point in time fewer nodes are updated on the latest state of the blockchain. + +Yonatan Sompolinsky and Aviv Zohar suggested in [1] an elegant model to relate network delay to network security, and this model is also used in the work of Rafael Pass, Lior Seeman and Abhi Shelat [2]. We briefly explain this model below, because we shall study it theoretically and validate it by empirical measurements to reach the suggested lower gas cost for Calldata. + +The model uses the following natural parameters: +* _lambda_ denotes the block creation rate [1/s]: We treat the process of finding a PoW +solution as a poisson process with rate _lambda_. +* _beta_ - chain growth rate [1/s]: the rate at which new blocks are added to +the heaviest chain. +* _D_ - block delay [s]: The time that elapses between the mining of a new block and its acceptance by all the miners (all miners switched to mining on top of that block). + +### _Beta_ Lower Bound +Notice that _lambda_ => _beta_, because not all blocks that are found will enter the main chain (as is the case with uncles). In [1] it was shown that for a blockchain using the longest chain rule, one may bound _beta_ from below by _lambda_/ (1+ D * _lambda_). This lower bound holds in the extremal case where the topology of the network is a clique in which the delay between each pair of nodes is D, the maximal possible delay. Recording both the lower and upper bounds on _beta_ we get + + _lambda_ >= _beta_ >= _lambda_ / (1 + D * _lambda_) (*) + +Notice, as a sanity check, that when there is no delay (D=0) then _beta_ equals _lambda_, as expected. + +### Security of the network +An attacker attempting to reorganize the main chain needs to generate blocks at a rate that is greater than _beta_. +Fixing the difficulty level of the PoW puzzle, the total hash rate in the system is correlated to _lambda_. Thus, _beta_ / _lambda_ is defined as the *efficiency* of the system, as it measures the fraction of total hash power that is used to generate the main chain of the network. + +Rearranging (*) gives the following lower bound on efficiency in terms of delay: + + _beta_ / _lambda_ >= 1 / (1 + D * _lambda_) (**) + +### The _delay_ parameter D +The network delay depends on the location of the mining node within the network and on the current network topology (which changes dynamically), and consequently is somewhat difficult to measure directly. +Previously, Christian Decker and Roger Wattenhofer [3] showed that propagation time scales with blocksize, and Vitalik Buterin showed that uncle rate, which is tightly related to efficiency (**) measure, also scales with block size [4]. + +However, the delay function can be decomposed into two parts D = *D_t* + *D_p*, where _D_t_ is the delay caused by the transmission of the block and _D_p_ is the delay caused by the processing of the block by the node. Our model and tests will examine the effect of Calldata on each of _D_t_ and _D_p_, postulating that their effect is different. This may be particularly relevant for Layer 2 Scalability and for Stateless Clients (Rationales 2, 3 above) because most of the Calldata associated with these goals are Merkle authentication paths that have a large _D_t_ component but relatively small _D_p_ values. + +## Test Cases +To suggest the gas cost of calldata we shall conduct two types of tests: +1. Network tests, conducted on the Ethereum mainnet, used to estimate the effect on increasing block size on _D_p_ and _D_t_, on the overall network delay D and the efficiency ratio (**), as well as delays between different mining pools. Those tests will include regression tests on existing data, and stress tests to introduce extreme scenarios. +2. Local tests, conducted on a single node and measuring the processing time as a function of Calldata amount and general computation limits. + +## Reference Implementation +[Parity](https://github.com/liorgold2/parity-ethereum/pull/1) +[Geth](https://github.com/liorgold2/go-ethereum/pull/1) + +## References +[1] Yonatan Sompolinsky, Aviv Zohar: [Secure High-Rate Transaction Processing in Bitcoin](https://eprint.iacr.org/2013/881.pdf). Financial Cryptography 2015: 507-527 + +[2] Rafael Pass, Lior Seeman, Abhi Shelat: [Analysis of the Blockchain Protocol in Asynchronous Networks](https://eprint.iacr.org/2016/454.pdf), ePrint report 2016/454 + +[3] Christian Decker, Roger Wattenhofer: [Information propagation in the Bitcoin network](https://www.gsd.inesc-id.pt/~ler/docencia/rcs1314/papers/P2P2013_041.pdf). P2P 2013: 1-10 + +[4] Vitalik Buterin: [Uncle Rate and Transaction Fee Analysis](https://blog.ethereum.org/2016/10/31/uncle-rate-transaction-fee-analysis/) + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2029.md b/EIPS/eip-2029.md new file mode 100644 index 0000000..d3cacaa --- /dev/null +++ b/EIPS/eip-2029.md @@ -0,0 +1,51 @@ +--- +eip: 2029 +title: State Rent A - State counters contract +author: Alexey Akhunov (@AlexeyAkhunov) +discussions-to: https://ethereum-magicians.org/t/eip-2029-state-counters-contract-change-a-from-state-rent-v3-proposal/3279 +status: Stagnant +type: Standards Track +category: Core +created: 2019-05-15 +--- + +## Simple Summary +A smart contract is deployed on all Ethereum networks, at a pre-determined address, with the code that simply reads the slot in its storage specified by the +only parameter. Later, this contract becomes "special" in that Ethereum start writing state counters (number of total transactions, number of accounts, +etc.) into that contract. + +## Abstract +This is part of the State Rent roadmap. This particular change introduces a place in the Ethereum state where various state counters can be stored. At this +point, the most important counter is the total number of transactions happened, and this counter will be used to populate the nonces of newly created +non-contract accounts. This way of populating nonce ensures replay protection for accounts that were evicted and then brought back by sending ether to them. + +## Motivation +Ethereum currently does not have a special place in the state for tracking state counters such as number of transactions or number of accounts. + +## Specification +Prior to the block A, a contract is deployed with the following code: +`0x60 0x20 0x60 0x00 0x80 0x80 0x35 0x54 0x90 0x52 0xF3`, which corresponds to this assembly: +`PUSH1 32 PUSH1 0 DUP1 DUP1 CALLDATALOAD SLOAD SWAP1 MSTORE RETURN` +Call to this contract accepts one 32-byte argument, `x`, and returns the value of the storage item [`x`]. + +This contract is deployed using `CREATE2` opcode in such a way that it has the same address on any network. + +## Rationale +Two alternative solutions were considered so far: +1. Extending the structure of the Ethereum state to introduce more fields, and hence change the way the state root is constructed. The main downside of this +approach is the impact on the software what is currently coupled with the particular way the state root is constructed. Particularly it affects the software +that deals with merkle proofs derived from the state root. +2. Extended State Oracle ([EIP-2014](./eip-2014.md)). Under such proposal, there will be a precompile contract with standardised interface, capable of returning +current values of the counters. However, the actual data being returned by such oracle is not explicitly in the state, and is not Merkelised. It means that all the counters need to be added to the snapshots when the snapshot sync is perform, so they still present in the state, but implicitly. + +## Backwards Compatibility +This change is backwards compatible and does not require hard fork to be activated. + +## Test Cases +Tests cases will be created to ensure that the state counter contract returns its storage items correctly. + +## Implementation +Implementation is envisaged as a transaction that can be posted from any Ethereum address and will cause the deployment of the state counter contract. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2031.md b/EIPS/eip-2031.md new file mode 100644 index 0000000..ceb2723 --- /dev/null +++ b/EIPS/eip-2031.md @@ -0,0 +1,51 @@ +--- +eip: 2031 +title: State Rent B - Net transaction counter +author: Alexey Akhunov (@AlexeyAkhunov) +discussions-to: https://ethereum-magicians.org/t/eip-2031-net-transaction-counter-change-b-from-state-rent-v3-proposal/3283 +status: Stagnant +type: Standards Track +category: Core +created: 2019-05-15 +requires: 2029 +--- + +## Simple Summary +Ethereum starts to track the number of transactions inside its state (for now, only number of transactions after this change is introduced, therefore +it is called *Net* transaction count). +It is done by incrementing a storage slot in the special contract, called *State counter contract* ([EIP-2029](./eip-2029.md)). + +## Abstract +It is part of the State Rent roadmap. This particular change makes any Ethereum transaction increment the transaction counter, which is a special storage slot +in the *State counter contract*. This counter will be used to populate the nonces of newly created +non-contract accounts. This way of populating nonce ensures replay protection for accounts that were evicted and then brought back by sending ether to them. + +## Motivation +Ethereum currently does not have a special place in the state for tracking number of transactions. + +## Specification +A new field, with the location 0 (that means it resides in the storage slot 0 in the state counter contract, and can +be read by calling that contract with argument being 32 zero bytes), is added to the state counter contract. It will eventually contain `txCount`, the total number of transactions processed up until that point. +On an after block B, or after the deployment of the state counter contract (which comes first), the field `txCount` is incremented after each transaction. Updating `txCount` means updating the storage of state counter contract at the location 0. These changes are never reverted. + +## Rationale +Two main alternatives were proposed for the replay protection of the accounts that were evicted by subsequently brought back by sending ether to them: +1. Temporal replay protection. The nonce of the new accounts (and those brought back) is still zero, but a new `valid-until` field is introduced, making +transactions invalid for inclusion after the time specified in this field. This, however, has unwanted side effected related to the fact that account +nonces are not only used for replay protection, but also for computing the addresses of the deployed contracts (except those created by `CREATE2`). +2. Setting nonce of new accounts (and those brought back) to something depending on the current block number. This approach requires coming up with +an arbitrary parameter, which is the maximum number of transaction in the block, so that the new nonces never clash with the existing nonces. +This is mostly a concern for private networks at the moment, because they will potentially have significantly more transactions in a block. + +## Backwards Compatibility +This change is not backwards compatible and requires hard fork to be activated. + +## Test Cases +Tests cases will be generated out of a reference implementation. + +## Implementation +There will be proof of concept implementation to refine and clarify the specification. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). + diff --git a/EIPS/eip-2035.md b/EIPS/eip-2035.md new file mode 100644 index 0000000..11342e6 --- /dev/null +++ b/EIPS/eip-2035.md @@ -0,0 +1,71 @@ +--- +eip: 2035 +title: Stateless Clients - Repricing SLOAD and SSTORE to pay for block proofs +author: Alexey Akhunov (@AlexeyAkhunov) +discussions-to: https://ethereum-magicians.org/t/eip-2035-stateless-clients-repricing-sload-and-sstore-to-pay-for-block-proofs/3284 +status: Stagnant +type: Standards Track +category: Core +created: 2019-05-16 +--- + +## Simple Summary +The gas cost of EVM opcodes `SLOAD` and `SSTORE` increases in order to accommodate extra bandwidth required to propagate block proof together with the block +headers and block bodies, as explained [here](https://medium.com/@akhounov/data-from-the-ethereum-stateless-prototype-8c69479c8abc). + +## Abstract +It is part of the State Rent roadmap. This particular change prepares Ethereum for introduction of the block proofs (current understanding is that they +can be introuced without a hard fork). The introduction of the block proofs allows any Ethereum node that wishes to receive them, to process transactions +in the blocks without needing to access the Ethereum state. All necessary information for the execution (and the proof of validity) is continued in the +block proofs. In most Ethereum nodes, it will speed up the block processing and reduce the memory footprint of such processing. For mining nodes, however, +there will be more work to do to construct and transmit the block proofs. Therefore, the extra charge (payable to the miners) is introduced. In the first +phase, only contract storage will be covered by the block proofs. It means that the Ethereum nodes will still need access to the accounts in the state, +but block proofs will make it optional to have access to contract storage for executing transactions. Therefore, only `SSTORE` and `SLOAD` opcodes are +affected. + +## Motivation +There is [empirical analysis](https://github.com/holiman/vmstats/blob/master/README.md) showing that `SLOAD` opcode is currently underpriced in terms +of execution latency it adds to the block processing. The hypothesis is that it is due to the latency of the database accesses. In the same +analysis, `SSTORE` is not considered, because its effect on the database accesses can be (and are in many implementations) delayed until the end of +the block. Stateless clients approach to the contract storage will largely negate that latency because no database accesses will be required. +Instead, bandwidth consumption goes up. There is emprical analysis (unpublished, but will be) suggesting that 1 uncached `SSTORE` or `SLOAD` adds +at most 1 kB to the block proofs. At the current cost of data transmission (68 gas per byte), this translates to the increase of gas cost of both +operations by 69k gas. However, in light of proposal in [EIP-2028](./eip-2028.md), the increase can be made much smaller. + +## Specification +Not very formal at the moment, but will be formalised with more research and prototyping. Gas of operations `SLOAD` and `SSTORE` increases by `X` gas when the storage slots accessed (read by `SLOAD` or written by `SSTORE`) were not previously accessed (by another `SLOAD` or `SSTORE`) during the same transaction. + +Future variant (will be possible after the implementation of the *Gross contract size acccounting*) is researched, where the increase is varied +depending on the size of the contract storage, i.e. `SLOAD` and `SSTORE` for smaller contracts will be cheaper. + +## Rationale +[EIP-1884](./eip-1884.md) seeks to increase the gas cost of `SLOAD` but using a different justification +(latency of the execution as described in the Motivation). This EIP is likely to increase the cost of `SLOAD` by a larger amount, therefore partially +(because EIP-1884 also proposed other increases) supersedes EIP-1884. + +[EIP-2028](./eip-2028.md) describes the model that can be used for deciding the gas cost of data transmission. It is relevant +because in the stateless client regime `SSTORE` and `SLOAD` operations add more data to be transmitted (as well as computation to verify the proofs). + +The main alternate design is the rent proportional to the size of the contract storage, which unfortunately introduces a serious griefing +vulnerability problem, and so far the solution seems to be in redesigning and rewriting smart contracts in a way, which makes them not vulnerable. +However, this approach is likely to be very expensive on the non-technical (ecosystem) level. + +## Backwards Compatibility +This change is not backwards compatible and requires hard fork to be activated. +There might also be an adverse effect of this change on the already deployed contract. It is expected that after this EIP and +[EIP-2026](./eip-2026.md) (rent prepayment for accounts), the recommendation will be made to raise the gas limit. This can somewhat dampen the +adverse effect of EIP. The most problematic cases would be with the contracts that assume certain gas costs of `SLOAD` +and `SSTORE` and hard-code them in their internal gas computations. For others, the cost of interacting with the contract +storage will rise and may make some dApps based on such interactions, non-viable. This is a trade off to avoid even bigger +adverse effect of the rent proportional to the contract storage size. However, more research is needed to more fully +analyse the potentially impacted contracts. + +## Test Cases +Tests cases will be generated out of a reference implementation. + +## Implementation +There will be proof of concept implementation to refine and clarify the specification. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). + diff --git a/EIPS/eip-2045.md b/EIPS/eip-2045.md new file mode 100644 index 0000000..e5a8b60 --- /dev/null +++ b/EIPS/eip-2045.md @@ -0,0 +1,121 @@ +--- +eip: 2045 +title: Particle gas costs for EVM opcodes +author: Casey Detrio (@cdetrio), Alex Beregszaszi (@axic) +discussions-to: https://ethereum-magicians.org/t/eip-2045-fractional-gas-costs/3311 +status: Stagnant +type: Standards Track +category: Core +created: 2019-05-17 +--- + +## Abstract +According to recent benchmarks, EVM opcodes for computation (`ADD`, `SUB`, `MUL`, etc.) are generally overpriced relative to opcodes for storage I/O (`SLOAD`, `SSTORE`, etc.). Currently the minimum gas cost is 1 (i.e. one unit of gas), and most computational opcodes have a cost near to 1 (e.g. 3, 5, or 8), so the range in possible cost reduction is limited. A new minimum unit of gas, called a "particle", which is a fraction of 1 gas, would expand the range of gas costs and thus enable reductions below the current minimum. + +## Motivation +The transaction capacity of an Ethereum block is determined by the gas cost of transactions relative to the block gas limit. One way to boost the transaction capacity is to raise the block gas limit. Unfortunately, raising the block gas limit would also increase the rate of state growth, unless the costs of state-expanding storage opcodes (`SSTORE`, `CREATE`, etc.) are simultaneously increased to the same proportion. Increasing the cost of storage opcodes may have adverse side effects, such as shifting the economic assumptions around gas fees of deployed contracts, or possibly breaking invariants in current contract executions (as mentioned in [EIP-2035](./eip-2035.md)[1](#eip2035), more research is needed on the potential effects of increasing the cost of storage opcodes). + +Another way to boost the transaction capacity of a block is to reduce the gas cost of transactions. Reducing the gas costs of computational opcodes while keeping the cost of storage opcodes the same, is effectively equivalent to raising the block gas limit and simultaneously increasing the cost of storage opcodes. However, reducing the cost of computational opcodes might avoid the adverse side effects of an increase in cost of storage opcodes (again, more research is needed on this topic). + +Currently, computational opcode costs are already too close to the minimum unit of 1 gas to achieve the large degree of cost reductions that recent benchmarks[2](#evmbenchmarks) indicate would be needed to tune opcode gas costs to the performance of optimized EVM implementations. A smaller minimum unit called a "particle", which is a fraction (or subdivision) of 1 gas, would enable large cost reductions. + +## Specification +A new gas counter `particlesUsed` is added to the EVM, in addition to the existing gas counter `gasUsed`. The unit 1 gas is equal to 10000 particles (`PARTICLES_PER_GAS`). The `particlesUsed` counter is only increased for opcodes priced in particles (i.e. opcodes that cost less than 1 gas). If increasing `particlesUsed` results in an excess of 1 gas, then 1 gas is added to `gasUsed` (and deducted from `particlesUsed`). + +Where the current gas logic looks like this: +```python +def vm_execute(ext, msg, code): + # Initialize stack, memory, program counter, etc + compustate = Compustate(gas=msg.gas) + codelen = len(code) + + while compustate.pc < codelen: + opcode = code[compustate.pc] + compustate.pc += 1 + + compustate.gasUsed += opcode.gas_fee + + # out of gas error + if compustate.gasUsed > compustate.gasLimit: + return vm_exception('OUT OF GAS') + + if op == 'STOP': + return peaceful_exit() + elif op == 'ADD': + stk.append(stk.pop() + stk.pop()) + elif op == 'SUB': + stk.append(stk.pop() - stk.pop()) + elif op == 'MUL': + stk.append(stk.pop() * stk.pop()) + +..... +``` + +The new gas logic using particles might look like this: +```python +PARTICLES_PER_GAS = 10000 + +def vm_execute(ext, msg, code): + # Initialize stack, memory, program counter, etc + compustate = Compustate(gas=msg.gas) + codelen = len(code) + + while compustate.pc < codelen: + opcode = code[compustate.pc] + compustate.pc += 1 + + if opcode.gas_fee: + compustate.gasUsed += opcode.gas_fee + elif opcode.particle_fee: + compustate.particlesUsed += opcode.particle_fee + if compustate.particlesUsed >= PARTICLES_PER_GAS: + # particlesUsed will be between 1 and 2 gas (over 10000 but under 20000) + compustate.gasUsed += 1 + # remainder stays in particle counter + compustate.particlesUsed = compustate.particlesUsed % PARTICLES_PER_GAS + + # out of gas error + if compustate.gasUsed > compustate.gasLimit: + return vm_exception('OUT OF GAS') + + if op == 'STOP': + return peaceful_exit() + elif op == 'ADD': + stk.append(stk.pop() + stk.pop()) + elif op == 'SUB': + stk.append(stk.pop() - stk.pop()) + elif op == 'MUL': + stk.append(stk.pop() * stk.pop()) + +..... +``` + +The above pseudocode is written for clarity. A more performant implementation might instead keep a single `particlesUsed` counter by multiplying opcode gas costs by 10000 and the `gasLimit` by 10000, and convert particles back to gas with `ceil(particlesUsed / PARTICLES_PER_GAS)` at the end of execution. It may also be more performant to use a `PARTICLES_PER_GAS` ratio that is a power of 2 (such as 8192 or 16384) instead of 10000; the spec above is a draft and updates in response to feedback are expected. + +#### Opcode cost changes +Many computational opcodes will undergo a cost reduction, with new costs suggested by benchmark analyses. For example, the cost of `DUP` and `SWAP` are reduced from 3 gas to 3000 particles (i.e. 0.3 gas). The cost of `ADD` and `SUB` are reduced from 3 gas to 6000 particles. The cost of `MUL` is reduced from 5 gas to 5000 particles (i.e. 0.5 gas). + +## Rationale +Adoption of fractional gas costs should only be an implementation detail inside the EVM, and not alter the current user experience around transaction gas limits and block gas limits. The concept of `particles` need not be exposed to Ethereum users nor most contract authors, but only to EVM implementers and contract developers concerned with optimized gas usage. Furthermore, only the EVM logic for charging gas per opcode executed should be affected by this change. All other contexts dealing with gas and gas limits, such as block headers and transaction formats, should be unaffected. + +### Ewasm +The term "particles" was first introduced for Ewasm[3](#particle) to enable gas accounting for low cost wasm instructions, while remaining compatible with EVM gas costs. This EIP proposes introducing particles as a new minimum gas unit for EVM opcodes, and is not related to Ewasm. + +## Backwards Compatibility +This change is not backwards compatible and requires a hard fork to be activated. + +## Test Cases +TODO + +## Implementation +TODO + +## References +1. [EIP-2035](./eip-2035.md): Stateless Clients - Repricing SLOAD and SSTORE to pay for block proofs + +2. https://github.com/ewasm/benchmarking + +3. The term "particle" was inspired by a proposal for [Ewasm gas costs](https://github.com/ewasm/design/blob/e77d8e3de42784f40a803a23f58ef06881142d9f/determining_wasm_gas_costs.md). + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2046.md b/EIPS/eip-2046.md new file mode 100644 index 0000000..91171d0 --- /dev/null +++ b/EIPS/eip-2046.md @@ -0,0 +1,69 @@ +--- +eip: 2046 +title: Reduced gas cost for static calls made to precompiles +author: Alex Beregszaszi (@axic) +discussions-to: https://ethereum-magicians.org/t/eip-2046-reduced-gas-cost-for-static-calls-made-to-precompiles/3291 +status: Stagnant +type: Standards Track +category: Core +created: 2019-05-17 +requires: 214, 1352 +--- + +## Simple Summary + +This change reduces the gas cost of using precompiled contracts. + +## Abstract + +Reduce the base gas cost of calling precompiles using `STATICCALL` from 700 to 40. This should allow more efficient use of precompiles as well as precompiles with a total cost below 700. + +## Motivation + +The Spurious Dragon hard fork increased the cost of calls significantly to account for loading contract code from the state without making an exception for precompiles, whose "code" is always loaded. + +This made use of certain precompiles impractical. + +FIXME: extend this with recent reasoning about ECC repricings. + +## Specification + +After block `HF` the `STATICCALL` (`0xfa`) instruction charges different basic gas cost (Gcall in [Yellow Paper]'s notation) depending on the destination address provided: +- for precompiles (address range as per [EIP-1352]) the cost is `40` +- for every other address the cost remains unchanged (`700`) + +## Rationale + +Only the `STATICCALL` instruction was changed to reduce the impact of the change. This should not be a limiting factor, given precompiles (currently) do not have a state and cannot change the state. +However, contracts created and deployed before Byzantium likely will not use `STATICCALL` and as a result this change will not reduce their costs. + +Contrary to EIP-1109 gas reduction to `0` is not proposed. The cost `40` is kept as a cost representing the context switching needed. + +## Backwards Compatibility + +This EIP should be backwards compatible. The only effect is that the cost is reduced. Since the cost is not reduced to zero, it should not be possible for a malicious proxy contract, when deployed before +the `HF`, to do any state changing operation. + +## Test Cases + +TBA + +## Implementation + +TBA + +## References + +This has been previously suggested as part of [EIP-1109](./eip-1109.md) and [EIP-1231](https://github.com/ethereum/EIPs/pull/1231). +However EIP-1109 was later changed to a very different approach. The author [has suggested to change EIP-1109](https://ethereum-magicians.org/t/eip-1109-remove-call-costs-for-precompiled-contracts/447/7). + +## Acknowledgements + +Jordi Baylina (@jbaylina) and Matthew Di Ferrante (@mattdf) who have proposed this before. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). + +[Yellow Paper]: https://github.com/ethereum/yellowpaper +[EIP-1352]: ./eip-1352.md diff --git a/EIPS/eip-205.md b/EIPS/eip-205.md new file mode 100644 index 0000000..451cc83 --- /dev/null +++ b/EIPS/eip-205.md @@ -0,0 +1,69 @@ +--- +eip: 205 +title: ENS support for contract ABIs +author: Nick Johnson +type: Standards Track +category: ERC +status: Stagnant +created: 2017-02-06 +requires: 137, 181 +--- + +## Simple Summary +This EIP proposes a mechanism for storing ABI definitions in ENS, for easy lookup of contract interfaces by callers. + +## Abstract +ABIs are important metadata required for interacting with most contracts. At present, they are typically supplied out-of-band, which adds an additional burden to interacting with contracts, particularly on a one-off basis or where the ABI may be updated over time. The small size of ABIs permits an alternative solution, storing them in ENS, permitting name lookup and ABI discovery via the same process. + +ABIs are typically quite compact; the largest in-use ABI we could find, that for the DAO, is 9450 bytes uncompressed JSON, 6920 bytes uncompressed CBOR, and 1128 bytes when the JSON form is compressed with zlib. Further gains on CBOR encoding are possible using a CBOR extension that permits eliminating repeated strings, which feature extensively in ABIs. Most ABIs, however, are far shorter than this, consisting of only a few hundred bytes of uncompressed JSON. + +This EIP defines a resolver profile for retrieving contract ABIs, as well as encoding standards for storing ABIs for different applications, allowing the user to select between different representations based on their need for compactness and other considerations such as onchain access. + +## Specification +### ABI encodings +In order to allow for different tradeoffs between onchain size and accessibility, several ABI encodings are defined. Each ABI encoding is defined by a unique constant with only a single bit set, allowing for the specification of 256 unique encodings in a single uint. + +The currently recognised encodings are: + +| ID | Description | +|----|----------------------| +| 1 | JSON | +| 2 | zlib-compressed JSON | +| 4 | CBOR | +| 8 | URI | + +This table may be extended in future through the EIP process. + +Encoding type 1 specifies plaintext JSON, uncompressed; this is the standard format in which ABIs are typically encoded, but also the bulkiest, and is not easily parseable onchain. + +Encoding type 2 specifies zlib-compressed JSON. This is significantly smaller than uncompressed JSON, and is straightforward to decode offchain. However, it is impracticalfor onchain consumers to use. + +Encoding type 4 is [CBOR](https://cbor.io/). CBOR is a binary encoding format that is a superset of JSON, and is both more compact and easier to parse in limited environments such as the EVM. Consumers that support CBOR are strongly encouraged to also support the [stringref extension](http://cbor.schmorp.de/stringref) to CBOR, which provides significant additional reduction in encoded size. + +Encoding type 8 indicates that the ABI can be found elsewhere, at the specified URI. This is typically the most compact of the supported forms, but also adds external dependencies for implementers. The specified URI may use any schema, but HTTP, IPFS, and Swarm are expected to be the most common. + +### Resolver profile +A new resolver interface is defined, consisting of the following method: + + function ABI(bytes32 node, uint256 contentType) constant returns (uint256, bytes); + +The interface ID of this interface is 0x2203ab56. + +contentType is a bitfield, and is the bitwise OR of all the encoding types the caller will accept. Resolvers that implement this interface must return an ABI encoded using one of the requested formats, or `(0, "")` if they do not have an ABI for this function, or do not support any of the requested formats. + +The `abi` resolver profile is valid on both forward and reverse records. + +### ABI lookup process + +When attempting to fetch an ABI based on an ENS name, implementers should first attempt an ABI lookup on the name itself. If that lookup returns no results, they should attempt a reverse lookup on the Ethereum address the name resolves to. + +Implementers should support as many of the ABI encoding formats as practical. + +## Rationale + +Storing ABIs onchain avoids the need to introduce additional dependencies for applications wishing to fetch them, such as swarm or HTTP access. Given the typical compactness of ABIs, we believe this is a worthwhile tradeoff in many cases. + +The two-step resolution process permits different names to provide different ABIs for the same contract, such as in the case where it's useful to provide a minimal ABI to some callers, as well as specifying ABIs for contracts that did not specify one of their own. The fallback to looking up an ABI on the reverse record permits contracts to specify their own canonical ABI, and prevents the need for duplication when multiple names reference the same contract without the need for different ABIs. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2069.md b/EIPS/eip-2069.md new file mode 100644 index 0000000..14d8d52 --- /dev/null +++ b/EIPS/eip-2069.md @@ -0,0 +1,105 @@ +--- +eip: 2069 +title: Recommendation for using YAML ABI in ERCs/EIPs +author: Alex Beregszaszi (@axic) +discussions-to: https://ethereum-magicians.org/t/eip-2069-recommendation-for-using-yaml-abi-in-specifications/3347 +status: Stagnant +type: Informational +created: 2017-02-11 +--- + +## Simple Summary + +Recommendation for including contract ABI descriptions in EIPs and ERCs as YAML. + +## Motivation + +In the past, most ERCs/EIPs included an ABI description purely as a Solidity contract and/or interface. This has several drawbacks: +- Prefers a single language over others and could hinder the development of new languages. +- Locks the specification to a certain version of the Solidity language. +- Allows the use of syntactical elements and features of the Solidity language, which may not be well representable in the ABI. This puts other languages at even more disadvantage. + +This proposal aims to solve all these issues. + +## Specification + +The [Standard Contract ABI] is usually represented as a JSON object. This works well and several tools – including compilers and clients – support it to handle data encoding. + +One shortcoming of the JSON description is its inability to contain comments. To counter this, we suggest the use of YAML for providing user readable specifications. Given YAML was designed to be compatible with JSON, several tools exists to convert between the two formats. + +The following example contains a single function, `transfer` with one input and one output in YAML: + +```yaml +# The transfer function. Takes the recipient address +# as an input and returns a boolean signaling the result. +- name: transfer + type: function + payable: false + constant: false + stateMutability: nonpayable + inputs: + - name: recipient + type: address + - name: amount + type: uint256 + outputs: + - name: '' + type: bool +``` + +Specifications are encouraged to include comments in the YAML ABI. + +For details on what fields and values are valid in the ABI, please consult the [Standard Contract ABI] specification. + +The same in JSON: + +```json +[ + { + "name": "transfer", + "type": "function", + "payable": false, + "constant": false, + "stateMutability": "nonpayable", + "inputs": [ + { + "name": "recipient", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "bool" + } + ] + } +] +``` + +## Rationale + +The aim was to chose a representation which is well supported by tools and supports comments. While inventing a more concise description language seems like a good idea, it felt as an unnecessary layer of complexity. + +## Backwards Compatibility + +This has no effect on backwards compatibility. + +## Test Cases + +TBA + +## Implementation + +[yamabi] is a Javascript tool to convert between the above YAML and the more widely used JSON format. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). + +[Standard Contract ABI]: https://solidity.readthedocs.io/en/latest/abi-spec.html +[yamabi]: https://github.com/axic/yamabi/ diff --git a/EIPS/eip-2070.md b/EIPS/eip-2070.md new file mode 100644 index 0000000..6ed8df9 --- /dev/null +++ b/EIPS/eip-2070.md @@ -0,0 +1,25 @@ +--- +eip: 2070 +title: "Hardfork Meta: Berlin" +author: Alex Beregszaszi (@axic) +discussions-to: https://ethereum-magicians.org/t/hardfork-meta-eip-2070-berlin-discussion/3561 +type: Meta +status: Stagnant +created: 2019-05-20 +requires: 1679 +--- + +## Abstract + +This meta-EIP specifies the changes included in the Ethereum hardfork named Berlin. + +## Specification + +- Codename: Berlin + +In the current stage of coordination, the changes are tracked and discussed in the [eth1.0-specs](https://github.com/ethereum/eth1.0-specs) repository. +For an accurate status please refer to the [`berlin.md`](https://github.com/ethereum/eth1.0-specs/blob/master/network-upgrades/mainnet-upgrades/berlin.md) file. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2098.md b/EIPS/eip-2098.md new file mode 100644 index 0000000..f440b40 --- /dev/null +++ b/EIPS/eip-2098.md @@ -0,0 +1,138 @@ +--- +eip: 2098 +title: Compact Signature Representation +description: A compact representation of an Ethereum Signature. +status: Final +type: Standards Track +category: ERC +author: Richard Moore (@ricmoo), Nick Johnson +discussions-to: https://github.com/ethereum/EIPs/issues/2440 +created: 2019-03-14 +requires: 2 +--- + + +## Abstract + +The secp256k1 curve permits the computation of the public key of signed +digest when coupled with a signature, which is used implicitly to +establish the origin of a transaction from an Externally Owned Account +as well as on-chain in EVM contracts for example, in meta-transactions and +multi-sig contracts. + +Currently signatures require 65 bytes to represent, which when aligned +to 256-bit words, requires 96 bytes (with 31 zero bytes injected). The +yParity in RLP-encoded transactions also require (on average) 1.5 bytes. +With compact signatures, this can be reduced to 64 bytes, which remains 64 +bytes when word-aligned, and in the case of RLP-encoded transactions +saves the 1.5 bytes required for the yParity. + +## Motivation + +The motivations for a compact representation are to simplify handling +transactions in client code, reduce gas costs and reduce transaction sizes. + + +## Specification + +A secp256k1 signature is made up of 3 parameters, `r`, `s` and `yParity`. +The `r` represents the `x` component on the curve (from which the `y` can be +computed), and the `s` represents the challenge solution for signing by a +private key. Due to the symmetric nature of an elliptic curve, a `yParity` +is required, which indicates which of the 2 possible solutions was intended, +by indicating its parity (odd-ness). + +Two key observations are required to create a compact representation. + +First, the `yParity` parameter is always either 0 or 1 (canonically the values +used have historically been 27 and 28, as these values didn't collide with other +binary prefixes used in Bitcoin). + +Second, the top bit of the `s` parameters is **always** 0, due to the use of +canonical signatures which flip the solution parity to prevent negative values, +which was introduced as [a constraint in Homestead](./eip-2.md). + +So, we can hijack the top bit in the `s` parameter to store the value of +`yParity`, resulting in: + +``` +[256-bit r value][1-bit yParity value][255-bit s value] +``` + + +### Example Implementation In Python + +```python +# Assume yParity is 0 or 1, normalized from the canonical 27 or 28 +def to_compact(r, s, yParity): + return { + "r": r, + "yParityAndS": (yParity << 255) | s + } + +def to_canonical(r, yParityAndS): + return { + "r": r, + "s": yParityAndS & ((1 << 255) - 1), + "yParity": (yParityAndS >> 255) + } +``` + + +## Rationale + +The compact representation proposed is simple to both compose and decompose +in clients and in Solidity, so that it can be easily (and intuitively) supported, +while reducing transaction sizes and gas costs. + + +## Backwards Compatibility + +The Compact Representation does not collide with canonical signature as +it uses 2 parameters (r, yParityAndS) and is 64 bytes long while canonical +signatures involve 3 separate parameters (r, s, yParity) and are 65 bytes long. + + +## Test Cases + +``` +Private Key: 0x1234567890123456789012345678901234567890123456789012345678901234 +Message: "Hello World" +Signature: + r: 0x68a020a209d3d56c46f38cc50a33f704f4a9a10a59377f8dd762ac66910e9b90 + s: 0x7e865ad05c4035ab5792787d4a0297a43617ae897930a6fe4d822b8faea52064 + v: 27 +Compact Signature: + r: 0x68a020a209d3d56c46f38cc50a33f704f4a9a10a59377f8dd762ac66910e9b90 + yParityAndS: 0x7e865ad05c4035ab5792787d4a0297a43617ae897930a6fe4d822b8faea52064 +``` + +``` +Private Key: 0x1234567890123456789012345678901234567890123456789012345678901234 +Message: "It's a small(er) world" +Signature: + r: 0x9328da16089fcba9bececa81663203989f2df5fe1faa6291a45381c81bd17f76 + s: 0x139c6d6b623b42da56557e5e734a43dc83345ddfadec52cbe24d0cc64f550793 + v: 28 +Compact Signature: + r: 0x9328da16089fcba9bececa81663203989f2df5fe1faa6291a45381c81bd17f76 + yParityAndS: 0x939c6d6b623b42da56557e5e734a43dc83345ddfadec52cbe24d0cc64f550793 +``` + + +## Reference Implementation + +The ethers.js library [supports this in v5](https://github.com/ethers-io/ethers.js/blob/ethers-v5-beta/packages/bytes/src.ts/index.ts#L323) +as an unofficial property of split signatures (i.e. `sig._vs`), but should be +considered an internal property that may change at discretion of the community +and any changes to this EIP. + + +## Security Considerations + +There are no additional security concerns introduced by this EIP. + + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-210.md b/EIPS/eip-210.md new file mode 100644 index 0000000..ecb4e37 --- /dev/null +++ b/EIPS/eip-210.md @@ -0,0 +1,86 @@ +--- +eip: 210 +title: Blockhash refactoring +author: Vitalik Buterin (@vbuterin) +type: Standards Track +category: Core +status: Stagnant +created: 2017-02-10 +--- + +### Summary + +Stores blockhashes in the state, reducing the protocol complexity and the need for client implementation complexity in order to process the BLOCKHASH opcode. Also extends the range of how far back blockhash checking can go, with the side effect of creating direct links between blocks with very distant block numbers, facilitating much more efficient initial light client syncing. + +### Parameters + +* `CONSTANTINOPLE_FORK_BLKNUM`: TBD +* `SUPER_USER`: 2**160 - 2 +* `BLOCKHASH_CONTRACT_ADDR`: 0xf0 (ie. 240) +* `BLOCKHASH_CONTRACT_CODE`: see below + +### Specification + +If `block.number == CONSTANTINOPLE_FORK_BLKNUM`, then when processing the block, before processing any transactions set the code of BLOCKHASH_CONTRACT_ADDR to BLOCKHASH_CONTRACT_CODE. + +If `block.number >= CONSTANTINOPLE_FORK_BLKNUM`, then when processing a block, before processing any transactions execute a call with the parameters: + +* `SENDER`: SUPER_USER +* `GAS`: 1000000 +* `TO`: BLOCKHASH_CONTRACT_ADDR +* `VALUE`: 0 +* `DATA`: <32 bytes corresponding to the block's prevhash> + +If `block.number >= CONSTANTINOPLE_FORK_BLKNUM + 256`, then the BLOCKHASH opcode instead returns the result of executing a call (NOT a transaction) with the parameters: + +* `SENDER`: +* `GAS`: 1000000 +* `TO`: BLOCKHASH_CONTRACT_ADDR +* `VALUE`: 0 +* `DATA`: 32 byte zero-byte-leftpadded integer representing the stack argument with which the opcode was called + +Also, for blocks where `block.number >= CONSTANTINOPLE_FORK_BLKNUM`, the gas cost is increased from 20 to 800 to reflect the higher costs of processing the algorithm in the contract code. + +### BLOCKHASH_CONTRACT_CODE + +The Serpent source code is: + +```python +with offset = 0: + if msg.sender == 0xfffffffffffffffffffffffffffffffffffffffe: + with bn = block.number - 1: + while bn: + ~sstore(offset + ~mod(bn, 256), ~calldataload(0)) + if ~mod(bn, 256): + ~stop() + bn = ~div(bn, 256) + offset += 256 + elif ~calldataload(0) >= 0 and ~calldataload(0) < block.number: + with tbn = ~calldataload(0): + with dist_minus_one = block.number - tbn - 1: + while dist_minus_one >= 256 && ~mod(tbn, 256) == 0: + offset += 256 + tbn = ~div(tbn, 256) + dist_minus_one = ~div(dist_minus_one, 256) + if dist_minus_one >= 256: + return(0) + return(~sload(offset + ~mod(tbn, 256))) + else: + return(0) +``` + +The EVM init code is: + +``` +0x6100f58061000e60003961010356600073fffffffffffffffffffffffffffffffffffffffe33141561005857600143035b801561005257600035610100820683015561010081061561003f57005b6101008104905061010082019150610022565b506100f3565b600060003512151561006e574360003512610071565b60005b156100e7576000356001814303035b6101008112151561009857600061010083061461009b565b60005b156100ba57610100830192506101008204915061010081049050610080565b610100811215156100d057600060a052602060a0f35b610100820683015460c052602060c0f350506100f2565b600060e052602060e0f35b5b505b6000f3 +``` + +The EVM bytecode that the contract code should be set to is: + +``` +0x600073fffffffffffffffffffffffffffffffffffffffe33141561005857600143035b801561005257600035610100820683015561010081061561003f57005b6101008104905061010082019150610022565b506100f3565b600060003512151561006e574360003512610071565b60005b156100e7576000356001814303035b6101008112151561009857600061010083061461009b565b60005b156100ba57610100830192506101008204915061010081049050610080565b610100811215156100d057600060a052602060a0f35b610100820683015460c052602060c0f350506100f2565b600060e052602060e0f35b5b50 +``` + +### Rationale + +This removes the need for implementations to have an explicit way to look into historical block hashes, simplifying the protocol definition and removing a large component of the "implied state" (information that is technically state but is not part of the state tree) and thereby making the protocol more "pure". Additionally, it allows blocks to directly point to blocks far behind them, which enables extremely efficient and secure light client protocols. diff --git a/EIPS/eip-211.md b/EIPS/eip-211.md new file mode 100644 index 0000000..29f8c19 --- /dev/null +++ b/EIPS/eip-211.md @@ -0,0 +1,63 @@ +--- +eip: 211 +title: "New opcodes: RETURNDATASIZE and RETURNDATACOPY" +author: Christian Reitwiessner +type: Standards Track +category: Core +status: Final +created: 2017-02-13 +--- + +## Simple Summary + +A mechanism to allow returning arbitrary-length data inside the EVM has been requested for quite a while now. Existing proposals always had very intricate problems associated with charging gas. This proposal solves the same problem while at the same time, it has a very simple gas charging mechanism and requires minimal changes to the call opcodes. Its workings are very similar to the way calldata is handled already; after a call, return data is kept inside a virtual buffer from which the caller can copy it (or parts thereof) into memory. At the next call, the buffer is overwritten. This mechanism is 100% backwards compatible. + +## Abstract + +Please see summary. + +## Motivation + +In some situations, it is vital for a function to be able to return data whose length cannot be anticipated before the call. In principle, this can be solved without alterations to the EVM, for example by splitting the call into two calls where the first is used to compute only the size. All of these mechanisms, though, are very expensive in at least some situations. A very useful example of such a worst-case situation is a generic forwarding contract; a contract that takes call data, potentially makes some checks and then forwards it as is to another contract. The return data should of course be transferred in a similar way to the original caller. Since the contract is generic and does not know about the contract it calls, there is no way to determine the size of the output without adapting the called contract accordingly or trying a logarithmic number of calls. + +Compiler implementors are advised to reserve a zero-length area for return data if the size of the return data is unknown before the call and then use `RETURNDATACOPY` in conjunction with `RETURNDATASIZE` to actually retrieve the data. + +Note that this proposal also makes the EIP that proposes to allow to return data in case of an intentional state reversion ([EIP-140](./eip-140.md)) much more useful. Since the size of the failure data might be larger than the regular return data (or even unknown), it is possible to retrieve the failure data after the CALL opcode has signalled a failure, even if the regular output area is not large enough to hold the data. + +## Specification + +If `block.number >= BYZANTIUM_FORK_BLKNUM`, add two new opcodes and amend the semantics of any opcode that creates a new call frame (like `CALL`, `CREATE`, `DELEGATECALL`, ...) called call-like opcodes in the following. It is assumed that the EVM (to be more specific: an EVM call frame) has a new internal buffer of variable size, called the return data buffer. This buffer is created empty for each new call frame. Upon executing any call-like opcode, the buffer is cleared (its size is set to zero). After executing a call-like opcode, the complete return data (or failure data, see [EIP-140](./eip-140.md)) of the call is stored in the return data buffer (of the caller), and its size changed accordingly. As an exception, `CREATE` and `CREATE2` are considered to return the empty buffer in the success case and the failure data in the failure case. If the call-like opcode is executed but does not really instantiate a call frame (for example due to insufficient funds for a value transfer or if the called contract does not exist), the return data buffer is empty. + +As an optimization, it is possible to share the return data buffer across call frames because at most one will be non-empty at any time. + +`RETURNDATASIZE`: `0x3d` + +Pushes the size of the return data buffer onto the stack. +Gas costs: 2 (same as `CALLDATASIZE`) + +`RETURNDATACOPY`: `0x3e` + +This opcode has similar semantics to `CALLDATACOPY`, but instead of copying data from the call data, it copies data from the return data buffer. Furthermore, accessing the return data buffer beyond its size results in a failure; i.e. if `start + length` overflows or results in a value larger than `RETURNDATASIZE`, the current call stops in an out-of-gas condition. In particular, reading 0 bytes from the end of the buffer will read 0 bytes; reading 0 bytes from one-byte out of the buffer causes an exception. + +Gas costs: `3 + 3 * ceil(amount / 32)` (same as `CALLDATACOPY`) + +## Rationale + +Other solutions that would allow returning dynamic data were considered, but they all had to deduct the gas from the call opcode and thus were both complicated to implement and specify ([5/8](https://github.com/ethereum/EIPs/issues/8)). Since this proposal is very similar to the way calldata is handled, it fits nicely into the concept. Furthermore, the eWASM architecture already handles return data in exactly the same way. + +Note that the EVM implementation needs to keep the return data until the next call or the return from the current call. Since this resource was already paid for as part of the memory of the callee, it should not be a problem. Implementations may either choose to keep the full memory of the callee alive until the next call or copy only the return data to a special memory area. + +Keeping the memory of the callee until the next call-like opcode does not increase the peak memory usage in the following sense; any memory allocation in the caller's frame that happens after the return from the call can be moved before the call without a change in gas costs, but will add this allocation to the peak allocation. + +The number values of the opcodes were allocated in the same nibble block that also contains `CALLDATASIZE` and `CALLDATACOPY`. + +## Backwards Compatibility + +This proposal introduces two new opcodes and stays fully backwards compatible apart from that. + +## Test Cases + +## Implementation + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2124.md b/EIPS/eip-2124.md new file mode 100644 index 0000000..32479f5 --- /dev/null +++ b/EIPS/eip-2124.md @@ -0,0 +1,311 @@ +--- +eip: 2124 +title: Fork identifier for chain compatibility checks +author: Péter Szilágyi , Felix Lange +discussions-to: https://github.com/ethereum/EIPs/issues/2125 +status: Final +type: Standards Track +category: Networking +created: 2019-05-03 +--- + +## Simple Summary + +Currently nodes in the Ethereum network try to find each other by establishing random connections to remote machines "looking" like an Ethereum node (public networks, private networks, test networks, etc), hoping that they found a useful peer (same genesis, same forks). This wastes time and resources, especially for smaller networks. + +To avoid this overhead, Ethereum needs a mechanism that can precisely identify whether a node will be useful, as early as possible. Such a mechanism requires a way to summarize chain configurations, as well as a way to disseminate said summaries in the network. + +This proposal focuses only on the definition of said summary - a generally useful *fork identifier* - and it's validation rules, allowing it to be embedded into arbitrary network protocols (e.g. [discovery ENRs](./eip-778.md) or `eth/6x` handshakes). + +## Abstract + +There are many public and private Ethereum networks, but the discovery protocol doesn't differentiate between them. The only way to check if a peer is good or bad (same chain or not), is to establish a TCP/IP connection, wrap it with RLPx cryptography, then execute an `eth` handshake. This is an extreme cost to bear if it turns out that the remote peer is on a different network and it's not even precise enough to differentiate Ethereum and Ethereum Classic. This cost is magnified for small networks, where a lot more trial and errors are needed to find good nodes. + +Even if the peer **is** on the same chain, during non-controversial consensus upgrades, not everybody updates their nodes in time (developer nodes, leftovers, etc). These stale nodes put a meaningless burden on the peer-to-peer network, since they just latch on to good nodes, but don't accept upgraded blocks. This causes valuable peer slots and bandwidth to be lost until the stale nodes finally update. This is a serious issue for test networks, where leftovers can linger for months. + +This EIP proposes a new identity scheme to both precisely and concisely summarize the chain's current status (genesis and all applied forks). The conciseness is particularly important to make the identity useful across datagram protocols too. The EIP solves a number of issues: + + * If two nodes are on different networks, they should never even consider connecting. + * If a hard fork passes, upgraded nodes should reject non-upgraded ones, but **NOT** before. + * If two chains share the same genesis, but not forks (ETH / ETC), they should reject each other. + +This EIP does not attempt to solve the clean separation of 3-way-forks! If at the same future block number, the network splits into three (non-fork, fork-A and fork-B), separating the forkers from each another will need case-by-case special handling. Not handling this keeps the proposal pragmatic, simple and also avoids making it too easy to fork off mainnet. + +To keep the scope limited, this EIP only defines the identity scheme and validation rules. The same scheme and algorithm can be embedded into various networking protocols, allowing both the `eth/6x` handshake to be more precise (Ethereum vs. Ethereum Classic); as well as the discovery to be more useful (reject surely peers without ever connecting). + +## Motivation + +Peer-to-peer networking is messy and hard due to firewalls and network address translation (NAT). Generally only a small fraction of nodes have publicly routed addresses and P2P networks rely mainly on these for forwarding data for everyone else. The best way to maximize the utility of the public nodes is to ensure their resources aren't wasted on tasks that are worthless to the network. + +By aggressively cutting off incompatible nodes from each other we can extract a lot more value from the public nodes, making the entire P2P network much more robust and reliable. Supporting this network partitioning at a discovery layer can further enhance performance as we avoid the costly crypto and latency/bandwidth hit associated with establishing a stream connection in the first place. + +## Specification + +Each node maintains the following values: + +- **`FORK_HASH`**: IEEE CRC32 checksum (`[4]byte`) of the genesis hash and fork blocks numbers that already passed. + - The fork block numbers are fed into the CRC32 checksum in ascending order. + - If multiple forks are applied at the same block, the block number is checksummed only once. + - Block numbers are regarded as `uint64` integers, encoded in big endian format when checksumming. + - If a chain is configured to start with a non-Frontier ruleset already in its genesis, that is NOT considered a fork. +- **`FORK_NEXT`**: Block number (`uint64`) of the next upcoming fork, or `0` if no next fork is known. + +E.g. `FORK_HASH` for mainnet would be: + +- forkhash₀ = `0xfc64ec04` (Genesis) = `CRC32()` +- forkhash₁ = `0x97c2c34c` (Homestead) = `CRC32( || uint64(1150000))` +- forkhash₂ = `0x91d1f948` (DAO fork) = `CRC32( || uint64(1150000) || uint64(1920000))` + +The *fork identifier* is defined as `RLP([FORK_HASH, FORK_NEXT])`. This `forkid` is cross validated (**NOT** naively compared) to assess a remote chain's compatibility. Irrespective of fork state, both parties must come to the same conclusion to avoid indefinite reconnect attempts from one side. + +#### Validation rules + +- 1) If local and remote `FORK_HASH` matches, compare local head to `FORK_NEXT`. + - The two nodes are in the same fork state currently. They might know of differing future forks, but that's not relevant until the fork triggers (might be postponed, nodes might be updated to match). + - 1a) A remotely announced but remotely not passed block is already passed locally, disconnect, since the chains are incompatible. + - 1b) No remotely announced fork; or not yet passed locally, connect. + +- 2) If the remote `FORK_HASH` is a subset of the local past forks and the remote `FORK_NEXT` matches with the locally following fork block number, connect. + - Remote node is currently syncing. It might eventually diverge from us, but at this current point in time we don't have enough information. +- 3) If the remote `FORK_HASH` is a superset of the local past forks and can be completed with locally known future forks, connect. + - Local node is currently syncing. It might eventually diverge from the remote, but at this current point in time we don't have enough information. +- 4) Reject in all other cases. + +#### Stale software examples + +The examples below try to exhaust the fork combination possibilities that arise when nodes do not run matching software versions, but otherwise follow the same chain (mainnet nodes, testnet nodes, etc). + +| Past forks | Future forks | Head | Remote `FORK_HASH` | Remote `FORK_NEXT` | Connect | Reason | +|:---:|:---:|:---:|:---:|:---:|:---:|:---:| +| A | | | A | | Yes (1b) | Same forks, same sync state. | +| A | | < B | A | B | Yes (1b) | Remote is advertising a future fork, but that is uncertain. | +| A | | >= B | A | B | No (1a) | Remote is advertising a future fork that passed locally. | +| A | B | | A | | Yes (1b) | Local knows about a future fork, but that is uncertain. | +| A | B | | A | B | Yes (1b) | Both know about a future fork, but that is uncertain. | +| A | B1 | < B2 | A | B2 | Yes (1b) | Both know about differing future forks, but those are uncertain. | +| A | B1 | >= B2 | A | B2 | No (1a) | Both know about differing future forks, but the remote one passed locally. | +| [A,B] | | | A | B | Yes (2) | Remote out of sync. | +| [A,B,C] | | | A | B | Yes¹ (2) | Remote out of sync. Remote will need a software update, but we don't know it yet. | +| A | B | | A ⊕ B | | Yes (3) | Local out of sync. | +| A | B,C | | A ⊕ B | | Yes (3) | Local out of sync. Local also knows about a future fork, but that is uncertain yet. | +| A | | | A ⊕ B | | No (4) | Local needs software update. | +| A | B | | A ⊕ B ⊕ C | | No² (4) | Local needs software update. | +| [A,B] | | | A | | No (4) | Remote needs software update. | + +*Note, there's one asymmetry in the table, marked with ¹ and ². Since we don't have access to a remote node's future fork list (just the next one), we can't detect that it's software is stale until it syncs up. This is acceptable as 1) the remote node will disconnect from us anyway, and 2) this is a temporary fluke during sync, not permanent with a leftover node.* + +## Rationale + +##### Why flatten `FORK_HASH` into 4 bytes? Why not share the entire genesis and fork list? + +Whilst the `eth` devp2p protocol permits arbitrarily much data to be transmitted, the discovery protocol's total space allowance for all ENR entries is 300 bytes. + +Reducing the `FORK_HASH` into a 4 bytes checksum ensures that we leave ample room in the ENR for future extensions; and 4 bytes is more than enough for arbitrarily many Ethereum networks from a (practical) collision perspective. + +##### Why use IEEE CRC32 as the checksum instead of Keccak256? + +We need a mechanism that can flatten arbitrary data into 4 bytes, without ignoring any of the input. Any other checksum or hashing algorithm would work, but since nodes can lie at any time, there's no value in cryptographic hash functions. + +Instead of just taking the first 4 bytes of a Keccak256 hash (seems odd) or XOR-ing all the 4-byte groups (messy), CRC32 is a better alternative, as this is exactly what it was designed for. IEEE CRC32 is also used by ethernet, gzip, zip, png, etc, so every programming language support should not be a problem. + +##### We're not using `FORK_NEXT` for much, can't we get rid of it somehow? + +We need to be able to differentiate whether a remote node is out of sync or whether its software is stale. Sharing only the past forks cannot tell us if the node is legitimately behind or stuck. + +##### Why advertise only one next fork, instead of "hashing" all known future ones like the `FORK_HASH`? + +Opposed to past forks that have already passed (for us locally) and can be considered immutable, we don't know anything about future ones. Maybe we're out of sync or maybe the fork didn't pass yet. If it didn't pass yet, it might be postponed, so enforcing it would split the network apart. It could also happen that we're not yet aware of all future forks (haven't updated our software in a while). + +## Backwards Compatibility + +This EIP only defines an identity scheme, it does not define functional changes. + +## Test Cases + +Here's a full suite of tests for all possible fork IDs that Mainnet, Ropsten, Rinkeby and Görli can advertise given the Petersburg fork cap (time of writing). + +```go +type testcase struct { + head uint64 + want ID +} +tests := []struct { + config *params.ChainConfig + genesis common.Hash + cases []testcase +}{ + // Mainnet test cases + { + params.MainnetChainConfig, + params.MainnetGenesisHash, + []testcase{ + {0, ID{Hash: 0xfc64ec04, Next: 1150000}}, // Unsynced + {1149999, ID{Hash: 0xfc64ec04, Next: 1150000}}, // Last Frontier block + {1150000, ID{Hash: 0x97c2c34c, Next: 1920000}}, // First Homestead block + {1919999, ID{Hash: 0x97c2c34c, Next: 1920000}}, // Last Homestead block + {1920000, ID{Hash: 0x91d1f948, Next: 2463000}}, // First DAO block + {2462999, ID{Hash: 0x91d1f948, Next: 2463000}}, // Last DAO block + {2463000, ID{Hash: 0x7a64da13, Next: 2675000}}, // First Tangerine block + {2674999, ID{Hash: 0x7a64da13, Next: 2675000}}, // Last Tangerine block + {2675000, ID{Hash: 0x3edd5b10, Next: 4370000}}, // First Spurious block + {4369999, ID{Hash: 0x3edd5b10, Next: 4370000}}, // Last Spurious block + {4370000, ID{Hash: 0xa00bc324, Next: 7280000}}, // First Byzantium block + {7279999, ID{Hash: 0xa00bc324, Next: 7280000}}, // Last Byzantium block + {7280000, ID{Hash: 0x668db0af, Next: 0}}, // First and last Constantinople, first Petersburg block + {7987396, ID{Hash: 0x668db0af, Next: 0}}, // Today Petersburg block + }, + }, + // Ropsten test cases + { + params.TestnetChainConfig, + params.TestnetGenesisHash, + []testcase{ + {0, ID{Hash: 0x30c7ddbc, Next: 10}}, // Unsynced, last Frontier, Homestead and first Tangerine block + {9, ID{Hash: 0x30c7ddbc, Next: 10}}, // Last Tangerine block + {10, ID{Hash: 0x63760190, Next: 1700000}}, // First Spurious block + {1699999, ID{Hash: 0x63760190, Next: 1700000}}, // Last Spurious block + {1700000, ID{Hash: 0x3ea159c7, Next: 4230000}}, // First Byzantium block + {4229999, ID{Hash: 0x3ea159c7, Next: 4230000}}, // Last Byzantium block + {4230000, ID{Hash: 0x97b544f3, Next: 4939394}}, // First Constantinople block + {4939393, ID{Hash: 0x97b544f3, Next: 4939394}}, // Last Constantinople block + {4939394, ID{Hash: 0xd6e2149b, Next: 6485846}}, // First Petersburg block + {6485845, ID{Hash: 0xd6e2149b, Next: 6485846}}, // Last Petersburg block + {6485846, ID{Hash: 0x4bc66396, Next: 0}}, // First Istanbul block + {7500000, ID{Hash: 0x4bc66396, Next: 0}}, // Future Istanbul block + }, + }, + // Rinkeby test cases + { + params.RinkebyChainConfig, + params.RinkebyGenesisHash, + []testcase{ + {0, ID{Hash: 0x3b8e0691, Next: 1}}, // Unsynced, last Frontier block + {1, ID{Hash: 0x60949295, Next: 2}}, // First and last Homestead block + {2, ID{Hash: 0x8bde40dd, Next: 3}}, // First and last Tangerine block + {3, ID{Hash: 0xcb3a64bb, Next: 1035301}}, // First Spurious block + {1035300, ID{Hash: 0xcb3a64bb, Next: 1035301}}, // Last Spurious block + {1035301, ID{Hash: 0x8d748b57, Next: 3660663}}, // First Byzantium block + {3660662, ID{Hash: 0x8d748b57, Next: 3660663}}, // Last Byzantium block + {3660663, ID{Hash: 0xe49cab14, Next: 4321234}}, // First Constantinople block + {4321233, ID{Hash: 0xe49cab14, Next: 4321234}}, // Last Constantinople block + {4321234, ID{Hash: 0xafec6b27, Next: 5435345}}, // First Petersburg block + {5435344, ID{Hash: 0xafec6b27, Next: 5435345}}, // Last Petersburg block + {5435345, ID{Hash: 0xcbdb8838, Next: 0}}, // First Istanbul block + {6000000, ID{Hash: 0xcbdb8838, Next: 0}}, // Future Istanbul block + }, + }, + // Goerli test cases + { + params.GoerliChainConfig, + params.GoerliGenesisHash, + []testcase{ + {0, ID{Hash: 0xa3f5ab08, Next: 1561651}}, // Unsynced, last Frontier, Homestead, Tangerine, Spurious, Byzantium, Constantinople and first Petersburg block + {1561650, ID{Hash: 0xa3f5ab08, Next: 1561651}}, // Last Petersburg block + {1561651, ID{Hash: 0xc25efa5c, Next: 0}}, // First Istanbul block + {2000000, ID{Hash: 0xc25efa5c, Next: 0}}, // Future Istanbul block + }, + }, +} +``` + + Here's a suite of tests of the different states a Mainnet node might be in and the different remote fork identifiers it might be required to validate and decide to accept or reject: + +```go +tests := []struct { + head uint64 + id ID + err error +}{ + // Local is mainnet Petersburg, remote announces the same. No future fork is announced. + {7987396, ID{Hash: 0x668db0af, Next: 0}, nil}, + + // Local is mainnet Petersburg, remote announces the same. Remote also announces a next fork + // at block 0xffffffff, but that is uncertain. + {7987396, ID{Hash: 0x668db0af, Next: math.MaxUint64}, nil}, + + // Local is mainnet currently in Byzantium only (so it's aware of Petersburg), remote announces + // also Byzantium, but it's not yet aware of Petersburg (e.g. non updated node before the fork). + // In this case we don't know if Petersburg passed yet or not. + {7279999, ID{Hash: 0xa00bc324, Next: 0}, nil}, + + // Local is mainnet currently in Byzantium only (so it's aware of Petersburg), remote announces + // also Byzantium, and it's also aware of Petersburg (e.g. updated node before the fork). We + // don't know if Petersburg passed yet (will pass) or not. + {7279999, ID{Hash: 0xa00bc324, Next: 7280000}, nil}, + + // Local is mainnet currently in Byzantium only (so it's aware of Petersburg), remote announces + // also Byzantium, and it's also aware of some random fork (e.g. misconfigured Petersburg). As + // neither forks passed at neither nodes, they may mismatch, but we still connect for now. + {7279999, ID{Hash: 0xa00bc324, Next: math.MaxUint64}, nil}, + + // Local is mainnet Petersburg, remote announces Byzantium + knowledge about Petersburg. Remote + // is simply out of sync, accept. + {7987396, ID{Hash: 0xa00bc324, Next: 7280000}, nil}, + + // Local is mainnet Petersburg, remote announces Spurious + knowledge about Byzantium. Remote + // is definitely out of sync. It may or may not need the Petersburg update, we don't know yet. + {7987396, ID{Hash: 0x3edd5b10, Next: 4370000}, nil}, + + // Local is mainnet Byzantium, remote announces Petersburg. Local is out of sync, accept. + {7279999, ID{Hash: 0x668db0af, Next: 0}, nil}, + + // Local is mainnet Spurious, remote announces Byzantium, but is not aware of Petersburg. Local + // out of sync. Local also knows about a future fork, but that is uncertain yet. + {4369999, ID{Hash: 0xa00bc324, Next: 0}, nil}, + + // Local is mainnet Petersburg. remote announces Byzantium but is not aware of further forks. + // Remote needs software update. + {7987396, ID{Hash: 0xa00bc324, Next: 0}, ErrRemoteStale}, + + // Local is mainnet Petersburg, and isn't aware of more forks. Remote announces Petersburg + + // 0xffffffff. Local needs software update, reject. + {7987396, ID{Hash: 0x5cddc0e1, Next: 0}, ErrLocalIncompatibleOrStale}, + + // Local is mainnet Byzantium, and is aware of Petersburg. Remote announces Petersburg + + // 0xffffffff. Local needs software update, reject. + {7279999, ID{Hash: 0x5cddc0e1, Next: 0}, ErrLocalIncompatibleOrStale}, + + // Local is mainnet Petersburg, remote is Rinkeby Petersburg. + {7987396, ID{Hash: 0xafec6b27, Next: 0}, ErrLocalIncompatibleOrStale}, + + // Local is mainnet Petersburg, far in the future. Remote announces Gopherium (non existing fork) + // at some future block 88888888, for itself, but past block for local. Local is incompatible. + // + // This case detects non-upgraded nodes with majority hash power (typical Ropsten mess). + {88888888, ID{Hash: 0x668db0af, Next: 88888888}, ErrLocalIncompatibleOrStale}, + + // Local is mainnet Byzantium. Remote is also in Byzantium, but announces Gopherium (non existing + // fork) at block 7279999, before Petersburg. Local is incompatible. + {7279999, ID{Hash: 0xa00bc324, Next: 7279999}, ErrLocalIncompatibleOrStale}, +} +``` + +Here's a couple of tests to verify the proper RLP encoding (since `FORK_HASH` is a 4 byte binary but `FORK_NEXT` is an 8 byte quantity): + +```go +tests := []struct { + id ID + want []byte +}{ + { + ID{Hash: 0, Next: 0}, + common.Hex2Bytes("c6840000000080"), + }, + { + ID{Hash: 0xdeadbeef, Next: 0xBADDCAFE}, + common.Hex2Bytes("ca84deadbeef84baddcafe"), + }, + { + ID{Hash: math.MaxUint32, Next: math.MaxUint64}, + common.Hex2Bytes("ce84ffffffff88ffffffffffffffff"), + }, +} +``` + +## Implementation + +Geth: https://github.com/ethereum/go-ethereum/tree/master/core/forkid + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2135.md b/EIPS/eip-2135.md new file mode 100644 index 0000000..8112588 --- /dev/null +++ b/EIPS/eip-2135.md @@ -0,0 +1,168 @@ +--- +eip: 2135 +title: Consumable Interface (Tickets, etc) +description: An interface extending EIP-721 and EIP-1155 for consumability, supporting use case such as an event ticket. +author: Zainan Victor Zhou (@xinbenlv) +discussions-to: https://ethereum-magicians.org/t/eip-2135-erc-consumable-interface/3439 +status: Last Call +last-call-deadline: 2023-02-01 +type: Standards Track +category: ERC +created: 2019-06-23 +requires: 165, 721, 1155 +--- + +## Abstract + +This EIP defines an interface to mark a digital asset as "consumable" and to react to its "consumption." + +## Motivation + +Digital assets sometimes need to be consumaed. One of the most common examples is a concert ticket. +It is "consumed" when the ticket-holder enters the concert hall. + +Having a standard interface enables interoperability for services, clients, UI, and inter-contract functionalities on top of this use-case. + +## Specification + +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. + +1. Any compliant contract **MUST** implement the following interface: + +```solidity +pragma solidity >=0.7.0 <0.9.0; + +/// The EIP-165 identifier of this interface is 0xdd691946 +interface IERC2135 { + /// @notice The consume function consumes a token every time it succeeds. + /// @param _consumer the address of consumer of this token. It doesn't have + /// to be the EOA or contract Account that initiates the TX. + /// @param _assetId the NFT asset being consumed + /// @param _data extra data passed in for consume for extra message + /// or future extension. + function consume( + address _consumer, + uint256 _assetId, + uint256 _amount, + bytes calldata _data + ) external returns (bool _success); + + /// @notice The interface to check whether an asset is consumable. + /// @param _consumer the address of consumer of this token. It doesn't have + /// to be the EOA or contract Account that initiates the TX. + /// @param _assetId the NFT asset being consumed. + /// @param _amount the amount of the asset being consumed. + function isConsumableBy( + address _consumer, + uint256 _assetId, + uint256 _amount + ) external view returns (bool _consumable); + + /// @notice The event emitted when there is a successful consumption. + /// @param consumer the address of consumer of this token. It doesn't have + /// to be the EOA or contract Account that initiates the TX. + /// @param assetId the NFT asset being consumed + /// @param amount the amount of the asset being consumed. + /// @param data extra data passed in for consume for extra message + /// or future extension. + event OnConsumption( + address indexed consumer, + uint256 indexed assetId, + uint256 amount, + bytes data + ); +} +``` + +2. If the compliant contract is an [EIP-721](./eip-721.md) or [EIP-1155](./eip-1155.md) token, in addition to `OnConsumption`, it **MUST** also emit the `Transfer` / `TransferSingle` event (as applicable) as if a token has been transferred from the current holder to the zero address if the call to `consume` method succeeds. + +3. `supportsInterface(0xdd691946)` **MUST** return `true` for any compliant contract, as per [EIP-165](./eip-165.md). + +## Rationale + +1. The function `consume` performs the consume action. This EIP does not assume: + +- who has the power to perform consumption +- under what condition consumption can occur + +It does, however, assume the asset can be identified in a `uint256` asset id as in the parameter. A design convention and compatibility consideration is put in place to follow the EIP-721 pattern. + +2. The event notifies subscribers whoever are interested to learn an asset is being consumed. + +3. To keep it simple, this standard *intentionally* contains no functions or events related to the creation of a consumable asset. This is because the creation of a consumable asset will need to make assumptions about the nature of an actual use-case. If there are common use-cases for creation, another follow up standard can be created. + +4. Metadata associated to the consumables is not included the standard. If necessary, related metadata can be created with a separate metadata extension interface like `ERC721Metadata` from [EIP-721](./eip-721.md) + +5. We choose to include an `address consumer` for `consume` function and `isConsumableBy` so that an NFT MAY be consumed for someone other than the transaction initiator. + +6. We choose to include an extra `_data` field for future extension, such as +adding crypto endorsements. + +7. We explicitly stay opinion-less about whether EIP-721 or EIP-1155 shall be required because +while we design this EIP with EIP-721 and EIP-1155 in mind mostly, we don't want to rule out +the potential future case someone use a different token standard or use it in different use cases. + +8. The boolean view function of `isConsumableBy` can be used to check whether an asset is +consumable by the `_consumer`. + +## Backwards Compatibility + +This interface is designed to be compatible with EIP-721 and NFT of EIP-1155. It can be tweaked to used for [EIP-20](./eip-20.md), [EIP-777](./eip-777.md) and Fungible Token of EIP-1155. + +## Test Cases + +```ts + + describe("Consumption", function () { + it("Should consume when minted", async function () { + const fakeTokenId = "0x1234"; + const { contract, addr1 } = await loadFixture(deployFixture); + await contract.safeMint(addr1.address, fakeTokenId); + expect(await contract.balanceOf(addr1.address)).to.equal(1); + expect(await contract.ownerOf(fakeTokenId)).to.equal(addr1.address); + expect(await contract.isConsumableBy(addr1.address, fakeTokenId, 1)).to.be.true; + const tx = await contract.consume(addr1.address, fakeTokenId, 1, []); + const receipt = await tx.wait(); + const events = receipt.events.filter((x: any) => { return x.event == "OnConsumption" }); + expect(events.length).to.equal(1); + expect(events[0].args.consumer).to.equal(addr1.address); + expect(events[0].args.assetId).to.equal(fakeTokenId); + expect(events[0].args.amount).to.equal(1); + expect(await contract.balanceOf(addr1.address)).to.equal(0); + await expect(contract.ownerOf(fakeTokenId)) + .to.be.rejectedWith('ERC721: invalid token ID'); + await expect(contract.isConsumableBy(addr1.address, fakeTokenId, 1)) + .to.be.rejectedWith('ERC721: invalid token ID'); + }); + }); + + describe("EIP-165 Identifier", function () { + it("Should match", async function () { + const { contract } = await loadFixture(deployFixture); + expect(await contract.get165()).to.equal("0xdd691946"); + expect(await contract.supportsInterface("0xdd691946")).to.be.true; + }); + }); +``` + +## Reference Implementation + +A deployment of version 0x1002 has been deployed onto `goerli` testnet at address `0x3682bcD67b8A5c0257Ab163a226fBe07BF46379B`. + +Find the reference contract verified source code on Etherscan's +`goerli` site for the address above. + +## Security Considerations + +Compliant contracts should pay attention to the balance change when a token is consumed. +When the contract is being paused, or the user is being restricted from transferring a token, +the consumeability should be consistent with the transferral restriction. + +Compliant contracts should also carefully define access control, particularlly whether any EOA or contract account may or may not initiate a `consume` method in their own use case. + +Security audits and tests should be used to verify that the access control to the `consume` +function behaves as expected. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-214.md b/EIPS/eip-214.md new file mode 100644 index 0000000..009d0e1 --- /dev/null +++ b/EIPS/eip-214.md @@ -0,0 +1,51 @@ +--- +eip: 214 +title: New opcode STATICCALL +author: Vitalik Buterin , Christian Reitwiessner +type: Standards Track +category: Core +status: Final +created: 2017-02-13 +--- + +## Simple Summary + +To increase smart contract security, this proposal adds a new opcode that can be used to call another contract (or itself) while disallowing any modifications to the state during the call (and its subcalls, if present). + +## Abstract + +This proposal adds a new opcode that can be used to call another contract (or itself) while disallowing any modifications to the state during the call (and its subcalls, if present). Any opcode that attempts to perform such a modification (see below for details) will result in an exception instead of performing the modification. + +## Motivation + +Currently, there is no restriction about what a called contract can do, as long as the computation can be performed with the amount of gas provided. This poses certain difficulties about smart contract engineers; after a regular call, unless you know the called contract, you cannot make any assumptions about the state of the contracts. Furthermore, because you cannot know the order of transactions before they are confirmed by miners, not even an outside observer can be sure about that in all cases. + +This EIP adds a way to call other contracts and restrict what they can do in the simplest way. It can be safely assumed that the state of all accounts is the same before and after a static call. + +## Specification + +Introduce a new `STATIC` flag to the virtual machine. This flag is set to `false` initially. Its value is always copied to sub-calls with an exception for the new opcode below. + +Opcode: `0xfa`. + +`STATICCALL` functions equivalently to a `CALL`, except it takes only 6 arguments (the "value" argument is not included and taken to be zero), and calls the child with the `STATIC` flag set to `true` for the execution of the child. Once this call returns, the flag is reset to its value before the call. + +Any attempts to make state-changing operations inside an execution instance with `STATIC` set to `true` will instead throw an exception. These operations include `CREATE`, `CREATE2`, `LOG0`, `LOG1`, `LOG2`, `LOG3`, `LOG4`, `SSTORE`, and `SELFDESTRUCT`. They also include `CALL` with a non-zero value. As an exception, `CALLCODE` is not considered state-changing, even with a non-zero value. + +## Rationale + +This allows contracts to make calls that are clearly non-state-changing, reassuring developers and reviewers that re-entrancy bugs or other problems cannot possibly arise from that particular call; it is a pure function that returns an output and does nothing else. This may also make purely functional HLLs easier to implement. + +## Backwards Compatibility + +This proposal adds a new opcode but does not modify the behaviour of other opcodes and thus is backwards compatible for old contracts that do not use the new opcode and are not called via the new opcode. + +## Test Cases + +To be written. + +## Implementation + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2157.md b/EIPS/eip-2157.md new file mode 100644 index 0000000..4748122 --- /dev/null +++ b/EIPS/eip-2157.md @@ -0,0 +1,136 @@ +--- +eip: 2157 +title: dType Storage Extension - Decentralized Type System for EVM +author: Loredana Cirstea (@loredanacirstea), Christian Tzurcanu (@ctzurcanu) +discussions-to: https://github.com/ethereum/EIPs/issues/2157 +status: Stagnant +type: Standards Track +category: ERC +created: 2019-06-28 +requires: 1900 +--- + +## Simple Summary + +This ERC is an extension of ERC-1900, proposing an optional storage extension for dType, a decentralized type system, specifying a general ABI for all storage contracts that contain type instances. + +## Abstract + +The storage extension will enable easy navigation and retrieval of type data that is intended to be of public use. This is possible through standardizing the ABI of the dType storage contracts, with the effect of having a deterministic path to a type instance record. This standardization enables a more effective on-chain and off-chain use of data and opens up possibilities for decentralized applications, enabling developers to build on top of public global data. + +## Motivation + +Currently, Ethereum does not have standardization of data addressability. This might not be needed for data that is meant to be quasi-private, however, it is needed for data that is meant for public consumption. ERC-1900 has started standardizing data types for increasing interoperability between projects, but this is not enough if we want to build a global ecosystem. Deterministic data addressability will enable anyone to build upon the same public data sets, off-chain or on-chain. + +It is true that with ERC-1900, blockchain data analysis and type-specific data retrieval will be possible off-chain, but this implies relying on centralized data caches (blockchain explorers) or maintaining your own data cache. Moreover, this option does not allow on-chain standardization on data retrieval paths, therefore limiting the type of on-chain interoperable operations that can be done. + +Having a clear way of retrieving data, instead of analyzing the blockchain for contracts that have a certain type in their ABI or bytecode, will make development easier and more decentralized for applications that target global data on specific types. + +For example, a decentralized market place can be built on top of some marketplace-specific types, and by knowing exactly where the type data is stored, it is easy to create custom algorithms that provide the user with the product information they seek. Everyone has access to the data and the data path is standardized. + +Moreover, by standardizing storage contract interfaces, ABI inference is possible. The common interface, together with the dType registry will provide all the data needed to reconstruct the ABI. + +This system can be extended with access and mutability control later on, in a future proposal. Access and mutability control will be necessary for public-use global systems. Moreover, we can have a homogeneous application of permissions across system components. This is not detailed in the present proposal. + +Another use case is data bridges between Ethereum shards or between Ethereum and other chains. Data syncing between shards/chains can be done programmatically, across data types (from various projects). Imagine a user having a public profile/identity contract on one chain, wishing to move that profile on Ethereum. By supporting the origin chain types and having a standardized storage mechanism, data moving processes will be the same. + +This pattern of separating data type definitions and storage allows developers to create functional programming-like patterns on Ethereum, even though languages such as Solidity are not functional. + +## Specification + +### TypeRootContract + +ERC-1900 defines a `contractAddress` field in the type metadata. For the limited purpose of ERC-1900, this field contains the value of the Ethereum type library in which the type definition exists. For the purpose of this ERC, the `contractAddress` will contain the Etherereum address of a `TypeRootContract`. + +```solidity +contract TypeRootContract { + address public libraryAddress; + address public storageAddress; + + constructor(address _library, address _storage) public { + libraryAddress = _library; + storageAddress = _storage; + } +} +``` + +- `libraryAddress` - Ethereum address of the type definition library, from ERC-1900 +- `storageAddress` - Ethereum address of the type data storage contract + + +### TypeStorageContract + +This contract will use the type library to define the internal data stored in it. Each record will be a type instance, addressable by a primary identifier. The primary identifier is calculated by the type library's `getIdentifier` function, based on the type instance values. + +We propose a Solidity CRUD pattern, as described in https://medium.com/robhitchens/solidity-crud-part-1-824ffa69509a, where records can also be retrieved using their index - a monotonically increasing counter. + +An stub implementation for the TypeStorageContract would look like: + +```solidity +import './TypeALib.sol'; + +contract TypeAStorage { + using TypeALib for TypeALib.TypeA; + + bytes32[] public typeIndex; + mapping(bytes32 => Type) public typeStruct; + + struct Type { + TypeALib.TypeA data; + uint256 index; + } + + event LogNew(bytes32 indexed identifier, uint256 indexed index); + event LogUpdate(bytes32 indexed identifier, uint256 indexed index); + event LogRemove(bytes32 indexed identifier, uint256 indexed index); + + function insert(TypeALib.TypeA memory data) public returns (bytes32 identifier); + + function insertBytes(bytes memory data) public returns (bytes32 identifier); + + function remove(bytes32 identifier) public returns(uint256 index); + + function update(bytes32 identifier, TypeALib.TypeA memory data) public returns(bytes32 identifier) + + function isStored(bytes32 identifier) public view returns(bool stored); + + function getByHash(bytes32 identifier) public view returns(TypeALib.TypeA memory data); + + function getByIndex(uint256 index) public view returns(TypeALib.TypeA memory data); + + function count() public view returns(uint256 counter); +} +``` + +## Rationale + +We are now thinking about a building block as a smart contract with an encapsulated object that contains state changing functions that are only understood from within. This is more akin to Object-Oriented Programming and poses interoperability and scalability issues. Not necessarily for an individual project, but for a global Ethereum OS. This is why we are proposing to separate data from business logic and data structure definitions. + +When you have public aggregated data, categorized on each type, anyone can build tools on top of it. This is a radical change from the closed or dispersed data patterns that we find in web2. + +We have chosen to define a `TypeRootContract` instead of extending the dType registry with fields for the TypeStorage contract, because this approach enables easier interface updates in the future. It is more extensible. + +The storage pattern used for dType itself and all the Type Storage contracts can be the same. This lowers the cost of building, testing and auditing the code. + +The `TypeStorageContract` pattern should ensure: +- type instance addressability by the primary identifier +- a way to retrieve all records from the contract +- counting the number of records + + +## Backwards Compatibility + +This proposal does not affect existent Ethereum standards or implementations. It uses the present experimental version of ABIEncoderV2. + +## Test Cases + +Will be added. + +## Implementation + +An in-work implementation can be found at https://github.com/pipeos-one/dType/tree/master/contracts/contracts. +This proposal will be updated with an appropriate implementation when consensus is reached on the specifications. + + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2159.md b/EIPS/eip-2159.md new file mode 100644 index 0000000..ca57907 --- /dev/null +++ b/EIPS/eip-2159.md @@ -0,0 +1,59 @@ +--- +eip: 2159 +title: Common Prometheus Metrics Names for Clients +author: Adrian Sutton (@ajsutton) +discussions-to: https://ethereum-magicians.org/t/common-chain-metrics/3415 +status: Final +type: Standards Track +category: Interface +created: 2019-07-01 +--- + +## Simple Summary +Standardized names of common metrics for Ethereum clients to use with Prometheus, a widely used monitoring and alerting solution. + +## Abstract +Many Ethereum clients expose a range of metrics in a format compatible with Prometheus to allow operators to monitor the client's behaviour and performance and raise alerts if the chain isn't progressing or there are other indications of errors. +While the majority of these metrics are highly client-specific, reporting on internal implementation details of the client, some are applicable to all clients. +By standardizing the naming and format of these common metrics, operators are able to monitor the operation of multiple clients in a single dashboard or alerting configuration. + +## Motivation +Using common names and meanings for metrics which apply to all clients allows node operators to monitor clusters of nodes using heterogeneous clients using a single dashboard and alerting configuration. +Currently there are no agreed names or meanings, leaving client developers to invent their own making it difficult to monitor a heterogeneous cluster. + +## Specification +The table below defines metrics which may be captured by Ethereum clients which expose metrics to Prometheus. Clients may expose additional metrics however these should not use the `ethereum_` prefix. + +| Name | Metric type | Definition | JSON-RPC Equivalent | +|----------------------------------|-------------|-------------------------------------------------------------------|---------------------------------------------------------------------| +| ethereum_blockchain_height | Gauge | The current height of the canonical chain | `eth_blockNumber` | +| ethereum_best_known_block_number | Gauge | The estimated highest block available | `highestBlock` of `eth_syncing` or `eth_blockNumber` if not syncing | +| ethereum_peer_count | Gauge | The current number of peers connected | `net_peerCount` | +| ethereum_peer_limit | Gauge | The maximum number of peers this node allows to connect | No equivalent | + +Note that `ethereum_best_known_block_number` always has a value. When the `eth_syncing` JSON-RPC method would return `false`, the current chain height is used. + +## Rationale +The defined metrics are independent of Ethereum client implementation but provide sufficient information to create an overview dashboard to support monitoring a group of Ethereum nodes. + +There is a similar, though more prescriptive, specification for beacon chain client metrics. +The specific details of how to expose the metrics has been omitted as there is variance in existing implementations and standardising this does not provide any significant benefit. + +## Backwards Compatibility +This is *not* a consensus affecting change. + +Clients may already be publishing these metrics using different names and changing to the new form may break existing alerts or dashboards. Clients that want to avoid this incompatibility can expose the metrics under both the old and new names. + +Clients may also be publishing metrics with a different meaning using these names. Backwards compatibility cannot be preserved in this case. + + +## Implementation +Pantheon switched to using these standard metric names in its 1.2 release: https://github.com/PegaSysEng/pantheon/pull/1634. + +## References + + 1. Prometheus. https://prometheus.io + 2. Beacon chain metrics specification. https://github.com/ethereum/eth2.0-metrics/blob/master/metrics.md + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2193.md b/EIPS/eip-2193.md new file mode 100644 index 0000000..3415279 --- /dev/null +++ b/EIPS/eip-2193.md @@ -0,0 +1,92 @@ +--- +eip: 2193 +title: dType Alias Extension - Decentralized Type System +author: Loredana Cirstea (@loredanacirstea), Christian Tzurcanu (@ctzurcanu) +discussions-to: https://github.com/ethereum/EIPs/issues/2192 +status: Stagnant +type: Standards Track +category: ERC +created: 2019-07-16 +requires: 155, 1900, 2157 +--- + +## Simple Summary + +We are proposing Alias - a semantic standard for identifying on-chain resources by human-readable qualifiers, supporting any type of data. + +## Abstract + +The dType Alias is a system for providing human-readable resource identifiers to on-chain content. A resource identifier is based on the type of data (identifier provided by dType, [EIP-1900](./eip-1900.md)) and the data content (identifier provided by a dType Storage Contract, [EIP-2157](./eip-2157.md)). It is a universal way of addressing content, supporting any type of data. + +## Motivation + +There are standards that currently address the need for attaching human-readable identifiers to Ethereum accounts, such as [EIP-137](./eip-137.md). These standards are an attempt to bring domain names to Ethereum, following the same format as DNS: `subdomain.domain.tld`. This leaf -> root format is unintuitive and contradicts the semantic meaning that `.` has in programming languages, which is a root -> leaf connection (e.g. in OOP, when accessing an object's property). A more intuitive and widely used approach is a root->leaf format, used in file browsers, hierarchical menus, and even in other decentralized systems, which give unique identifiers to resources (e.g. `0x56.Currency.TCoin` in [Libra](https://medium.com/r/?url=https%3A%2F%2Fdevelopers.libra.org). + +Moreover, [EIP-137](./eip-137.md) is not flexible enough to address smart contract content, which can contain heterogeneous data that belongs to various accounts. For example, a `PaymentChannel` smart contract can have an domain name. However, the `Alice-Bob` channel data from inside the smart contract, cannot have a subdomain name. Having uniquely identified, granular resources opens the way to creating both human and machine-readable protocols on top of Ethereum. It also provides a basis for protocols based on functional programming. + +This ERC proposes a set of separators which maintain their semantic meaning and provides a way to address any type of resource - from Ethereum addresses, to individual `struct` instances inside smart contracts. + +Imagine the following dType types: `SocialNetwork` and `Profile`, with related storage data about user profiles. One could access such a profile using an alias for the data content: `alice@socialnetwork.profile`. For a `PaymentChannel` type, Alice can refer to her channel with Bob with `alice-bob.paymentchannel`. +This alias system can be used off-chain, to replace the old DNS system with a deterministic and machine-readable way of displaying content, based on the dType type's metadata. + +## Specification + +The dType registry will provide domain and subdomain names for the resource type. Subdomains can be attributed recursively, to dType types which contain other complex types in their composition. + +We define an `Alias` registry contract, that keeps track of the human-readable identifiers for data resources, which exist in dType storage contracts. +Anyone can set an alias in the `Alias` registry, as long as the Ethereum address that signs the alias data has ownership on the resource, in the dType storage contract. Storage contract data ownership will be detailed in [EIP-2157](./eip-2157.md). An owner can update or delete an alias at any time. + +```solidity +interface Alias { + + event AliasSet(bytes32 dtypeIdentifier, bytes1 separator, string name, bytes32 indexed identifier); + + function setAlias(bytes32 dtypeIdentifier, bytes1 separator, string memory name, bytes32 identifier, bytes memory signature) external; + + function getAliased(bytes1 separator, string memory name) view external returns (bytes32 identifier); +} +``` + +- `dtypeIdentifier`: Type identifier from the dType registry, needed to ensure uniqueness of `name` for a dType type. `dtypeIdentifier` is checked to see if it exists in the dType registry. The dType registry also links the type's data storage contract, where the existence and ownership of the `identifier` is checked. +- `name`: user-defined human-readable name for the resource referenced by `identifier` +- `separator`: Character acting as a separator between the name and the rest of the alias. Allowed values: + - `.`: general domain separation, using root->leaf semantics. E.g. `domain.subdomain.leafsubdomain.resource` + - `@`: identifying actor-related data, such as user profiles, using leaf->root semantics. E.g. `alice@socialnetwork.profile` or `alice@dao@eth` + - `#`: identifying concepts, using root->leaf semantics. E.g. `topicX#postY` + - `/`: general resource path definition, using root->leaf semantics. E.g. `resourceRoot/resource` +- `identifier`: Resource identifier from a smart contract linked with dType +- `signature`: Alias owner signature on `dtypeIdentifier`, `identifier`, `name`, `separator`, `nonce`, `aliasAddress`, `chainId`. + - `nonce`: monotonically increasing counter, used to prevent replay attacks + - `aliasAddress`: Ethereum address of `Alias` contract + - `chainId`: chain on which the `Alias` contract is deployed, as detailed in [EIP-155](./eip-155.md), used to prevent replay attacks when updating the `identifier` for an alias. + +Content addressability can be done: +- using the `bytes32` identifiers directly, e.g. `0x0b5e76559822448f6243a6f76ac7864eba89c810084471bdee2a63429c92d2e7@0x9dbb9abe0c47484c5707699b3ceea23b1c2cca2ac72681256ab42ae01bd347da` +- using the human identifiers, e.g. `alice@socialnetwork` + +Both of the above examples will resolve to the same content. + + +## Rationale + +Current attempts to solve content addressability, such as [EIP-137](./eip-137.md), only target Ethereum accounts. These are based on inherited concepts from HTTP and DNS, which are not machine friendly. + +With [EIP-1900](./eip-1900.md) and [EIP-2157](./eip-2157.md), general content addressability can be achieved. dType provides type information and a reference to the smart contract where the type instances are stored. Additionally, Alias uses the semantic meaning of subdomain separators to have a [intuitive order rule](https://github.com/loredanacirstea/articles/blob/master/articles/Flexible_Alias_or_Why_ENS_is_Obsolete.md). + +Multiple aliases can be assigned to a single resource. Either by using a different `name` or by using a different `separator`. Each `separator` can have a specific standard for displaying and processing data, based on its semantic meaning. + +## Backwards Compatibility + +Will be added. + +## Test Cases + +Will be added. + +## Implementation + +An in-work implementation can be found at https://github.com/pipeos-one/dType/blob/master/contracts/contracts/Alias.sol. +This proposal will be updated with an appropriate implementation when consensus is reached on the specifications. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2200.md b/EIPS/eip-2200.md new file mode 100644 index 0000000..9da3dc1 --- /dev/null +++ b/EIPS/eip-2200.md @@ -0,0 +1,322 @@ +--- +eip: 2200 +title: Structured Definitions for Net Gas Metering +author: Wei Tang (@sorpaas) +discussions-to: https://github.com/sorpaas/EIPs/issues/1 +status: Final +type: Standards Track +category: Core +created: 2019-07-18 +--- + +## Simple Summary + +This is an EIP that implements net gas metering. It's a combined +version of [EIP-1283] and [EIP-1706], with a structured definition so as +to make it interoperable with other gas changes such as [EIP-1884]. + +## Abstract + +This EIP provides a structured definition of net gas metering changes +for `SSTORE` opcode, enabling new usages for contract storage, and +reducing excessive gas costs where it doesn’t match how most +implementation works. + +This is a combination of [EIP-1283] and [EIP-1706]. + +## Motivation + +This EIP proposes a way for gas metering on `SSTORE`, using information +that is more universally available to most implementations, and +require as little change in implementation structures as possible. + +* Storage slot’s original value. +* Storage slot’s current value. +* Refund counter. + +Usages that benefits from this EIP’s gas reduction scheme includes: + +* Subsequent storage write operations within the same call frame. This + includes reentry locks, same-contract multi-send, etc. +* Exchange storage information between sub call frame and parent call + frame, where this information does not need to be persistent outside + of a transaction. This includes sub-frame error codes and message + passing, etc. + +The original definition of EIP-1283 created a danger of a new kind of +reentrancy attacks on existing contracts as Solidity by default grants +a "stipend" of 2300 gas to simple transfer calls. This danger is +easily mitigated if `SSTORE` is not allowed in low gasleft state, +without breaking the backward compatibility and the original intention +of EIP-1283. + +This EIP also replaces the original EIP-1283 value definitions of gas +by parameters, so that it's more structured, and easier to define +changes in the future. + +## Specification + +Define variables `SLOAD_GAS`, `SSTORE_SET_GAS`, `SSTORE_RESET_GAS` and +`SSTORE_CLEARS_SCHEDULE`. The old and new values for those variables +are: + +* `SLOAD_GAS`: changed from `200` to `800`. +* `SSTORE_SET_GAS`: `20000`, not changed. +* `SSTORE_RESET_GAS`: `5000`, not changed. +* `SSTORE_CLEARS_SCHEDULE`: `15000`, not changed. + +Change the definition of EIP-1283 using those variables. The new +specification, combining EIP-1283 and EIP-1706, will look like +below. The terms *original value*, *current value* and *new value* are +defined in EIP-1283. + +Replace `SSTORE` opcode gas cost calculation (including refunds) with +the following logic: + +* If *gasleft* is less than or equal to gas stipend, fail the current + call frame with 'out of gas' exception. +* If *current value* equals *new value* (this is a no-op), `SLOAD_GAS` + is deducted. +* If *current value* does not equal *new value* + * If *original value* equals *current value* (this storage slot has + not been changed by the current execution context) + * If *original value* is 0, `SSTORE_SET_GAS` is deducted. + * Otherwise, `SSTORE_RESET_GAS` gas is deducted. If *new value* is + 0, add `SSTORE_CLEARS_SCHEDULE` gas to refund counter. + * If *original value* does not equal *current value* (this storage + slot is dirty), `SLOAD_GAS` gas is deducted. Apply both of the + following clauses. + * If *original value* is not 0 + * If *current value* is 0 (also means that *new value* is not + 0), remove `SSTORE_CLEARS_SCHEDULE` gas from refund + counter. + * If *new value* is 0 (also means that *current value* is not + 0), add `SSTORE_CLEARS_SCHEDULE` gas to refund counter. + * If *original value* equals *new value* (this storage slot is + reset) + * If *original value* is 0, add `SSTORE_SET_GAS - SLOAD_GAS` to + refund counter. + * Otherwise, add `SSTORE_RESET_GAS - SLOAD_GAS` gas to refund + counter. + +An implementation should also note that with the above definition, if +the implementation uses call-frame refund counter, the counter can go +negative. If the implementation uses transaction-wise refund counter, +the counter always stays positive. + +## Rationale + +This EIP mostly achieves what a transient storage tries to do +([EIP-1087] and [EIP-1153]), but without the complexity of introducing the +concept of "dirty maps", or an extra storage struct. + +* We don't suffer from the optimization limitation of + EIP-1087. EIP-1087 requires keeping a dirty map for storage changes, + and implicitly makes the assumption that a transaction's storage + changes are committed to the storage trie at the end of a + transaction. This works well for some implementations, but not for + others. After [EIP-658], an efficient storage cache implementation + would probably use an in-memory trie (without RLP encoding/decoding) + or other immutable data structures to keep track of storage changes, + and only commit changes at the end of a block. For them, it is + possible to know a storage's original value and current value, but + it is not possible to iterate over all storage changes without + incurring additional memory or processing costs. +* It never costs more gas compared with the current scheme. +* It covers all usages for a transient storage. Clients that are easy + to implement EIP-1087 will also be easy to implement this + specification. Some other clients might require a little bit extra + refactoring on this. Nonetheless, no extra memory or processing cost + is needed on runtime. + +Regarding `SSTORE` gas cost and refunds, see Appendix for proofs of +properties that this EIP satisfies. + +* For *absolute gas used* (that is, actual *gas used* minus *refund*), + this EIP is equivalent to EIP-1087 for all cases. +* For one particular case, where a storage slot is changed, reset to + its original value, and then changed again, EIP-1283 would move more + gases to refund counter compared with EIP-1087. + +Examine examples provided in EIP-1087's Motivation (with `SLOAD_GAS` being +`200`): + +* If a contract with empty storage sets slot 0 to 1, then back to 0, + it will be charged `20000 + 200 - 19800 = 400` gas. +* A contract with empty storage that increments slot 0 5 times will be + charged `20000 + 5 * 200 = 21000` gas. +* A balance transfer from account A to account B followed by a + transfer from B to C, with all accounts having nonzero starting and + ending balances, it will cost `5000 * 3 + 200 - 4800 = 10400` gas. + +In order to keep in place the implicit reentrancy protection of +existing contracts, transactions should not be allowed to modify state +if the remaining gas is lower then the gas stipend given to +"transfer"/"send" in Solidity. These are other proposed remediations +and objections to implementing them: + +* Drop EIP-1283 and abstain from modifying `SSTORE` cost + * EIP-1283 is an important update + * It was accepted and implemented on test networks and in clients. +* Add a new call context that permits LOG opcodes but not changes to state. + * Adds another call type beyond existing regular/staticcall +* Raise the cost of `SSTORE` to dirty slots to >=2300 gas + * Makes net gas metering much less useful. +* Reduce the gas stipend + * Makes the stipend almost useless. +* Increase the cost of writes to dirty slots back to 5000 gas, but add + 4800 gas to the refund counter + * Still doesn’t make the invariant explicit. + * Requires callers to supply more gas, just to have it refunded +* Add contract metadata specifying per-contract EVM version, and only + apply `SSTORE` changes to contracts deployed with the new version. + +## Backwards Compatibility + +This EIP requires a hard fork to implement. No gas cost increase is +anticipated, and many contracts will see gas reduction. + +Performing `SSTORE` has never been possible with less than 5000 gas, so +it does not introduce incompatibility to the Ethereum Mainnet. Gas +estimation should account for this requirement. + +## Test Cases + +| Code | Used Gas | Refund | Original | 1st | 2nd | 3rd | +|------------------------------------|----------|--------|----------|-----|-----|-----| +| `0x60006000556000600055` | 1612 | 0 | 0 | 0 | 0 | | +| `0x60006000556001600055` | 20812 | 0 | 0 | 0 | 1 | | +| `0x60016000556000600055` | 20812 | 19200 | 0 | 1 | 0 | | +| `0x60016000556002600055` | 20812 | 0 | 0 | 1 | 2 | | +| `0x60016000556001600055` | 20812 | 0 | 0 | 1 | 1 | | +| `0x60006000556000600055` | 5812 | 15000 | 1 | 0 | 0 | | +| `0x60006000556001600055` | 5812 | 4200 | 1 | 0 | 1 | | +| `0x60006000556002600055` | 5812 | 0 | 1 | 0 | 2 | | +| `0x60026000556000600055` | 5812 | 15000 | 1 | 2 | 0 | | +| `0x60026000556003600055` | 5812 | 0 | 1 | 2 | 3 | | +| `0x60026000556001600055` | 5812 | 4200 | 1 | 2 | 1 | | +| `0x60026000556002600055` | 5812 | 0 | 1 | 2 | 2 | | +| `0x60016000556000600055` | 5812 | 15000 | 1 | 1 | 0 | | +| `0x60016000556002600055` | 5812 | 0 | 1 | 1 | 2 | | +| `0x60016000556001600055` | 1612 | 0 | 1 | 1 | 1 | | +| `0x600160005560006000556001600055` | 40818 | 19200 | 0 | 1 | 0 | 1 | +| `0x600060005560016000556000600055` | 10818 | 19200 | 1 | 0 | 1 | 0 | + +## Implementation + +To be added. + +## Appendix: Proof + +Because the *storage slot's original value* is defined as the value +when a reversion happens on the *current transaction*, it's easy to +see that call frames won't interfere `SSTORE` gas calculation. So +although the below proof is discussed without call frames, it applies +to all situations with call frames. We will discuss the case +separately for *original value* being zero and not zero, and use +*induction* to prove some properties of `SSTORE` gas cost. + +*Final value* is the value of a particular storage slot at the end of +a transaction. *Absolute gas used* is the absolute value of *gas used* +minus *refund*. We use `N` to represent the total number of `SSTORE` +operations on a storage slot. For states discussed below, refer to +*State Transition* in *Explanation* section. + +Below we do the proof under the assumption that all parameters are +unchanged, meaning `SLOAD_GAS` is `200`. However, note that the proof +still applies no matter how `SLOAD_GAS` is changed. + +### Original Value Being Zero + +When *original value* is 0, we want to prove that: + +* **Case I**: If the *final value* ends up still being 0, we want to charge `200 * + N` gases, because no disk write is needed. +* **Case II**: If the *final value* ends up being a non-zero value, we want to + charge `20000 + 200 * (N-1)` gas, because it requires writing this + slot to disk. + +#### Base Case + +We always start at state A. The first `SSTORE` can: + +* Go to state A: 200 gas is deducted. We satisfy *Case I* because + `200 * N == 200 * 1`. +* Go to state B: 20000 gas is deducted. We satisfy *Case II* because + `20000 + 200 * (N-1) == 20000 + 200 * 0`. + +#### Inductive Step + +* From A to A. The previous gas cost is `200 * (N-1)`. The current + gas cost is `200 + 200 * (N-1)`. It satisfy *Case I*. +* From A to B. The previous gas cost is `200 * (N-1)`. The current + gas cost is `20000 + 200 * (N-1)`. It satisfy *Case II*. +* From B to B. The previous gas cost is `20000 + 200 * (N-2)`. The + current gas cost is `200 + 20000 + 200 * (N-2)`. It satisfy + *Case II*. +* From B to A. The previous gas cost is `20000 + 200 * (N-2)`. The + current gas cost is `200 - 19800 + 20000 + 200 * (N-2)`. It satisfy + *Case I*. + +### Original Value Not Being Zero + +When *original value* is not 0, we want to prove that: + +* **Case I**: If the *final value* ends up unchanged, we want to + charge `200 * N` gases, because no disk write is needed. +* **Case II**: If the *final value* ends up being zero, we want to + charge `5000 - 15000 + 200 * (N-1)` gas. Note that `15000` is the + refund in actual definition. +* **Case III**: If the *final value* ends up being a changed non-zero + value, we want to charge `5000 + 200 * (N-1)` gas. + +#### Base Case + +We always start at state X. The first `SSTORE` can: + +* Go to state X: 200 gas is deducted. We satisfy *Case I* because + `200 * N == 200 * 1`. +* Go to state Y: 5000 gas is deducted. We satisfy *Case III* because + `5000 + 200 * (N-1) == 5000 + 200 * 0`. +* Go to state Z: The absolute gas used is `5000 - 15000` where 15000 + is the refund. We satisfy *Case II* because `5000 - 15000 + 200 * + (N-1) == 5000 - 15000 + 200 * 0`. + +#### Inductive Step + +* From X to X. The previous gas cost is `200 * (N-1)`. The current gas + cost is `200 + 200 * (N-1)`. It satisfy *Case I*. +* From X to Y. The previous gas cost is `200 * (N-1)`. The current gas + cost is `5000 + 200 * (N-1)`. It satisfy *Case III*. +* From X to Z. The previous gas cost is `200 * (N-1)`. The current + absolute gas cost is `5000 - 15000 + 200 * (N-1)`. It satisfy *Case + II*. +* From Y to X. The previous gas cost is `5000 + 200 * (N-2)`. The + absolute current gas cost is `200 - 4800 + 5000 + 200 * (N-2)`. It + satisfy *Case I*. +* From Y to Y. The previous gas cost is `5000 + 200 * (N-2)`. The + current gas cost is `200 + 5000 + 200 * (N-2)`. It satisfy *Case + III*. +* From Y to Z. The previous gas cost is `5000 + 200 * (N-2)`. The + current absolute gas cost is `200 - 15000 + 5000 + 200 * (N-2)`. It + satisfy *Case II*. +* From Z to X. The previous gas cost is `5000 - 15000 + 200 * + (N-2)`. The current absolute gas cost is `200 + 10200 + 5000 - + 15000 + 200 * (N-2)`. It satisfy *Case I*. +* From Z to Y. The previous gas cost is `5000 - 15000 + 200 * + (N-2)`. The current absolute gas cost is `200 + 15000 + 5000 - + 15000 + 200 * (N-2)`. It satisfy *Case III*. +* From Z to Z. The previous gas cost is `5000 - 15000 + 200 * + (N-2)`. The current absolute gas cost is `200 + 5000 - 15000 + 200 * + (N-2)`. It satisfy *Case II*. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). + +[EIP-1283]: ./eip-1283.md +[EIP-1706]: ./eip-1706.md +[EIP-1884]: ./eip-1884.md +[EIP-1087]: ./eip-1087.md +[EIP-1153]: ./eip-1153.md +[EIP-658]: ./eip-658.md diff --git a/EIPS/eip-2228.md b/EIPS/eip-2228.md new file mode 100644 index 0000000..c8ac1a4 --- /dev/null +++ b/EIPS/eip-2228.md @@ -0,0 +1,95 @@ +--- +eip: 2228 +title: Canonicalize the name of network ID 1 and chain ID 1 +author: William Entriken (@fulldecent) +discussions-to: https://github.com/ethereum/EIPs/issues/2228 +status: Final +type: Informational +created: 2019-08-04 +--- + +## Simple Summary + +The Ethereum network with network ID 1 and chain ID 1 is named Ethereum Mainnet. + +## Abstract + +The name for the Ethereum network with network ID 1 and chain ID 1 shall be Ethereum Mainnet or just Mainnet. This is a proper noun. + +This standard specifies the name for this network and provides reference examples in an effort to standardize the word choice and provide a common language for use to refer to this network. + +## Motivation + +The Ethereum network with network ID 1 and chain ID 1 is referenced using several conflicting names across EIPs, client implementations, and information published on the internet at large. In several locations, even documents written by the same author use inconsistent names to refer to the Ethereum network with network ID 1 and chain ID 1. Names in use at the time of this writing include: + +* "main net" +* "mainnet" +* "Main net" +* "Mainnet" + +## Specification + +The network name for network ID 1 and chain ID 1 shall be Ethereum Mainnet, or just Mainnet if the context is known to be discussing Ethereum networks. This IS a proper noun. Several examples are given below which differentiate between usage of the name of the network versus a descriptive reference to the network. + +Any name or word styling (i.e. capitalization of the letters) of the network which is inconsistent with the test cases cited below shall NOT be used. + +### Trademark note + +"Ethereum" is trademarked by the Ethereum Foundation. For more information on your obligations when mentioning "Ethereum", and possibly "Ethereum Mainnet", see: + +* USPTO registration number 5110579 by Ethereum Foundation +* The note "you must not use [this mark] without the prior written permission of the Foundation" on the Ethereum Foundation website, Terms of Use page + +## Rationale + +Choosing common word use promotes interoperability of implementations and increases customer awareness. Also, it adds a sense of professionalism when customers see the same word and word styling (i.e. capitalization of letters) across different implementations. + +Anybody that has travelled to certain countries and seen an "IPhone [sic]" repair store should immediately recognize that this is off-brand and unofficial. Likewise, the astute customer of Ethereum should recognize if they see the network referred to using inconsistent names in different software, so let's avoid this. + +## Backwards Compatibility + +- MetaMask previously used "Main Ethereum Network" in the account network chooser. MetaMask has been updated consistent with this EIP. + +- References to Mainnet that are inconsistent with this specification are made in: [EIP-2](./eip-2.md), [EIP-779](./eip-779.md), [EIP-150](./eip-150.md), [EIP-155](./eip-155.md), [EIP-190](./eip-190.md), [EIP-225](./eip-225.md), [EIP-1013](./eip-1013.md), [EIP-2028](./eip-2028.md), and [EIP-2387](./eip-2387.md). For consistency, we recommend the editor will update EIPs to consistently use the name as specified in this EIP. + +## Test Cases + +### Examples referencing the name of the network ✅ + +> The contract was deployed to Ethereum Mainnet. + +> Ethereum runs many applications, this Dapp was deployed to Mainnet. + +No specification is made on whether Dapp, dapp, dApp, etc. is preferred. + +> SWITCH TO MAINNET + +This example shows a user interface which is in uppercase. To be semantically correct, this could be written in HTML as `Switch to Mainnet`. + +> switch to mainnet + +This example shows a user interface which is in lowercase. To be semantically correct, this could be written in HTML as `Switch to Mainnet`. + +### Examples referencing the network in a descriptive way ✅ + +> Mainnet has ### times the number of transactions as the test networks. + +### Examples of other correct word usage ✅ + +> The main network on Ethereum is Mainnet + +This shows that "main" is used as a descriptive word, but Mainnet is the specific network which is having network ID 1 and chain ID 1. + +### Examples of poor word choice (avoid this) ❌ + +> Deploy your contract to the Ethereum main network. + +This is referring to a "main" network which is context-dependent. If you were reading this text on a page about Ethereum Classic, they would be referring to network ID 2 and chain ID 62. Therefore this word usage is less crisp. Do NOT use wording like this. + +> Connect to mainnet. + +These words literally mean nothing. The lowercase, not-proper-noun word "mainnet" is not a plain English word and it should not be in any dictionary. Do NOT use wording like this. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-223.md b/EIPS/eip-223.md new file mode 100644 index 0000000..9392bcd --- /dev/null +++ b/EIPS/eip-223.md @@ -0,0 +1,366 @@ +--- +eip: 223 +title: 223 Token with communication model +description: Token with event handling and communication model +author: Dexaran (@Dexaran) +discussions-to: https://ethereum-magicians.org/t/erc-223-token-standard/12894 +status: Draft +type: Standards Track +category: ERC +created: 2017-05-03 +--- + +## Abstract + +The following describes an interface for fungible tokens that supports a `tokenReceived` callback to notify contract recipients when tokens are received. + +## Motivation + +This token introduces a communication model for contracts that can be utilized to straighten the behavior of contracts that interact with such tokens. Specifically, this proposal: + +1. Informs receiving contracts of tokens, as opposed to [ERC-20](./eip-20.md) where the recipient of a token transfer gets no notification. +2. Is more gas-efficient when depositing tokens to contracts. +3. Allows for `_data` recording for financial transfers. + +## Specification + +Contracts intending to receive these tokens MUST implement `tokenReceived`. + +Token transfers to contracts not implementing `tokenReceived` as described below MUST revert. + +### Token contract + +#### Token Methods + +##### `totalSupply` + +```solidity +function totalSupply() view returns (uint256 totalSupply) +``` + +Gets the total supply of the token. The functionality of this method is identical to that of ERC-20. + +##### `name` + +```solidity +function name() view returns (string _name) +``` + +Gets the name of the token. The functionality of this method is identical to that of ERC-20. + +OPTIONAL - This method can be used to improve usability, but interfaces and other contracts MUST NOT expect these values to be present. + +##### `symbol` + +```solidity +function symbol() view returns (bytes32 _symbol) +``` + +Gets the symbol of the token. The functionality of this method is identical to that of ERC-20. + +OPTIONAL - This method can be used to improve usability, but interfaces and other contracts MUST NOT expect these values to be present. + +##### `decimals` + +```solidity +function decimals() view returns (uint8 _decimals) +``` + +Gets the number of decimals of the token. The functionality of this method is identical to that of ERC-20. + +OPTIONAL - This method can be used to improve usability, but interfaces and other contracts MUST NOT expect these values to be present. + +##### `balanceOf` + +```solidity +function balanceOf(address _owner) view returns (uint256 balance) +``` + +Gets the account balance of another account with address `_owner`. The functionality of this method is identical to that of ERC-20. + +##### `transfer(address, uint)` + +```solidity +function transfer(address _to, uint _value) returns (bool) +``` + +This function must transfer tokens, and if `_to` is a contract, it must call the `tokenReceived(address, uint256, bytes calldata)` function of `_to`. If the `tokenReceived` function is not implemented in `_to` (recipient contract), then the transaction must fail and the transfer of tokens must be reverted. +If `_to` is an externally owned address, then the transaction must be sent without executing `tokenReceived` in `_to`. + `_data` can be attached to this token transaction, but it requires more gas. `_data` can be empty. + +##### `transfer(address, uint, bytes)` + +```solidity +function transfer(address _to, uint _value, bytes calldata _data) returns (bool) +``` + +This function must transfer tokens and invoke the function `tokenReceived (address, uint256, bytes)` in `_to`, if `_to` is a contract. If the `tokenReceived` function is not implemented in `_to` (recipient contract), then the transaction must fail and the transfer of tokens must not occur. +If `_to` is an externally owned address (determined by the code size being zero), then the transaction must be sent without executing `tokenReceived` in `_to`. + `_data` can be attached to this token transaction, but it requires more gas. `_data` can be empty. + +NOTE: A possible way to check whether the `_to` is a contract or an address is to assemble the code of `_to`. If there is no code in `_to`, then this is an externally owned address, otherwise it's a contract.116 + +#### Events + +##### `Transfer` + +```solidity +event Transfer(address indexed _from, address indexed _to, uint256 _value, bytes _data) +``` + +Triggered when tokens are transferred. Compatible with and similar to the ERC-20 `Transfer` event. + +### [ERC-223](./eip-223.md) Token Receiver + +#### Receiver Methods + +```solidity +function tokenReceived(address _from, uint _value, bytes calldata _data) +``` + +A function for handling token transfers, which is called from the token contract, when a token holder sends tokens. `_from` is the address of the sender of the token, `_value` is the amount of incoming tokens, and `_data` is attached data similar to `msg.data` of Ether transactions. It works by analogy with the fallback function of Ether transactions and returns nothing. + +NOTE: `msg.sender` will be a token-contract inside the `tokenReceived` function. It may be important to filter which tokens are sent (by token-contract address). The token sender (the person who initiated the token transaction) will be `_from` inside the `tokenReceived` function. + +IMPORTANT: This function must be named `tokenReceived` and take parameters `address`, `uint256`, `bytes` to match the function signature `0x8943ec02`. + +## Rationale + +This standard introduces a communication model by enforcing the `transfer` to execute a handler function in the destination address. This is an important security consideration as it is required that the receiver explicitly implements the token handling function. In cases where the receiver does not implements such function the transfer MUST be reverted. + +This standard sticks to the push transaction model where the transfer of assets is initiated on the senders side and handled on the receivers side. As the result, ERC-223 transfers are more gas-efficient while dealing with depositing to contracts as ERC-223 tokens can be deposited with just one transaction while ERC-20 tokens require at least two calls (one for `approve` and the second that will invoke `transferFrom`). + +- [ERC-20](./eip-20.md) deposit: `approve` ~46 gas, `transferFrom` ~75K gas + +- ERC-223 deposit: `transfer` and handling on the receivers side ~54K gas + +This standard introduces the ability to correct user errors by allowing to handle ANY transactions on the recipient side and reject incorrect or improper transactions. This tokens utilize ONE transferring method for both types of interactions with tokens and externally owned addresses which can simplify the user experience and allow to avoid possible user mistakes. + +One downside of the commonly used [ERC-20](./eip-20.md) standard that ERC-223 is intended to solve is that [ERC-20](./eip-20.md) implements two methods of token transferring: (1) `transfer` function and (2) `approve + transferFrom` pattern. Transfer function of [ERC-20](./eip-20.md) standard does not notify the receiver and therefore if any tokens are sent to a contract with the `transfer` function then the receiver will not recognize this transfer and the tokens can become stuck in the receivers address without any possibility of recovering them. + +ERC-223 is intended to simplify the interaction with contracts that are intended to work with tokens. ERC-223 utilizes a "deposit" pattern, similar to that of plain Ether. An ERC-223 deposit to a contract is a simple call of the `transfer` function. This is one transaction as opposed to two step process of `approve + transferFrom` depositing. + +This standard allows payloads to be attached to transactions using the `bytes calldata _data` parameter, which can encode a second function call in the destination address, similar to how `msg.data` does in an Ether transaction, or allow for public logging on chain should it be necessary for financial transactions. + +## Backwards Compatibility + +The interface of this token is similar to that of ERC-20 and most functions serve the same purpose as their analogues in ERC-20. +`transfer(address, uint256, bytes calldata)` function is not backwards compatible with ERC-20 interface. + +ERC-20 tokens can be delivered to a non-contract address with `transfer` function. ERC-20 tokens can be deposited to a contract address with `approve` + `transferFrom` pattern. Depositing ERC-20 tokens to the contract address with `transfer` function will always result in token deposit not being recognized by the recipient contract. + +Here is an example of the contract code that handles ERC-20 token deposit. The following contract can accepts `tokenA` deposits. It is impossible to prevent deposits of tokenA or any other ERC-20 token to this contract that are made with `transfer` function. + +```solidity +contract ERC20Receiver +{ + event Deposit(); + address tokenA; + function deposit(uint _value, address _token) public + { + require(_token == tokenA); + IERC20(_token).transferFrom(msg.sender, address(this), _value); + emit Deposit(); + } +} +``` + +ERC-223 tokens must be delivered to non-contract address or contract address in the same way with `transfer` function. + +Here is an example of the contract code that handles ERC-223 token deposit. The following contract can filter tokens and only accepts `tokenA`. Other ERC-223 tokens would be rejected. + +```solidity +contract ERC223Receiver +{ + event Deposit(); + address tokenA; + function tokenReceived(address _from, uint _value, bytes memory _data) public + { + require(msg.sender == tokenA); + emit Deposit(); + } +} +``` + +## Security Considerations + +This token utilizes the model similar to plain Ether behavior. Therefore replay issues must be taken into account. + +### Reference Implementation + +```solidity +pragma solidity ^0.8.19; + +library Address { + /** + * @dev Returns true if `account` is a contract. + * + * This test is non-exhaustive, and there may be false-negatives: during the + * execution of a contract's constructor, its address will be reported as + * not containing a contract. + * + * > It is unsafe to assume that an address for which this function returns + * false is an externally-owned account (EOA) and not a contract. + */ + function isContract(address account) internal view returns (bool) { + // This method relies in extcodesize, which returns 0 for contracts in + // construction, since the code is only stored at the end of the + // constructor execution. + + uint256 size; + // solhint-disable-next-line no-inline-assembly + assembly { size := extcodesize(account) } + return size > 0; + } +} + +abstract contract IERC223Recipient { +/** + * @dev Standard ERC223 function that will handle incoming token transfers. + * + * @param _from Token sender address. + * @param _value Amount of tokens. + * @param _data Transaction metadata. + */ + function tokenReceived(address _from, uint _value, bytes memory _data) public virtual; +} + +/** + * @title Reference implementation of the ERC223 standard token. + */ +contract ERC223Token { + + /** + * @dev Event that is fired on successful transfer. + */ + event Transfer(address indexed from, address indexed to, uint value, bytes data); + + string private _name; + string private _symbol; + uint8 private _decimals; + uint256 private _totalSupply; + + mapping(address => uint256) public balances; // List of user balances. + + /** + * @dev Sets the values for {name} and {symbol}, initializes {decimals} with + * a default value of 18. + * + * To select a different value for {decimals}, use {_setupDecimals}. + * + * All three of these values are immutable: they can only be set once during + * construction. + */ + + constructor(string memory new_name, string memory new_symbol, uint8 new_decimals) + { + _name = new_name; + _symbol = new_symbol; + _decimals = new_decimals; + } + + /** + * @dev Returns the name of the token. + */ + function name() public view returns (string memory) + { + return _name; + } + + /** + * @dev Returns the symbol of the token, usually a shorter version of the + * name. + */ + function symbol() public view returns (string memory) + { + return _symbol; + } + + /** + * @dev Returns the number of decimals used to get its user representation. + * For example, if `decimals` equals `2`, a balance of `505` tokens should + * be displayed to a user as `5,05` (`505 / 10 ** 2`). + * + * Tokens usually opt for a value of 18, imitating the relationship between + * Ether and Wei. This is the value {ERC223} uses, unless {_setupDecimals} is + * called. + * + * NOTE: This information is only used for _display_ purposes: it in + * no way affects any of the arithmetic of the contract, including + * {IERC223-balanceOf} and {IERC223-transfer}. + */ + function decimals() public view returns (uint8) + { + return _decimals; + } + + /** + * @dev See {IERC223-totalSupply}. + */ + function totalSupply() public view returns (uint256) + { + return _totalSupply; + } + + + /** + * @dev Returns balance of the `_owner`. + * + * @param _owner The address whose balance will be returned. + * @return balance Balance of the `_owner`. + */ + function balanceOf(address _owner) public view returns (uint256) + { + return balances[_owner]; + } + + /** + * @dev Transfer the specified amount of tokens to the specified address. + * Invokes the `tokenFallback` function if the recipient is a contract. + * The token transfer fails if the recipient is a contract + * but does not implement the `tokenFallback` function + * or the fallback function to receive funds. + * + * @param _to Receiver address. + * @param _value Amount of tokens that will be transferred. + * @param _data Transaction metadata. + */ + function transfer(address _to, uint _value, bytes calldata _data) public returns (bool success) + { + // Standard function transfer similar to ERC20 transfer with no _data . + // Added due to backwards compatibility reasons . + balances[msg.sender] = balances[msg.sender] - _value; + balances[_to] = balances[_to] + _value; + if(Address.isContract(_to)) { + IERC223Recipient(_to).tokenReceived(msg.sender, _value, _data); + } + emit Transfer(msg.sender, _to, _value, _data); + return true; + } + + /** + * @dev Transfer the specified amount of tokens to the specified address. + * This function works the same with the previous one + * but doesn't contain `_data` param. + * Added due to backwards compatibility reasons. + * + * @param _to Receiver address. + * @param _value Amount of tokens that will be transferred. + */ + function transfer(address _to, uint _value) public returns (bool success) + { + bytes memory _empty = hex"00000000"; + balances[msg.sender] = balances[msg.sender] - _value; + balances[_to] = balances[_to] + _value; + if(Address.isContract(_to)) { + IERC223Recipient(_to).tokenReceived(msg.sender, _value, _empty); + } + emit Transfer(msg.sender, _to, _value, _empty); + return true; + } +} +``` + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2242.md b/EIPS/eip-2242.md new file mode 100644 index 0000000..7dc308c --- /dev/null +++ b/EIPS/eip-2242.md @@ -0,0 +1,60 @@ +--- +eip: 2242 +title: Transaction Postdata +author: John Adler (@adlerjohn) +discussions-to: https://ethereum-magicians.org/t/eip-2242-transaction-postdata/3557 +status: Stagnant +type: Standards Track +category: Core +created: 2019-08-16 +--- + +## Simple Summary +An additional, optional transaction field is added for "postdata," data that is posted on-chain but that cannot be read from the EVM. + +## Abstract +A paradigm shift in how blockchains are used has been seen recently in Eth 2.0, with the rise of [_Execution Environments_](https://notes.ethereum.org/w1Pn2iMmSTqCmVUTGV4T5A?view) (EEs), and [_stateless clients_](https://ethresear.ch/t/the-stateless-client-concept/172). This shift involves blockchains serving as a secure data availability and arbitration layer, _i.e._, they provide a globally-accepted source of available data, and process fraud/validity and data availability proofs. This same paradigm can be applied on Eth 1.x, replacing EEs with [trust-minimized side chains](https://ethresear.ch/t/building-scalable-decentralized-payment-systems-request-for-feedback/5312). + +## Motivation +While [EIP-2028](./eip-2028.md) provides a reduction in gas cost of calldata, and is a step in the right direction of encouraging use of history rather than state, the EVM does not actually need to see all data that is posted on-chain. Following the principle of "don't pay for what you don't use," a distinct way of posting data on-chain, but without actually being usable within the EVM, is needed. + +For [trust-minimized side chains with fraud proofs](https://ethresear.ch/t/minimal-viable-merged-consensus/5617), we simply need to ensure that the side chain block proposer has attested that _some_ data is available. Authentication can be performed as part of a fraud proof should that data end up invalid. Note that [trust-minimized side chains with validity proofs](https://ethresear.ch/t/on-chain-scaling-to-potentially-500-tx-sec-through-mass-tx-validation/3477) can't make use of the changes proposed in this EIP, as they required immediate authentication of the posted data. This will be [the topic of a future EIP](https://ethresear.ch/t/multi-threaded-data-availability-on-eth-1/5899). + +## Specification +We propose a consensus modification, beginning at `FORK_BLKNUM`: + +An additional optional field, `postdata`, is added to transactions. Serialized transactions now have the format: +``` +"from": bytes20, +"to": bytes20, +"startGas": uint256, +"gasPrice": uint256, +"value": uint256, +"data": bytes, +"nonce": uint256, +["postdata": bytes], +``` +with witnesses signing over the [RLP encoding](https://github.com/ethereum/wiki/wiki/RLP) of the above. `postdata` is data that is posted on-chain, for later historical retrieval by layer-2 systems. + +`postdata` is an RLP-encoded twople `(version: uint64, data: bytes)`. +1. `version` is `0`. +1. `data` is an RLP-encoded list of binary data. This EIP does not interpret the data in any way, simply considering it as a binary blob, though future EIPs may introduce different interpretation schemes for different values of `version`. + +The gas cost of the posted data is `1 gas per byte`. This cost is deducted from the `startGas`; if the remaining gas is non-positive the transaction immediately reverts with an out of gas exception. + +## Rationale +The changes proposed are as minimal and non-disruptive to the existing EVM and transaction format as possible while also supporting possible [future extensions](https://ethresear.ch/t/multi-threaded-data-availability-on-eth-1/5899) through a version code. + +## Backwards Compatibility +The new transaction format is backwards compatible, as the new `postdata` field is optionally appended to existing transactions. + +The proposed changes are not forwards-compatible, and will require a hard fork. + +## Test Cases +TODO + +## Implementation +TODO + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-225.md b/EIPS/eip-225.md new file mode 100644 index 0000000..ce084a7 --- /dev/null +++ b/EIPS/eip-225.md @@ -0,0 +1,478 @@ +--- +eip: 225 +title: Clique proof-of-authority consensus protocol +author: Péter Szilágyi +discussions-to: https://github.com/ethereum/EIPs/issues/225 +status: Final +type: Standards Track +category: Core +created: 2017-03-06 +--- + +## Abstract + +Clique is a proof-of-authority consensus protocol. It shadows the design of Ethereum mainnet, so it can be added to any client with minimal effort. + +## Motivation + +Ethereum's first official testnet was Morden. It ran from July 2015 to about November 2016, when due to the accumulated junk and some testnet consensus issues between Geth and Parity, it was finally laid to rest in favor of a testnet reboot. + +Ropsten was thus born, clearing out all the junk and starting with a clean slate. This ran well until the end of February 2017, when malicious actors decided to abuse the low PoW and gradually inflate the block gas limits to 9 billion (from the normal 4.7 million), at which point sending in gigantic transactions crippling the entire network. Even before that, attackers attempted multiple extremely long reorgs, causing network splits between different clients, and even different versions. + +The root cause of these attacks is that a PoW network is only as secure as the computing capacity placed behind it. Restarting a new testnet from zero wouldn't solve anything, since the attacker can mount the same attack over and over again. The Parity team decided to go with an emergency solution of rolling back a significant number of blocks, and enacting a soft-fork rule that disallows gas limits above a certain threshold. + +While this solution may work in the short term: + +* It's not elegant: Ethereum supposed to have dynamic block limits +* It's not portable: other clients need to implement new fork logic themselves +* It's not compatible with sync modes: fast and light clients are both out of luck +* It's just prolonging the attacks: junk can still be steadily pushed in ad infinitum + +Parity's solution although not perfect, is nonetheless workable. I'd like to propose a longer term alternative solution, which is more involved, yet should be simple enough to allow rolling out in a reasonable amount of time. + +### Standardized proof-of-authority + +As reasoned above, proof-of-work cannot work securely in a network with no value. Ethereum has its long term goal of proof-of-stake based on Casper, but that is heavy research so we cannot rely on that any time soon to fix today's problems. One solution however is easy enough to implement, yet effective enough to fix the testnet properly, namely a proof-of-authority scheme. + +The main design goals of the PoA protocol described here is that it should be very simple to implement and embed into any existing Ethereum client, while at the same time allow using existing sync technologies (fast, light, warp) without needing client developers to add custom logic to critical software. + +## Design constraints + +There are two approaches to syncing a blockchain in general: + + * The classical approach is to take the genesis block and crunch through all the transactions one by one. This is tried and proven, but in Ethereum complexity networks quickly turns out to be very costly computationally. + * The other is to only download the chain of block headers and verify their validity, after which point an arbitrary recent state may be downloaded from the network and checked against recent headers. + +A PoA scheme is based on the idea that blocks may only be minted by trusted signers. As such, every block (or header) that a client sees can be matched against the list of trusted signers. The challenge here is how to maintain a list of authorized signers that can change in time? The obvious answer (store it in an Ethereum contract) is also the wrong answer: fast, light and warp sync don't have access to the state during syncing. + +**The protocol of maintaining the list of authorized signers must be fully contained in the block headers.** + +The next obvious idea would be to change the structure of the block headers so it drops the notions of PoW, and introduces new fields to cater for voting mechanisms. This is also the wrong answer: changing such a core data structure in multiple implementations would be a nightmare development, maintenance and security wise. + +**The protocol of maintaining the list of authorized signers must fit fully into the current data models.** + +So, according to the above, we can't use the EVM for voting, rather have to resort to headers. And we can't change header fields, rather have to resort to the currently available ones. Not much wiggle room. + +### Repurposing header fields for signing and voting + +The most obvious field that currently is used solely as *fun metadata* is the 32 byte **extra-data** section in block headers. Miners usually place their client and version in there, but some fill it with alternative "messages". The protocol would extend this field ~~to~~ with 65 bytes with the purpose of a secp256k1 miner signature. This would allow anyone obtaining a block to verify it against a list of authorized signers. It also makes the **miner** section in block headers obsolete (since the address can be derived from the signature). + +*Note, changing the length of a header field is a non invasive operation as all code (such as RLP encoding, hashing) is agnostic to that, so clients wouldn't need custom logic.* + +The above is enough to validate a chain, but how can we update a dynamic list of signers. The answer is that we can repurpose the newly obsoleted **miner** field and the PoA obsoleted **nonce** field to create a voting protocol: + + * During regular blocks, both of these fields would be set to zero. + * If a signer wishes to enact a change to the list of authorized signers, it will: + * Set the **miner** to the signer it wishes to vote about + * Set the **nonce** to `0` or `0xff...f` to vote in favor of adding or kicking out + +Any clients syncing the chain can "tally" up the votes during block processing, and maintain a dynamically changing list of authorized signers by popular vote. + +To avoid having an infinite window to tally up votes in, and also to allow periodically flushing stale proposals, we can reuse the concept of an epoch from ethash, where every epoch transition flushes all pending votes. Furthermore, these epoch transitions can also act as stateless checkpoints containing the list of current authorized signers within the header extra-data. This permits clients to sync up based only on a checkpoint hash without having to replay all the voting that was done on the chain up to that point. It also allows the genesis header to fully define the chain, containing the list of initial signers. + +### Attack vector: Malicious signer + +It may happen that a malicious user gets added to the list of signers, or that a signer key/machine is compromised. In such a scenario the protocol needs to be able to defend itself against reorganizations and spamming. The proposed solution is that given a list of N authorized signers, any signer may only mint 1 block out of every K. This ensures that damage is limited, and the remainder of the miners can vote out the malicious user. + +### Attack vector: Censoring signer + +Another interesting attack vector is if a signer (or group of signers) attempts to censor out blocks that vote on removing them from the authorization list. To work around this, we restrict the allowed minting frequency of signers to 1 out of N/2. This ensures that malicious signers need to control at least 51% of signing accounts, at which case it's game over anyway. + +### Attack vector: Spamming signer + +A final small attack vector is that of malicious signers injecting new vote proposals inside every block they mint. Since nodes need to tally up all votes to create the actual list of authorized signers, they need to track all votes through time. Without placing a limit on the vote window, this could grow slowly, yet unbounded. The solution is to place a ~~moving~~ window of W blocks after which votes are considered stale. ~~A sane window might be 1-2 epochs.~~ We'll call this an epoch. + +### Attack vector: Concurrent blocks + +If the number of authorized signers are N, and we allow each signer to mint 1 block out of K, then at any point in time N-K+1 miners are allowed to mint. To avoid these racing for blocks, every signer would add a small random "offset" to the time it releases a new block. This ensures that small forks are rare, but occasionally still happen (as on the main net). If a signer is caught abusing it's authority and causing chaos, it can be voted out. + +## Specification + +We define the following constants: + + * **`EPOCH_LENGTH`**: Number of blocks after which to checkpoint and reset the pending votes. + * Suggested `30000` for the testnet to remain analogous to the mainnet `ethash` epoch. + * **`BLOCK_PERIOD`**: Minimum difference between two consecutive block's timestamps. + * Suggested `15s` for the testnet to remain analogous to the mainnet `ethash` target. + * **`EXTRA_VANITY`**: Fixed number of extra-data prefix bytes reserved for signer *vanity*. + * Suggested `32 bytes` to retain the current extra-data allowance and/or use. + * **`EXTRA_SEAL`**: Fixed number of extra-data suffix bytes reserved for signer seal. + * `65 bytes` fixed as signatures are based on the standard `secp256k1` curve. + * Filled with zeros on genesis block. + * **`NONCE_AUTH`**: Magic nonce number `0xffffffffffffffff` to vote on adding a new signer. + * **`NONCE_DROP`**: Magic nonce number `0x0000000000000000` to vote on removing a signer. + * **`UNCLE_HASH`**: Always `Keccak256(RLP([]))` as uncles are meaningless outside of PoW. + * **`DIFF_NOTURN`**: Block score (difficulty) for blocks containing out-of-turn signatures. + * Suggested `1` since it just needs to be an arbitrary baseline constant. + * **`DIFF_INTURN`**: Block score (difficulty) for blocks containing in-turn signatures. + * Suggested `2` to show a slight preference over out-of-turn signatures. + +We also define the following per-block constants: + + * **`BLOCK_NUMBER`**: Block height in the chain, where the height of the genesis is block `0`. + * **`SIGNER_COUNT`**: Number of authorized signers valid at a particular instance in the chain. + * **`SIGNER_INDEX`**: Zero-based index of the block signer in the sorted list of current authorized signers. + * **`SIGNER_LIMIT`**: Number of consecutive blocks out of which a signer may only sign one. + * Must be `floor(SIGNER_COUNT / 2) + 1` to enforce majority consensus on a chain. + +We repurpose the `ethash` header fields as follows: + + * **`beneficiary`** / **`miner`**: Address to propose modifying the list of authorized signers with. + * Should be filled with zeroes normally, modified only while voting. + * Arbitrary values are permitted nonetheless (even meaningless ones such as voting out non signers) to avoid extra complexity in implementations around voting mechanics. + * **Must** be filled with zeroes on checkpoint (i.e. epoch transition) blocks. + * Transaction execution **must** use the actual block signer (see `extraData`) for the `COINBASE` opcode and transaction fees **must** be attributed to the signer account. + * **`nonce`**: Signer proposal regarding the account defined by the `beneficiary` field. + * Should be **`NONCE_DROP`** to propose deauthorizing `beneficiary` as an existing signer. + * Should be **`NONCE_AUTH`** to propose authorizing `beneficiary` as a new signer. + * **Must** be filled with zeroes on checkpoint (i.e. epoch transition) blocks. + * **Must** not take up any other value apart from the two above (for now). + * **`extraData`**: Combined field for signer vanity, checkpointing and signer signatures. + * First **`EXTRA_VANITY`** bytes (fixed) may contain arbitrary signer vanity data. + * Last **`EXTRA_SEAL`** bytes (fixed) is the signer's signature sealing the header. + * Checkpoint blocks **must** contain a list of signers (`N*20 bytes`) in between, **omitted** otherwise. + * The list of signers in checkpoint block extra-data sections **must** be sorted in ascending byte order. + * **`mixHash`**: Reserved for fork protection logic, similar to the extra-data during the DAO. + * **Must** be filled with zeroes during normal operation. + * **`ommersHash`**: **Must** be **`UNCLE_HASH`** as uncles are meaningless outside of PoW. + * **`timestamp`**: **Must** be at least the parent timestamp + **`BLOCK_PERIOD`**. + * **`difficulty`**: Contains the standalone score of the block to derive the quality of a chain. + * **Must** be **`DIFF_NOTURN`** if `BLOCK_NUMBER % SIGNER_COUNT != SIGNER_INDEX` + * **Must** be **`DIFF_INTURN`** if `BLOCK_NUMBER % SIGNER_COUNT == SIGNER_INDEX` + +### Authorizing a block + +To authorize a block for the network, the signer needs to sign the block's sighash containing **everything except the signature itself**. This means that this hash contains every field of the header (`nonce` and `mixDigest` included), and also the `extraData` with the exception of the 65 byte signature suffix. The fields are hashed in the order of their definition in the yellow paper. Note that this sighash differs from the final block hash which also includes the signature. + +The sighash is signed using the standard `secp256k1` curve, and the resulting 65 byte signature (`R`, `S`, `V`, where `V` is `0` or `1`) is embedded into the `extraData` as the trailing 65 byte suffix. + +To ensure malicious signers (loss of signing key) cannot wreck havoc in the network, each singer is allowed to sign **maximum one** out of **`SIGNER_LIMIT`** consecutive blocks. The order is not fixed, but in-turn signing weighs more (**`DIFF_INTURN`**) than out of turn one (**`DIFF_NOTURN`**). + +#### Authorization strategies + +As long as signers conform to the above specs, they can authorize and distribute blocks as they see fit. The following suggested strategy will however reduce network traffic and small forks, so it's a suggested feature: + + * If a signer is allowed to sign a block (is on the authorized list and didn't sign recently). + * Calculate the optimal signing time of the next block (parent + **`BLOCK_PERIOD`**). + * If the signer is in-turn, wait for the exact time to arrive, sign and broadcast immediately. + * If the signer is out-of-turn, delay signing by `rand(SIGNER_COUNT * 500ms)`. + +This small strategy will ensure that the in-turn signer (who's block weighs more) has a slight advantage to sign and propagate versus the out-of-turn signers. Also the scheme allows a bit of scale with the increase of the number of signers. + +### Voting on signers + +Every epoch transition (genesis block included) acts as a stateless checkpoint, from which capable clients should be able to sync without requiring any previous state. This means epoch headers **must not** contain votes, all non settled votes are discarded, and tallying starts from scratch. + +For all non-epoch transition blocks: + + * Signers may cast one vote per own block to propose a change to the authorization list. + * Only the latest proposal per target beneficiary is kept from a single signer. + * Votes are tallied live as the chain progresses (concurrent proposals allowed). + * Proposals reaching majority consensus **`SIGNER_LIMIT`** come into effect immediately. + * Invalid proposals are **not** to be penalized for client implementation simplicity. + +**A proposal coming into effect entails discarding all pending votes for that proposal (both for and against) and starting with a clean slate.** + +#### Cascading votes + +A complex corner case may arise during signer deauthorization. When a previously authorized signer is dropped, the number of signers required to approve a proposal might decrease by one. This might cause one or more pending proposals to reach majority consensus, the execution of which might further cascade into new proposals passing. + +Handling this scenario is non obvious when multiple conflicting proposals pass simultaneously (e.g. add a new signer vs. drop an existing one), where the evaluation order might drastically change the outcome of the final authorization list. Since signers may invert their own votes in every block they mint, it's not so obvious which proposal would be "first". + +To avoid the pitfalls cascading executions would entail, the Clique proposal explicitly forbids cascading effects. In other words: **Only the `beneficiary` of the current header/vote may be added to/dropped from the authorization list. If that causes other proposals to reach consensus, those will be executed when their respective beneficiaries are "touched" again (given that majority consensus still holds at that point).** + +#### Voting strategies + +Since the blockchain can have small reorgs, a naive voting mechanism of "cast-and-forget" may not be optimal, since a block containing a singleton vote may not end up on the final chain. + +A simplistic but working strategy is to allow users to configure "proposals" on the signers (e.g. "add 0x...", "drop 0x..."). The signing code can then pick a random proposal for every block it signs and inject it. This ensures that multiple concurrent proposals as well as reorgs get eventually noted on the chain. + +This list may be expired after a certain number of blocks / epochs, but it's important to realize that "seeing" a proposal pass doesn't mean it won't get reorged, so it should not be immediately dropped when the proposal passes. + +## Test Cases + +```go +// block represents a single block signed by a parcitular account, where +// the account may or may not have cast a Clique vote. +type block struct { + signer string // Account that signed this particular block + voted string // Optional value if the signer voted on adding/removing someone + auth bool // Whether the vote was to authorize (or deauthorize) + checkpoint []string // List of authorized signers if this is an epoch block +} + +// Define the various voting scenarios to test +tests := []struct { + epoch uint64 // Number of blocks in an epoch (unset = 30000) + signers []string // Initial list of authorized signers in the genesis + blocks []block // Chain of signed blocks, potentially influencing auths + results []string // Final list of authorized signers after all blocks + failure error // Failure if some block is invalid according to the rules +}{ + { + // Single signer, no votes cast + signers: []string{"A"}, + blocks: []block{ + {signer: "A"} + }, + results: []string{"A"}, + }, { + // Single signer, voting to add two others (only accept first, second needs 2 votes) + signers: []string{"A"}, + blocks: []block{ + {signer: "A", voted: "B", auth: true}, + {signer: "B"}, + {signer: "A", voted: "C", auth: true}, + }, + results: []string{"A", "B"}, + }, { + // Two signers, voting to add three others (only accept first two, third needs 3 votes already) + signers: []string{"A", "B"}, + blocks: []block{ + {signer: "A", voted: "C", auth: true}, + {signer: "B", voted: "C", auth: true}, + {signer: "A", voted: "D", auth: true}, + {signer: "B", voted: "D", auth: true}, + {signer: "C"}, + {signer: "A", voted: "E", auth: true}, + {signer: "B", voted: "E", auth: true}, + }, + results: []string{"A", "B", "C", "D"}, + }, { + // Single signer, dropping itself (weird, but one less cornercase by explicitly allowing this) + signers: []string{"A"}, + blocks: []block{ + {signer: "A", voted: "A", auth: false}, + }, + results: []string{}, + }, { + // Two signers, actually needing mutual consent to drop either of them (not fulfilled) + signers: []string{"A", "B"}, + blocks: []block{ + {signer: "A", voted: "B", auth: false}, + }, + results: []string{"A", "B"}, + }, { + // Two signers, actually needing mutual consent to drop either of them (fulfilled) + signers: []string{"A", "B"}, + blocks: []block{ + {signer: "A", voted: "B", auth: false}, + {signer: "B", voted: "B", auth: false}, + }, + results: []string{"A"}, + }, { + // Three signers, two of them deciding to drop the third + signers: []string{"A", "B", "C"}, + blocks: []block{ + {signer: "A", voted: "C", auth: false}, + {signer: "B", voted: "C", auth: false}, + }, + results: []string{"A", "B"}, + }, { + // Four signers, consensus of two not being enough to drop anyone + signers: []string{"A", "B", "C", "D"}, + blocks: []block{ + {signer: "A", voted: "C", auth: false}, + {signer: "B", voted: "C", auth: false}, + }, + results: []string{"A", "B", "C", "D"}, + }, { + // Four signers, consensus of three already being enough to drop someone + signers: []string{"A", "B", "C", "D"}, + blocks: []block{ + {signer: "A", voted: "D", auth: false}, + {signer: "B", voted: "D", auth: false}, + {signer: "C", voted: "D", auth: false}, + }, + results: []string{"A", "B", "C"}, + }, { + // Authorizations are counted once per signer per target + signers: []string{"A", "B"}, + blocks: []block{ + {signer: "A", voted: "C", auth: true}, + {signer: "B"}, + {signer: "A", voted: "C", auth: true}, + {signer: "B"}, + {signer: "A", voted: "C", auth: true}, + }, + results: []string{"A", "B"}, + }, { + // Authorizing multiple accounts concurrently is permitted + signers: []string{"A", "B"}, + blocks: []block{ + {signer: "A", voted: "C", auth: true}, + {signer: "B"}, + {signer: "A", voted: "D", auth: true}, + {signer: "B"}, + {signer: "A"}, + {signer: "B", voted: "D", auth: true}, + {signer: "A"}, + {signer: "B", voted: "C", auth: true}, + }, + results: []string{"A", "B", "C", "D"}, + }, { + // Deauthorizations are counted once per signer per target + signers: []string{"A", "B"}, + blocks: []block{ + {signer: "A", voted: "B", auth: false}, + {signer: "B"}, + {signer: "A", voted: "B", auth: false}, + {signer: "B"}, + {signer: "A", voted: "B", auth: false}, + }, + results: []string{"A", "B"}, + }, { + // Deauthorizing multiple accounts concurrently is permitted + signers: []string{"A", "B", "C", "D"}, + blocks: []block{ + {signer: "A", voted: "C", auth: false}, + {signer: "B"}, + {signer: "C"}, + {signer: "A", voted: "D", auth: false}, + {signer: "B"}, + {signer: "C"}, + {signer: "A"}, + {signer: "B", voted: "D", auth: false}, + {signer: "C", voted: "D", auth: false}, + {signer: "A"}, + {signer: "B", voted: "C", auth: false}, + }, + results: []string{"A", "B"}, + }, { + // Votes from deauthorized signers are discarded immediately (deauth votes) + signers: []string{"A", "B", "C"}, + blocks: []block{ + {signer: "C", voted: "B", auth: false}, + {signer: "A", voted: "C", auth: false}, + {signer: "B", voted: "C", auth: false}, + {signer: "A", voted: "B", auth: false}, + }, + results: []string{"A", "B"}, + }, { + // Votes from deauthorized signers are discarded immediately (auth votes) + signers: []string{"A", "B", "C"}, + blocks: []block{ + {signer: "C", voted: "D", auth: true}, + {signer: "A", voted: "C", auth: false}, + {signer: "B", voted: "C", auth: false}, + {signer: "A", voted: "D", auth: true}, + }, + results: []string{"A", "B"}, + }, { + // Cascading changes are not allowed, only the account being voted on may change + signers: []string{"A", "B", "C", "D"}, + blocks: []block{ + {signer: "A", voted: "C", auth: false}, + {signer: "B"}, + {signer: "C"}, + {signer: "A", voted: "D", auth: false}, + {signer: "B", voted: "C", auth: false}, + {signer: "C"}, + {signer: "A"}, + {signer: "B", voted: "D", auth: false}, + {signer: "C", voted: "D", auth: false}, + }, + results: []string{"A", "B", "C"}, + }, { + // Changes reaching consensus out of bounds (via a deauth) execute on touch + signers: []string{"A", "B", "C", "D"}, + blocks: []block{ + {signer: "A", voted: "C", auth: false}, + {signer: "B"}, + {signer: "C"}, + {signer: "A", voted: "D", auth: false}, + {signer: "B", voted: "C", auth: false}, + {signer: "C"}, + {signer: "A"}, + {signer: "B", voted: "D", auth: false}, + {signer: "C", voted: "D", auth: false}, + {signer: "A"}, + {signer: "C", voted: "C", auth: true}, + }, + results: []string{"A", "B"}, + }, { + // Changes reaching consensus out of bounds (via a deauth) may go out of consensus on first touch + signers: []string{"A", "B", "C", "D"}, + blocks: []block{ + {signer: "A", voted: "C", auth: false}, + {signer: "B"}, + {signer: "C"}, + {signer: "A", voted: "D", auth: false}, + {signer: "B", voted: "C", auth: false}, + {signer: "C"}, + {signer: "A"}, + {signer: "B", voted: "D", auth: false}, + {signer: "C", voted: "D", auth: false}, + {signer: "A"}, + {signer: "B", voted: "C", auth: true}, + }, + results: []string{"A", "B", "C"}, + }, { + // Ensure that pending votes don't survive authorization status changes. This + // corner case can only appear if a signer is quickly added, removed and then + // readded (or the inverse), while one of the original voters dropped. If a + // past vote is left cached in the system somewhere, this will interfere with + // the final signer outcome. + signers: []string{"A", "B", "C", "D", "E"}, + blocks: []block{ + {signer: "A", voted: "F", auth: true}, // Authorize F, 3 votes needed + {signer: "B", voted: "F", auth: true}, + {signer: "C", voted: "F", auth: true}, + {signer: "D", voted: "F", auth: false}, // Deauthorize F, 4 votes needed (leave A's previous vote "unchanged") + {signer: "E", voted: "F", auth: false}, + {signer: "B", voted: "F", auth: false}, + {signer: "C", voted: "F", auth: false}, + {signer: "D", voted: "F", auth: true}, // Almost authorize F, 2/3 votes needed + {signer: "E", voted: "F", auth: true}, + {signer: "B", voted: "A", auth: false}, // Deauthorize A, 3 votes needed + {signer: "C", voted: "A", auth: false}, + {signer: "D", voted: "A", auth: false}, + {signer: "B", voted: "F", auth: true}, // Finish authorizing F, 3/3 votes needed + }, + results: []string{"B", "C", "D", "E", "F"}, + }, { + // Epoch transitions reset all votes to allow chain checkpointing + epoch: 3, + signers: []string{"A", "B"}, + blocks: []block{ + {signer: "A", voted: "C", auth: true}, + {signer: "B"}, + {signer: "A", checkpoint: []string{"A", "B"}}, + {signer: "B", voted: "C", auth: true}, + }, + results: []string{"A", "B"}, + }, { + // An unauthorized signer should not be able to sign blocks + signers: []string{"A"}, + blocks: []block{ + {signer: "B"}, + }, + failure: errUnauthorizedSigner, + }, { + // An authorized signer that signed recently should not be able to sign again + signers: []string{"A", "B"}, + blocks []block{ + {signer: "A"}, + {signer: "A"}, + }, + failure: errRecentlySigned, + }, { + // Recent signatures should not reset on checkpoint blocks imported in a batch + epoch: 3, + signers: []string{"A", "B", "C"}, + blocks: []block{ + {signer: "A"}, + {signer: "B"}, + {signer: "A", checkpoint: []string{"A", "B", "C"}}, + {signer: "A"}, + }, + failure: errRecentlySigned, + },, +} +``` + +## Implementation + +A reference implementation is part of [go-ethereum](https://github.com/ethereum/go-ethereum/tree/master/consensus/clique) and has been functioning as the consensus engine behind the [Rinkeby](https://www.rinkeby.io) testnet since April, 2017. +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2255.md b/EIPS/eip-2255.md new file mode 100644 index 0000000..14d31a8 --- /dev/null +++ b/EIPS/eip-2255.md @@ -0,0 +1,151 @@ +--- +eip: 2255 +title: Wallet Permissions System +description: An interface to restrict access to sensitive methods +author: Dan Finlay (@danfinlay), Erik Marks (@rekmarks), Gavin John (@Pandapip1) +discussions-to: https://ethereum-magicians.org/t/web3-login-permissions/3583 +status: Final +type: Standards Track +category: Interface +created: 2019-08-22 +requires: 1193 +--- + +## Abstract + +This EIP adds two new wallet-namespaced RPC endpoints, `wallet_getPermissions` and `wallet_requestPermissions`, providing a standard interface for requesting and checking permissions. + +## Motivation + +Wallets are responsible for mediating interactions between untrusted applications and users' keys through appropriate user consent. Today, wallets always prompt the user for every action. This provides security at the cost of substantial user friction. We believe that a single permissions request can achieve the same level of security with vastly improved UX. + +The pattern of permissions requests (typically using Oauth2) is common around the web, making it a very familiar pattern: + +![Facebook Permissions](../assets/eip-2255/facebook_permissions.png) + +![Log in With Apple](../assets/eip-2255/log_in_with_apple.jpeg) + +Many web3 applications today begin their sessions with a series of repetitive requests: + +- Reveal your wallet address to this site. +- Switch to a preferred network. +- Sign a cryptographic challenge. +- Grant a token allowance to our contract. +- Send a transaction to our contract. + +Many of these (and possibly all), and many more (like decryption), could be generalized into a set of human-readable permissions prompts on the original sign-in screen, and additional permissions could be requested only as needed: + +![Sample prompt screenshot](../assets/eip-2255/permissions.png) + +Each of these permissions could be individually rejected, or even _attenuated_--adjusted to meet the user's terms (for example, a sign-in request could have a user-added expiration date, and a token allowance could be adjusted by the user when it is requested). + +## Specification + +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. + +This proposal adds two new methods to a wallet's web3 provider API: `wallet_getPermissions` and `wallet_requestPermissions`. + +### `wallet_getPermissions` + +The `wallet_getPermissions` method is used for getting an array of current permissions (empty by default). It takes no parameters and returns an array of `Permission` objects. + +#### `wallet_getPermissions` Returns + +The format of the returned permissions MUST be an array of `Permission` objects, which are defined as follows: + +```typescript +interface Caveat { + type: string; + value: any; +} + +interface Permission { + invoker: string; + parentCapability: string; + caveats: Caveat[]; +} +``` + +The `invoker` is a URI used to identify the source of the current dapp (e.g. `https://your-site.com/`). The term `parentCapability` refers to the method that is being permitted (e.g. `eth_accounts`). The `caveats` array represents the specific restrictions applied to the permitted method. The `type` of a `Caveat` is a string, and the `value` is an arbitrary JSON value. The `value` of a `Caveat` is only meaningful in the context of the `type` of the `Caveat`. + +### `wallet_requestPermissions` + +The `wallet_requestPermissions` method is used for an application to request additional permissions. It MUST take a single parameter, a `PermissionRequest` object, and MUST return an array of `RequestedPermission` objects. + +#### `wallet_requestPermissions` Parameters + +The `wallet_requestPermissions` method takes a single parameter, a `PermissionRequest` object, which is defined as follows: + +```typescript +interface PermissionRequest { + [methodName: string]: { + [caveatName: string]: any; + }; +} +``` + +The `methodName` is the name of the method for which the permission is being requested (e.g. `eth_accounts`). The `caveatName` is the name of the caveat being applied to the permission (e.g. `requiredMethods`). The caveat value is the value of the caveat (e.g. `["signTypedData_v3"]`). + +Attempted requests to a restricted method must fail with an error, until a `wallet_requestPermissions` request is made and accepted by the user. + +If a `wallet_requestPermissions` request is rejected, it should throw an error with a `code` value equal to `4001` as per [EIP-1193](./eip-1193.md). + +#### `wallet_requestPermissions` Returns + +The `wallet_requestPermissions` method returns an array of `RequestedPermission` objects, which are defined as follows: + +```typescript +interface RequestedPermission { + parentCapability: string; + date?: number; +} +``` + +The `parentCapability` is the name of the method for which the permission is being requested (e.g. `eth_accounts`). The `date` is the timestamp of the request, in Unix time, and is optional. + +## Rationale + +While the current model of getting user consent on a per-action basis has high security, there are huge usability gains to be had bo getting more general user consent which can cover broad categories of usage, which can be expressed in a more human-readable way. This pattern has a variety of benefits to offer different functions within a web3 wallet. + +The `requestPermissions` method can be expanded to include other options related to the requested permissions, for example, sites could request accounts with specific abilities. For example, a website like an exchange that requires `signTypedData_v3` (which is not supported by some hardware wallets), might want to specify that requirement. This would allow wallets to display only compatible accounts, while preserving the user's privacy and choice regarding how they are storing their keys. + +## Test Cases + +### Requesting permissions + +The following example should prompt the user to approve the `eth_accounts` permission, and return the permission object if approved. + +```javascript +provider.request({ + method: 'requestPermissions', + params: [ + { + 'eth_accounts': { + requiredMethods: ['signTypedData_v3'] + } + } + ] +}); +``` + +### Getting permissions + +The following example should return the current permissions object. + +```javascript +provider.request({ + method: 'getPermissions' +}); +``` + +## Security Considerations + +### Server-Side Request Forgery (SSRF) + +This consideration is applicable if the favicon of a website is to be displayed. + +Wallets should be careful about making arbitrary requests to URLs. As such, it is recommended for wallets to sanitize the URI by whitelisting specific schemes and ports. A vulnerable wallet could be tricked into, for example, modifying data on a locally-hosted redis database. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2256.md b/EIPS/eip-2256.md new file mode 100644 index 0000000..eb556ff --- /dev/null +++ b/EIPS/eip-2256.md @@ -0,0 +1,197 @@ +--- +eip: 2256 +title: wallet_getOwnedAssets JSON-RPC Method +author: Loredana Cirstea (@loredanacirstea) +discussions-to: https://ethereum-magicians.org/t/eip-2256-add-wallet-getownedassets-json-rpc-method/3600 +status: Stagnant +type: Standards Track +category: Interface +created: 2019-08-29 +requires: 55, 155, 1474 +--- + +## Simple Summary + +This is a proposal for a new JSON-RPC call for retrieving from a wallet a selection of owned assets by an Ethereum address, with the user's permission. + +## Abstract + +There is no standardized way for a dApp to request a list of owned assets from a user. Now, each dApp needs to keep a list of all the popular or existing assets and check the user's balance against the blockchain, for each of these assets. This leads to duplicated effort across dApps. It also leads to the user being presented with asset options that the user does not care about, from various, unwanted airdrops. + +## Motivation + +There are financial dApps that require a list of owned assets from a user, for various purposes - calculating taxes, selecting customized payment options, etc. Each of these dApps are now forced to keep a list of popular assets (smart contract addresses, ABIs) and retrieve the user's data from the blockchain, for each asset. This leads to effort duplication and nonoptimal UX where the user is presented with either more or less asset options than the user would like - various airdrops, incomplete list of assets kept by the dApp. + +This list of owned assets can be retrieved from the wallet used by the user. The wallet can allow the user to manage only the assets that the user is interested in. Therefore, a new JSON-RPC method is proposed: `wallet_getOwnedAssets`. This method is complementary to [EIP-747](./eip-747.md), which proposes a way for sites to suggest users new assets to watch on their wallet. + +## Specification + +New JSON-RPC method to be added to web3 browsers: `wallet_getOwnedAssets`. This method is for dApp-wallet communication and only targets the assets that have already been whitelisted by the wallet, for the user account. + +**Arguments:** +- type `address`, Ethereum address that owns the assets +- options object, optional: + - `chainId` - type `uint`, chain id respecting [EIP-155](./eip-155.md); optional + - `limit` - type `uint`, the maximum number of owned assets expected by the dApp to be returned; optional + - `types` - type `string[]`, array of asset interface identifiers such as `['ERC20', 'ERC721']`; optional + - `justification` - type `string`, human-readable text provided by the dApp, explaining the intended purpose of this request; optional but recommended + +**Result:** +- array with asset records: + - `address` - type `address`, Ethereum checksummed address + - `chainId` - type `uint`, identifier for the chain on which the assets are deployed + - `type` - type `string`, asset interface ERC identifier; e.g. `ERC20`; optional - [EIP-1820](./eip-1820.md) could be used + - `options` - an object with asset-specific fields; `ERC20` tokens example: + - `name` - type `string`, token name; optional if the token does not implement it + - `symbol` - type `string`, token symbol; optional if the token does not implement it + - `icon`- type `base64`, token icon; optional + - `balance` - type `uint`, the number of tokens that the user owns, in the smallest token denomination + - `decimals` - type `uint`, the number of decimals implemented by the token; optional + +### Examples + +**1) A request to return all of the user's owned assets:** +```json +{ + "id":1, + "jsonrpc": "2.0", + "method": "wallet_getOwnedAssets", + "params": [ + "0x3333333333333333333333333333333333333333", + { + "justification": "The dApp needs to know about all your assets in order to calculate your taxes properly." + } + ] +} +``` +Result: + +```json +{ + "id":1, + "jsonrpc": "2.0", + "result": [ + { + "address": "0x0000000000000000000000000000000000000001", + "chainId": 1, + "type": "ERC20", + "options": { + "name": "TokenA", + "symbol": "TKA", + "icon": "data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==", + "balance": 1000000000000, + "decimals": 18 + } + }, + { + "address": "0x0000000000000000000000000000000000000002", + "chainId": 3, + "type": "ERC20", + "options": { + "name": "TokenB", + "symbol": "TKB", + "icon": "data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==", + "balance": 2000000000000, + "decimals": 18 + } + }, + { + "address": "0x0000000000000000000000000000000000000003", + "chainId": 42, + "type": "ERC721", + "options": { + "name": "TokenC", + "symbol": "TKC", + "icon": "data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==", + "balance": 10 + } + }, + ] +} +``` + +**2) A request to return one `ERC20` owned asset, deployed on `chainId` 1:** +```json +{ + "id":1, + "jsonrpc": "2.0", + "method": "wallet_getOwnedAssets", + "params": [ + "0x3333333333333333333333333333333333333333", + { + "chainId": 1, + "limit": 1, + "types": ["ERC20"], + "justification": "Select your token of choice, in order to pay for our services." + } + ] +} +``` +Result: + +```json +{ + "id":1, + "jsonrpc": "2.0", + "result": [ + { + "address": "0x0000000000000000000000000000000000000001", + "chainId": 1, + "type": "ERC20", + "options": { + "name": "TokenA", + "symbol": "TKA", + "icon": "data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==", + "balance": 1000000000000, + "decimals": 18 + } + } + ] +} +``` + +### UI Best Practices + +The wallet should display a UI to the user, showing the request. +The user can: +- accept the request, in which case the dApp receives all the requested assets +- reject the request +- amend the request by lowering the number of owned assets returned to the dApp + + +If all owned assets are requested, the total number of owned assets will be shown to the user. The user can also choose to select the assets that will be returned to the dApp, amending the request. + +If a selection is requested, the user will select from the list of owned assets. + +As an optimization, wallets can keep a list of frequently used assets by the user, and show that list first, with the option of expanding that list with owned assets that the user uses less frequently. + +## Rationale + +In order to avoid duplication of effort for dApps that require keeping a list of all or popular assets and to provide optimal UX, the `wallet_getOwnedAssets` JSON-RPC method is proposed. + +The `chainId` and `types` optional parameters enable dApps to provide options in order to restrict the selection list that the user will be presented with by the wallet, in accordance with the dApp's functionality. The `limit` parameter enables the dApp to tell the user an upper limit of accounts that the user can select. It remains to be seen if a lower bound should also be provided. At the moment, this lower bound can be considered as being `1`. + +The `options` response field provides the dApp with asset-specific options, enabling better UX through using the same visual and text identifiers that the wallet uses, making it easier for the user to understand the dApp's UI. + +The `address`, `type` response fields provide enough information about the asset, enabling dApps to provide additional asset-specific functionality. + +The `balance` response field is an optimization, allowing dApps to show the user's balance without querying the blockchain. Usually, this information is already public. + + +## Backwards Compatibility + +Not relevant, as this is a new method. + + +## Test Cases + +To be done. + + +## Implementation + +To be done. + + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2266.md b/EIPS/eip-2266.md new file mode 100644 index 0000000..2489d32 --- /dev/null +++ b/EIPS/eip-2266.md @@ -0,0 +1,252 @@ +--- +eip: 2266 +title: Atomic Swap-based American Call Option Contract Standard +author: Runchao Han , Haoyu Lin , Jiangshan Yu +discussions-to: https://github.com/ethereum/EIPs/issues/2266 +status: Last Call +type: Standards Track +category: ERC +created: 2019-08-17 +last-call-deadline: 2020-12-31 +--- + +## Simple Summary + +A standard for token contracts providing Atomic Swap-based American Call Option functionalities. + +## Abstract + +This standard provides functionality to make Atomic Swap-based American Call Option payment. The Atomic Swap protocol based on Hashed Time-Locked Contract (HTLC) [^1] has optionality [^2], and such optionality can be utilised to construct American Call Options without trusted third party. This standard defines the common way of implementing this protocol. In particular, this EIP defines technical terms, provides interfaces, and gives reference implementations of this protocol. + + +## Motivation + +Atomic Swap allows users to atomically exchange their tokens without trusted third parties while the HTLC is commonly used for the implementation. However, the HTLC-based Atomic Swap has optionality. More specifically, the swap initiator can choose to proceed or abort the swap for several hours, which gives him time for speculating according to the exchange rate. A discussion[^2] shows that the HTLC-based Atomic Swap is equivalent to an American Call Option in finance. On the other hand,thanks to such optionality, the HTLC-based Atomic Swap can be utilised to construct American Call Options without trusted third party. A paper[^3] proposes a secure Atomic-Swap-based American Call Option protocol on smart contracts. This protocol not only eliminates the arbitrage opportunity but also prevents any party from locking the other party's money maliciously. This EIP aims at providing the standard of implementing this protocol in existing token standards. + +## Specification + +The Atomic Swap-based American Call Option smart contract should follow the syntax and semantics of Ethereum smart contracts. + +### Definitions + ++ `initiator`: the party who publishes the advertisement of the swap. ++ `participant`: the party who agrees on the advertisement and participates in the swap with `initiator`. ++ `asset`: the amount of token(s) to be exchanged. ++ `premium`: the amount of token(s) that `initiator` pays to `participant` as the premium. ++ `redeem`: the action to claim the token from the other party. ++ `refund`: the action to claim the token from the party herself/himself, because of timelock expiration. ++ `secrect`: a random string chosen by `initiator` as the preimage of a hash. ++ `secrectHash`: a string equals to the hash of `secrect`, used for constructing HTLCs. ++ `timelock`: a timestamp representing the timelimit, before when the asset can be redeemed, and otherwise can only be refunded. + +### Storage Variables + +#### swap + +This mapping stores the metadata of the swap contracts, including the parties and tokens involved. Each contract uses different `secretHash`, and is distinguished by `secretHash`. + +```solidity +mapping(bytes32 => Swap) public swap; +``` + +#### initiatorAsset + +This mapping stores the detail of the asset initiators want to sell, including the amount, the timelock and the state. It is associated with the swap contract with the same `secretHash`. + +```solidity +mapping(bytes32 => InitiatorAsset) public initiatorAsset; +``` + +#### participantAsset + +This mapping stores the details of the asset participants want to sell, including the amount, the timelock and the state. It is associated with the swap contract with the same `secretHash`. + +```solidity +mapping(bytes32 => ParticipantAsset) public participantAsset; +``` + +#### premiumAsset + +This mapping stores the details of the premium initiators attach in the swap contract, including the amount, the timelock and the state. It is associated with the swap contract with the same `secretHash`. + +```solidity +mapping(bytes32 => Premium) public premium; +``` + + +### Methods + +#### setup + +This function sets up the swap contract, including the both parties involved, the tokens to exchanged, and so on. + +```solidity +function setup(bytes32 secretHash, address payable initiator, address tokenA, address tokenB, uint256 initiatorAssetAmount, address payable participant, uint256 participantAssetAmount, uint256 premiumAmount) public payable +``` + +#### initiate + +The initiator invokes this function to fill and lock the token she/he wants to sell and join the contract. + +```solidity +function initiate(bytes32 secretHash, uint256 assetRefundTime) public payable +``` + +#### fillPremium + +The initiator invokes this function to fill and lock the premium. + +```solidity +function fillPremium(bytes32 secretHash, uint256 premiumRefundTime) public payable +``` + +#### participate + +The participant invokes this function to fill and lock the token she/he wants to sell and join the contract. + +```solidity +function participate(bytes32 secretHash, uint256 assetRefundTime) public payable +``` + +#### redeemAsset + +One of the parties invokes this function to get the token from the other party, by providing the preimage of the hash lock `secret`. + +```solidity +function redeemAsset(bytes32 secret, bytes32 secretHash) public +``` + +#### refundAsset + +One of the parties invokes this function to get the token back after the timelock expires. + +```solidity +function refundAsset(bytes32 secretHash) public +``` + +#### redeemPremium + +The participant invokes this function to get the premium. This can be invoked only if the participant has already invoked `participate` and the participant's token is redeemed or refunded. + +```solidity +function redeemPremium(bytes32 secretHash) public +``` + +#### refundPremium + +The initiator invokes this function to get the premium back after the timelock expires. + +```solidity +function refundPremium(bytes32 secretHash) public +``` + + +### Events + +#### SetUp + +This event indicates that one party has set up the contract using the function `setup()`. + +```solidity +event SetUp(bytes32 secretHash, address initiator, address participant, address tokenA, address tokenB, uint256 initiatorAssetAmount, uint256 participantAssetAmount, uint256 premiumAmount); +``` + +#### Initiated + +This event indicates that `initiator` has filled and locked the token to be exchanged using the function `initiate()`. + +```solidity +event Initiated(uint256 initiateTimestamp, bytes32 secretHash, address initiator, address participant, address initiatorAssetToken, uint256 initiatorAssetAmount, uint256 initiatorAssetRefundTimestamp); +``` + +#### Participated + +This event indicates that `participant` has filled and locked the token to be exchanged using the function `participate()`. + +```solidity +event Participated(uint256 participateTimestamp, bytes32 secretHash, address initiator, address participant, address participantAssetToken, uint256 participantAssetAmount, uint256 participantAssetRefundTimestamp); +``` + +#### PremiumFilled + +This event indicates that `initiator` has filled and locked `premium` using the function `fillPremium()`. + +```solidity +event PremiumFilled(uint256 fillPremiumTimestamp, bytes32 secretHash, address initiator, address participant, address premiumToken, uint256 premiumAmount, uint256 premiumRefundTimestamp); +``` + +#### InitiatorAssetRedeemed/ParticipantAssetRedeemed + +These two events indicate that `asset` has been redeemed by the other party before the timelock by providing `secret`. + +```solidity +event InitiatorAssetRedeemed(uint256 redeemTimestamp, bytes32 secretHash, bytes32 secret, address redeemer, address assetToken, uint256 amount); +``` + +```solidity +event ParticipantAssetRedeemed(uint256 redeemTimestamp, bytes32 secretHash, bytes32 secret, address redeemer, address assetToken, uint256 amount); +``` + +#### InitiatorAssetRefunded/ParticipantAssetRefunded + +These two events indicate that `asset` has been refunded by the original owner after the timelock expires. + +```solidity +event InitiatorAssetRefunded(uint256 refundTimestamp, bytes32 secretHash, address refunder, address assetToken, uint256 amount); +``` + +```solidity +event ParticipantAssetRefunded(uint256 refundTimestamp, bytes32 secretHash, address refunder, address assetToken, uint256 amount); +``` + +#### PremiumRedeemed + +This event indicates that `premium` has been redeemed by `participant`. This implies that `asset` is either redeemed by `initiator` if it can provide the preimage of `secrectHash` before `asset` timelock expires; or refunded by `participant` if `asset` timelock expires. + +```solidity +event PremiumRedeemed(uint256 redeemTimestamp,bytes32 secretHash,address redeemer,address token,uint256 amount); +``` + +#### PremiumRefunded + +This event indicates that `premium` has been refunded back to `initiator`, because of `participant` doesn't participate at all, by the time of `premium` timelock expires. + +```solidity +event PremiumRefunded(uint256 refundTimestamp, bytes32 secretHash, address refunder, address token, uint256 amount); +``` + +## Rationale + ++ To achieve the atomicity, HTLC is used. ++ The participant should decide whether to participate after the initiator locks the token and sets up the timelock. ++ The initiator should decide whether to proceed the swap (redeem the tokens from the participant and reveal the preimage of the hash lock), after the participant locks the tokens and sets up the time locks. ++ Premium is redeemable for the participant only if the participant participates in the swap and redeems the initiator's token before premium's timelock expires. ++ Premium is refundable for the initiator only if the initiator initiates but the participant does not participate in the swap at all. + + +## Security Considerations + ++ The `initiateTimestamp` should cover the whole swap process. ++ The participant should never participate before the premium has been deposited. + + +## Backwards Compatibility + +This proposal is fully backward compatible. Functionalities of existing standards will not be affected by this proposal, as it only provides additional features to them. + + +## Implementation + +Please visit [here](../assets/eip-2266/Example.sol) to find our example implementation. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). + +## References + +[^1]: [Hash Time Locked Contracts](https://en.bitcoin.it/wiki/Hash_Time_Locked_Contracts) + +[^2]: [An Argument For Single-Asset Lightning Network](https://lists.linuxfoundation.org/pipermail/lightning-dev/2019-January/001798.html) + +[^3]: [On the optionality and fairness of Atomic Swaps](https://eprint.iacr.org/2019/896) diff --git a/EIPS/eip-2294.md b/EIPS/eip-2294.md new file mode 100644 index 0000000..9837e6a --- /dev/null +++ b/EIPS/eip-2294.md @@ -0,0 +1,59 @@ +--- +eip: 2294 +title: Explicit bound to Chain ID size +description: Adds a maximum value to the Chain ID parameter to avoid potential encoding issues that may occur when using large values of the parameter. +author: Zainan Victor Zhou (@xinbenlv), Alex Beregszaszi (@axic) +discussions-to: https://ethereum-magicians.org/t/eip-2294-explicit-bound-to-chain-id/11090 +status: Review +type: Standards Track +category: Core +created: 2019-09-19 +requires: 155 +--- + +## Abstract + +Starting from `blocknum = FORK_BLKNUM`, this EIP restricts the size of the [EIP-155](./eip-155.md) Chain ID parameter to a particular maximum value `floor(MAX_UINT64 / 2) - 36`, in order to ensure that there is a standard around how this parameter is to be used between different projects. + +## Motivation + +EIP-155 introduces the Chain ID parameter, which is an important parameter used for domain separation (replay protection) of Ethereum protocol signed messages. However, it does not specify any properties about the size that this parameter takes. Allowing it to be 256-bit wide means that the RLP encoding of a transaction must use >256-bit arithmetic to calculate the v field. + +and suggests a reasonable maximum enforced size in order to ensure that there are no issues when encoding this parameter. This would allow a sufficient amount of different values for this parameter, which is typically chosen by community consensus as a genesis parameter for a given chain and thus does not change often. + + +Without a well-chosen value of Chain ID, there could be differences in the implementation of [EIP-155](./eip-155.md) (and [EIP-1344](./eip-1344.md) by derivative) in both client codebase and external tooling that could lead to consensus-critical vulnerabilities being introduced to the network. By making this limit explicit, we avoid this scenario for Ethereum and any project which uses the Ethereum codebase. + +There have been suggestions of using a hash-based identifier in place on Chain ID to allow the value to adapt over time to different contentious forks and other scenarios. This proposal does not describe this behavior, but ~63 bits of entropy should be enough to ensure that no collisions are likely for reasonable (e.g. non-malicious) uses of this feature for that purpose. + +Also, the field `chainID` have experienced increasing usage and dependencies, due more and more contracts are depending on [EIP-1344](./eip-1344.md) to expose CHAIN ID in the smart contract execution. For example when used with [EIP-712](./eip-712.md), [EIP-1271](./eip-1271.md) for on-contract signature verification, chainId has been increasingly introduced for replay attack prevention. It's security critical to ensure clients depending on the chainId computation in cryptography yields identical result for verification in +all cases. + +Originally, this EIP was created by Bryant Eisenbach (@fubuloubu) and Alex Beregszaszi (@axic). + +## Specification + +Starting from `blocknum = FORK_BLKNUM`, the maximum value of Chain ID is `9,223,372,036,854,775,771` (`MAX_CHAIN_ID`). + +1. Compliant client MUST reject a value outside of the range of `[0, MAX_CHAIN_ID]` in a provided transaction starting from `blocknum = FORK_BLKNUM` +2. Compliant client MUST disallow a genesis configuration with a value for Chain ID outside of this limit. + +Due to how the calculation for chain ID is performed, the maximum value seen during the arithmetic is `CHAIN_ID * 2 + 36`, so clients must test to ensure no overflow conditions are encountered when the highest value is used. No underflow is possible. + +## Rationale + +The `MAX_CHAIN_ID` is calculated to avoid overflow when performing uint64 math. For reference, a value of 0 or less is also disallowed. + +## Backwards Compatibility + +This EIP introduces a change that affects previous implementations of this feature. However, as of time of writing(2022-10-18) no known chain makes use of a value outside of the suggested bounds, there should not be an issue in adopting this limit on the size of this parameter, therefore the impact should be non-existent. + +If any other chain is operating with a incompatible `chainId`, we advised they make proper arrangement when this EIP becomes adopted. + +## Security Considerations + +Needs discussion. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2304.md b/EIPS/eip-2304.md new file mode 100644 index 0000000..16a550e --- /dev/null +++ b/EIPS/eip-2304.md @@ -0,0 +1,219 @@ +--- +eip: 2304 +title: Multichain address resolution for ENS +author: Nick Johnson +type: Standards Track +category: ERC +status: Stagnant +created: 2019-09-09 +discussions-to: https://discuss.ens.domains/t/new-standard-proposal-ens-multicoin-support/1148 +requires: 137 +--- + +## Abstract + +This EIP introduces new overloads for the the `addr` field for ENS resolvers, which permit resolution of addresses for other blockchains via ENS. + +## Motivation + +With the increasing uptake of ENS by multi-coin wallets, wallet authors have requested the ability to resolve addresses for non-Ethereum chains inside ENS. This specification standardises a way to enter and retrieve these addresses in a cross-client fashion. + +## Specification + +A new accessor function for resolvers is specified: + +```solidity +function addr(bytes32 node, uint coinType) external view returns(bytes memory); +``` + +The EIP165 interface ID for this function is 0xf1cb7e06. + +When called on a resolver, this function must return the cryptocurrency address for the specified namehash and coin type. A zero-length string must be returned if the specified coin ID does not exist on the specified node. + +`coinType` is the cryptocurrency coin type index from [SLIP44](https://github.com/satoshilabs/slips/blob/master/slip-0044.md). + +The return value is the cryptocurency address in its native binary format. Detailed descriptions of the binary encodings for several popular chains are provided in the Address Encoding section below. + +A new event for resolvers is defined: + +```solidity +event AddressChanged(bytes32 indexed node, uint coinType, bytes newAddress); +``` + +Resolvers MUST emit this event on each change to the address for a name and coin type. + +### Recommended accessor functions + +The following function provides the recommended interface for changing the addresses stored for a node. Resolvers SHOULD implement this interface for setting addresses unless their needs dictate a different interface. + +```solidity +function setAddr(bytes32 node, uint coinType, bytes calldata addr); +``` + +`setAddr` adds or replaces the address for the given node and coin type. The parameters for this function are as per those described in `addr()` above. + +This function emits an `AddressChanged` event with the new address; see also the backwards compatibility section below for resolvers that also support `addr(bytes32)`. + +### Address Encoding + +In general, the native binary representation of the address should be used, without any checksum commonly used in the text representation. + +A table of encodings for common blockchains is provided, followed by a more detailed description of each format. In the table, 'encodings' lists the address encodings supported by that chain, along with any relevant parameters. Details of those address encodings are described in the following sections. + +| Cryptocurrency | Coin Type | Encoding | +| --- | --- | --- | +| Bitcoin | 0 | P2PKH(0x00), P2SH(0x05), SegWit('bc') | +| Litecoin | 2 | P2PKH(0x30), P2SH(0x32), P2SH(0x05), SegWit('ltc') | +| Dogecoin | 3 | P2PKH(0x1e), P2SH(0x16) | +| Monacoin | 22 | P2PKH(0x32), P2SH(0x05) | +| Ethereum | 60 | ChecksummedHex | +| Ethereum Classic | 61 | ChecksummedHex | +| Rootstock | 137 | ChecksummedHex(30) | +| Ripple | 144 | Ripple | +| Bitcoin Cash | 145 | P2PKH(0x00), P2SH(0x05), CashAddr | +| Binance | 714 | Bech32('bnb') | + +#### P2PKH(version) + +Pay to Public Key Hash addresses are [base58check](https://en.bitcoin.it/wiki/Base58Check_encoding) encoded. After decoding, the first byte is a version byte. For example, the Bitcoin address `1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa` base58check decodes to the 21 bytes `0062e907b15cbf27d5425399ebf6f0fb50ebb88f18`. + +P2PKH addresses have a version byte, followed by a 20 byte pubkey hash. Their canonical encoding is their scriptPubkey encoding (specified [here](https://en.bitcoin.it/wiki/Transaction#Types_of_Transaction)) is `OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG`. + +The above example address is thus encoded as the 25 bytes `76a91462e907b15cbf27d5425399ebf6f0fb50ebb88f1888ac`. + +##### P2SH(version) + +P2SH addresses are base58check encoded in the same manner as P2PKH addresses. +P2SH addresses have a version, followed by a 20 byte script hash. Their scriptPubkey encoding (specified [here](https://en.bitcoin.it/wiki/Transaction#Pay-to-Script-Hash)) is `OP_HASH160 OP_EQUAL`. A Bitcoin address of `3Ai1JZ8pdJb2ksieUV8FsxSNVJCpoPi8W6` decodes to the 21 bytes `0562e907b15cbf27d5425399ebf6f0fb50ebb88f18` and is encoded as the 23 bytes `a91462e907b15cbf27d5425399ebf6f0fb50ebb88f1887`. + +##### SegWit(hrp) + +SegWit addresses are encoded with [bech32](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki). Bech32 addresses consist of a human-readable part - 'bc' for Bitcoin mainnet - and a machine readable part. For SegWit addresses, this decodes to a 'witness version', between 0 and 15, and a 'witness program', as defined in [BIP141](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki). + +The scriptPubkey encoding for a bech32 address, as defined in BIP141, is `OP_n`, where `n` is the witness version, followed by a push of the witness program. Note this warning from BIP173: + +> Implementations should take special care when converting the address to a scriptPubkey, where witness version n is stored as OP_n. OP_0 is encoded as 0x00, but OP_1 through OP_16 are encoded as 0x51 though 0x60 (81 to 96 in decimal). If a bech32 address is converted to an incorrect scriptPubKey the result will likely be either unspendable or insecure. + +For example, the Bitcoin SegWit address `BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4` decodes to a version of `0` and a witness script of `751e76e8199196d454941c45d1b3a323f1433bd6`, and then encodes to a scriptPubkey of `0014751e76e8199196d454941c45d1b3a323f1433bd6`. + +#### ChecksummedHex(chainId?) + +To translate a text format checksummed hex address into binary format, simply remove the '0x' prefix and hex decode it. `0x314159265dD8dbb310642f98f50C066173C1259b` is hex-decoded and stored as the 20 bytes `314159265dd8dbb310642f98f50c066173c1259b`. + +A checksum format is specified by [EIP-55](./eip-55.md), and extended by [RSKIP60](https://github.com/rsksmart/RSKIPs/blob/master/IPs/RSKIP60.md), which specifies a means of including the chain ID in the checksum. The checksum on a text format address must be checked. Addresses with invalid checksums that are not all uppercase or all lowercase MUST be rejected with an error. Implementations may choose whether to accept non-checksummed addresses, but the authors recommend at least providing a warning to users in this situation. + +When encoding an address from binary to text, an EIP55/RSKIP60 checksum MUST be used - so the correct encoding of the above address for Ethereum is `0x314159265dD8dbb310642f98f50C066173C1259b`. + +#### Ripple + +Ripple addresses are encoded using a version of base58check with an alternative alphabet, described [here](https://xrpl.org/base58-encodings.html). Two types of ripple addresses are supported, 'r-addresses', and 'X-addresss'. r-addresses consist of a version byte followed by a 20 byte hash, while X-addresses consist of a version byte, a 20 byte hash, and a tag, specified [here](https://github.com/xrp-community/standards-drafts/issues/6). + +Both address types should be stored in ENS by performing ripple's version of base58check decoding and storing them directly (including version byte). For example, the ripple address `rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn` decodes to and is stored as `004b4e9c06f24296074f7bc48f92a97916c6dc5ea9`, while the address `X7qvLs7gSnNoKvZzNWUT2e8st17QPY64PPe7zriLNuJszeg` decodes to and is stored as `05444b4e9c06f24296074f7bc48f92a97916c6dc5ea9000000000000000000`. + +#### CashAddr + +Bitcoin Cash defines a new address format called 'CashAddr', specified [here](https://github.com/bitcoincashorg/bitcoincash.org/blob/master/spec/cashaddr.md). This uses a variant of bech32 encoding to encode and decode (non-segwit) Bitcoin Cash addresses, using a prefix of 'bitcoincash:'. A CashAddr should be decoded using this bech32 variant, then converted and stored based on its type (P2PKH or P2SH) as described in the relevant sections above. + +#### Bech32 + +[Bech32](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki) addresses consist of a human-readable part - for example, 'bnb' for Binance - and a machine readable part. The encoded data is simply the address, which can be converted to binary and stored directly. + +For example, the BNB address `bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2` decodes to the binary representation `40c2979694bbc961023d1d27be6fc4d21a9febe6`, which is stored directly in ENS. + +### Example + +An example implementation of a resolver that supports this EIP is provided here: + +```solidity +pragma solidity ^0.5.8; + +contract AddrResolver is ResolverBase { + bytes4 constant private ADDR_INTERFACE_ID = 0x3b3b57de; + bytes4 constant private ADDRESS_INTERFACE_ID = 0xf1cb7e06; + uint constant private COIN_TYPE_ETH = 60; + + event AddrChanged(bytes32 indexed node, address a); + event AddressChanged(bytes32 indexed node, uint coinType, bytes newAddress); + + mapping(bytes32=>mapping(uint=>bytes)) _addresses; + + /** + * Sets the address associated with an ENS node. + * May only be called by the owner of that node in the ENS registry. + * @param node The node to update. + * @param a The address to set. + */ + function setAddr(bytes32 node, address a) external authorised(node) { + setAddr(node, COIN_TYPE_ETH, addressToBytes(a)); + } + + /** + * Returns the address associated with an ENS node. + * @param node The ENS node to query. + * @return The associated address. + */ + function addr(bytes32 node) public view returns (address) { + bytes memory a = addr(node, COIN_TYPE_ETH); + if(a.length == 0) { + return address(0); + } + return bytesToAddress(a); + } + + function setAddr(bytes32 node, uint coinType, bytes memory a) public authorised(node) { + emit AddressChanged(node, coinType, a); + if(coinType == COIN_TYPE_ETH) { + emit AddrChanged(node, bytesToAddress(a)); + } + _addresses[node][coinType] = a; + } + + function addr(bytes32 node, uint coinType) public view returns(bytes memory) { + return _addresses[node][coinType]; + } + + function supportsInterface(bytes4 interfaceID) public pure returns(bool) { + return interfaceID == ADDR_INTERFACE_ID || interfaceID == ADDRESS_INTERFACE_ID || super.supportsInterface(interfaceID); + } +} +``` + +### Implementation + +An implementation of this interface is provided in the [ensdomains/resolvers](https://github.com/ensdomains/resolvers/) repository. + +## Backwards Compatibility + +If the resolver supports the `addr(bytes32)` interface defined in EIP137, the resolver MUST treat this as a special case of this new specification in the following ways: + + 1. The value returned by `addr(node)` from EIP137 should always match the value returned by `addr(node, 60)` (60 is the coin type ID for Ethereum). + 2. Anything that causes the `AddrChanged` event from EIP137 to be emitted must also emit an `AddressChanged` event from this EIP, with the `coinType` specified as 60, and vice-versa. + +## Tests + +The table below specifies test vectors for valid address encodings for each cryptocurrency described above. + +| Cryptocurrency | Coin Type | Text | Onchain (hex) | +| --- | --- | --- | --- | +| Bitcoin | 0 | `1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa` | `76a91462e907b15cbf27d5425399ebf6f0fb50ebb88f1888ac` | +| | | `3Ai1JZ8pdJb2ksieUV8FsxSNVJCpoPi8W6` | `a91462e907b15cbf27d5425399ebf6f0fb50ebb88f1887` | +| | | `BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4` | `0014751e76e8199196d454941c45d1b3a323f1433bd6` | +| Litecoin | 2 | `LaMT348PWRnrqeeWArpwQPbuanpXDZGEUz` | `76a914a5f4d12ce3685781b227c1f39548ddef429e978388ac` | +| | | `MQMcJhpWHYVeQArcZR3sBgyPZxxRtnH441` | `a914b48297bff5dadecc5f36145cec6a5f20d57c8f9b87` | +| | | `ltc1qdp7p2rpx4a2f80h7a4crvppczgg4egmv5c78w8` | `0014687c150c26af5493befeed7036043812115ca36c` | +| Dogecoin | 3 | `DBXu2kgc3xtvCUWFcxFE3r9hEYgmuaaCyD` | `76a9144620b70031f0e9437e374a2100934fba4911046088ac` | +| | | `AF8ekvSf6eiSBRspJjnfzK6d1EM6pnPq3G` | `a914f8f5d99a9fc21aa676e74d15e7b8134557615bda87` | +| Monacoin | 22 | `MHxgS2XMXjeJ4if2PRRbWYcdwZPWfdwaDT` | `76a9146e5bb7226a337fe8307b4192ae5c3fab9fa9edf588ac` | +| Ethereum | 60 | `0x314159265dD8dbb310642f98f50C066173C1259b` | `314159265dd8dbb310642f98f50c066173c1259b` | +| Ethereum Classic | 61 | `0x314159265dD8dbb310642f98f50C066173C1259b` | `314159265dd8dbb310642f98f50c066173c1259b` | +| Rootstock | 137 | `0x5aaEB6053f3e94c9b9a09f33669435E7ef1bEAeD` | `5aaeb6053f3e94c9b9a09f33669435e7ef1beaed` | +| Ripple | 144 | `rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn` | `004b4e9c06f24296074f7bc48f92a97916c6dc5ea9` | +| | | `X7qvLs7gSnNoKvZzNWUT2e8st17QPY64PPe7zriLNuJszeg` | `05444b4e9c06f24296074f7bc48f92a97916c6dc5ea9000000000000000000` | +| Bitcoin Cash | 145 | `1BpEi6DfDAUFd7GtittLSdBeYJvcoaVggu` | `76a91476a04053bda0a88bda5177b86a15c3b29f55987388ac` | +| | | `bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a` | `76a91476a04053bda0a88bda5177b86a15c3b29f55987388ac` | +| | | `3CWFddi6m4ndiGyKqzYvsFYagqDLPVMTzC` | `a91476a04053bda0a88bda5177b86a15c3b29f55987387` | +| | | `bitcoincash:ppm2qsznhks23z7629mms6s4cwef74vcwvn0h829pq` | `a91476a04053bda0a88bda5177b86a15c3b29f55987387` | +| Binance | 714 | `bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2` | `40c2979694bbc961023d1d27be6fc4d21a9febe6` | + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2309.md b/EIPS/eip-2309.md new file mode 100644 index 0000000..a98b8e0 --- /dev/null +++ b/EIPS/eip-2309.md @@ -0,0 +1,121 @@ +--- +eip: 2309 +title: ERC-721 Consecutive Transfer Extension +author: Sean Papanikolas (@pizzarob) +discussions-to: https://github.com/ethereum/EIPs/issues/2309 +status: Final +type: Standards Track +category: ERC +created: 2019-10-08 +requires: 721 +--- + +## Simple Summary + +A standardized event emitted when creating/transferring one, or many non-fungible tokens using consecutive token identifiers. + +## Abstract + +The optional ERC-721 Consecutive Transfer Extension provides a standardized event which could be emitted during the creation/transfer of one, or many non-fungible tokens. This standard does not set the expectation of how you might create/transfer many tokens it is only concerned with the event emitted after the creation, or transfer of ownership of these tokens. This extension assumes that token identifiers are in consecutive order. + +## Motivation + +This extension provides even more scalibility of the [ERC-721 specification](./eip-721.md). It is possible to create, transfer, and burn 2^256 non-fungible tokens in one transaction. However, it is not possible to emit that many `Transfer` events in one transaction. The `Transfer` event is part of the original specification which states: + +> This emits when ownership of any NFT changes by any mechanism. +> This event emits when NFTs are created (`from` == 0) and destroyed +> (`to` == 0). Exception: during contract creation, any number of NFTs +> may be created and assigned without emitting Transfer. At the time of +> any transfer, the approved address for that NFT (if any) is reset to none. + +This allows for the original `Transfer` event to be emitted for one token at a time, which in turn gives us O(n) time complexity. Minting one billion NFTs can be done in one transaction using efficient data structures, but in order to emit the `Transfer` event - according to the original spec - one would need a loop with one billion iterations which is bound to run out of gas, or exceed transaction timeout limits. This cannot be accomplished with the current spec. This extension solves that problem. + +Many decentralized marketplaces and block explorers utilize the `Transfer` event as a way to determine which NFTs an address owns. The Consecutive Transfer Extension provides a standard mechanism for these platforms to use to determine ownership of many tokens. + +## Specification + +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. + +**ERC-721 compliant contracts MAY implement this Consecutive Transfer Extension to provide a standard event to be emitted at the time of creation, burn, or transfer of one or many consecutive tokens** + +The address executing the transaction **MUST** own all the tokens within the range of `fromTokenId` and `toTokenId`, or **MUST** be an approved operator to act on the owners behalf. + +The `fromTokenId` and `toTokenId` **MUST** be a consecutive range of tokens IDs. + +The `fromTokenId`, `fromAddress`, and `toAddress` **MUST** be indexed parameters + +The `toTokenId` **MUST NOT** be an indexed parameter + +When minting/creating tokens, the `fromAddress` argument **MUST** be set to `0x0` (i.e. zero address). + +When burning/destroying tokens, the `toAddress` argument **MUST** be set to `0x0` (i.e. zero address). + +When emitting the ConsecutiveTransfer event the Transfer event **MUST NOT** be emitted + +Contracts that implement the `ConsecutiveTransfer` event **MAY** still use the original `Transfer` event, however when emitting the `ConsecutiveTransfer` event the `Transfer` event **MUST NOT** be emitted. + +```solidity + event ConsecutiveTransfer(uint256 indexed fromTokenId, uint256 toTokenId, address indexed fromAddress, address indexed toAddress); +``` + +### Examples + +The `ConsecutiveTransfer` event can be used for a single token as well as many tokens: + +**Single token creation** + +`emit ConsecutiveTransfer(1, 1, address(0), toAddress);` + +**Batch token creation** + +`emit ConsecutiveTransfer(1, 100000, address(0), toAddress);` + +**Batch token transfer** + +`emit ConsecutiveTransfer(1, 100000, fromAddress, toAddress);` + +**Burn** + +`emit ConsecutiveTransfer(1, 100000, from, address(0));` + + +## Rationale + +Standardizing the `ConsecutiveTransfer` event gives decentralized platforms a standard way of determining ownership of large quantities of non-fungible tokens without the need to support a new token standard. There are many ways in which the batch creation and transfer of NFTs can be implemented. The Consecutive Transfer Extension allows contract creators to implement batch creation, transfer, and burn methods however they see fit, but provides a standardized event in which all implementations can use. By specifying a range of consecutive token identifiers we can easily cover the transfer, or creation of 2^(256) tokens and decentralized platforms can react accordingly. + +Take this example. I sell magical fruit and have a farm with 10,000 magical fruit trees each with different fruit and 1,000 new trees every few years. I want to turn each tree into a non-fungible token that people can own. Each person that owns one of my non-fungible tree tokens will receive a quarterly percentage of each harvest from that tree. The problem is that I would need to create and transfer each of these tokens individually - which will cost me a lot of time and money and frankly would keep me from doing this. + +With this extension I would be able to to mint my initial 10,000 tree tokens in one transaction. I would be able to quickly and cheaply mint my additional 1,000 tree tokens when a new batch is planted. I would then be able to transfer all of the 10,000+ tree tokens to a special smart contract that keeps track of the selling and distribution of funds in one transaction all while adhering to a specified standard. + +**Rationale to have a single event that covers minting, burning, and transferring** + +The `ConsecutiveTransfer` event can be used to cover minting, burning, and transferring events. While there may have been confusion in the beginning adhering to transfer to/from "0" pattern this is mitigated by checking for the `ConsecutiveTransfer` topic and verifying the emitting contract supports the ERC-721 interface by using the ERC-165 standard. + +**Indexed event parameters** + +Events in Solidity can have up to three indexed parameters which will make it possible to filter for specific values of indexed arguments. This standard sets the `fromAddress`, `toAddress`, and `fromTokenId` as the indexed parameters. The `toTokenId` can be retrieved from the data part of the log. The reason for this is that more often than not one may be searching for events to learn about the history of ownership for a given address. The `fromTokenId` can then be retrieved along with the other two indexed parameters for simplicity. Then one only needs to decode the log data which is ensured to be the `toTokenId`. + +**Rationale to not emit `Transfer` when `ConsecutiveTransfer` is also emitted** + +This can lead to bugs and unnecessary complex logic for platforms using these events to track token ownership. When transferring a single token it is acceptable to emit the original `Transfer` event, but the `ConsecutiveTransfer` event should not be emitted during the same transaction and vice-versa. + +**Comparing 2309 and 1155** + +As the NFT market continues to grow so does the need for the ability to scale the smart contracts. Users need to be able to do things like mint a massive amount of tokens at one time, transfer a massive amount of tokens, and be able to track ownership of all these assets. We need to do this in a way that is cost effective and doesn’t fail under the confines of the Ethereum blockchain. As millions of tokens are minted we need contracts with the ability to scale. + +[ERC-1155](./eip-1155.md) was created and added as a standard in 2019 to try to solve these problems, but it falls short when it comes to minting massive amounts of unique tokens in a cost-effective way. With ERC-1155 it’s either going to cost hundreds (or thousands) of dollars or it’s going to run out of gas. ERC-1155 works well when minting many semi-fungible tokens but falls short when minting many unique tokens. Using the 2309 standard you could mint millions of blank NFTs upfront and update the metadata for each one in a cost effective way. + + +## Backwards Compatibility + +This extension was written to allow for the smallest change possible to the original ERC-721 spec while still providing a mechanism to track the creation, transfer, and deletion of a massive amount of tokens. While it is a minimal change the effects on platforms that only use the original `Transfer` event to index token ownership would be severe. They would not be properly recording token ownership information that could be known by listening for the `ConsecutiveTransfer` event. For platforms that wish to support the `ConsecutiveTransfer` event it would be best to support both the original `Transfer` event and the `ConsecutiveTransfer` event to track token ownership. + +## Security Considerations +There are no security considerations related directly to the implementation of this standard. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2315.md b/EIPS/eip-2315.md new file mode 100644 index 0000000..2760345 --- /dev/null +++ b/EIPS/eip-2315.md @@ -0,0 +1,445 @@ +--- +eip: 2315 +title: Simple Subroutines for the EVM +description: Two opcodes for efficient, safe, and static subroutines. +author: Greg Colvin (@gcolvin), Martin Holst Swende (@holiman), Brooklyn Zelenka (@expede), John Max Skaller +discussions-to: https://ethereum-magicians.org/t/eip-2315-simple-subroutines-for-the-evm/3941 +status: Withdrawn +type: Standards Track +category: Core +created: 2019-10-17 +requires: 3540, 3670, 4200 +withdrawal-reason: This proposal has been superceded by the EOF proposals. +--- + +## Abstract + +This proposal provides a _complete_, _efficient_, _safe_ and _static_ control-flow facility. + +It introduces two new opcodes to support calling and returning from subroutines: + +* `RJUMPSUB relative_offset` -- relative jump to subroutine +* `RETURNSUB` -- return to `PC` after most recent `RJUMPSUB`. + +It depends on the two new opcodes proposed by [EIP-4200](./eip-4200.md) to support static jumps: + +* `RJUMP relative_offset` — relative jump to `PC + relative_offset` +* `RJUMPI relative_offset` — conditional relative jump + +It deprecates `JUMP` and `JUMPI`, allowing valid code to support streaming, one-pass, and other near-linear compilers. + +In concert with [EIP-3540](./eip-3540.md) and [EIP-3670](./eip-3670.md) it ensures, at initialization time, that valid code will not execute invalid instructions or jump to invalid locations, will not underflow stack, will maintain consistent numbers of inputs and outputs for subroutines, and will have bounded stack height in the absence of recursion. + +This is among the simplest possible proposals that meets these requirements. + +## Motivation + +### A complete control-flow facility. + +Jumps, conditional jumps and subroutines were proposed by Alan Turing in 1945 as a means of organizing the logic of the code and the design of the memory crystals for his Automatic Computing Engine: +> We wish to be able to arrange that sequences of orders can divide at various points, continuing in different ways according to the outcome of the calculations to date... We also wish to be able to arrange for the splitting up of operations into subsidiary operations... To start on a subsidiary operation we need only make a note of where we left off the major operation and then apply the first instruction of the subsidiary. When the subsidiary is over we look up the note and continue with the major operation. +> +> — Alan Turing — in B.E. Carpenter, R.W. Doran, "The other Turing machine." The Computer Journal, Volume 20, Issue 3, January 1977. + +In more contemporary terms, we have sequences of instructions, jumps and conditional jumps that divide sequences into blocks, subroutine calls, and a stack of addresses to return to. The details vary, but similar facilities have proven their value across a long line of important machines over the last 75 years, including most all of the machines we have programmed or implemented -- physical machines including the Burroughs 5000, CDC 7600, IBM 360, DEC PDP-11 and VAX, Motorola 68000, Sun SPARC, and Intel x86s, as well as virtual machines for Scheme, Forth, Pascal, Java, Wasm, and others. + +Unlike these machines, the Ethereum Virtual Machine _does not_ provide subroutine operations. Instead, they must be synthesized using the dynamic `JUMP` instruction, which takes its destination on the stack. Further, the EVM provides _only_ dynamic jumps, impeding the static analysis we need. + +### Efficient control-flow. + +Efficient to write by hand, compile from high level labguages, validate at deploy time, interpret by VMs, and compile to machine code. + +Static jumps, conditional jumps, and subroutines are sufficient and efficient in space and time, as shown by historical experience and as we will show for the EVM below. + +### Safe control-flow. + +The EVM has unusually high requirements for safety. Not only do many smart contracts control inordinately large amounts of valuable Ether, but once placed on the blockchain any defects are visible to attackers and cannot be repaired. We propose to statically validate important safety constraints on code at initialization time. + +### Static control-flow. + +The EVM's dynamic jumps cause two major problems. First, the need to synthesize static jumps and subroutines with dynamic jumps wastes space and gas with needlessly complex code, as we will show below. + +Worse, jumps that can dynamically branch to any destination in the code can cause quadratic "path explosions" when traversing the flow of control. For Ethereum, this is a denial-of-service vulnerability that prevents us, at initialization time, from validating the safe use of EVM code and from compiling EVM code to machine code. + +We _need_ static control-flow to validate program safety and to compile EVM bytecode to machine code -- in time and space linear in the size of the code. + +## Specification + +### Opcodes + +#### `RJUMPSUB (0x5f) relative_offset` + +Transfers control to a subroutine. + +1. Decode the `relative_offset` from the immediate data at `PC`. +2. Push the current `PC + 3` to the `return stack`. +3. Set `PC` to `PC + relative_offset`. + +The `relative_offset` is relative to the current `PC`. The offset is encoded as a two-byte, twos-complement signed integer, stored MSB-first. + +The gas cost is _low_. + +#### `RETURNSUB (0x5e)` + +Returns control to the caller of a subroutine. + +1. Pop the `return stack` to `PC`. + +The gas cost is _verylow_. + +_Notes:_ + +* _Values popped off the `return stack` do not need to be validated, since they are alterable only by `RJUMPSUB` and `RETURNSUB`._ +* _The description above lays out the semantics of these instructions in terms of a `return stack`. But the actual state of the `return stack` is not observable by EVM code or consensus-critical to the protocol. (For example, a node implementer may code `RJUMPSUB` to unobservably push `PC` on the `return stack` rather than `PC + 1`, which is allowed so long as `RETURNSUB` observably returns control to the `PC + 3` location.)_ + +### Validity + +_Execution_ is defined in the Yellow Paper as a sequence of changes in the EVM state. The conditions on valid code are preserved by state changes. At runtime, if execution of an instruction would violate a condition the execution is in an exceptional halting state. The Yellow Paper defines six such states. + +1. Insufficient gas +2. More than 1024 stack items +3. State modification during a static call +4. Insufficient stack items +5. Invalid jump destination +6. Invalid instruction + +We would like to consider EVM code valid iff no execution of the program can lead to an exceptional halting state. In practice, we must test at runtime for the first three conditions. We don’t know how much gas there will be, we don’t know how deep a recursion may go, analysis of stack depth even for non-recursive programs is nontrivial, and we don't know whether a call will be static. All of the remaining conditions MUST be validated statically, in time and space quasi-linear in the size of the code. + +#### Static Constraints on Valid Code + +* Every instruction MUST be valid: + * The `JUMP` and `JUMPI` instructions ARE NOT valid. +* Every jump MUST be valid: + * The `RJUMP`, `RJUMPI`, or `RJUMPSUB` instructions MUST NOT address immediate data or addresses outside of their code section. +* The stacks MUST be valid: + * The number of items on the `data stack` MUST always be positive. + * The number of items on the `return stack `MUST always be positive. +* The data stack MUST be consistently aligned: + * The data stack height is + * the absolute difference between the current `stack pointer` and the `stack pointer` on entry to the current subroutine. + * It MUST be the same for every reachable path through a given `PC` and + * MUST NOT exceed 1024. + +## Rationale + +This is a purely semantic specification, placing no constraints on the syntax of code sections beyond being a sequence of opcodes and immediate data – a subroutine is not a contiguous sequence of bytecode, it is a subgraph of the bytecode's control-flow graph. The EVM is a simple state machine. We only promise that valid code will not, as it were, jam up the gears of the machine. + +By avoiding syntactic constraints we allow for optimizations like tail call elimination, multiple-entry subroutines, moving "cold" code out of line, and other ways of reducing redundancy and keeping "hot" code in cache. Since we wish to support one-pass compilation of EVM code to machine code it is crucial that the EVM code be as well optimized as possible up front. + +### Validation + +Rather than enforce constraints via syntax, we enforce them via validation. + +The constraints on valid code cover all of the exceptional halting states that we can validate and allow code to be validated and compiled in time and space quasi-linear in the size of the code. + +The `RJUMP`, `RJUMPI` and `RJUMPSUB` instructions take their *relative_offset* as immediate arguments, which cannot change at runtime. Having constant destinations for all jumps means that all jump destinations can be validated at initialization time, not runtime. Dynamic jumps can branch to any destination in the code, so exploitable quadratic "path explosions" are possible when traversing the control flow graph. Deprecating `JUMP` and `JUMPI` prevents this. + +Requiring a consistently aligned `data stack` + +* prevents stack underflow +* ensures that all calls to a subroutine have the same number of inputs and the same number of outputs and +* ensures that stack height is bounded in the absence of recursion. + +Requiring a consistently aligned `data stack` also allows some algorithms that traverse the control-flow graph -- including code validation and compilation -- to break cycles at joins, again preventing quadratic path explosion. When a traversal gets to a `PC` it has visited before it is either at the beginning of a loop or the entry to a function. Since the stack height at that `PC` is constant we know that loops will not grow stack, and that the number of arguments to a subroutine will always be the same -- there may be no need to traverse that path again. + +_Note: The JVM and Wasm enforce similar constraints for similar reasons._ + +### Alternative Designs + +There are a few major designs for a subroutine facility, two of which are considered here. The others are mostly not appropriate for the EVM, such as the Wheeler Jump -- self-modifying code that writes return addresses into called subroutines. + +*1. Keep return addresses on a dedicated return stack.* Turing's design is often used by stack machines, including those for Forth, Java, Wasm, and others. The data stack is used for computation, with a dedicated stack for return addresses. A single instruction suffices to call, and another to return from a routine. + +*2. Keep return addresses on the data stack.* This design is often used by register machines, including those from CDC, IBM, DEC, Intel, and ARM. The registers are used primarily for computation, and the stack maintains call frames for return addresses, arguments, and local variables. On the EVM there are no registers for computation, so using the stack for both purposes can cause the sort of inefficiencies we see below. Pascal p-code does use this design, but as part of a complex calling convention with dedicated registers. + +#### We prefer the dedicated return stack. + +* It maintains a clear separation between calculation and flow of control: + * the data stack is free of vulnerable return addresses and + * it's impossible to overwrite the return stack. +* It improves efficiency: + * it uses native arithmetic rather than 256-bit EVM instructions for the return address, + * doesn't use up a `data stack` slot for the return address and + * needs less motion of 256-bit data on the stack. + +### Efficiency + +We illustrate here how subroutine instructions can be used to reduce the complexity and gas costs of both ordinary and optimized subroutine calls compared to using `JUMP`. + +#### **Simple Subroutine Call** + +Consider these examples of a fairly minimal subroutine, including the code to call it. + +Subroutine call, using `RJUMPSUB`: +``` +SQUARE: + dup1 ; 3 gas + mul ; 5 gas + returnsub ; 3 gas + +CALL_SQUARE: + push 0x02 ; 3 gas + rjumpsub SQUARE ; 5 gas +``` +_Total gas: 19_ + +Subroutine call, using `JUMP`: +``` +SQUARE: + jumpdest ; 1 gas + swap1 ; 3 gas + dup1 ; 3 gas + mul ; 5 gas + swap1 ; 3 gas + jump ; 8 gas + +CALL_SQUARE: + jumpdest ; 1 gas + push 0x02 ; 3 gas + push RTN_CALL: ; 3 gas + push SQUARE ; 3 gas + jump ; 8 gas +RTN_CALL: + jumpdest ; 1 gas +``` +_Total: 41 gas_. + +Using `RJUMPSUB` versus `JUMP` saves _41 - 19 = 22 gas_ — a _54%_ improvement. + +#### **Tail Call Optimization** + +Of course in cases like this one we can optimize the tail call, so that the return from `SQUARE` actually returns from `TEST_SQUARE`. + +Tail call optimization, using `RJUMPSUB` and `RETURNSUB`: +```SQUARE: + dup1 ; 3 gas + mul ; 5 gas + returnsub ; 3 gas + +CALL_SQUARE: + push 0x02 ; 3 gas + rjump SQUARE ; 3 gas +``` +_Total: 17 gas_ + + +Tail call optimization, using `JUMP`: +``` +SQUARE: + jumpdest ; 1 gas + swap1 ; 3 gas + dup1 ; 3 gas + mul ; 5 gas + swap2 ; 3 gas + jump ; 8 gas + +CALL_SQUARE: + jumpdest ; 1 gas + push 0x02 ; 3 gas + push SQUARE ; 3 gas + jump ; 8 gas +``` +_Total: 38 gas_ + +Using `RJUMPSUB` versus `JUMP` saves _38 - 17 = 21 gas_ — a _55%_ improvement. + +#### Efficiency Caveats + +We can see that these instructions provide a simpler and more gas-efficient subroutine mechanism than using `JUMP` — in our examples they cut gas use by about half. + +Clearly, the benefits of this efficiency are greater for programs that have been factored into smaller subroutines. How small? Wrapping code in a subroutine costs only _8 gas_ using `RJUMPSUB` and `RETURNSUB` versus _30 gas_ using `JUMP`, `PUSH` and `SWAP` as above. + +### Costs + +The _low_ cost of `RJUMPSUB` versus the _mid_ cost of `JUMP` is justified by needing only to decode the immediate two byte destination to the `PC` and push the return address on the `return stack`, all using native arithmetic, versus using the data stack with emulated 256-bit instructions. + +The _verylow_ cost of `RETURNSUB` is justified by needing only to pop the `return stack` into the `PC`. Benchmarking will be needed to tell if the costs are well-balanced. + +## Backwards Compatibility + +These changes affect the semantics of existing EVM code: bytes that would have been interpreted as valid jump destinations may now be interpreted as immediate data. Since this proposal depends on the Ethereum Object Format to signal the change this is not a practical issue. + +## Test Cases + +### Simple routine + +This should jump into a subroutine, back out and stop. + +Bytecode: `0x5f0003005e` (`RJUMPSUB 3, RETURNSUB, STOP`) + +| Pc | Op | Cost | Stack | RStack | +|-------|-------------|------|-----------|-----------| +| 0 | RJUMPSUB | 5 | [] | [] | +| 2 | STOP | 0 | [] | [] | +| 3 | RETURNSUB | 3 | [] | [] | + +Output: 0x +Consumed gas: `10` + +### Two levels of subroutines + +This should execute fine, going into one two depths of subroutines + +Bytecode: `0x5f00045F00025200` (`RJUMPSUB 4, RJUMPSUB 2, RETURNSUB, RETURNSUB, STOP`) + +| Pc | Op | Cost | Stack | RStack | +|-------|-------------|------|-----------|-----------| +| 0 | RJUMPSUB | 5 | [] | [] | +| 3 | RJUMPSUB | 5 | [] | [] | +| 4 | RETURNSUB | 5 | [] | [] | +| 5 | RETURNSUB | 5 | [] | [] | +| 6 | STOP | 0 | [] | [] | + +Consumed gas: `20` + +### Failure 1: invalid jump + +This should fail, since the given location is outside of the code-range. + +Bytecode: `0X5fff`(`RJUMPSUB -1`) + +| Pc | Op | Cost | Stack | RStack | +|-------|-------------|------|-----------|-----------| +| 0 | RJUMPSUB | 10 | [] | [] | + +``` +Error: at pc=0, op=RJUMPSUB: invalid jump destination +``` + +### Failure 2: shallow `return stack` + +This should fail at first opcode, due to shallow `return_stack` + +Bytecode: `0x5e` (`RETURNSUB`) + +| Pc | Op | Cost | Stack | RStack | +|-------|-------------|------|-----------|-----------| +| 0 | RETURNSUB | 5 | [] | [] | + +``` +Error: at pc=0, op=RETURNSUB: invalid retsub +``` + +### Subroutine at end of code + +In this example the RJUMPSUB is on the last byte of code. When the subroutine returns, it should hit the 'virtual stop' _after_ the bytecode, and not exit with error + +Bytecode: `0x5c00045e5fffff` (`RJUMP 4, RETURNSUB, RJUMPSUB -1`) + +| Pc | Op | Cost | Stack | RStack | +|-------|-------------|------|-----------|-----------| +| 0 | RJUMP | 5 | [] | [] | +| 3 | RETURNSUB | 5 | [] | [] | +| 4 | RJUMPSUB | 5 | [] | [] | +| 7 | STOP | 0 | [] | [] | + +Consumed gas: `15` + +## Reference Implementation + +The following is a pseudo-Python implementation of an algorithm for predicating code validity. An equivalent algorithm must be run at initialization time. + +This algorithm performs a symbolic execution of the program that recursively traverses the _code_, emulating its control flow and stack use and checking for violations of the rules above. + +It runs in time equal to `O(vertices + edges)` in the program's control-flow graph, where edges represent control flow and the vertices represent _basic blocks_ — thus the algorithm takes time proportional to the size of the _code_. It maintains a stack of continuations for conditional jumps, the size of which is at most proportional to the size of the _code_. + +### Validation Function + +** Note: this function is known to be incomplete and incorrect. ** + +For simplicity's sake we assume that all jumpdest analysis and prior validation has been done, including EIP-3540, EIP-3670, and EIP-4200, so EOF headers and sections are well-formed, and there are no invalid instructions or jumps. In practice, all passes of validation can be folded into a single loop no recursion. + +We also assume some helper functions. +* `is_valid(opcode)` returns true iff opcode is valid. +* `is_terminator(opcode)` returns true iff opcode is terminator. +* `is_valid_jumpdest(pc)` returns true iff `pc` is a valid jump destination. +* `immediate_data(pc)` returns the immediate data for the instruction at `pc`. +* `immediate_size(opcode)` returns the size of the immediate data for an opcode. +* `removed_items(opcode)` returns the number of items removed from the `data_stack` by the `opcode`. +* `added_items(opcode)` returns the number of items added to the `data_stack` by the `opcode`. + +``` +# returns true iff code is valid +def validate_code(code: bytes, pc: int, sp: int, bp: int) -> boolean: + continuations = [] + do + while pc < len(code): + opcode = code[pc] + if !is_valid(opcode): + return false + if is_terminator(opcode): + return true + + # check stack height and return if we have been here before + stack_height = sp - bp + if stack_height > 1024 + return false + if pos in stack_heights: + if stack_height != stack_heights[pos]: + return false + return true + else: + stack_heights[pos] = stack_height + + if opcode == RJUMP: + + # reset pc to destination of jump + jumpdest = immediate_data(pc) + pc += jumpdest + if !is_valid_jumpdest(pc) + return false + + elif opcode == RJUMPI: + + jumpdest = pc + immediate_data(pc) + if !is_valid_jumpdest(pc) + return false + + # continue true side of conditional later + continations.push((jumpdest, sp, bp)) + + # continue false side of conditional now + + elif opcode == RJUMPSUB: + + # will enter subroutine at destination + bp = sp + + # push return address and reset pc to destination + jumpdest = pc + immediate_data(pc) + if !is_valid_jumpdest(pc) + return false + push(return_stack, pc + 3) + pc = jumpdest + continue + + elif opcode == RETURNSUB: + + # will return to subroutine at destination + bp = sp + + # pop return address and check for preceding call + pc = pop(return_stack) + if code[pc - 3] != RJUMPSUB: + return false + + # apply instructions to stack + sp -= removed_items(opcode) + if sp < 0 + return false + sp += added_items(opcode) + + # Skip opcode and immediate data + pc += 1 + immediate_size(opcode) + + while (pc, sp, bp) = continuations.pop() + + return true +``` + +## Security Considerations + +These changes introduce new flow control instructions. They do not introduce any new security considerations. This EIP is intended to improve security by validating a higher level of safety for EVM code deployed on the blockchain. The validation algorithm must be quasi-linear in time and space to not be a denial of service vulnerability. The algorithm here makes one linear-time pass of the bytecode, and uses a stack of continuations that cannot exceed the number of `RJUMPI` instructions in the code. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2327.md b/EIPS/eip-2327.md new file mode 100644 index 0000000..f2e4449 --- /dev/null +++ b/EIPS/eip-2327.md @@ -0,0 +1,49 @@ +--- +eip: 2327 +title: BEGINDATA opcode +author: Martin Lundfall (@MrChico) +discussions-to: https://ethereum-magicians.org/t/new-opcode-begindata/3727 +status: Stagnant +type: Standards Track +category: Core +created: 2019-10-28 +--- + +## Simple Summary +Introduces a new opcode `BEGINDATA`, which indicates that the remaining bytes of the contract should be regarded as data rather than contract code +and cannot be executed. + +## Abstract +It is common for smart contracts to efficiently store data directly in the contract bytecode. Examples include constructor arguments, constant variables, compiler metadata and the contract runtime during the init phase. Currently, such data is not distinguished from normal bytecode and is still being analysed for `JUMPDEST`s by EVM interpreters. This EIP introduces a new opcode `BEGINDATA` at byte `0xb6`, which marks the remainding bytecode as data, indicating to EVM interpreters, static analysis tools and chain explorers that the remaining bytes do not represent opcodes. + +## Motivation +The `BEGINDATA` opcode has been suggested before as part of the EIP `Subroutines and Static Jumps for the EVM` [EIP-615](./eip-615.md) as a way to determine the position of jumptables in contract bytecode. It is here introduced in its own right in order to exclude data from the `JUMPDEST` analysis of contracts, making it impossible to jump to data. This makes it easier for static analysis tools to analyse contracts, allows disassemblers, chain explorers and debuggers to not display data as a mess of INVALID opcodes and may even provide a marginal improvement in performance. It also helps scalability because it improves on-chain evaluation of transactions from other chains in that the validation that the code conforms to a certain pattern does not need to do a full jumpdest analysis to see that data is not executed and thus does not have to conform to the pattern (used by the optimism project). Additionally, it paves the way for suggestions such as [EIP-1712](https://github.com/ethereum/EIPs/pull/1712) to disallow unused opcodes, jumptables [EIP-615](./eip-615.md) and speculative proposals to disallow for deployment of contracts with stack usage violations. + +## Specification +While computing the valid `JUMPDEST`s of a contract, halt analysis once the first `BEGINDATA` is encountered. In other words: A jump to any codelocation equal to or greater than the location of the first `BEGINDATA` causes a `BAD_JUMP_DESTINATION` error. +If `BEGINDATA` is encountered during contract execution, it has the same semantics as `STOP`. It uses 0 gas. + +Bytes past `BEGINDATA` remain accessible via `CODECOPY` and `EXTCODECOPY`. `BEGINDATA` does not influence `CODESIZE` or `EXTCODESIZE`. + +## Rationale +The byte `0xb6` was chosen to align with [EIP-615](./eip-615.md). +The choice to `STOP` if `BEGINDATA` is encountered is somewhat arbitrary. An alternative would be to be to abort the execution with an out-of-gas error. + +## Backwards Compatibility +The proposal will not change any existing contracts unless their current behaviour relies upon the usage of unused opcodes. + +Since contracts have been using data from the very start, in a sense all of them use unused opcodes, +but they would have to use data in a way that it is skipped during execution and jumped over. +The Solidity compiler never generated such code. It has to be evaluated whether contracts created by other means +could have such a code structure. + +## Test Cases +Test cases should include: +1) A contract which jumps to a destination `X`, where `X` has a pc value higher than the `BEGINDATA` opcode, and the byte at `X` is `0x5b`. This should fail with a `BAD_JUMP_DESTINATION` error. +2) A contract which encounters the `BEGINDATA` opcode (should stop executing the current call frame) + +## Implementation +Not yet. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-233.md b/EIPS/eip-233.md new file mode 100644 index 0000000..dd23be5 --- /dev/null +++ b/EIPS/eip-233.md @@ -0,0 +1,118 @@ +--- +eip: 233 +title: Formal process of hard forks +author: Alex Beregszaszi (@axic) +discussions-to: https://ethereum-magicians.org/t/eip-233-formal-process-of-hard-forks/1387 +type: Meta +status: Stagnant +created: 2017-03-23 +--- + +## Abstract + +To describe the formal process of preparing and activating hard forks. + +## Motivation + +Today discussions about hard forks happen at various forums and sometimes in ad-hoc ways. + +## Specification + +A Meta EIP should be created and merged as a *Draft* as soon as a new hard fork is planned. + +This EIP should contain: +- the desired codename of the hard fork, +- activation block number once decided +- a timeline section +- an EIPs to include section +- the **Requires** header should point to the previous hard fork meta EIP. + +The draft shall be updated with summaries of the decisions around the hard fork. + +### Timeline + +Once a timeline with key dates is agreed upon for other crucial dates. The basic outline of a hardfork timeline should include: +* Hard deadline to accept proposals for this hard fork +* Soft deadline for major client implementations +* Projected date for testnet network upgrade +* Projected date for mainnet upgrade (the activation block number / projected date for this block) + +### EIP Inclusion Process + +Anyone that wishes to propose a Core EIP for the hard fork should make a PR against the Meta EIP representing the hard fork. The EIP must be published as at least `Draft`. It enters the _Proposed EIPs_ section, along with at least one person who is a point of contact for wanting to include the EIP. + +EIPs can move states by discussion done on the "[All Core Devs Meetings](https://github.com/ethereum/pm/)": +- If accepted for a hard fork, the EIP should be moved to the _Accepted EIPs_ section. If the EIP has major client implementations and no security issues by the timeline date, it is scheduled for inclusion. +- If rejected from a hard fork, the EIP should be moved to the _Rejected EIPs_ section. +- Once the EIPs in the _Accepted EIPs_ section have successfully launched on a testnet roll out, they are moved to the _Included EIPs_ section. + +--- + +The Meta EIP representing the hard fork should move in to the `Accepted` state once the changes are frozen (i.e. all referenced EIPs are in the `Accepted` state) and in to the `Final` state once the hard fork has been activated. + +## Template + +A template for the [Istanbul Hardfork Meta 1679](./eip-1679.md) is included below ([source file on GitHub](./eip-1679.md)): + +``` +{% raw %} +--- +eip: 1679 +title: "Hardfork Meta: Istanbul" +author: Alex Beregszaszi (@axic), Afri Schoedon (@5chdn) +type: Meta +status: Draft +created: 2019-01-04 +requires: 1716 +--- + +## Abstract + +This meta-EIP specifies the changes included in the Ethereum hardfork named Istanbul. + +## Specification + +- Codename: Istanbul +- Activation: TBD + +### Included EIPs + +- TBD + +### Accepted EIPs + +- TBD + +### Rejected EIPs + +- TBD + +### Proposed EIPs + +- TBD + +## Timeline + +* 2019-05-17 (Fri) hard deadline to accept proposals for "Istanbul" +* 2019-07-19 (Fri) soft deadline for major client implementations +* 2019-08-14 (Wed) projected date for testnet network upgrade (Ropsten, Görli, or ad-hoc testnet) +* 2019-10-16 (Wed) projected date for mainnet upgrade ("Istanbul") + +## References + +- TBD (e.g. link to Core Dev notes or other references) + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). + +{% endraw %} +``` + +## Rationale + +A meta EIP for coordinating the hard fork should help in visibility and traceability of the scope of changes as well as provide a simple name and/or number for referring to the proposed fork. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2330.md b/EIPS/eip-2330.md new file mode 100644 index 0000000..a648f15 --- /dev/null +++ b/EIPS/eip-2330.md @@ -0,0 +1,62 @@ +--- +eip: 2330 +title: EXTSLOAD opcode +description: A new EVM opcode to read external contract storage data. +author: Dominic Letz (@dominicletz), Santiago Palladino (@spalladino) +discussions-to: https://ethereum-magicians.org/t/eip-2330-extsload-and-abi-for-lower-gas-cost-and-off-chain-apps/3733 +status: Stagnant +type: Standards Track +category: Core +created: 2019-10-29 +requires: 2929 +--- + +## Abstract + +This proposal adds a new opcode `EXTSLOAD` at `0x5c` which pops two items from the stack: ` ` and pushes one item: ``. The gas cost is sum of account access cost and storage read based on [EIP-2929](./eip-2929.md) Access Lists. + +## Motivation + +While any off-chain application can read all contract storage data of all contracts, this is not possible for deployed smart contracts themselves. These are bound to use contract calls for any interaction including reading data from other contracts. This EIP adds an EVM opcode to directly read external contract storage. + +The gas cost when reading from registry style contract such as [EIP-20s](./eip-20.md), ENS and other data contracts is very high, because they incur cross contract call cost, cost for ABI encoding, decoding and dispatching and finally loading the data. In many cases the underlying storage that is being queried is though just a simple mapping. On top of that, the view function may SLOAD many other slots which caller may not be interested in, which further adds to the gas costs. In these cases a new `EXTSLOAD` call directly accessing the mapping in storage could not only **reduce the gas cost** of the interaction more than 10x, but also it would make the gas cost **predictable** for the reading contract. + +## Specification + +A new EVM instruction `EXTSLOAD (0x5c)` that works like `SLOAD (0x54)` but an additional parameter representing the contract that is to be read from. + +```shell +EXTSLOAD (0x5c) +``` + +The `EXTSLOAD` instruction pops 2 values from the stack, first `contract` a contract address and then second `slot` a storage address within `contract`. As result `EXTSLOAD` pushes on the stack the value from the contract storage of `contract` at the storage `slot` address or `0` in case the account `contract` does not exist. + +### Gas cost pre-verkle + +Gas to be charged before Verkle Tree change is specified as `ACCOUNT_ACCESS_COST + STORAGE_READ_COST` where: + +- `ACCOUNT_ACCESS_COST` is `0` if the account address is already in `accessed_addresses` set, otherwise `COLD_ACCOUNT_ACCESS_COST`. +- `STORAGE_READ_COST` is `WARM_STORAGE_READ_COST` if storage key is already in `accessed_storage_keys` set, otherwise `COLD_STORAGE_READ_COST`. + +### Gas cost post-verkle + +It is important to consider that post Verkle tree change, `ACCOUNT_ACCESS_COST` will not be needed since a single account's storage would be spread across the entire global trie. Hence gas to be charged post Verkle Tree change is just `STORAGE_READ_COST`, which is as specified in [Gas cost pre-verkle](#gas-cost-pre-verkle). + +## Rationale + +- Without this EIP, a contract can still opt-in to make their entire state public, by having a method that simply SLOADs and returns the values ([example](../assets/eip-2330/Extsload.sol)). The complexity of the gas cost can be seen as `1`x CALL cost + `N`x SLOAD cost. Hence, the gas cost specified for using EXTSLOAD opcode on an account for `N` times, the charge of `1`x `COLD_ACCOUNT_ACCESS_COST` and `N`x `STORAGE_READ_COST` is hereby justified. +- Without this EIP, a contract can still use internal state of other contracts. An external party can supply a value and proof to a contract, which the contract can verify using `BLOCKHASH`. This is only possible for the previous blocks and not the latest state (since current blockhash cannot be determined before execution). +- This opcode can be seen as breaking object-oriented (OO) model because it allows to read storage of other contracts. In usual systems using OO is net positive, because there is no limit on machine code and it hardly adds any cost to add more methods or use single method to get a ton of data while the caller needs to just a small portion of data. However on EVM, there are visible costs, i.e. about $0.2 per SLOAD (20 gwei and ETHUSD 2000). Also, OO has caused misleading assumptions for developers where variables marked as "private" in smart contracts are encrypted in some way/impossible to read which has resulted bad designs. Hence, this EIP can be beneficial in terms of making smart contract systems more efficient as well as preventing misconceptions as well. + +## Backwards Compatibility + +This change is fully backwards compatible since it adds a new instruction. + +## Security Considerations + +- Since the opcode is similar to SLOAD, it should be easy to implement in various clients. +- This opcode allows the callee `A` to re-enter a caller contract `B` and read state of `B` and `B` cannot stop `A` from doing that. Since this does not change any state, it should not be a security issue. Contracts generally use re-entrancy guards, but that is only added to write methods. So even currently without EXTSLOAD, `A` can re-enter `B` and read their state exposed by any view methods and it has not been an issue. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2333.md b/EIPS/eip-2333.md new file mode 100644 index 0000000..095c82a --- /dev/null +++ b/EIPS/eip-2333.md @@ -0,0 +1,816 @@ +--- +eip: 2333 +title: BLS12-381 Key Generation +author: Carl Beekhuizen +discussions-to: https://github.com/ethereum/EIPs/issues/2337 +status: Stagnant +type: Standards Track +category: ERC +created: 2019-09-30 +--- + +## Simple Summary + +This EIP is a method based on a tree structure for deriving BLS private keys from a single source of entropy while providing a post-quantum cryptographic fallback for each key. + +## Abstract + +This standard is a method for deriving a tree-hierarchy of BLS12-381 keys based on an entropy seed. Starting with the aforementioned seed, a tree of keys is built out using only the parent node's private key and the index of the desired child. This allows for a practically limitless number of keys to be derived for many different purposes while only requiring knowledge of a single ancestor key in the tree. This allows for keys, or families thereof, to be provisioned for different purposes by further standards. + +In addition to the above, this method of deriving keys provides an emergency backup signature scheme that is resistant to quantum computers for in the event that BLS12-381 is ever deemed insecure. + +## A note on purpose + +This specification is designed not only to be an Ethereum 2.0 standard, but one that is adopted by the wider community who have adopted [BLS signatures over BLS12-381](https://datatracker.ietf.org/doc/draft-irtf-cfrg-bls-signature/). It is therefore important also to consider the needs of the wider industry along with those specific to Ethereum. As a part of these considerations, it is the intention of the author that this standard eventually migrate to a more neutral repository in the future. + +## Motivation + +### Deficiencies of the existing mechanism + +The curve BLS12-381 used for BLS signatures within Ethereum 2.0 (alongside many other projects) mandates a new key derivation scheme. The most commonly used scheme for key derivation within Ethereum 1.x is [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) (also known as HD derivation) which deems keys greater than the curve order invalid. Based on the order of the private key subgroup of BLS12-381 and the size of the entropy utilised, more than 54% of keys generated by BIP32 would be invalid. (secp256k1 keys derived by BIP32 are invalid with probability less than 1 in 2-127.) + +### Establishing a multi-chain standard early on + +By establishing a standard before the first users begin to generate their keys, the hope is that a single standard is highly pervasive and therefore can be assumed to be the method by which the majority of keys are provided. This is valuable for two reasons, firstly in order for a post-quantum backup mechanism to be effective, there needs to be an enshrined mechanism whereby users can switch to a post-quantum signature scheme with pre-shared public keys (something this EIP provides at 0 extra storage cost). Secondly, this unifies the inter- and intra-chain ecosystem by having common tooling ideally allowing users to switch between key-management systems. + +### A post-quantum backup + +This key derivation scheme has a Lamport key pair which is generated as a intermediate step in the key generation process. This key pair can be used to provide a Lamport signature which is a useful backup in the event of BLS12-381 no longer being considered secure (in the event of quantum computing making a sudden advancement, for example). The idea is the Lamport signature will act as a bridge to a new signature scheme which is deemed to be secure. + +## Specification + +### Version + +Due to the evolving BLS signatures CFRG draft (currently v4), the `KeyGen` function was updated, meaning that `hkdf_mod_r` no longer reflected what appeared in the BLS standard. This EIP was updated on the 17th of September 2020 to reflect this new method for deriving keys, **if you are implementing this EIP, please make sure your version is up to date.** + +### Specification + +Keys are defined in terms of a tree structure where a key is determined by the tree's seed and a tree path. This is very useful as one can start with a single source of entropy and build out a practically unlimited number of keys. The specification can be broken into two sub-components: generating the master key, and constructing a child key from its parent. The master key is used as the root of the tree and then the tree is built in layers on top of this root. + +### The Tree Structure + +The key tree is defined purely through the relationship between a child-node and its ancestors. Starting with the root of the tree, the *master key*, a child node can be derived by knowing the parent's private key and the index of the child. The tree is broken up into depths which are indicated by `/` and the master node is described as `m`. The first child of the master node is therefore described as `m / 0` and `m / 0`'s siblings are `m / i` for all `0 <= i < 2**32`. + +```text + [m / 0] - [m / 0 / 0] + / \ + / [m / 0 / 1] +[m] - [m / 1] + \ + ... + [m / i] +``` + +### Key derivation + +Every key generated via the key derivation process derives a child key via a set of intermediate Lamport keys. The idea behind the Lamport keys is to provide a post-quantum backup in case BLS12-381 is no longer deemed secure. At a high level, the key derivation process works by using the parent node's privkey as an entropy source for the Lamport private keys which are then hashed together into a compressed Lamport public key, this public key is then hashed into BLS12-381's private key group. + +#### `IKM_to_lamport_SK` + +##### Inputs + +* `IKM`, a secret octet string +* `salt`, an octet string + +##### Outputs + +* `lamport_SK`, an array of 255 32-octet strings + +##### Definitions + +* `HKDF-Extract` is as defined in [RFC5869](https://tools.ietf.org/html/rfc5869), instantiated with SHA256 +* `HKDF-Expand` is as defined in [RFC5869](https://tools.ietf.org/html/rfc5869), instantiated with SHA256 +* `K = 32` is the digest size (in octets) of the hash function (SHA256) +* `L = K * 255` is the HKDF output size (in octets) +* `""` is the empty string +* `bytes_split` is a function takes in an octet string and splits it into `K`-byte chunks which are returned as an array + +##### Procedure + +``` text +0. PRK = HKDF-Extract(salt, IKM) +1. OKM = HKDF-Expand(PRK, "" , L) +2. lamport_SK = bytes_split(OKM, K) +3. return lamport_SK +``` + +#### `parent_SK_to_lamport_PK` + +##### Inputs + +* `parent_SK`, the BLS Secret Key of the parent node +* `index`, the index of the desired child node, an integer `0 <= index < 2^32` + +##### Outputs + +* `lamport_PK`, the compressed lamport PK, a 32 octet string + +##### Definitions + +* `I2OSP` is as defined in [RFC3447](https://ietf.org/rfc/rfc3447.txt) (Big endian decoding) +* `flip_bits` is a function that returns the bitwise negation of its input +* `""` is the empty string +* `a | b` is the concatenation of `a` with `b` + +##### Procedure + +```text +0. salt = I2OSP(index, 4) +1. IKM = I2OSP(parent_SK, 32) +2. lamport_0 = IKM_to_lamport_SK(IKM, salt) +3. not_IKM = flip_bits(IKM) +4. lamport_1 = IKM_to_lamport_SK(not_IKM, salt) +5. lamport_PK = "" +6. for i in 1, .., 255 + lamport_PK = lamport_PK | SHA256(lamport_0[i]) +7. for i in 1, .., 255 + lamport_PK = lamport_PK | SHA256(lamport_1[i]) +8. compressed_lamport_PK = SHA256(lamport_PK) +9. return compressed_lamport_PK +``` + +**Note:** The indexing, `i`, in the above procedure iterates from 1 to 255 (inclusive). This is due to the limit to which HKDF can stretch the input bytes (255 times the length of the input bytes). The result of this is that the security of the lamport-backup signature is \*only\* 127.5 bit. + +#### `HKDF_mod_r` + +`hkdf_mod_r()` is used to hash 32 random bytes into the subgroup of the BLS12-381 private keys. + +##### Inputs + +* `IKM`, a secret octet string >= 256 bits in length +* `key_info`, an optional octet string (default=`""`, the empty string) + +##### Outputs + +* `SK`, the corresponding secret key, an integer 0 <= SK < r. + +##### Definitions + +* `HKDF-Extract` is as defined in RFC5869, instantiated with hash H. +* `HKDF-Expand` is as defined in RFC5869, instantiated with hash H. +* `L` is the integer given by `ceil((3 * ceil(log2(r))) / 16)`.(`L=48`) +* `"BLS-SIG-KEYGEN-SALT-"` is an ASCII string comprising 20 octets. +* `OS2IP` is as defined in [RFC3447](https://ietf.org/rfc/rfc3447.txt) (Big endian encoding) +* `I2OSP` is as defined in [RFC3447](https://ietf.org/rfc/rfc3447.txt) (Big endian decoding) +* `r` is the order of the BLS 12-381 curve defined in [the v4 draft IETF BLS signature scheme standard](https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04) `r=52435875175126190479447740508185965837690552500527637822603658699938581184513` + +##### Procedure + +```text +1. salt = "BLS-SIG-KEYGEN-SALT-" +2. SK = 0 +3. while SK == 0: +4. salt = H(salt) +5. PRK = HKDF-Extract(salt, IKM || I2OSP(0, 1)) +6. OKM = HKDF-Expand(PRK, key_info || I2OSP(L, 2), L) +7. SK = OS2IP(OKM) mod r +8. return SK +``` + +### `derive_child_SK` + +The child key derivation function takes in the parent's private key and the index of the child and returns the child private key. + +##### Inputs + +* `parent_SK`, the secret key of the parent node, a big endian encoded integer +* `index`, the index of the desired child node, an integer `0 <= index < 2^32` + +##### Outputs + +* `child_SK`, the secret key of the child node, a big endian encoded integer + +##### Procedure + +```text +0. compressed_lamport_PK = parent_SK_to_lamport_PK(parent_SK, index) +1. SK = HKDF_mod_r(compressed_lamport_PK) +2. return SK +``` + +### `derive_master_SK` + +The child key derivation function takes in the parent's private key and the index of the child and returns the child private key. The seed should ideally be derived from a mnemonic, with the intention being that [BIP39 mnemonics](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki), with the associated [mnemonic_to_seed method](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki#from-mnemonic-to-seed) be used. + +##### Inputs + +* `seed`, the source entropy for the entire tree, a octet string >= 256 bits in length + +##### Outputs + +* `SK`, the secret key of master node within the tree, a big endian encoded integer + +##### Procedure + +```text +0. SK = HKDF_mod_r(seed) +1. return SK +``` + +## Rationale + +### Lamport signatures + +Lamport signatures are used as the backup mechanism because of their relative simplicity for a post-quantum signature scheme. Lamport signatures are very easy both to explain and implement as the sole cryptographic dependency is a secure hash function. This is important as it minimises the complexity of implementing this standard as well as the compute time for deriving a key. Lamport signatures have very large key sizes which make them impractical for many use cases, but this is not deemed to be an issue in this case as this scheme is only meant to be a once-off event to migrate to a new scheme. + +Revealing the associated Lamport public key for a corresponding BLS key is done by verifying that the Lamport public key is the pre-image of the corresponding BLS private key (which in turn is verified against the BLS public key). This means that using a key's Lamport signature reveals the BLS private key rendering the BLS key pair unsafe. This has the upside of not requiring additional storage space for backup keys alongside BLS keys but does require that the Lamport signatures be used once and that the BLS key is no longer trusted after that point. + +The Lamport signatures used within this scheme have 255 bits worth of security, not 256. This is done because HKDF-SHA256, the mechanism used to stretch a key's entropy, has a length-limit of `255 * hash_function_digest_size`. The 1-bit reduction in security is deemed preferable over increasing the complexity of the entropy stretching mechanism. + +### SHA256 + +SHA256 is used as the hash function throughout this standard as it is the hash function chosen by the [IETF BLS signature proposed standard](https://datatracker.ietf.org/doc/draft-irtf-cfrg-bls-signature/). Using a single hash function for everything decreases the number of cryptographic primitives required to implement the entire BLS standardised key-stack while reducing the surface for flaws in the overall system. + +### `hkdf_mod_r()` + +The function `hkdf_mod_r()` in this standard is the same as the `KeyGen` function described in the [proposed standard](https://datatracker.ietf.org/doc/draft-irtf-cfrg-bls-signature/) and therefore the private key obtained from `KeyGen` is equal to that obtained from `hkdf_mod_r` for the same seed bytes. This means that common engineering can be done when implementing this function. Additionally because of its inclusion in an IETF standard, it has had much scrutiny by many cryptographers and cryptanalysts, thereby lending credence to its safety as a key derivation mechanism. + +While `hkdf_mod_r()` has modulo bias, the magnitude of this bias is minuscule (the output size of HKDF is set to 48 bytes which is greater 2128 time larger than the curve order). This bias is deemed acceptable in light of the simplicity of the constant time scheme. + +### Only using hardened keys + +Widely accepted standards that existed before this one ([BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) and [BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki)) utilise the notion of hardened and non-hardened keys whereas this specification only offers the former. Non-hardened keys are primarily useful in a UTXO system in which having one's balance spilt amongst many accounts does not present much additionally complexity, but such keys are much less useful outside of this context. Further complicating matters is the problem of deriving non-hardened keys using a post-quantum signature scheme as non-hardened keys are made possible by the very group arithmetic quantum computers gain an advantage over. + +## Backwards Compatibility + +There are no major backwards compatibility issues brought upon by this EIP as it is not designed for use within Ethereum 1.0 as it currently stands. That said, this standard is not compatible with BIP32/ BIP44 style paths as paths specified by these systems make use of non-hardened keys, something that does not exist within this standard. + +## Test Cases + +### Test Case 0 + +```text +seed = 0xc55257c360c07c72029aebc1b53c05ed0362ada38ead3e3e9efa3708e53495531f09a6987599d18264c1e1c92f2cf141630c7a3c4ab7c81b2f001698e7463b04 +master_SK = 6083874454709270928345386274498605044986640685124978867557563392430687146096 +child_index = 0 +child_SK = 20397789859736650942317412262472558107875392172444076792671091975210932703118 +``` + +This test case can be extended to test the entire mnemonic-to-`child_SK` stack, assuming [BIP39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki) is used as the mnemonic generation mechanism. Using the following parameters, the above seed can be calculated: + +```test +mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" +passphrase = "TREZOR" +``` + +This test case can be extended to test the entire `mnemonic-to -child_SK` stack, assuming [BIP39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki) is used as the mnemonic generation mechanism. Using the following parameters, the above seed can be calculated: + +```text +mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" +passphrase = "TREZOR" +``` + +### Test Case 1 + +```text +seed = 0x3141592653589793238462643383279502884197169399375105820974944592 +master_SK = 29757020647961307431480504535336562678282505419141012933316116377660817309383 +child_index = 3141592653 +child_SK = 25457201688850691947727629385191704516744796114925897962676248250929345014287 +``` + +### Test Case 2 + +```text +seed = 0x0099FF991111002299DD7744EE3355BBDD8844115566CC55663355668888CC00 +master_SK = 27580842291869792442942448775674722299803720648445448686099262467207037398656 +child_index = 4294967295 +child_SK = 29358610794459428860402234341874281240803786294062035874021252734817515685787 +``` + +### Test Case 3 + +```text +seed = 0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3 +master_SK = 19022158461524446591288038168518313374041767046816487870552872741050760015818 +child_index = 42 +child_SK = 31372231650479070279774297061823572166496564838472787488249775572789064611981 +``` + +### Test Vector with Intermediate values + +```text +seed = 0xc55257c360c07c72029aebc1b53c05ed0362ada38ead3e3e9efa3708e53495531f09a6987599d18264c1e1c92f2cf141630c7a3c4ab7c81b2f001698e7463b04 +master_SK = 6083874454709270928345386274498605044986640685124978867557563392430687146096 +child_index = 0 +lamport_0 = [0xe345d0ad7be270737de05cf036f688f385d5f99c7fddb054837658bdd2ebd519, +0x65050bd4db9c77c051f67dcc801bf1cdf33d81131e608505bb3e4523868eb76c, +0xc4f8e8d251fbdaed41bdd9c135b9ed5f83a614f49c38fffad67775a16575645a, +0x638ad0feace7567255120a4165a687829ca97e0205108b8b73a204fba6a66faa, +0xb29f95f64d0fcd0f45f265f15ff7209106ab5f5ce6a566eaa5b4a6f733139936, +0xbcfbdd744c391229f340f02c4f2d092b28fe9f1201d4253b9045838dd341a6bf, +0x8b9cf3531bfcf0e4acbfd4d7b4ed614fa2be7f81e9f4eaef53bedb509d0b186f, +0xb32fcc5c4e2a95fb674fa629f3e2e7d85335f6a4eafe7f0e6bb83246a7eced5f, +0xb4fe80f7ac23065e30c3398623b2761ac443902616e67ce55649aaa685d769ce, +0xb99354f04cfe5f393193c699b8a93e5e11e6be40ec16f04c739d9b58c1f55bf3, +0x93963f58802099ededb7843219efc66a097fab997c1501f8c7491991c780f169, +0x430f3b027dbe9bd6136c0f0524a0848dad67b253a11a0e4301b44074ebf82894, +0xd635c39b4a40ad8a54d9d49fc8111bd9d11fb65c3b30d8d3eaef7d7556aac805, +0x1f7253a6474cf0b2c05b02a7e91269137acddedcb548144821f9a90b10eccbab, +0x6e3bdb270b00e7b6eb8b044dbfae07b51ea7806e0d24218c59a807a7fd099c18, +0x895488ad2169d8eaae332ce5b0fe1e60ffab70e62e1cb15a2a1487544af0a6e8, +0x32d45a99d458c90e173a3087ea3661ab62d429b285089e92806a9663ba825342, +0xc15c52106c3177f5848a173076a20d46600ca65958a1e3c7d45a593aaa9670ed, +0xd8180c550fbe4cd6d5b676ff75e0728729d8e28a3b521d56152594ac6959d563, +0x58fe153fac8f4213aaf175e458435e06304548024bcb845844212c774bdffb2a, +0x10fff610a50f4bee5c978f512efa6ab4fafacb65929606951ba5b93eeb617b5a, +0x78ac9819799b52eba329f13dd52cf0f6148a80bf04f93341814c4b47bb4aa5ec, +0xa5c3339caa433fc11e74d1765bec577a13b054381a44b23c2482e750696876a9, +0x9f716640ab5cdc2a5eb016235cddca2dc41fa4ec5acd7e58af628dade99ec376, +0x2544364320e67577c4fed8c7c7c839deed93c24076d5343c5b8faca4cc6dc2d8, +0x62553e782541f822c589796be5d5c83bfc814819100b2be0710b246f5aa7149c, +0x229fb761c46c04b22ba5479f2696be0f936fded68d54dd74bcd736b8ba512afb, +0x0af23996a65b98a0ebaf19f3ec0b3ef20177d1bfd6eb958b3bd36e0bdbe04c8c, +0x6f0954f9deab52fd4c8d2daba69f73a80dea143dd49d9705c98db3d653adf98c, +0xfa9221dd8823919a95b35196c1faeb59713735827f3e84298c25c83ac700c480, +0x70c428e3ff9e5e3cda92d6bb85018fb89475c19f526461cca7cda64ebb2ff544, +0xdcaac3413e22314f0f402f8058a719b62966b3a7429f890d947be952f2e314ba, +0xb6b383cb5ec25afa701234824491916bfe6b09d28cf88185637e2367f0cf6edc, +0x7b0d91488fc916aba3e9cb61a5a5645b9def3b02e4884603542f679f602afb8d, +0xe9c20abca284acfde70c59584b9852b85c52fa7c263bb981389ff8d638429cd7, +0x838524f798daee6507652877feb9597f5c47e9bb5f9aa52a35fb6fff796813b9, +0xbe1ca18faf9bf322474fad1b3d9b4f1bc76ae9076e38e6dd2b16e2faf487742b, +0xbf02d70f1a8519343a16d24bade7f7222912fd57fe4f739f367dfd99d0337e8e, +0xc979eb67c107ff7ab257d1c0f4871adf327a4f2a69e01c42828ea27407caf058, +0xf769123d3a3f19eb7b5c3fd4f467a042944a7c5ff8834cebe427f47dbd71460c, +0xaefc8edc23257e1168a35999fe3832bcbc25053888cc89c38667482d6748095b, +0x8ff399f364d3a2428b1c92213e4fdc5341e7998007da46a5a2f671929b42aaab, +0xcf2a3d9e6963b24c5001fbba1e5ae7f45dd6cf520fd24861f745552db86bab48, +0xb380e272d7f3091e5c887fa2e7c690c67d59f4d95f8376d150e555da8c738559, +0xc006a749b091d91204dbb64f59059d284899de5986a7f84f8877afd5e0e4c253, +0x818d8bb9b7da2dafa2ef059f91975e7b6257f5e199d217320de0a576f020de5c, +0x7aabf4a1297d2e550a2ee20acb44c1033569e51b6ec09d95b22a8d131e30fd32, +0xdd01c80964a5d682418a616fb10810647c9425d150df643c8ddbbe1bfb2768b7, +0x1e2354e1d97d1b06eb6cfe9b3e611e8d75b5c57a444523e28a8f72a767eff115, +0x989c9a649dca0580256113e49ea0dd232bbfd312f68c272fe7c878acc5da7a2c, +0x14ee1efe512826fff9c028f8c7c86708b841f9dbf47ce4598298b01134ebdc1a, +0x6f861dba4503f85762d9741fa8b652ce441373f0ef2b7ebbd5a794e48cdab51b, +0xda110c9492ffdb87efe790214b7c9f707655a5ec08e5af19fb2ab2acc428e7dc, +0x5576aa898f6448d16e40473fcb24c46c609a3fc46a404559faa2d0d34d7d49ce, +0x9bd9a35675f2857792bc45893655bfdf905ffeaee942d93ad39fbcadd4ca9e11, +0xfa95e4c37db9303d5213890fd984034089cbc9c6d754741625da0aa59cc45ccf, +0xfef7d2079713f17b47239b76c8681bf7f800b1bfeac7a53265147579572ddf29, +0x39aa7c0fecf9a1ed037c685144745fda16da36f6d2004844cf0e2d608ef6ed0e, +0x5530654d502d6ba30f2b16f49cc5818279697308778fd8d40db8e84938144fb6, +0xb1beaa36397ba1521d7bf7df16536969d8a716e63510b1b82a715940180eb29f, +0x21abe342789f7c15a137afa373f686330c0db8c861572935a3cd8dcf9e4e1d45, +0x27b5a1acda55b4e0658887bd884d3203696fcae0e94f19e31bfe931342b1c257, +0x58401a02502d7708a812c0c72725f768f5a556480517258069f2d72543cda888, +0x4b38f291548f51bee7e4cf8cc5c8aa8f4ad3ec2461dba4ccbab70f1c1bfd7feb, +0x9b39a53fdafaaf1d23378e0aa8ae65d38480de69821de2910873eefc9f508568, +0x932200566a3563ee9141913d12fd1812cb008cb735724e8610890e101ec10112, +0x6a72f70b4ec5491f04780b17c4776a335fcc5bff5073d775150e08521dc74c91, +0x86d5c60e627a4b7d5d075b0ba33e779c45f3f46d22ed51f31360afd140851b67, +0x5ca2a736bb642abc4104faa781c9aff13d692a400d91dc961aec073889836946, +0xa14bca5a262ac46ceac21388a763561fc85fb9db343148d786826930f3e510cd, +0x87be03a87a9211504aa70ec149634ee1b97f7732c96377a3c04e98643dcba915, +0x8fe283bc19a377823377e9c326374ebb3f29527c12ea77bfb809c18eef8943b0, +0x8f519078b39a3969f7e4caeca9839d4e0eccc883b89e4a86d0e1731bfc5e33fc, +0x33d7c28c3d26fdfc015a8c2131920e1392ef0aea55505637b54ea63069c7858e, +0xe57de7c189fcc9170320c7acedb38798562a48dbc9943b2a8cd3441d58431128, +0x513dac46017050f82751a07b6c890f14ec43cadf687f7d202d2369e35b1836b4, +0xfd967d9f805bb7e78f7b7caa7692fdd3d6b5109c41ad239a08ad0a38eeb0ac4c, +0xf2013e4da9abcc0f03ca505ed94ec097556dbfd659088cd24ec223e02ac43329, +0xe0dcfac50633f7417f36231df2c81fa1203d358d5f57e896e1ab4b512196556b, +0xf022848130e73fe556490754ef0ecfcdaaf3b9ff16ae1eda7d38c95c4f159ded, +0x2147163a3339591ec7831d2412fb2d0588c38da3cd074fa2a4d3e5d21f9f1d2d, +0x11ee2404731962bf3238dca0d9759e06d1a5851308b4e6321090886ec5190b69, +0xf7679ecd07143f8ac166b66790fa09aed39352c09c0b4766bbe500b1ebace5a5, +0xc7a0e95f09076472e101813a95e6ea463c35bd5ee9cfda3e5d5dbccb35888ef0, +0xde625d3b547eb71bea5325a0191a592fa92a72e4b718a499fdba32e245ddf37e, +0x7e5bdccd95df216e8c59665073249072cb3c9d0aef6b341afc0ca90456942639, +0xc27f65fd9f797ede374e06b4ddb6e8aa59c7d6f36301f18b42c48b1889552fe3, +0x8175730a52ea571677b035f8e2482239dda1cfbff6bc5cde00603963511a81af, +0x09e440f2612dad1259012983dc6a1e24a73581feb1bd69d8a356eea16ba5fd0e, +0x59dcc81d594cbe735a495e38953e8133f8b3825fd84767af9e4ea06c49dbabfa, +0x6c8480b59a1a958c434b9680edea73b1207077fb9a8a19ea5f9fbbf6f47c4124, +0x81f5c89601893b7a5a231a7d37d6ab9aa4c57f174fcfc6b40002fa808714c3a1, +0x41ba4d6b4da141fcc1ee0f4b47a209cfd143d34e74fc7016e9956cedeb2db329, +0x5e0b5b404c60e9892040feacfb4a84a09c2bc4a8a5f54f3dad5dca4acdc899dc, +0xe922eebf1f5f15000d8967d16862ed274390cde808c75137d2fb9c2c0a80e391, +0xbf49d31a59a20484f0c08990b2345dfa954509aa1f8901566ab9da052b826745, +0xb84e07da828ae668c95d6aa31d4087504c372dbf4b5f8a8e4ded1bcf279fd52b, +0x89288bf52d8c4a9561421ad199204d794038c5d19ae9fee765ee2b5470e68e7e, +0xf6f618be99b85ec9a80b728454a417c647842215e2160c6fe547dd5a69bd9302, +0xdd9adc002f98c9a47c7b704fc0ce0a5c7861a5e2795b6014749cde8bcb8a034b, +0xd119a4b2c0db41fe01119115bcc35c4b7dbfdb42ad3cf2cc3f01c83732acb561, +0x9c66bc84d416b9193bad9349d8c665a9a06b835f82dc93ae0cccc218f808aad0, +0xd4b50eefcd2b5df075f14716cf6f2d26dfc8ae02e3993d711f4a287313038fde, +0xaf72bfb346c2f336b8bc100bff4ba35d006a3dad1c5952a0adb40789447f2704, +0xc43ca166f01dc955e7b4330227635feb1b0e0076a9c5633ca5c614a620244e5b, +0x5efca76970629521cfa053fbbbda8d3679cadc018e2e891043b0f52989cc2603, +0x35c57de1c788947f187051ce032ad1e899d9887d865266ec6fcfda49a8578b2b, +0x56d4be8a65b257216eab7e756ee547db5a882b4edcd12a84ed114fbd4f5be1f1, +0x257e858f8a4c07a41e6987aabaa425747af8b56546f2a3406f60d610bcc1f269, +0x40bd9ee36d52717ab22f1f6b0ee4fb38b594f58399e0bf680574570f1b4b8c90, +0xcb6ac01c21fc288c12973427c5df6eb8f6aefe64b92a6420c6388acdf36bc096, +0xa5716441312151a5f0deb52993a293884c6c8f445054ce1e395c96adeee66c6d, +0xe15696477f90113a10e04ba8225c28ad338c3b6bdd7bdeb95c0722921115ec85, +0x8faeaa52ca2f1d791cd6843330d16c75eaf6257e4ba236e3dda2bc1a644aee00, +0xc847fe595713bf136637ce8b43f9de238762953fed16798878344da909cc76ae, +0xb5740dc579594dd110078ce430b9696e6a308078022dde2d7cfe0ef7647b904e, +0x551a06d0771fcd3c53aea15aa8bf700047138ef1aa22265bee7fb965a84c9615, +0x9a65397a5907d604030508d41477de621ce4a0d79b772e81112d634455e7a4da, +0x6462d4cc2262d7faf8856812248dc608ae3d197bf2ef410f00c3ae43f2040995, +0x6782b1bd319568e30d54b324ab9ed8fdeac6515e36b609e428a60785e15fb301, +0x8bcdcf82c7eb2a07e14db20d80d9d2efea8d40320e121923784c92bf38250a8e, +0x46ed84fa17d226d5895e44685747ab82a97246e97d6237014611aaaba65ed268, +0x147e87981673326c5a2bdb06f5e90eaaa9583857129451eed6dde0c117fb061f, +0x4141d6fe070104c29879523ba6669552f3d457c0929bb878d2751f4ff059b895, +0xd866ce4ef226d74841f950fc28cdf2235db21e0e3f07a0c8f807704464db2210, +0xa804f9118bf92558f684f90c2bda832a4f51ef771ffb2765cde3ec6f48124f32, +0xc436d4a65910124e00cded9a637178914a8fbc090400f3f031c03eac4d0295a5, +0x643fdb9243656512316528de04dcc7344ca33783580ad0c3debf8c4a6e7c8bc4, +0x7f4a345b41706b281b2de998e91ff62d908eb29fc333ee336221757753c96e23, +0x6bdc086a5b11de950cabea33b72d98db886b291c4c2f02d3e997edc36785d249, +0xfb10b5b47d374078c0a52bff7174bf1cd14d872c7d20b4a009e2afd3017a9a17, +0x1e07e605312db5380afad8f3d7bd602998102fdd39565b618ac177b13a6527e6, +0xc3161b5a7b93aabf05652088b0e5b4803a18be693f590744c42c24c7aaaeef48, +0xa47e4f25112a7d276313f153d359bc11268b397933a5d5375d30151766bc689a, +0xb24260e2eff88716b5bf5cb75ea171ac030f5641a37ea89b3ac45acb30aae519, +0x2bcacbebc0a7f34406db2c088390b92ee34ae0f2922dedc51f9227b9afb46636, +0xc78c304f6dbe882c99c5e1354ce6077824cd42ed876db6706654551c7472a564, +0x6e2ee19d3ee440c78491f4e354a84fa593202e152d623ed899e700728744ac85, +0x2a3f438c5dc012aa0997b66f661b8c10f4a0cd7aa5b6e5922b1d73020561b27f, +0xd804f755d93173408988b95e9ea0e9feae10d404a090f73d9ff84df96f081cf7, +0xe06fda941b6936b8b33f00ffa02c8b05fd78fbec953da61da2043f5644b30a50, +0x45ee279b465d53148850a16cc7f6bd33e7627aef554a9418ed012ca8f9717f80, +0x9c79348c1bcd6aa2135452491d73564413a247ea8cc38fa7dcc6c43f8a2d61d5, +0x7c91e056f89f2a77d3e3642e595bcf4973c3bca68dd2b10f51ca0d8945e4255e, +0x669f976ebe38cbd22c5b1f785e14b76809d673d2cb1458983dbda41f5adf966b, +0x8bc71e99ffcc119fd8bd604af54c0663b0325a3203a214810fa2c588089ed5a7, +0x36b3f1ffeae5d9855e0965eef33f4c5133d99685802ac5ce5e1bb288d308f889, +0x0aad33df38b3f31598e04a42ec22f20bf2e2e9472d02371eb1f8a06434621180, +0x38c5632b81f90efbc51a729dcae03626a3063aa1f0a102fd0e4326e86a08a732, +0x6ea721753348ed799c98ffa330d801e6760c882f720125250889f107915e270a, +0xe700dd57ce8a653ce4269e6b1593a673d04d3de8b79b813354ac7c59d1b99adc, +0xe9294a24b560d62649ca898088dea35a644d0796906d41673e29e4ea8cd16021, +0xf20bb60d13a498a0ec01166bf630246c2f3b7481919b92019e2cfccb331f2791, +0xf639a667209acdd66301c8e8c2385e1189b755f00348d614dc92da14e6866b38, +0x49041904ee65c412ce2cd66d35570464882f60ac4e3dea40a97dd52ffc7b37a2, +0xdb36b16d3a1010ad172fc55976d45df7c03b05eab5432a77be41c2f739b361f8, +0x71400cdd2ea78ac1bf568c25a908e989f6d7e2a3690bc869c7c14e09c255d911, +0xf0d920b2d8a00b88f78e7894873a189c580747405beef5998912fc9266220d98, +0x1a2baefbbd41aa9f1cc5b10e0a7325c9798ba87de6a1302cf668a5de17bc926a, +0x449538a20e52fd61777c45d35ff6c2bcb9d9165c7eb02244d521317f07af6691, +0x97006755b9050b24c1855a58c4f4d52f01db4633baff4b4ef3d9c44013c5c665, +0xe441363a27b26d1fff3288222fa8ed540f8ca5d949ddcc5ff8afc634eec05336, +0xed587aa8752a42657fea1e68bc9616c40c68dcbbd5cb8d781e8574043e29ef28, +0x47d896133ba81299b8949fbadef1c00313d466827d6b13598685bcbb8776c1d2, +0x7786bc2cb2d619d07585e2ea4875f15efa22110e166af87b29d22af37b6c047d, +0x956b76194075fe3daf3ca508a6fad161deb05d0026a652929e37c2317239cbc6, +0xec9577cb7b85554b2383cc4239d043d14c08d005f0549af0eca6994e203cb4e7, +0x0722d0c68d38b23b83330b972254bbf9bfcf32104cc6416c2dad67224ac52887, +0x532b19d54fb6d77d96452d3e562b79bfd65175526cd793f26054c5f6f965df39, +0x4d62e065e57cbf60f975134a360da29cabdcea7fcfc664cf2014d23c733ab3b4, +0x09be0ea6b363fd746b303e482cb4e15ef25f8ae57b7143e64cbd5c4a1d069ebe, +0x69dcddc3e05147860d8d0e90d602ac454b609a82ae7bb960ee2ecd1627d77777, +0xa5e2ae69d902971000b1855b8066a4227a5be7234ac9513b3c769af79d997df4, +0xc287d4bc953dcff359d707caf2ccba8cc8312156eca8aafa261fb72412a0ea28, +0xb27584fd151fb30ed338f9cba28cf570f7ca39ebb03eb2e23140423af940bd96, +0x7e02928194441a5047af89a6b6555fea218f1df78bcdb5f274911b48d847f5f8, +0x9ba611add61ea6ba0d6d494c0c4edd03df9e6c03cafe10738cee8b7f45ce9476, +0x62647ec3109ac3db3f3d9ea78516859f0677cdde3ba2f27f00d7fda3a447dd01, +0xfa93ff6c25bfd9e17d520addf5ed2a60f1930278ff23866216584853f1287ac1, +0x3b391c2aa79c2a42888102cd99f1d2760b74f772c207a39a8515b6d18e66888a, +0xcc9ae3c14cbfb40bf01a09bcde913a3ed208e13e4b4edf54549eba2c0c948517, +0xc2b8bce78dd4e876da04c54a7053ca8b2bedc8c639cee82ee257c754c0bea2b2, +0xdb186f42871f438dba4d43755c59b81a6788cb3b544c0e1a3e463f6c2b6f7548, +0xb7f8ba137c7783137c0729de14855e20c2ac4416c33f5cac3b235d05acbab634, +0x282987e1f47e254e86d62bf681b0803df61340fdc9a8cf625ef2274f67fc6b5a, +0x04aa195b1aa736bf8875777e0aebf88147346d347613b5ab77bef8d1b502c08c, +0x3f732c559aee2b1e1117cf1dec4216a070259e4fa573a7dcadfa6aab74aec704, +0x72699d1351a59aa73fcede3856838953ee90c6aa5ef5f1f7e21c703fc0089083, +0x6d9ce1b8587e16a02218d5d5bed8e8d7da4ac40e1a8b46eeb412df35755c372c, +0x4f9c19b411c9a74b8616db1357dc0a7eaf213cb8cd2455a39eb7ae4515e7ff34, +0x9163dafa55b2b673fa7770b419a8ede4c7122e07919381225c240d1e90d90470, +0x268ff4507b42e623e423494d3bb0bc5c0917ee24996fb6d0ebedec9ce8cd9d5c, +0xff6e6169d233171ddc834e572024586eeb5b1bda9cb81e5ad1866dbc53dc75fe, +0xb379a9c8279205e8753b6a5c865fbbf70eb998f9005cd7cbde1511f81aed5256, +0x3a6b145e35a592e037c0992c9d259ef3212e17dca81045e446db2f3686380558, +0x60fb781d7b3137481c601871c1c3631992f4e01d415841b7f5414743dcb4cfd7, +0x90541b20b0c2ea49bca847e2db9b7bba5ce15b74e1d29194a12780e73686f3dd, +0xe2b0507c13ab66b4b769ad1a1a86834e385b315da2f716f7a7a8ff35a9e8f98c, +0xeefe54bc9fa94b921b20e7590979c28a97d8191d1074c7c68a656953e2836a72, +0x8676e7f59d6f2ebb0edda746fc1589ef55e07feab00d7008a0f2f6f129b7bb3a, +0x78a3d93181b40152bd5a8d84d0df7f2adde5db7529325c13bc24a5b388aed3c4, +0xcc0e2d0cba7aaa19c874dbf0393d847086a980628f7459e9204fda39fad375c0, +0x6e46a52cd7745f84048998df1a966736d2ac09a95a1c553016fef6b9ec156575, +0x204ac2831d2376d4f9c1f5c106760851da968dbfc488dc8a715d1c764c238263, +0xbdb8cc7b7e5042a947fca6c000c10b9b584e965c3590f92f6af3fe4fb23e1358, +0x4a55e4b8a138e8508e7b11726f617dcf4155714d4600e7d593fd965657fcbd89, +0xdfe064bb37f28d97b16d58b575844964205e7606dce914a661f2afa89157c45b, +0x560e374fc0edda5848eef7ff06471545fcbdd8aefb2ecddd35dfbb4cb03b7ddf, +0x10a66c82e146da5ec6f48b614080741bc51322a60d208a87090ad7c7bf6b71c6, +0x62534c7dc682cbf356e6081fc397c0a17221b88508eaeff798d5977f85630d4f, +0x0138bba8de2331861275356f6302b0e7424bbc74d88d8c534479e17a3494a15b, +0x580c7768bf151175714b4a6f2685dc5bcfeb088706ee7ed5236604888b84d3e4, +0xd290adb1a5dfc69da431c1c0c13da3be788363238d7b46bc20185edb45ab9139, +0x1689879db6c78eb4d3038ed81be1bc106f8cfa70a7c6245bd4be642bfa02ebd7, +0x6064c384002c8b1594e738954ed4088a0430316738def62822d08b2285514918, +0x01fd23493f4f1cc3c5ff4e96a9ee386b2a144b50a428a6b5db654072bddadfe7, +0xd5d05bb7f23ab0fa2b82fb1fb14ac29c2477d81a85423d0a45a4b7d5bfd81619, +0xd72b9a73ae7b24db03b84e01106cea734d4b9d9850b0b7e9d65d6001d859c772, +0x156317cb64578db93fee2123749aff58c81eae82b189b0d6f466f91de02b59df, +0x5fba299f3b2c099edbac18d785be61852225890fc004bf6be0787d62926a79b3, +0x004154f28f685bdbf0f0d6571e7a962a4c29b6c3ebedaaaf66097dfe8ae5f756, +0x4b45816f9834c3b289affce7a3dc80056c2b7ffd3e3c250d6dff7f923e7af695, +0x6ca53bc37816fff82346946d83bef87860626bbee7fd6ee9a4aeb904d893a11f, +0xf48b2f43184358d66d5b5f7dd2b14a741c7441cc7a33ba3ebcc94a7b0192d496, +0x3cb98f4baa429250311f93b46e745174f65f901fab4eb8075d380908aaaef650, +0x343dfc26b4473b3a20e706a8e87e5202a4e6b96b53ed448afb9180c3f766e5f8, +0x1ace0e8a735073bcbaea001af75b681298ef3b84f1dbab46ea52cee95ab0e7f9, +0xd239b110dd71460cdbc41ddc99494a7531186c09da2a697d6351c116e667733b, +0x22d6955236bd275969b8a6a30c23932670a6067f68e236d2869b6a8b4b493b83, +0x53c1c01f8d061ac89187e5815ef924751412e6a6aa4dc8e3abafb1807506b4e0, +0x2f56dd20c44d7370b713e7d7a1bfb1a800cac33f8a6157f278e17a943806a1f7, +0xc99773d8a5b3e60115896a65ac1d6c15863317d403ef58b90cb89846f4715a7f, +0x9f4b6b77c254094621cd336da06fbc6cbb7b8b1d2afa8e537ceca1053c561ef5, +0x87944d0b210ae0a6c201cba04e293f606c42ebaed8b4a5d1c33f56863ae7e1b5, +0xa7d116d962d03ca31a455f9cda90f33638fb36d3e3506605aa19ead554487a37, +0x4042e32e224889efd724899c9edb57a703e63a404129ec99858048fbc12f2ce0, +0x36759f7a0faeea1cd4cb91e404e4bf09908de6e53739603d5f0db52b664158a3, +0xa4d50d005fb7b9fea8f86f1c92439cc9b8446efef7333ca03a8f6a35b2d49c38, +0x80cb7c3e20f619006542edbe71837cdadc12161890a69eea8f41be2ee14c08a3, +0xbb3c44e1df45f2bb93fb80e7f82cee886c153ab484c0095b1c18df03523629b4, +0x04cb749e70fac3ac60dea779fceb0730b2ec5b915b0f8cf28a6246cf6da5db29, +0x4f5189b8f650687e65a962ef3372645432b0c1727563777433ade7fa26f8a728, +0x322eddddf0898513697599b68987be5f88c0258841affec48eb17cf3f61248e8, +0x6416be41cda27711d9ec22b3c0ed4364ff6975a24a774179c52ef7e6de9718d6, +0x0622d31b8c4ac7f2e30448bdadfebd5baddc865e0759057a6bf7d2a2c8b527e2, +0x40f096513588cc19c08a69e4a48ab6a43739df4450b86d3ec2fb3c6a743b5485, +0x09fcf7d49290785c9ea2d54c3d63f84f6ea0a2e9acfcdbb0cc3a281ce438250e, +0x2000a519bf3da827f580982d449b5c70fcc0d4fa232addabe47bb8b1c471e62e, +0xf4f80008518e200c40b043f34fb87a6f61b82f8c737bd784292911af3740245e, +0x939eaab59f3d2ad49e50a0220080882319db7633274a978ced03489870945a65, +0xadcad043d8c753fb10689280b7670f313253f5d719039e250a673d94441ee17c, +0x58b7b75f090166b8954c61057074707d7e38d55ce39d9b2251bbc3d72be458f8, +0xf61031890c94c5f87229ec608f2a9aa0a3f455ba8094b78395ae312cbfa04087, +0x356a55def50139f94945e4ea432e7a9defa5db7975462ebb6ca99601c614ea1d, +0x65963bb743d5db080005c4db59e29c4a4e86f92ab1dd7a59f69ea7eaf8e9aa79] +lamport_1 = [0x9c0bfb14de8d2779f88fc8d5b016f8668be9e231e745640096d35dd5f53b0ae2, +0x756586b0f3227ab0df6f4b7362786916bd89f353d0739fffa534368d8d793816, +0x710108dddc39e579dcf0819f9ad107b3c56d1713530dd94325db1d853a675a37, +0x8862b5f428ce5da50c89afb50aa779bb2c4dfe60e6f6a070b3a0208a4a970fe5, +0x54a9cd342fa3a4bf685c01d1ce84f3068b0d5b6a58ee22dda8fbac4908bb9560, +0x0fa3800efeaddd28247e114a1cf0f86b9014ccae9c3ee5f8488168b1103c1b44, +0xbb393428b7ebfe2eda218730f93925d2e80c020d41a29f4746dcbb9138f7233a, +0x7b42710942ef38ef2ff8fe44848335f26189c88c22a49fda84a51512ac68cd5d, +0x90e99786a3e8b04db95ccd44d01e75558d75f3ddd12a1e9a2c2ce76258bf4813, +0x3f6f71e40251728aa760763d25deeae54dc3a9b53807c737deee219120a2230a, +0xe56081a7933c6eaf4ef2c5a04e21ab8a3897785dd83a34719d1b62d82cfd00c2, +0x76cc54fa15f53e326575a9a2ac0b8ed2869403b6b6488ce4f3934f17db0f6bee, +0x1cd9cd1d882ea3830e95162b5de4beb5ddff34fdbf7aec64e83b82a6d11b417c, +0xb8ca8ae36d717c448aa27405037e44d9ee28bb8c6cc538a5d22e4535c8befd84, +0x5c4492108c25f873a23d5fd7957b3229edc22858e8894febe7428c0831601982, +0x907bcd75e7465e9791dc34e684742a2c0dc7007736313a95070a7e6b961c9c46, +0xe7134b1511559e6b2440672073fa303ec3915398e75086149eb004f55e893214, +0x2ddc2415e4753bfc383d48733e8b2a3f082883595edc5515514ebb872119af09, +0xf2ad0f76b08ffa1eee62228ba76f4982fab4fbede5d4752c282c3541900bcd5b, +0x0a84a6b15abd1cbc2da7092bf7bac418b8002b7000236dfba7c8335f27e0f1d4, +0x97404e02b9ff5478c928e1e211850c08cc553ebac5d4754d13efd92588b1f20d, +0xfa6ca3bcff1f45b557cdec34cb465ab06ade397e9d9470a658901e1f0f124659, +0x5bd972d55f5472e5b08988ee4bccc7240a8019a5ba338405528cc8a38b29bc21, +0x52952e4f96c803bb76749800891e3bfe55f7372facd5b5a587a39ac10b161bcc, +0xf96731ae09abcad016fd81dc4218bbb5b2cb5fe2e177a715113f381814007314, +0xe7d79e07cf9f2b52623491519a21a0a3d045401a5e7e10dd8873a85076616326, +0xe4892f3777a4614ee6770b22098eaa0a3f32c5c44b54ecedacd69789d676dffe, +0x20c932574779e2cc57780933d1dc6ce51a5ef920ce5bf681f7647ac751106367, +0x057252c573908e227cc07797117701623a4835f4b047dcaa9678105299e48e70, +0x20bad780930fa2a036fe1dea4ccbf46ac5b3c489818cdb0f97ae49d6e2f11fbf, +0xc0d7dd26ffecdb098585a1694e45a54029bb1e31c7c5209289058efebb4cc91b, +0x9a8744beb1935c0abe4b11812fc02748ef7c8cb650db3024dde3c5463e9d8714, +0x8ce6eea4585bbeb657b326daa4f01f6aef34954338b3ca42074aedd1110ba495, +0x1c85b43f5488b370721290d2faea19d9918d094c99963d6863acdfeeca564363, +0xe88a244347e448349e32d0525b40b18533ea227a9d3e9b78a9ff14ce0a586061, +0x352ca61efc5b8ff9ee78e738e749142dd1606154801a1449bbb278fa6bcc3dbe, +0xa066926f9209220b24ea586fb20eb8199a05a247c82d7af60b380f6237429be7, +0x3052337ccc990bfbae26d2f9fe5d7a4eb8edfb83a03203dca406fba9f4509b6e, +0x343ce573a93c272688a068d758df53c0161aa7f9b55dec8beced363a38b33069, +0x0f16b5593f133b58d706fe1793113a10750e8111eadee65301df7a1e84f782d3, +0x808ae8539357e85b648020f1e9d255bc4114bee731a6220d7c5bcb5b85224e03, +0x3b2bd97e31909251752ac57eda6015bb05b85f2838d475095cfd146677430625, +0xe4f857c93b2d8b250050c7381a6c7c660bd29066195806c8ef11a2e6a6640236, +0x23d91589b5070f443ddcefa0838c596518d54928119251ecf3ec0946a8128f52, +0xb72736dfad52503c7f5f0c59827fb6ef4ef75909ff9526268abc0f296ee37296, +0x80a8c66436d86b8afe87dde7e53a53ef87e057a5d4995963e76d159286de61b6, +0xbec92c09ee5e0c84d5a8ba6ca329683ff550ace34631ea607a3a21f99cd36d67, +0x83c97c9807b9ba6d9d914ae49dabdb4c55e12e35013f9b179e6bc92d5d62222b, +0x8d9c79f6af3920672dc4cf97a297c186e75083d099aeb5c1051207bad0c98964, +0x2aaa5944a2bd852b0b1be3166e88f357db097b001c1a71ba92040b473b30a607, +0x46693d27ec4b764fbb516017c037c441f4558aebfe972cdcd03da67c98404e19, +0x903b25d9e12208438f203c9ae2615b87f41633d5ffda9cf3f124c1c3922ba08f, +0x3ec23dc8bc1b49f5c7160d78008f3f235252086a0a0fa3a7a5a3a53ad29ec410, +0xa1fe74ceaf3cccd992001583a0783d7d7b7a245ea374f369133585b576b9c6d8, +0xb2d6b0fe4932a2e06b99531232398f39a45b0f64c3d4ebeaaebc8f8e50a80607, +0xe19893353f9214eebf08e5d83c6d44c24bffe0eceee4dc2e840d42eab0642536, +0x5b798e4bc099fa2e2b4b5b90335c51befc9bbab31b4dd02451b0abd09c06ee79, +0xbab2cdec1553a408cac8e61d9e6e19fb8ccfb48efe6d02bd49467a26eeeca920, +0x1c1a544c28c38e5c423fe701506693511b3bc5f2af9771b9b2243cd8d41bebfc, +0x704d6549d99be8cdefeec9a58957f75a2be4af7bc3dc4655fa606e7f3e03b030, +0x051330f43fe39b08ed7d82d68c49b36a8bfa31357b546bfb32068712df89d190, +0xe69174c7b03896461cab2dfaab33d549e3aac15e6b0f6f6f466fb31dae709b9b, +0xe5f668603e0ddbbcde585ac41c54c3c4a681fffb7a5deb205344de294758e6ac, +0xca70d5e4c3a81c1f21f246a3f52c41eaef9a683f38eb7c512eac8b385f46cbcd, +0x3173a6b882b21cd147f0fc60ef8f24bbc42104caed4f9b154f2d2eafc3a56907, +0xc71469c192bf5cc36242f6365727f57a19f924618b8a908ef885d8f459833cc3, +0x59c596fc388afd8508bd0f5a1e767f3dda9ed30f6646d15bc59f0b07c4de646f, +0xb200faf29368581f551bd351d357b6fa8cbf90bdc73b37335e51cad36b4cba83, +0x275cede69b67a9ee0fff1a762345261cb20fa8191470159cc65c7885cfb8313c, +0x0ce4ef84916efbe1ba9a0589bed098793b1ea529758ea089fd79151cc9dc7494, +0x0f08483bb720e766d60a3cbd902ce7c9d835d3f7fdf6dbe1f37bcf2f0d4764a2, +0xb30a73e5db2464e6da47d10667c82926fa91fceb337d89a52db5169008bc6726, +0x6b9c50fed1cc404bf2dd6fffbfd18e30a4caa1500bfeb080aa93f78d10331aaf, +0xf17c84286df03ce175966f560600dd562e0f59f18f1d1276b4d8aca545d57856, +0x11455f2ef96a6b2be69854431ee219806008eb80ea38c81e45b2e58b3f975a20, +0x9a61e03e2157a5c403dfcde690f7b7d704dd56ea1716cf14cf7111075a8d6491, +0x30312c910ce6b39e00dbaa669f0fb7823a51f20e83eaeb5afa63fb57668cc2f4, +0x17c18d261d94fba82886853a4f262b9c8b915ed3263b0052ece5826fd7e7d906, +0x2d8f6ea0f5b9d0e4bc1478161f5ed2ad3d8495938b414dcaec9548adbe572671, +0x19954625f13d9bab758074bf6dee47484260d29ee118347c1701aaa74abd9848, +0x842ef2ad456e6f53d75e91e8744b96398df80350cf7af90b145fea51fbbcf067, +0x34a8b0a76ac20308aa5175710fb3e75c275b1ff25dba17c04e3a3e3c48ca222c, +0x58efcbe75f32577afe5e9ff827624368b1559c32fcca0cf4fd704af8ce019c63, +0x411b4d242ef8f14d92bd8b0b01cb4fa3ca6f29c6f9073cfdd3ce614fa717463b, +0xf76dbda66ede5e789314a88cff87ecb4bd9ca418c75417d4d920e0d21a523257, +0xd801821a0f87b4520c1b003fe4936b6852c410ee00b46fb0f81621c9ac6bf6b4, +0x97ad11d6a29c8cf3c548c094c92f077014de3629d1e9053a25dbfaf7eb55f72d, +0xa87012090cd19886d49521d564ab2ad0f18fd489599050c42213bb960c9ee8ff, +0x8868d8a26e758d50913f2bf228da0444a206e52853bb42dd8f90f09abe9c859a, +0xc257fb0cc9970e02830571bf062a14540556abad2a1a158f17a18f14b8bcbe95, +0xfe611ce27238541b14dc174b652dd06719dfbcda846a027f9d1a9e8e9df2c065, +0xc9b25ea410f420cc2d4fc6057801d180c6cab959bce56bf6120f555966e6de6d, +0x95437f0524ec3c04d4132c83be7f1a603e6f4743a85ede25aa97a1a4e3f3f8fc, +0x82a12910104065f35e983699c4b9187aed0ab0ec6146f91728901efecc7e2e20, +0x6622dd11e09252004fb5aaa39e283333c0686065f228c48a5b55ee2060dbd139, +0x89a2879f25733dab254e4fa6fddb4f04b8ddf018bf9ad5c162aea5c858e6faaa, +0x8a71b62075a6011fd9b65d956108fa79cc9ebb8f194d64d3105a164e01cf43a6, +0x103f4fe9ce211b6452181371f0dc4a30a557064b684645a4495136f4ebd0936a, +0x97914adc5d7ce80147c2f44a6b29d0b495d38dedd8cc299064abcc62ed1ddabc, +0x825c481da6c836a8696d7fda4b0563d204a9e7d9e4c47b46ded26db3e2d7d734, +0xf8c0637ba4c0a383229f1d730db733bc11d6a4e33214216c23f69ec965dcaaad, +0xaed3bdaf0cb12d37764d243ee0e8acdefc399be2cabbf1e51dc43454efd79cbd, +0xe8427f56cc5cec8554e2f5f586b57adccbea97d5fc3ef7b8bbe97c2097cf848c, +0xba4ad0abd5c14d526357fd0b6f8676ef6126aeb4a6d80cabe1f1281b9d28246c, +0x4cff20b72e2ab5af3fafbf9222146949527c25f485ec032f22d94567ff91b22f, +0x0d32925d89dd8fed989912afcbe830a4b5f8f7ae1a3e08ff1d3a575a77071d99, +0xe51a1cbeae0be5d2fdbc7941aea904d3eade273f7477f60d5dd6a12807246030, +0xfb8615046c969ef0fa5e6dc9628c8a9880e86a5dc2f6fc87aff216ea83fcf161, +0x64dd705e105c88861470d112c64ca3d038f67660a02d3050ea36c34a9ebf47f9, +0xb6ad148095c97528180f60fa7e8609bf5ce92bd562682092d79228c2e6f0750c, +0x5bae0cd81f3bd0384ca3143a72068e6010b946462a73299e746ca639c026781c, +0xc39a0fc7764fcfc0402b12fb0bbe78fe3633cbfb33c7f849279585a878a26d7c, +0x2b752fda1c0c53d685cc91144f78d371db6b766725872b62cc99e1234cca8c1a, +0x40ee6b9635d87c95a528757729212a261843ecb06d975de91352d43ca3c7f196, +0x75e2005d3726cf8a4bb97ea5287849a361e3f8fdfadc3c1372feed1208c89f6b, +0x0976f8ab556153964b58158678a5297da4d6ad92e284da46052a791ee667aee4, +0xdbeef07841e41e0672771fb550a5b9233ae8e9256e23fa0d34d5ae5efe067ec8, +0xa890f412ab6061c0c5ee661e80d4edc5c36b22fb79ac172ddd5ff26a7dbe9751, +0xb666ae07f9276f6d0a33f9efeb3c5cfcba314fbc06e947563db92a40d7a341e8, +0x83a082cf97ee78fbd7f31a01ae72e40c2e980a6dab756161544c27da86043528, +0xfa726a919c6f8840c456dc77b0fec5adbed729e0efbb9317b75f77ed479c0f44, +0xa8606800c54faeab2cbc9d85ff556c49dd7e1a0476027e0f7ce2c1dc2ba7ccbf, +0x2796277836ab4c17a584c9f6c7778d10912cb19e541fb75453796841e1f6cd1c, +0xf648b8b3c7be06f1f8d9cda13fd6d60f913e5048a8e0b283b110ca427eeb715f, +0xa21d00b8fdcd77295d4064e00fbc30bed579d8255e9cf3a9016911d832390717, +0xe741afcd98cbb3bb140737ed77bb968ac60d5c00022d722f9f04f56e97235dc9, +0xbeecc9638fac39708ec16910e5b02c91f83f6321f6eb658cf8a96353cfb49806, +0x912eee6cabeb0fed8d6e6ca0ba61977fd8e09ea0780ff8fbec995e2a85e08b52, +0xc665bc0bb121a1229bc56ecc07a7e234fd24c523ea14700aa09e569b5f53ad33, +0x39501621c2bdff2f62ab8d8e3fe47fe1701a98c665697c5b750ee1892f11846e, +0x03d32e16c3a6c913daefb139f131e1e95a742b7be8e20ee39b785b4772a50e44, +0x4f504eb46a82d440f1c952a06f143994bc66eb9e3ed865080cd9dfc6d652b69c, +0xad753dc8710a46a70e19189d8fc7f4c773e4d9ccc7a70c354b574fe377328741, +0xf7f5464a2d723b81502adb9133a0a4f0589b4134ca595a82e660987c6b011610, +0x216b60b1c3e3bb4213ab5d43e04619d13e1ecedbdd65a1752bda326223e3ca3e, +0x763664aa96d27b6e2ac7974e3ca9c9d2a702911bc5d550d246631965cf2bd4a2, +0x292b5c8c8431b040c04d631f313d4e6b67b5fd3d4b8ac9f2edb09d13ec61f088, +0x80db43c2b9e56eb540592f15f5900222faf3f75ce62e78189b5aa98c54568a5e, +0x1b5fdf8969bcd4d65e86a2cefb3a673e18d587843f4f50db4e3ee77a0ba2ef1c, +0x11e237953fff3e95e6572da50a92768467ffdfd0640d3384aa1c486357e7c24a, +0x1fabd4faa8dba44808cc87d0bc389654a98496745578f3d17d134adc7f7b10f3, +0x5eca4aa96f20a56197772ae6b600762154ca9d2702cab12664ea47cbff1a440c, +0x0b4234f5bb02abcf3b5ce6c44ea85f55ec7db98fa5a7b90abef6dd0df034743c, +0x316761e295bf350313c4c92efea591b522f1df4211ce94b22e601f30aefa51ef, +0xe93a55ddb4d7dfe02598e8f909ff34b3de40a1c0ac8c7fba48cb604ea60631fb, +0xe6e6c877b996857637f8a71d0cd9a6d47fdeb03752c8965766f010073332b087, +0xa4f95c8874e611eddd2c4502e4e1196f0f1be90bfc37db35f8588e7d81d34aeb, +0x9351710a5633714bb8b2d226e15ba4caa6f50f56c5508e5fa1239d5cc6a7e1aa, +0x8d0aef52ec7266f37adb572913a6213b8448caaf0384008373dec525ae6cdff1, +0x718e24c3970c85bcb14d2763201812c43abac0a7f16fc5787a7a7b2f37288586, +0x3600ce44cebc3ee46b39734532128eaf715c0f3596b554f8478b961b0d6e389a, +0x50dd1db7b0a5f6bd2d16252f43254d0f5d009e59f61ebc817c4bbf388519a46b, +0x67861ed00f5fef446e1f4e671950ac2ddae1f3b564f1a6fe945e91678724ef03, +0x0e332c26e169648bc20b4f430fbf8c26c6edf1a235f978d09d4a74c7b8754aad, +0x6c9901015adf56e564dfb51d41a82bde43fb67273b6911c9ef7fa817555c9557, +0x53c83391e5e0a024f68d5ade39b7a769f10664e12e4942c236398dd5dbce47a1, +0x78619564f0b2399a9fcb229d938bf1e298d62b03b7a37fe6486034185d7f7d27, +0x4625f15381a8723452ec80f3dd0293c213ae35de737c508f42427e1735398c3a, +0x69542425ddb39d3d3981e76b41173eb1a09500f11164658a3536bf3e292f8b6a, +0x82ac4f5bb40aece7d6706f1bdf4dfba5c835c09afba6446ef408d8ec6c09300f, +0x740f9180671091b4c5b3ca59b9515bd0fc751f48e488a9f7f4b6848602490e21, +0x9a04b08b4115986d8848e80960ad67490923154617cb82b3d88656ec1176c24c, +0xf9ffe528eccffad519819d9eef70cef317af33899bcaee16f1e720caf9a98744, +0x46da5e1a14b582b237f75556a0fd108c4ea0d55c0edd8f5d06c59a42e57410df, +0x098f3429c8ccda60c3b5b9755e5632dd6a3f5297ee819bec8de2d8d37893968a, +0x1a5b91af6025c11911ac072a98b8a44ed81f1f3c76ae752bd28004915db6f554, +0x8bed50c7cae549ed4f8e05e02aa09b2a614c0af8eec719e4c6f7aee975ec3ec7, +0xd86130f624b5dcc116f2dfbb5219b1afde4b7780780decd0b42694e15c1f8d8b, +0x4167aa9bc0075f624d25d40eb29139dd2c452ebf17739fab859e14ac6765337a, +0xa258ce5db20e91fb2ea30d607ac2f588bdc1924b21bbe39dc881e19889a7f5c6, +0xe5ef8b5ab3cc8894452d16dc875b69a55fd925808ac7cafef1cd19485d0bb50a, +0x120df2b3975d85b6dfca56bb98a82025ade5ac1d33e4319d2e0105b8de9ebf58, +0xc964291dd2e0807a468396ebba3d59cfe385d949f6d6215976fc9a0a11de209a, +0xf23f14cb709074b79abe166f159bc52b50de687464df6a5ebf112aa953c95ad5, +0x622c092c9bd7e30f880043762e26d8e9c73ab7c0d0806f3c5e472a4152b35a93, +0x8a5f090662731e7422bf651187fb89812419ab6808f2c62da213d6944fccfe9f, +0xfbea3c0d92e061fd2399606f42647d65cc54191fa46d57b325103a75f5c22ba6, +0x2babfbcc08d69b52c3747ddc8dcad4ea5511edabf24496f3ff96a1194d6f680e, +0x4d3d019c28c779496b616d85aee201a3d79d9eecf35f728d00bcb12245ace703, +0xe76fcee1f08325110436f8d4a95476251326b4827399f9b2ef7e12b7fb9c4ba1, +0x4884d9c0bb4a9454ea37926591fc3eed2a28356e0506106a18f093035638da93, +0x74c3f303d93d4cc4f0c1eb1b4378d34139220eb836628b82b649d1deb519b1d3, +0xacb806670b278d3f0c84ba9c7a68c7df3b89e3451731a55d7351468c7c864c1c, +0x8660fb8cd97e585ea7a41bccb22dd46e07eee8bbf34d90f0f0ca854b93b1ebee, +0x2fc9c89cdca71a1c0224d469d0c364c96bbd99c1067a7ebe8ef412c645357a76, +0x8ec6d5ab6ad7135d66091b8bf269be44c20af1d828694cd8650b5479156fd700, +0x50ab4776e8cabe3d864fb7a1637de83f8fbb45d6e49645555ffe9526b27ebd66, +0xbf39f5e17082983da4f409f91c7d9059acd02ccbefa69694aca475bb8d40b224, +0x3135b3b981c850cc3fe9754ec6af117459d355ad6b0915beb61e84ea735c31bf, +0xa7971dab52ce4bf45813223b0695f8e87f64b614c9c5499faac6f842e5c41be9, +0x9e480f5617323ab104b4087ac4ef849a5da03427712fb302ac085507c77d8f37, +0x57a6d474654d5e8d408159be39ad0e7026e6a4c6a6543e23a63d30610dc8dfc1, +0x09eb3e01a5915a4e26d90b4c58bf0cf1e560fdc8ba53faed9d946ad3e9bc78fa, +0x29c6d25da80a772310226b1b89d845c7916e4a4bc94d75aa330ec3eaa14b1e28, +0x1a1ccfee11edeb989ca02e3cb89f062612a22a69ec816a625835d79370173987, +0x1cb63dc541cf7f71c1c4e8cabd2619c3503c0ea1362dec75eccdf1e9efdbfcfc, +0xac9dff32a69e75b396a2c250e206b36c34c63b955c9e5732e65eaf7ccca03c62, +0x3e1b4f0c3ebd3d38cec389720147746774fc01ff6bdd065f0baf2906b16766a8, +0x5cc8bed25574463026205e90aad828521f8e3d440970d7e810d1b46849681db5, +0x255185d264509bd3a768bb0d50b568e66eb1fec96d573e33aaacc716d7c8fb93, +0xe81b86ba631973918a859ff5995d7840b12511184c2865401f2693a71b9fa07e, +0x61e67e42616598da8d36e865b282127c761380d3a56d26b8d35fbbc7641433c5, +0x60c62ffef83fe603a34ca20b549522394e650dad5510ae68b6e074f0cd209a56, +0x78577f2caf4a54f6065593535d76216f5f4075af7e7a98b79571d33b1822920c, +0xfd4cb354f2869c8650200de0fe06f3d39e4dbebf19b0c1c2677da916ea84f44d, +0x453769cef6ff9ba2d5c917982a1ad3e2f7e947d9ea228857556af0005665e0b0, +0xe567f93f8f88bf1a6b33214f17f5d60c5dbbb531b4ab21b8c0b799b6416891e0, +0x7e65a39a17f902a30ceb2469fe21cba8d4e0da9740fcefd5c647c81ff1ae95fa, +0x03e4a7eea0cd6fc02b987138ef88e8795b5f839636ca07f6665bbae9e5878931, +0xc3558e2b437cf0347cabc63c95fa2710d3f43c65d380feb998511903f9f4dcf0, +0xe3a615f80882fb5dfbd08c1d7a8b0a4d3b651d5e8221f99b879cb01d97037a9c, +0xb56db4a5fea85cbffaee41f05304689ea321c40d4c108b1146fa69118431d9b2, +0xab28e1f077f18117945910c235bc9c6f9b6d2b45e9ef03009053006c637e3e26, +0xefcabc1d5659fd6e48430dbfcc9fb4e08e8a9b895f7bf9b3d6c7661bfc44ada2, +0xc7547496f212873e7c3631dafaca62a6e95ac39272acf25a7394bac6ea1ae357, +0xc482013cb01bd69e0ea9f447b611b06623352e321469f4adc739e3ee189298eb, +0x5942f42e91e391bb44bb2c4d40da1906164dbb6d1c184f00fa62899baa0dba2c, +0xb4bcb46c80ad4cd603aff2c1baf8f2c896a628a46cc5786f0e58dae846694677, +0xd0a7305b995fa8c317c330118fee4bfef9f65f70b54558c0988945b08e90ff08, +0x687f801b7f32fdfa7d50274cc7b126efedbdae8de154d36395d33967216f3086, +0xeb19ec10ac6c15ffa619fa46792971ee22a9328fa53bd69a10ed6e9617dd1bbf, +0xa2bb3f0367f62abdb3a9fa6da34b20697cf214a4ff14fd42826da140ee025213, +0x070a76511f32c882374400af59b22d88974a06fbc10d786dd07ca7527ebd8b90, +0x8f195689537b446e946b376ec1e9eb5af5b4542ab47be550a5700fa5d81440d5, +0x10cc09778699fc8ac109e7e6773f83391eeba2a6db5226fbe953dd8d99126ca5, +0x8cc839cb7dc84fd3b8c0c7ca637e86a2f72a8715cc16c7afb597d12da717530b, +0xa32504e6cc6fd0ee441440f213f082fcf76f72d36b5e2a0f3b6bdd50cdd825a2, +0x8f45151db8878e51eec12c450b69fa92176af21a4543bb78c0d4c27286e74469, +0x23f5c465bd35bcd4353216dc9505df68324a27990df9825a242e1288e40a13bb, +0x35f409ce748af33c20a6ae693b8a48ba4623de9686f9834e22be4410e637d24f, +0xb962e5845c1db624532562597a99e2acc5e434b97d8db0725bdeddd71a98e737, +0x0f8364f99f43dd52b4cfa9e426c48f7b6ab18dc40a896e96a09eceebb3363afe, +0xa842746868da7644fccdbb07ae5e08c71a6287ab307c4f9717eadb414c9c99f4, +0xa59064c6b7fe7d2407792d99ed1218d2dc2f240185fbd8f767997438241b92e9, +0xb6ea0d58e8d48e05b9ff4d75b2ebe0bd9752c0e2691882f754be66cdec7628d3, +0xf16b78c9d14c52b2b5156690b6ce37a5e09661f49674ad22604c7d3755e564d1, +0xbfa8ef74e8a37cd64b8b4a4260c4fc162140603f9c2494b9cf4c1e13de522ed9, +0xf4b89f1776ebf30640dc5ec99e43de22136b6ef936a85193ef940931108e408a, +0xefb9a4555d495a584dbcc2a50938f6b9827eb014ffae2d2d0aae356a57894de8, +0x0627a466d42a26aca72cf531d4722e0e5fc5d491f4527786be4e1b641e693ac2, +0x7d10d21542de3d8f074dbfd1a6e11b3df32c36272891aae54053029d39ebae10, +0x0f21118ee9763f46cc175a21de876da233b2b3b62c6f06fa2df73f6deccf37f3, +0x143213b96f8519c15164742e2350cc66e814c9570634e871a8c1ddae4d31b6b5, +0x8d2877120abae3854e00ae8cf5c8c95b3ede10590ab79ce2be7127239507e18d, +0xaccd0005d59472ac04192c059ed9c10aea42c4dabec9e581f6cb10b261746573, +0x67bc8dd5422f39e741b9995e6e60686e75d6620aa0d745b84191f5dba9b5bb18, +0x11b8e95f6a654d4373cefbbac29a90fdd8ae098043d1969b9fa7885318376b34, +0x431a0b8a6f08760c942eeff5791e7088fd210f877825ce4dcabe365e03e4a65c, +0x704007f11bae513f428c9b0d23593fd2809d0dbc4c331009856135dafec23ce4, +0xc06dee39a33a05e30c522061c1d9272381bde3f9e42fa9bd7d5a5c8ef11ec6ec, +0x66b4157baaae85db0948ad72882287a80b286df2c40080b8da4d5d3db0a61bd2, +0xef1983b1906239b490baaaa8e4527f78a57a0a767d731f062dd09efb59ae8e3d, +0xf26d0d5c520cce6688ca5d51dee285af26f150794f2ea9f1d73f6df213d78338, +0x8b28838382e6892f59c42a7709d6d38396495d3af5a8d5b0a60f172a6a8940bd, +0x261a605fa5f2a9bdc7cffac530edcf976e7ea7af4e443b625fe01ed39dad44b6] +compressed_lamport_PK = 0xdd635d27d1d52b9a49df9e5c0c622360a4dd17cba7db4e89bce3cb048fb721a5 +child_SK = 20397789859736650942317412262472558107875392172444076792671091975210932703118 +``` + +## Implementation + +* [Python](https://github.com/ethereum/eth2.0-deposit-cli) + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2334.md b/EIPS/eip-2334.md new file mode 100644 index 0000000..76a9b19 --- /dev/null +++ b/EIPS/eip-2334.md @@ -0,0 +1,104 @@ +--- +eip: 2334 +title: BLS12-381 Deterministic Account Hierarchy +author: Carl Beekhuizen +discussions-to: https://github.com/ethereum/EIPs/issues/2338 +status: Stagnant +type: Standards Track +category: ERC +created: 2019-09-30 +requires: 2333 +--- + +## Simple Summary + +This EIP defines the purpose of a given key, or family thereof, within a tree of keys. When combined with [EIP-2333](./eip-2333.md), the combination of a seed and knowledge of the desired purpose of a key is sufficient to determine a key pair. + +## Abstract + +A standard for allocating keys generated by [EIP-2333](./eip-2333.md) to a specific purpose. It defines a `path` which is a string that parses into the indices to be used when traversing the tree of keys that [EIP-2333](./eip-2333.md) generates. + +## A note on purpose + +This specification is designed not only to be an Ethereum 2.0 standard, but one that is adopted by the wider community who have adopted [BLS signatures over BLS12-381](https://datatracker.ietf.org/doc/draft-irtf-cfrg-bls-signature/). It is therefore important also to consider the needs of the wider industry along with those specific to Ethereum. As a part of these considerations, it is the intention of the author that this standard eventually migrate to a more neutral repository in the future. + +## Motivation + +Ethereum 2.0 alongside many other projects will use BLS signatures over BLS12-381, an [IETF proposed standard](https://datatracker.ietf.org/doc/draft-irtf-cfrg-bls-signature/). This new scheme requires a new key derivation mechanism, which is established within [EIP-2333](./eip-2333.md). This new scheme is incompatible with the current form of this specification ([BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki)) due to the: exclusive use of hardened keys, the increased number of keys per level, not using [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) for key derivation. It is therefore necessary to establish a new *path* for traversing the [EIP-2333](./eip-2333.md) key-tree. + +The path structure specified in this EIP aims to be more general than [BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki) by not having UTXO-centric features [which gave rise to the 4 different types of wallet paths being used within Ethereum 1.0](https://github.com/ethereum/EIPs/issues/84#issuecomment-292324521) and gave rise to (draft) [EIP-600](./eip-600.md) & [EIP-601](./eip-601.md) + +## Specification + +### Path + +The path traversed through the tree of keys is defined by integers (which indicate the sibling index) separated by `/` which denote ancestor relations. There are 4 levels (plus the master node) in the path and at least 4 (5 including the master node) MUST be used. + +```text +m / purpose / coin_type / account / use +``` + +#### Notation + +The notation used within the path is specified within the [EIP-2333](./eip-2333.md), but is summarized again below for convenience. + +* `m` Denotes the master node (or root) of the tree +* `/` Separates the tree into depths, thus `i / j` signifies that `j` is a child of `i` + +### Purpose + +The `purpose` is set to `12381` which is the name of the new curve (BLS12-381). In order to be in compliance with this standard, the [EIP-2333](./eip-2333.md) MUST be implemented as the KDF and therefore, the purpose `12381` MAY NOT be used unless this is the case. + +### Coin Type + +The `coin_type` here reflects the coin number for an individual coin thereby acting as a means of separating the keys used for different chains. + +### Account + +`account` is a field that provides the ability for a user to have distinct sets of keys for different purposes, if they so choose. This is the level at which different accounts for a single user SHOULD to be implemented. + +### Use + +This level is designed to provide a set of related keys that can be used for any purpose. The idea being that a single account has many uses which are related yet should remain separate for security reasons. It is required to support this level in the tree, although, for many purposes it will remain `0`. + +### Eth2 Specific Parameters + +#### Coin type + +The coin type used for the BLS12-381 keys in Ethereum 2 is `3600`. + +#### Validator keys + +Each Eth2 validator has two keys, one for withdrawals and transfers (called the *withdrawal key*), and the other for performing their duties as a validator (henceforth referred to as the *signing key*). + +The path for withdrawal keys is `m/12381/3600/i/0` where `i` indicates the `i`th set of validator keys. + +The path for the signing key is `m/12381/3600/i/0/0` where again, `i` indicates the `i`th set of validator keys. Another way of phrasing this is that the signing key is the `0`th child of the associated withdrawal key for that validator. + +**Note:** If the above description of key paths is not feasible in a specific use case (eg. with secret-shared or custodial validators), then the affected keys may be omitted and derived via another means. Implementations of this EIP, must endeavour to use the appropriate keys for the given use case to the extent that is reasonably possible. (eg, in the case of custodial staking, the user making the deposits will follow this standard for their withdrawal keys which has no bearing on how the service provide derives the corresponding signing keys.) + +## Rationale + +`purpose`, `coin_type`, and `account` are widely-adopted terms as per [BIP43](https://github.com/bitcoin/bips/blob/master/bip-0043.mediawiki) and [BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki) and therefore reusing these terms and their associated meanings makes sense. + +The purpose needs to be distinct from these standards as the KDF and path are not inter-compatible and `12381` is an obvious choice. + +`account` separates user activity into distinct categories thereby allowing users to separate their concerns however they desire. + +`use` will commonly be determined at the application level providing distinct keys for non-intersecting use cases. + +### Eth2 Specific Parameters + +A new coin type is chosen for Eth2 keys to help ensure a clean separation between Eth2 and Eth1 keys. Although the distinction between Eth1 ETH and Eth2 ETH is subtle, they are distinct entities and there are services which only distinguish between coins by their coin name (eg. [ENS' multichain address resolution](./eip-2304.md)). `3600` is chosen specifically because it is the square of the Eth1's `coin_type` (`3600==60^2`) thereby signaling that it is second instantiation of Ether the currency. + +The primary reason validators have separate signing and withdrawal keys is to allow for the different security concerns of actions within Eth2. The signing key is given to the validator client where it signs messages as per the requirements of being a validator, it is therefore a "hot key". If this key is compromised, the worst that can happen (locally) is that a slashable message is signed, resulting in the validator being slashed and forcibly exited. The withdrawal key is only needed when a validator wishes to perform an action not related to validating and has access to the full funds at stake for that validator. The withdrawal key therefore has higher security concerns and should be handled as a "cold key". By having the signing key be a child of the withdrawal key, secure storage of the withdrawal key is sufficient to recover the signing key should the need arise. + +## Backwards Compatibility + +[BIP43](https://github.com/bitcoin/bips/blob/master/bip-0043.mediawiki) and [BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki) are the commonly used standards for this purpose within Ethereum 1.0, however they have not been `Accepted` as standards as yet. Due to the use of a new KDF within [EIP-2333](./eip-2333.md), a new path standard is required. This EIP implements this, with minor changes. + +`purpose` `12381` paths do not support hardened keys and therefore the `'` character is invalid. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2335.md b/EIPS/eip-2335.md new file mode 100644 index 0000000..43ca624 --- /dev/null +++ b/EIPS/eip-2335.md @@ -0,0 +1,301 @@ +--- +eip: 2335 +title: BLS12-381 Keystore +author: Carl Beekhuizen +discussions-to: https://github.com/ethereum/EIPs/issues/2339 +status: Stagnant +type: Standards Track +category: ERC +created: 2019-09-30 +requires: 2333, 2334 +--- + +## Simple Summary + +A JSON format for the storage and interchange of BLS12-381 private keys. + +## Abstract + +A keystore is a mechanism for storing private keys. It is a JSON file that encrypts a private key and is the standard for interchanging keys between devices as until a user provides their password, their key is safe. + +## A note on purpose + +This specification is designed not only to be an Ethereum 2.0 standard, but one that is adopted by the wider community who have adopted the BLS12-381 signature standard. It is therefore important also to consider the needs of the wider industry along with those specific to Ethereum. As a part of these considerations, it is the intention of the author that this standard eventually migrate to a more neutral repository in the future. + +## Motivation + +The secure storage and exchange of keys is a vital component of the user experience as people are expected to hold their own keys. It allows users to control access to individual keys and their use by applications. + +In Ethereum 1, [the Web3 Secret Storage Definition](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) fulfills these requirements, however it is not perfectly suitable for these purposes moving forward. Specifically the problems with the existing standard are: + +* __The use of Keccak256.__ Eth1 keystores use Keccak for their checksum, a sensible choice considering its usage within Ethereum 1. BLS12-381 [signatures](https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-00), [keys (EIP-2333)](./eip-2333.md), and key-storage are inter-chain standards, the establishment and proliferation of which hinges on them being neutral to all chains, something which Keccak is not. + +* __A lack of abstraction.__ Eth1 keystores are a result of an iterative design process whereby functionality was added and modified as needed without considering how abstractions could simplify the notion of different properties. + +## Specification + +The process of decrypting the secret held within a keystore can be broken down into 3 sub-processes: obtaining the decryption key, verifying the password and decrypting the secret. Each process has its own functions which can be selected from as well as parameters required for the function all of which are specified within the keystore file itself. + +### Password requirements + +The password is a string of arbitrary unicode characters. The password is first converted to its NFKD representation, then the control codes (specified below) are stripped from the password and finally it is UTF-8 encoded. + +#### Control codes removal + +The C0, C1, and `Delete` control codes are not valid characters in the password and should therefore be stripped from the password. C0 are the control codes between `0x00` - `0x1F` (inclusive) and C1 codes lie between `0x80` and `0x9F` (inclusive). `Delete`, commonly known as "backspace", is the UTF-8 character `7F` which must also be stripped. Note that space (`Sp` UTF-8 `0x20`) is a valid character in passwords despite it being a pseudo-control character. + +### Modules + +This standard makes use of the notion of a _module_ which serves to represent, in an abstract sense, the different  cryptographic constructions and corresponding parameters for each component of the keystore. The idea being that components can be swapped out without affecting the rest of the specification should the need arise. + +A module is comprised of a `function`, which defines which cryptographic construct is being used, `params`, the parameters required by the function, and `message` the primary input to the function. + +### Decryption key + +The decryption key is an intermediate key which is used both to verify the user-supplied password is correct, as well as for the final secret decryption. This key is simply derived from the password, the `function`, and the `params` specified by the`kdf` module as per the keystore file. + +| KDF | `"function"` | `"params"` | `"message"` | Definition | +|----------------|--------------|------------------------------------------------------------------------------------------|-------------|--------------------------------------------------| +| PBKDF2-SHA-256 | `"pbkdf2"` |
  • `"c"`
  • `"dklen"`
  • `"prf: "hmac-sha256"`
  • `"salt"`
| | [RFC 2898](https://www.ietf.org/rfc/rfc2898.txt) | +| scrypt | `"scrypt"` |
  • `"dklen"`
  • `"n"`
  • `"p"`
  • `"r"`
  • `"salt"`
| | [RFC 7914](https://tools.ietf.org/html/rfc7914) | + +### Password verification + +The password verification step verifies that the password is correct with respect to the `checksum.message`, `cipher.message`, and `kdf`. This is done by appending the `cipher.message` to the 2nd 16 bytes of the decryption key, obtaining its SHA256 hash and verifying whether it matches the `checksum.message`. + +#### Inputs + +* `decryption_key`, the octet string obtained from decryption key process +* `cipher_message`, the octet string obtained from keystore file from `crypto.cipher.message` +* `checksum_message`, the octet string obtained from keystore file from `crypto.checksum.message` + +#### Outputs + +* `valid_password`, a boolean value indicating whether the password is valid + +#### Definitions + +* `a[0:3]` returns a slice of `a` including octets 0, 1, 2 +* `a | b` is the concatenation of `a` with `b` + +#### Procedure + +```text +0. DK_slice = decryption_key[16:32] +1. pre_image = DK_slice | cipher_message +2. checksum = SHA256(pre_image) +3. valid_password = checksum == checksum_message +4. return valid_password +``` + +| Hash | `"function"` | `"params"` | `"message"` | Definition | +|------------|-----------------|------------|-------------|-------------------------------------------------| +| SHA-256 | `"sha256"` | | | [RFC 6234](https://tools.ietf.org/html/rfc6234) | + +### Secret decryption + +The `cipher.function` encrypts the secret using the decryption key, thus to decrypt it, the decryption key along with the `cipher.function` and `cipher.params` must be used. If the `decryption_key` is longer than the key size required by the cipher, it is truncated to the correct number of bits. In the case of aes-128-ctr, only the first 16 bytes of the `decryption_key` are used as the AES key. + +| Cipher | `"function"` | `"params"` | `"message"` | Definition | +|----------------------|-----------------|--------------------------|-------------|-------------------------------------------------| +| AES-128 Counter Mode | `"aes-128-ctr"` |
  • `"iv"`
| | [RFC 3686](https://tools.ietf.org/html/rfc3686) | + +## Description + +This field is an optional field to help explain the purpose and identify a particular keystores in a user-friendly manner. While this field can, and should, be used to help distinguish keystores from one-another, the `description` **is not necessarily unique**. + +## PubKey + +The `pubkey` is the public key associated with the the private key secured within the keystore. It is stored here to improve user experience and security which is achieved by not requiring users to enter their password just to obtain their public keys. This field is required if the secret being stored within the keystore is a private key. The encoding of the `pubkey` is specified in the in the appropriate signature standard (eg. [BLS12-381 signature standard](https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-00)), but can be seen as a byte-string in the abstract and should be directly compatible with the appropriate signature library. + +## Path + +The `path` indicates where in the key-tree a key originates from. It is a string defined by [EIP-2334](./eip-2334.md), if no path is known or the path is not relevant, the empty string, `""` indicates this. The `path` can specify an arbitrary depth within the tree and the deepest node within the tree indicates the depth of the key stored within this file. + +## UUID + +The `uuid` provided in the keystore is a randomly generated UUID as specified by [RFC 4122](https://tools.ietf.org/html/rfc4122). It is used as a 128-bit proxy for referring to a particular set of keys or account. + +## Version + +The `version` is set to `4`. + +## JSON schema + +The keystore, at its core, is constructed with modules which allow for the configuration of the cryptographic constructions used password hashing, password verification and secret decryption. Each module is composed of: `function`, `params`, and `message` which corresponds with which construction is to be used, what the configuration for the construction is, and what the input is. + +```json +{ + "$ref": "#/definitions/Keystore", + "definitions": { + "Keystore": { + "type": "object", + "properties": { + "crypto": { + "type": "object", + "properties": { + "kdf": { + "$ref": "#/definitions/Module" + }, + "checksum": { + "$ref": "#/definitions/Module" + }, + "cipher": { + "$ref": "#/definitions/Module" + } + } + }, + "description": { + "type": "string" + }, + "pubkey": { + "type": "string" + }, + "path": { + "type": "string" + }, + "uuid": { + "type": "string", + "format": "uuid" + }, + "version": { + "type": "integer" + } + }, + "required": [ + "crypto", + "path", + "uuid", + "version" + ], + "title": "Keystore" + }, + "Module": { + "type": "object", + "properties": { + "function": { + "type": "string" + }, + "params": { + "type": "object" + }, + "message": { + "type": "string" + } + }, + "required": [ + "function", + "message", + "params" + ] + } + } +} +``` + +## Rationale + +The rationale behind the design of this specification is largely the same as that behind the [Ethereum 1 keystore definition](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) except for the lack of support for Keccak (explained in [motivation above](#motivation)) and the notion of modules. + +Modules provide a very useful level of abstraction which allow the Key-Derivation-Function, Checksum, and Cipher to be thought of as instances of the same thing allowing for their substitution with minimal effort. + +The `version` is set to 4 to prevent collisions with the existing Ethereum keystore standard. + +## Backwards Compatibility + +This specification is not backwards compatible with the [existing keystore standard](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) due to the lack of Keccak256 checksums as explained above. While this format is capable of supporting Keccak checksums via the Checksum module, it would defeat the purpose of this standard to include it as this standard could no longer be considered neutral with respect to other projects in the industry. + +## Test Cases + +### Scrypt Test Vector + +Password `"𝔱𝔢𝔰𝔱𝔭𝔞𝔰𝔰𝔴𝔬𝔯𝔡🔑"` +Encoded Password: `0x7465737470617373776f7264f09f9491` +Secret `0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f` + +```json +{ + "crypto": { + "kdf": { + "function": "scrypt", + "params": { + "dklen": 32, + "n": 262144, + "p": 1, + "r": 8, + "salt": "d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" + }, + "message": "" + }, + "checksum": { + "function": "sha256", + "params": {}, + "message": "d2217fe5f3e9a1e34581ef8a78f7c9928e436d36dacc5e846690a5581e8ea484" + }, + "cipher": { + "function": "aes-128-ctr", + "params": { + "iv": "264daa3f303d7259501c93d997d84fe6" + }, + "message": "06ae90d55fe0a6e9c5c3bc5b170827b2e5cce3929ed3f116c2811e6366dfe20f" + } + }, + "description": "This is a test keystore that uses scrypt to secure the secret.", + "pubkey": "9612d7a727c9d0a22e185a1c768478dfe919cada9266988cb32359c11f2b7b27f4ae4040902382ae2910c15e2b420d07", + "path": "m/12381/60/3141592653/589793238", + "uuid": "1d85ae20-35c5-4611-98e8-aa14a633906f", + "version": 4 +} +``` + +### PBKDF2 Test Vector + +Password `"𝔱𝔢𝔰𝔱𝔭𝔞𝔰𝔰𝔴𝔬𝔯𝔡🔑"` +Encoded Password: `0x7465737470617373776f7264f09f9491` +Secret `0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f` + +```json +{ + "crypto": { + "kdf": { + "function": "pbkdf2", + "params": { + "dklen": 32, + "c": 262144, + "prf": "hmac-sha256", + "salt": "d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" + }, + "message": "" + }, + "checksum": { + "function": "sha256", + "params": {}, + "message": "8a9f5d9912ed7e75ea794bc5a89bca5f193721d30868ade6f73043c6ea6febf1" + }, + "cipher": { + "function": "aes-128-ctr", + "params": { + "iv": "264daa3f303d7259501c93d997d84fe6" + }, + "message": "cee03fde2af33149775b7223e7845e4fb2c8ae1792e5f99fe9ecf474cc8c16ad" + } + }, + "description": "This is a test keystore that uses PBKDF2 to secure the secret.", + "pubkey": "9612d7a727c9d0a22e185a1c768478dfe919cada9266988cb32359c11f2b7b27f4ae4040902382ae2910c15e2b420d07", + "path": "m/12381/60/0/0", + "uuid": "64625def-3331-4eea-ab6f-782f3ed16a83", + "version": 4 +} +``` + +## Implementation + +Implementations exist in the following languages: + +* [Python3](https://github.com/ethereum/eth2.0-deposit-cli) +* [TypeScript](https://github.com/nodefactoryio/bls-keystore) +* [Go](https://github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4/) + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-234.md b/EIPS/eip-234.md new file mode 100644 index 0000000..5cd5a0f --- /dev/null +++ b/EIPS/eip-234.md @@ -0,0 +1,49 @@ +--- +eip: 234 +title: Add `blockHash` to JSON-RPC filter options. +author: Micah Zoltu (@MicahZoltu) +discussions-to: https://github.com/ethereum/EIPs/issues/234 +type: Standards Track +category: Interface +status: Final +created: 2017-03-24 +requires: 1474 +--- + +## Simple Summary + +Add an option to JSON-RPC filter options (used by `eth_newFilter` and `eth_getLogs`) that allows specifying the block hash that should be included in the results. This option would be an alternative to `fromBlock`/`toBlock` options. + +## Abstract + +This addition would allow clients to fetch logs for specific blocks, whether those blocks were in the current main chain or not. This resolves some issues that make it difficult/expensive to author robust clients due to the nature of chain reorgs, unreliable network connections and the result set not containing enough details in the empty case. + +## Specification + +The filter options used by `eth_newFilter` would have an additional optional parameter named `blockHash` whose value is a single block hash. The Ethereum node responding to the request would either send back an error if the block hash was not found or it would return the results matching the filter (per normal operation) constrained to the block provided. Internally, this would function (presumably) similar to the `fromBlock` and `toBlock` filter options. + +## Rationale + +A client (dApp) who needs reliable notification of both log additions (on new blocks) and log removals (on chain reorgs) cannot achieve this while relying solely on subscriptions and filters. This is because a combination of a network or remote node failure during a reorg can result in the client getting out of sync with reality. An example of where this can happen with Websockets is when the client opens a web socket connection, sets up a log filter subscription, gets notified of some new logs, then loses the web socket connection, then (while disconnected) a re-org occurs, then the client connects back and establishes a new log filter. In this scenario they will not receive notification of the log removals from the node because they were disconnected when the removals were broadcast and the loss of their connection resulted in the node forgetting about their existence. A similar scenario can be concocted for HTTP clients where between polls for updates, the node goes down and comes back (resulting in loss of filter state) and a re-org also occurs between the same two polls. + +In order to deal with this while still providing a robust mechanism for internal block/log additional/removal, the client can maintain a blockchain internally (last `n` blocks) and only subscribe/poll for new blocks. When a new block is received, the client can reconcile their internal model with the new block, potentially back-filling parents or rolling back/removing blocks from their internal model to get in sync with the node. This can account for any type of disconnect/reorg/outage scenario and also allows the client (as an added benefit) to talk to a cluster of Ethereum nodes (e.g., via round-robin) rather than being tightly coupled to a single node. + +Once the user has a reliable stream of blocks, they can then look at the bloom filter for the new block and if the block *may* have logs of interest they can fetch the filtered logs for that block from the node. The problem that arises is that a re-org may occur between when the client receives the block and when the client fetches the logs for that block. Given the current set of filter options, the client can only ask for logs by block number. In this scenario, the logs they get back will be for a block that *isn't* the block they want the logs for and is instead for a block that was re-orged in (and may not be fully reconciled with the internal client state). This can be partially worked around by looking at the resulting logs themselves and identifying whether or not they are for the block hash requested. However, if the result set is an empty array (no logs fetched) then the client is in a situation where they don't know what block the results are for. The results could have been legitimately empty (bloom filter can yield false positives) for the block in question, or they could be receiving empty logs for a block that they don't know about. At this point, there is no decision the client can make that allows them a guarantee of recovery. They can assume the empty logs were for the correct block, but if they weren't then they will never try to fetch again. This creates a problem if the block was only transiently re-orged out because it may come back before the next block poll so the client will never witness the reorg. They can assume the empty logs were for the wrong block, an refetch them, but they may continue to get empty results putting them right back into the same situation. + +By adding the ability to fetch logs by hash, the client can be guaranteed that if they get a result set, it is for the block in question. If they get an error, then they can take appropriate action (e.g., rollback that block client-side and re-fetch latest). + +## Backwards Compatibility + +The only potential issue here is the `fromBlock` and `toBlock` fields. It wouldn't make sense to include both the hash and the number so it seems like `fromBlock`/`toBlock` should be mutually exclusive with `blockHash`. + +## Test Cases + +`{ "jsonrpc": "2.0", "id": 1, "method": "eth_getLogs", params: [{"blockHash": "0xbl0cbl0cbl0cbl0cbl0cbl0cbl0cbl0cbl0cbl0cbl0cbl0cbl0cbl0cbl0cbl0c"}] }` should return all of the logs for the block with hash `0xbl0cbl0cbl0cbl0cbl0cbl0cbl0cbl0cbl0cbl0cbl0cbl0cbl0cbl0cbl0cbl0c`. If a `topics` field is added to the filter options then a filtered set of logs for that block should be returned. If no block exists with that hash then an error should be returned with a `code` of `-32000`, a `message` of `"Block not found."` and a `data` of `"0xbl0cbl0cbl0cbl0cbl0cbl0cbl0cbl0cbl0cbl0cbl0cbl0cbl0cbl0cbl0cbl0c"`. + +## Implementation + +- [x] [Geth](https://github.com/ethereum/go-ethereum/pull/16734) + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2364.md b/EIPS/eip-2364.md new file mode 100644 index 0000000..981f56d --- /dev/null +++ b/EIPS/eip-2364.md @@ -0,0 +1,76 @@ +--- +eip: 2364 +title: "eth/64: forkid-extended protocol handshake" +description: Introduces validation of the `forkid` when handshaking with peers. +author: Péter Szilágyi , Péter Szilágyi (@karalabe), Tim Beiko (@timbeiko) +discussions-to: https://github.com/ethereum/EIPs/issues/2365 +status: Final +type: Standards Track +category: Networking +created: 2019-11-08 +requires: 2124 +--- + +## Abstract + +This EIP specifies the inclusion of the `forkid`, originally defined in [(EIP-2124)](./eip-2124.md), as a new field in the Ethereum wire protocol (`eth`) handshake. This change is implemented as a new version of the wire protocol, `eth/64`. + +## Motivation + +The [`forkid` (EIP-2124)](./eip-2124.md) was designed to permit two Ethereum nodes to quickly and cheaply decide if they are compatible or not, not only at a genesis/networking level, but also from the perspective of the currently passed network updates (i.e. forks). + +[EIP-2124](./eip-2124.md) only defines how the `forkid` is calculated and validated, but does not specify how the `forkid` should be exchanged between peers. This EIP specifies the inclusion of the `forkid` as a new field in the Ethereum wire protocol (`eth`) handshake (releasing a new version, `eth/64`). + +By cross-validating `forkid` during the handshake, incompatible nodes can disconnect before expensive block exchanges and validations take place (PoW check, EVM execution, state reconstruction). This further prevents peer slots from being taken up by nodes that are incompatible, but have not yet been detected as such. + +From a micro perspective, cutting off incompatible nodes from one another ensures that a node only spends its resources on tasks that are genuinely useful to it. The sooner we can decide the remote peer is useless, the less time and processing we expend in vain. + +From a macro perspective, keeping incompatible nodes partitioned from one another ensures that disjoint clusters retain more resources for maintaining their own chain, thus raising the quality of service for all networks globally. + +## Specification + +- Implement `forkid` generation and validation per [EIP-2124](./eip-2124.md). +- Advertise a new `eth` protocol capability (version) at `eth/64`. + - The old `eth/63` protocol should still be kept alive side-by-side, until `eth/64` is sufficiently adopted by implementors. +- Redefine `Status (0x00)` for `eth/64` to add a trailing `forkid` field: + - Old packet: `[protocolVersion, networkId, td, bestHash, genesisHash]` + - New packet: `[protocolVersion, networkId, td, bestHash, genesisHash, forkid]`, + where `forkid` is `[forkHash: [4]byte, forkNext: uint64]` (fields per [EIP-2124](./eip-2124.md) ). + +Whenever two peers connect using the `eth/64` protocol, the updated `Status` message must be sent as the protocol handshake, and each peer must validate the remote `forkid`, disconnecting at a detected incompatibility. + +## Rationale + +The specification is tiny since most parts are already specified in EIP-2124. `eth/63` is not specified as an EIP, but is maintained in the [ethereum/devp2p](https://github.com/ethereum/devp2p) Github repository. + +### EIP-2124 mentions advertising the `forkid` in the discovery protocol too. How does that compare to advertising in the `eth` protocol? Why is the redundancy needed? + +Advertising and validating the `forkid` in the discovery protocol is a more optimal solution, as it can help avoid the cost of setting up the TCP connection and cryptographic RLPx stream, only to be torn down if `eth/64` rejects it. + +Compared to the `eth` protocol however, discovery is a bit fuzzy. The goal there is to suggest potential peers, not to be fool-proof. Information may be outdated, nodes may have changed or disappeared. Discovery can do a rough filtering, but more precision is still needed afterwards. + +Additionally, `forkid` validation via the discovery protocol requires ENR implementation ([EIP-778](./eip-778.md)) and ENR extension support ([EIP-868](./eip-868.md)), which is not mandated by the Ethereum network currently. Lastly, the discovery protocol is just one way to find peers, but systems that cannot use UDP or that rely on other mechanism (e.g. DNS discovery)) still need a way to filter connections. + +### The `forkid` implicitly contains the genesis hash checksummed into the `FORK_HASH` field. Why doesn't this proposal remove the `genesisHash` field from the `eth` handshake? + +Originally this EIP did remove it as redundant data, since filtering based on the `forkid` is a superset of filtering based on genesis hash. The reason for backing out of that decision was that the genesis hash may be useful for other things too, not just connection filtering (network crawlers use it currently to split nodes across networks). + +Although the `forkid` will hopefully take over all the roles of the genesis hash currently in use, there's no reason to be overly aggressive in deduplicating data. It's fine to keep both side-by-side for now, and remove in a future version when 3rd party infrastructures switch over. + +## Backwards Compatibility + +This EIP extends the `eth` protocol handshake in a backwards incompatible way and requires rolling out a new version, `eth/64`. However, `devp2p` supports running multiple versions of the same wire protocol side-by-side, so rolling out `eth/64` does not require client coordination, since non-updated clients can keep using `eth/63`. + +This EIP does not change the consensus engine, thus does _not_ require a hard fork. + +## Test Cases + +For calculating and validating fork IDs, see test cases in [EIP-2124](./eip-2124.md). + +## Security Considerations + +None. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2378.md b/EIPS/eip-2378.md new file mode 100644 index 0000000..7db43ca --- /dev/null +++ b/EIPS/eip-2378.md @@ -0,0 +1,70 @@ +--- +eip: 2378 +title: EIPs Eligible for Inclusion +author: James Hancock (@MadeofTin) +discussions-to: https://gitter.im/ethereum/EIPs +status: Stagnant +type: Meta +created: 2019-11-13 +--- + +## Simple Summary + +As part of an EIP centric forking model, this EIP tracks the first step in the approval process for any EIP to be included in a fork or upgrade. Specifically, the stage where the Core Developers vet the concept of an EIP and give a "green light" sufficient for EIP authors to move forward in development. + +## Abstract + +The pipeline for Core EIPs, per the EIP-Centric upgrade model, is as follows. +``` +[ DRAFT ] -> [ ELLIGLE FOR INCLUSION ] -> [ IMPLEMENTATION ] -> [ TESTING ] -> [ ACCEPTED ] -> [ DEPLOYED ] +``` + +This EIP documents all EIPs marked as **Eligible For Inclusion** by the All Core Devs. Typically to reach this stage, an EIP must be discussed in brief on an AllCoreDevs Call and motioned by rough consenses to be moved to this stage. Any additions to this list are required to provide a link to the meeting notes when this discussion and decision took place. + +The requirements for **Eligible for Inclusion** is that the AllCoreDevs, representing the major clients and ecosystem stakeholders etc: + + - Are positive towards the EIP, + - Would accept (well written) PRs to include the EIP into the codebase. + - So that it could be toggled on for testing… + - …but not with an actual block number for activation + +## Motivation + +Development of clear specifications and pull requests to existing Ethereum Clients is a large investment of time and resources. The state of *Eligible for Inclusion* is a signal from the Ethereum Core Developers to an EIP Author validiating the idea behind an EIP and confirms investing their time further pursing it is worthwhile. + +## Specification + +| EIP | Title | Pipeline Status | Date of Initial Decision | REF | +| -------- | ----------------------------------------------------- | -------- | ---------- | ---- | +| EIP-663 | Unlimited SWAP and DUP instructions | ELIGIBLE | 2019-11-01 | [🔗](https://github.com/ethereum/pm/blob/master/All%20Core%20Devs%20Meetings/Meeting%2074.md) | +| EIP-1057 | ProgPoW, a Programmatic Proof-of-Work | ELIGIBLE | 2019-11-01 | [🔗](https://github.com/ethereum/pm/blob/master/All%20Core%20Devs%20Meetings/Meeting%2074.md) | +| EIP-1380 | Reduced gas cost for call to self | ELIGIBLE | 2019-11-01 | [🔗](https://github.com/ethereum/pm/blob/master/All%20Core%20Devs%20Meetings/Meeting%2074.md) | +| EIP-1559 | Fee market change for ETH 1.0 chain | ELIGIBLE | 2019-11-01 | [🔗](https://github.com/ethereum/pm/blob/master/All%20Core%20Devs%20Meetings/Meeting%2074.md) | +| EIP-1702 | Generalized Account Versioning Scheme | ELIGIBLE | 2019-11-01 | [🔗](https://github.com/ethereum/pm/blob/master/All%20Core%20Devs%20Meetings/Meeting%2074.md) | +| EIP-1962 | EC arithmetic and pairings with runtime definitions | ELIGIBLE | 2019-11-01 | [🔗](https://github.com/ethereum/pm/blob/master/All%20Core%20Devs%20Meetings/Meeting%2074.md) | +| EIP-1985 | Sane limits for certain EVM parameters | ELIGIBLE | 2019-11-01 | [🔗](https://github.com/ethereum/pm/blob/master/All%20Core%20Devs%20Meetings/Meeting%2074.md) | +| EIP-2046 | Reduced gas cost for static calls made to precompiles | ELIGIBLE | 2019-11-01 | [🔗](https://github.com/ethereum/pm/blob/master/All%20Core%20Devs%20Meetings/Meeting%2074.md) | +| EIP-2315 | Simple Subroutines for the EVM | ELIGIBLE | 2020-02-21 | [🔗](https://github.com/ethereum/pm/blob/master/All%20Core%20Devs%20Meetings/Meeting%2081.md#decisions) | +| EIP-2537 | Precompile for BLS12-381 curve operations | ELIGIBLE | 2020-03-06 | [🔗](https://github.com/ethereum/pm/blob/master/All%20Core%20Devs%20Meetings/Meeting%2082.md) | + +## Rationale + +**EIP Number** + +**Title** + +**Pipeline Status** : Show the current status in the context of the EIP centric model. The list is sorted by furthest along in the process. + +**Date of Initial Decision** : Date of the initial decision for Eligibility for Inclusion + +**REF** : Link to the decision on the AllCoreDevs Notes + + +## References + + - EIP Centric Forking Model Proposal by @holiman - https://notes.ethereum.org/@holiman/S1ELAYY7S?type=view + + + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2384.md b/EIPS/eip-2384.md new file mode 100644 index 0000000..e8079af --- /dev/null +++ b/EIPS/eip-2384.md @@ -0,0 +1,40 @@ +--- +eip: 2384 +title: Muir Glacier Difficulty Bomb Delay +author: Eric Conner (@econoar) +discussions-to: https://ethereum-magicians.org/t/eip-2384-difficulty-bomb-delay +type: Standards Track +category: Core +status: Final +created: 2019-11-20 +--- + +## Simple Summary +The average block times are increasing due to the difficulty bomb (also known as the "_ice age_") and slowly accelerating. This EIP proposes to delay the difficulty bomb for another 4,000,000 blocks (~611 days). + +## Abstract +Starting with `MUIR_GLACIER_FORK_BLKNUM` the client will calculate the difficulty based on a fake block number suggesting to the client that the difficulty bomb is adjusting 9 million blocks later than the Homestead fork, which is also 7 million blocks later than the Byzantium fork and 4 million blocks later than the Constantinople fork. + +## Motivation +The difficulty bomb started to become noticeable again on October 5th 2019 at block 8,600,000. Block times have been around 13.1s on average and now as of block 8,900,000 are around 14.3s. This will start to accelerate exponentially every 100,000 blocks. Estimating the added impact from the difficulty bomb on block times shows that we will see 20s block times near the end of December 2019 and 30s+ block times starting February 2020. This will start making the chain bloated and more costly to use. It's best to delay the difficulty bomb again to around the time of expected launch of the Eth2 finality gadget. + +## Specification +#### Relax Difficulty with Fake Block Number +For the purposes of `calc_difficulty`, simply replace the use of `block.number`, as used in the exponential ice age component, with the formula: + + fake_block_number = max(0, block.number - 9_000_000) if block.number >= MUIR_GLACIER_FORK_BLKNUM else block.number + +## Rationale +This will delay the ice age by 52 million seconds (approximately 611 days), so the chain would be back at 20 second block times around July 2021. It's important to note this pushes the ice age 4,000,000 blocks from ~block 8,800,000 NOT from when this EIP is activated in a fork. + +## Backwards Compatibility +This EIP is not forward compatible and introduces backwards incompatibilities in the difficulty calculation. Therefore, it should be included in a scheduled hardfork at a certain block number. It's suggested to include this EIP shortly after the Istanbul fork. + +## Test Cases +Test cases shall be created once the specification is to be accepted by the developers or implemented by the clients. + +## Implementation +The implementation in it's logic does not differ from [EIP-649](./eip-649.md) or [EIP-1234](./eip-1234.md); an implementation for Parity-Ethereum is available in [parity-ethereum#9187](https://github.com/paritytech/parity-ethereum/pull/9187). + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2386.md b/EIPS/eip-2386.md new file mode 100644 index 0000000..f09e375 --- /dev/null +++ b/EIPS/eip-2386.md @@ -0,0 +1,201 @@ +--- +eip: 2386 +title: Ethereum 2 Hierarchical Deterministic Walletstore +author: Jim McDonald +discussions-to: https://ethereum-magicians.org/t/eip-2386-walletstore/3792 +status: Stagnant +type: Standards Track +category: ERC +created: 2019-11-21 +requires: 2334, 2335 +--- + +## Simple Summary + +A JSON format for the storage and retrieval of Ethereum 2 hierarchical deterministic (HD) wallet definitions. + +## Abstract + +Ethereum has the concept of keystores: pieces of data that define a key (see [EIP-2335](https://eips.ethereum.org/EIPS/eip-2335) for details). This adds the concept of walletstores: stores that define wallets and how keys in said wallets are created. + +## Motivation + +Hierarchical deterministic wallets create keys from a _seed_ and a _path_. The seed needs to be accessible to create new keys, however it should also be protected to the same extent as private keys to stop it from becoming an easy attack vector. The path, or at least the variable part of it, needs to be stored to ensure that keys are not duplicated. Providing a standard method to do this can promote interoperability between wallets and similar software. + +Given that a wallet has an amount of data and metadata that is useful when accessing existing keys and creating new keys, standardizing this information and how it is stored allows it to be portable between different wallet providers with minimal effort. + +## Specification + +The elements of a hierarchical deterministic walletstore are as follows: + +### UUID + +The `uuid` provided in the walletstore is a randomly-generated type 4 UUID as specified by [RFC 4122](https://tools.ietf.org/html/rfc4122). It is intended to be used as a 128-bit proxy for referring to a particular wallet, used to uniquely identify wallets. + +This element MUST be present. It MUST be a string following the syntactic structure as laid out in [section 3 of RFC 4122](https://tools.ietf.org/html/rfc4122#section-3). + +### Name + +The `name` provided in the walletstore is a UTF-8 string. It is intended to serve as the user-friendly accessor. The only restriction on the name is that it MUST NOT start with the underscore (`_`) character. + +This element MUST be present. It MUST be a string. + +### Version + +The `version` provided is the version of the walletstore. + +This element MUST be present. It MUST be the integer `1`. + +### Type + +The `type` provided is the type of wallet. This informs mechanisms such as key generation. + +This element MUST be present. It MUST be the string `hierarchical deterministic`. + +### Crypto + +The `crypto` provided is the secure storage of a secret for wallets that require this information. For hierarchical deterministic wallets this is the seed from which they calculate individual private keys. + +This element MUST be present. It MUST be an object that follows the definition described in [EIP-2335](https://eips.ethereum.org/EIPS/eip-2335). + +### Next Account + +The `nextaccount` provided is the index to be supplied to the path `m/12381/60//0` when creating a new private key from the seed. The path follows [EIP-2334](https://eips.ethereum.org/EIPS/eip-2334). + +This element MUST be present if the wallet type requires it. It MUST be a non-negative integer. + +### JSON schema + +The walletstore follows a similar format to that of the keystore described in [EIP-2335](https://eips.ethereum.org/EIPS/eip-2335). + +```json +{ + "$ref": "#/definitions/Walletstore", + "definitions": { + "Walletstore": { + "type": "object", + "properties": { + "crypto": { + "type": "object", + "properties": { + "kdf": { + "$ref": "#/definitions/Module" + }, + "checksum": { + "$ref": "#/definitions/Module" + }, + "cipher": { + "$ref": "#/definitions/Module" + } + } + }, + "name": { + "type": "string" + }, + "nextaccount": { + "type": "integer" + }, + "type": { + "type": "string" + }, + "uuid": { + "type": "string", + "format": "uuid" + }, + "version": { + "type": "integer" + } + }, + "required": [ + "name", + "type", + "uuid", + "version" + "crypto" + "nextaccount" + ], + "title": "Walletstore" + }, + "Module": { + "type": "object", + "properties": { + "function": { + "type": "string" + }, + "params": { + "type": "object" + }, + "message": { + "type": "string" + } + }, + "required": [ + "function", + "message", + "params" + ] + } + } +} +``` + +## Rationale + +A standard for walletstores, similar to that for keystores, provides a higher level of compatibility between wallets and allows for simpler wallet and key interchange between them. + +## Test Cases + +### Test Vector + +Password `'testpassword'` +Seed `0x147addc7ec981eb2715a22603813271cce540e0b7f577126011eb06249d9227c` + +```json +{ + "crypto": { + "checksum": { + "function": "sha256", + "message": "8bdadea203eeaf8f23c96137af176ded4b098773410634727bd81c4e8f7f1021", + "params": {} + }, + "cipher": { + "function": "aes-128-ctr", + "message": "7f8211b88dfb8694bac7de3fa32f5f84d0a30f15563358133cda3b287e0f3f4a", + "params": { + "iv": "9476702ab99beff3e8012eff49ffb60d" + } + }, + "kdf": { + "function": "pbkdf2", + "message": "", + "params": { + "c": 16, + "dklen": 32, + "prf": "hmac-sha256", + "salt": "dd35b0c08ebb672fe18832120a55cb8098f428306bf5820f5486b514f61eb712" + } + } + }, + "name": "Test wallet 2", + "nextaccount": 0, + "type": "hierarchical deterministic", + "uuid": "b74559b8-ed56-4841-b25c-dba1b7c9d9d5", + "version": 1 +} +``` + +## Implementation + +A Go implementation of the hierarchical deterministic wallet can be found at [https://github.com/wealdtech/go-eth2-wallet-hd](https://github.com/wealdtech/go-eth2-wallet-hd). + +## Security Considerations + +The seed stored in the `crypto` section of the wallet can be used to generate any key along the derived path. As such, the security of all keys generated by HD wallets is reduced to the security of the passphrase and strength of the encryption used to protect the seed, regardless of the security of the passphrase and strength of the encryption used to protect individual keystores. + +It is possible to work with only the walletstore plus an index for each key, in which case stronger passphrases can be used as decryption only needs to take place once. It is also possible to use generated keystores without the walletstore, in which case a breach of security will expose only the keystore. + +An example high-security configuration may involve the walletstore existing on an offline computer, from which keystores are generated. The keystores can then be moved individually to an online computer to be used for signing. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2387.md b/EIPS/eip-2387.md new file mode 100644 index 0000000..27a441e --- /dev/null +++ b/EIPS/eip-2387.md @@ -0,0 +1,75 @@ +--- +eip: 2387 +title: "Hardfork Meta: Muir Glacier" +author: James Hancock (@madeoftin) +discussions-to: https://ethereum-magicians.org/t/hard-fork-to-address-the-ice-age-eip-2387 +type: Meta +status: Final +created: 2019-11-22 +requires: 1679, 2384 +--- + +## Abstract + +This meta-EIP specifies the changes included in the Ethereum hard fork named Muir Glacier. This hard fork addresses the impending Ice Age on Ethereum Mainnet and includes a commitment to solving the problems with the ice age more permanently. + +## Motivation + +Ethereum achieves a consistent block time due to its' difficulty retargeting algorithm. If a block-time is higher than 20 seconds, it reduces the difficulty, and if a block time is lower than 10 seconds, it increases the difficulty. This mechanism reaches typically an equilibrium of around 13-14 seconds. Included within this mechanism is something we refer to as the Difficulty Bomb or the Ice Age. It artificially adds to the difficulty in such a way that the retargeting mechanism, at some point, can not adapt to the increase, and we see increased block times throughout the network. The ice age increments every 100,000 blocks. It at first is barely noticeable, but once it is visible, there is a drastic effect on block-times in the network. + +The primary problem with the Ice Age is that it is included in the complex mechanism that targets block times, which is an entirely separate in purpose. What is worse is due to being intwined with that algorithm, it is very difficult to simulate or predict its effect on the network. To predict the impact of the ice age, you must both make assumptions about the difficulty of main-net in the future, and predict the effect of changes in difficulty to the impact on the ice age and thus block-times. + +This fork will push back the Iceage as far as far as is reasonable and will give us time to update the Iceage to no longer have these design problems. There are two solutions to consider within that time frame. + + - Update the mechanism so that behavior is predictable. + - Remove the Iceage entirely + +## Specification + +- Codename: Muir Glacier + +### Activation + - `Block >= 9,200,000` on the Ethereum mainnet + - `Block >= 7,117,117` on the Ropsten testnet + - `Block >= N/A` on the Kovan testnet + - `Block >= N/A` on the Rinkeby testnet + - `Block >= N/A` on the Görli testnet + +### Included EIPs + - [EIP-2384](./eip-2384.md): Istanbul/Berlin Difficulty Bomb Delay + +## Rationale + +I want to address the rationale for the intention of the Iceage and the implementation of the Iceage separately. + +**The original intentions of the ice age include:** + + - At the time of upgrades, inhibit unintentional growth of the resulting branching forks leading up to Eth 2.0. * + - Encourage a prompt upgrade schedule for the path to Eth 2.0. * + - Forces the community to come back into agreement repeatedly...and it gives whatever portion of the community that wants to a chance to fork off + - Is a check for the Core Devs in the case that a decision is made to freeze the code base of clients without the blessing of the community. + +*Note: None of these effects the Freedom to Fork. They are meant to encourage core-devs and the community to upgrade along with the network and prevent the case where sleeper forks remain dormant only later to be resurrected. The requirement for an active fork is to change a client in a way to respond to the ice age. This is in fact what Ethereum Classic has done. + +This is not meant to be exhaustive, but the ideas above capture much of what has been written on the original intentions and process of creating the fork. Any additions to this list that need to be made, I am happy to include. Regardless, to effectively implement an updated design for the ice age, all of the intentions need to be revisited and clarified as part of any updates. This clarification will give a clear expectation for the community and core developers moving forward. + +**The implementation** + +The existing implementation of the ice age, while it does work in practice, is unnecessarily complex to model and confusing to communicate to the community. Any updates to the design should be: + + - Easy to model the effect on the network + - Easy to predict when it occurs + +This fork would give us time to address the community to understand their priorities better as far as the intentions of the Ice Age, and give time for proposals for better mechanisms to achieve those goals. + +### POA Testnets + +Muir Glacier never activates on PoA chains – thus will have zero impact on [forkid](./eip-2124.md). + +### Note on Issuance Reduction + +Previous Hardforks to address the Ice Age have also included reductions in the block reward from 5 Eth to 3 Eth to 2 Eth, respectively. In this case, there is no change in issuance, and the block reward remains 2 Eth per block. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2390.md b/EIPS/eip-2390.md new file mode 100644 index 0000000..4d87272 --- /dev/null +++ b/EIPS/eip-2390.md @@ -0,0 +1,316 @@ +--- +eip: 2390 +title: Geo-ENS +author: James Choncholas (@james-choncholas) +discussions-to: https://github.com/ethereum/EIPs/issues/2959 +status: Stagnant +type: Standards Track +category: ERC +created: 2019-11-15 +requires: 137, 165, 1062, 1185 +--- + +## Simple Summary +GeoENS brings geographic split horizon capabilities to ENS. It's GeoDNS for ENS! + +## Abstract +This EIP specifies an ENS resolver interface for geographically split horizon DNS. +Geographic split horizon DNS returns resource records that are specific to an end +user's location. +This technique is commonly used by CDNs to direct traffic to content caches nearest users. +Geographic split horizon resolution is primarily geared towards ENS +resolvers storing DNS resource records [EIP-1185](./eip-1185.md), although the technique could be +used on other interfaces like IPFS content hash storage [EIP-1062](./eip-1062.md). + +## Motivation +There are many use cases for traditional GeoDNS systems, like Amazon's Route53, +in the centralized web. +These use cases include proximity-based load balancing and serving content +specific to the geographic location of the query. +Unfortunately the ENS specification does not provide a mechanism for +geo-specific resolution. +ENS can respond to queries with IP addresses (as described in [EIP-1185](./eip-1185.md)) +however there is no way to respond to geo-specific queries. +This EIP proposes a standard to give the ENS system geo-proximal awareness +to serve a similar purpose as GeoDNS. + +GeoENS can do more than DNS-based solutions. +In addition to geographic split horizon DNS, GeoENS can be used for the following: + - Locating digital resources (like smart contracts) that represent physical objects in the real world. + - Smart contract managing access to a physical object associated with a specific location. + - ENS + IPFS web hosting (as described in [EIP-1062](./eip-1062.md)) with content translated to the native language of the query source. + - Tokenizing objects with a physical location. + +Because of the decentralized nature of ENS, geo-specific resolution is different than traditional GeoDNS. +GeoDNS works as follows. DNS queries are identified by their source IP address. +This IP is looked up in a database like [GeoIP2](https://www.maxmind.com/en/geoip2-services-and-databases) +from MaxMind which maps the IP address to a location. +This method of locating the source of a query is error prone and unreliable. +If the GeoIP database is out of date, queried locations can be vastly different than their true location. +GeoENS does not rely on a database because the user includes a location in their query. + +It follows that queries can be made by users for any location, not just their location. +Traditional DNS will only return the resource assigned to a query's provenance. +GeoENS does not correlate a query's provinance with a location, allowing the +entire globe to be queried from a single location. + +An additional shortcoming of traditional DNS is the fact that there is no way to return a list of servers in a certain proximity. +This is paramount for uses cases that require discovering the resource with the lowest latency. +GeoENS allows a list of resources, like IP addresses, to be gathered within a specific location. +Then a client to determine themselves which resource has the lowest latency. + +Lastly, publicly facing GeoDNS services do not give fine granularity control +over geographic regions for GeoDNS queries. +Cloud based DNS services like [Amazon's Route 53](https://aws.amazon.com/route53/) +only allow specifying geographic regions at the granularity of a State in +the United States. +GeoENS on the other hand gives 8 characters of geohash resolution which +corresponds to +-20 meter accuracy. + +## Specification +This EIP proposes a new interface to ENS resolvers such that geo-spacial information +can be recorded and retrieved from the blockchain. +The interface changes are described below for "address resolvers" described in EIP137 +however the idea applies to any record described in EIP1185 and EIP1062, namely DNS +Resolvers, Text Resolvers, ABI Resolvers, etc. + +### What is a geohash? +A [Geohash](https://en.m.wikipedia.org/wiki/Geohash#Algorithm_and_example) +is an interleaving of latitude and longitude bits, whose +length determines it's precision. +Geohashes are typically encoded in base 32 characters. + +### function setGeoAddr(bytes32 node, string calldata geohash, address addr) external authorised(node) +Sets a resource (contract address, IP, ABI, TEXT, etc.) by node and geohash. +Geohashes must be unique per address and are exactly 8 characters long. +This leads to an accuracy of +-20 meters. +Write default initialized resource value, `address(0)`, to remove a resource from the resolver. + +### function geoAddr(bytes32 node, string calldata geohash) external view returns (address[] memory ret) +Query the resolver contract for a specific node and location. +All resources (contract addresses, IP addresses, ABIs, TEXT records, etc.) matching +the node and prefix geohash provided are returned. +This permits querying by exact geohash of 8 characters to return the content at that location, +or querying by geographic bounding box described by a geohash of less than 8 character precision. + +Any type of geohash can be used including [Z-order](https://en.wikipedia.org/wiki/Z-order_curve) +[Hilbert](https://en.wikipedia.org/wiki/Hilbert_curve) or the more accurate +[S2 Geometry](https://s2geometry.io/devguide/s2cell_hierarchy.html) library +from Google. +There are also ways to search the geographic data using geohashes without +always ending up with a rectangular query region. +[Searching circular shaped regions](https://github.com/ashwin711/proximityhash) is +slightly more complex as it requires multiple queries. + +## Rationale +The proposed implementation uses a sparse [Quadtree](https://dl.acm.org/doi/10.1007/BF00288933) trie as an index for +resource records as it has low storage overhead and good search performance. +The leaf nodes of the tree store resource records while non-leaves represent one geohash character. +Each node in the tree at depth d corresponds to a geohash of precision d. +The tree has depth 8 because the maximum precision of a geohash is 8 characters. +The tree has fanout 32 because the radix of a geohash character is 32. +The path to get to a leaf node always has depth 8 and the leaf contains the content (like IP address) +of the geohash represented by the path to the leaf. +The tree is sparse as 71% of the Earth's surface is covered by water. +The tree facilitates common traversal algorithms (DFS, BFS) to return +lists of resource records within a geographic bounding box. + +## Backwards Compatibility +This EIP does not introduce issues with backwards compatibility. + +## Test Cases +See https://github.com/james-choncholas/resolvers/blob/master/test/TestPublicResolver.js + +## Implementation +This address resolver, written in Solidity, implements the specifications outlined above. +The same idea presented here can be applied to other resolver interfaces as specified in EIP137. +Note that geohashes are passed and stored using 64 bit unsigned integers. +Using integers instead of strings for geohashes is more performant, especially in the `geomap` mapping. +For comparison purposes, see https://github.com/james-choncholas/geoens/tree/master/contracts/StringOwnedGeoENSResolver.sol for the inefficient string implementation. + + +```solidity +pragma solidity ^0.5.0; + +import "../ResolverBase.sol"; + +contract GeoENSResolver is ResolverBase { + bytes4 constant ERC2390 = 0x8fbcc5ce; + uint constant MAX_ADDR_RETURNS = 64; + uint constant TREE_VISITATION_QUEUESZ = 64; + uint8 constant ASCII_0 = 48; + uint8 constant ASCII_9 = 57; + uint8 constant ASCII_a = 97; + uint8 constant ASCII_b = 98; + uint8 constant ASCII_i = 105; + uint8 constant ASCII_l = 108; + uint8 constant ASCII_o = 111; + uint8 constant ASCII_z = 122; + + struct Node { + address data; // 0 if not leaf + uint256 parent; + uint256[] children; // always length 32 + } + + // A geohash is 8, base-32 characters. + // A geomap is stored as tree of fan-out 32 (because + // geohash is base 32) and height 8 (because geohash + // length is 8 characters) + mapping(bytes32=>Node[]) private geomap; + + event GeoENSRecordChanged(bytes32 indexed node, bytes8 geohash, address addr); + + // only 5 bits of ret value are used + function chartobase32(byte c) pure internal returns (uint8 b) { + uint8 ascii = uint8(c); + require( (ascii >= ASCII_0 && ascii <= ASCII_9) || + (ascii > ASCII_a && ascii <= ASCII_z)); + require(ascii != ASCII_a); + require(ascii != ASCII_i); + require(ascii != ASCII_l); + require(ascii != ASCII_o); + + if (ascii <= (ASCII_0 + 9)) { + b = ascii - ASCII_0; + + } else { + // base32 b = 10 + // ascii 'b' = 0x60 + // note base32 skips the letter 'a' + b = ascii - ASCII_b + 10; + + // base32 also skips the following letters + if (ascii > ASCII_i) + b --; + if (ascii > ASCII_l) + b --; + if (ascii > ASCII_o) + b --; + } + require(b < 32); // base 32 can't be larger than 32 + return b; + } + + function geoAddr(bytes32 node, bytes8 geohash, uint8 precision) external view returns (address[] memory ret) { + bytes32(node); // single node georesolver ignores node + assert(precision <= geohash.length); + + ret = new address[](MAX_ADDR_RETURNS); + if (geomap[node].length == 0) { return ret; } + uint ret_i = 0; + + // walk into the geomap data structure + uint pointer = 0; // not actual pointer but index into geomap + for(uint8 i=0; i < precision; i++) { + + uint8 c = chartobase32(geohash[i]); + uint next = geomap[node][pointer].children[c]; + if (next == 0) { + // nothing found for this geohash. + // return early. + return ret; + } else { + pointer = next; + } + } + + // pointer is now node representing the resolution of the query geohash. + // DFS until all addresses found or ret[] is full. + // Do not use recursion because blockchain... + uint[] memory indexes_to_visit = new uint[](TREE_VISITATION_QUEUESZ); + indexes_to_visit[0] = pointer; + uint front_i = 0; + uint back_i = 1; + + while(front_i != back_i) { + Node memory cur_node = geomap[node][indexes_to_visit[front_i]]; + front_i ++; + + // if not a leaf node... + if (cur_node.data == address(0)) { + // visit all the chilins + for(uint i=0; i MAX_ADDR_RETURNS) break; + } + } + + return ret; + } + + // when setting, geohash must be precise to 8 digits. + function setGeoAddr(bytes32 node, bytes8 geohash, address addr) external authorised(node) { + bytes32(node); // single node georesolver ignores node + + // create root node if not yet created + if (geomap[node].length == 0) { + geomap[node].push( Node({ + data: address(0), + parent: 0, + children: new uint256[](32) + })); + } + + // walk into the geomap data structure + uint pointer = 0; // not actual pointer but index into geomap + for(uint i=0; i < geohash.length; i++) { + + uint8 c = chartobase32(geohash[i]); + + if (geomap[node][pointer].children[c] == 0) { + // nothing found for this geohash. + // we need to create a path to the leaf + geomap[node].push( Node({ + data: address(0), + parent: pointer, + children: new uint256[](32) + })); + geomap[node][pointer].children[c] = geomap[node].length - 1; + } + pointer = geomap[node][pointer].children[c]; + } + + Node storage cur_node = geomap[node][pointer]; // storage = get reference + cur_node.data = addr; + + emit GeoENSRecordChanged(node, geohash, addr); + } + + function supportsInterface(bytes4 interfaceID) public pure returns (bool) { + return interfaceID == ERC2390 || super.supportsInterface(interfaceID); + } +} +``` + +## Security Considerations +This contract has similar functionality to ENS Resolvers - refer there for security considerations. +Additionally, this contract has a dimension of data privacy. +Users query via the geoAddr function specifying a geohash of less than 8 characters +which defines the query region. +Users who run light clients leak the query region to their connected full-nodes. +Users who rely on nodes run by third parties (like Infura) will also leak +the query region. +Users who run their own full node or have access to a trusted full node do +not leak any location data. + +Given the way most location services work, the query region is likely to contain +the user's actual location. +The difference between API access, light, and full nodes has always had +an impact on privacy but now the impact is underscored by the involvement +of coarse granularity user location. + + + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2400.md b/EIPS/eip-2400.md new file mode 100644 index 0000000..1978fd5 --- /dev/null +++ b/EIPS/eip-2400.md @@ -0,0 +1,100 @@ +--- +eip: 2400 +title: Transaction Receipt URI +description: URI format for submitted transactions with complete information for transaction decoding +author: Ricardo Guilherme Schmidt (@3esmit), Eric Dvorsak (@yenda) +discussions-to: https://ethereum-magicians.org/t/eip-2400-transaction-receipt-uri/ +status: Stagnant +type: Standards Track +category: ERC +created: 2019-11-05 +requires: 155, 681 +--- +## Abstract + +A transaction hash is not very meaningful on its own, because it looks just like any other hash, and it might lack important information for reading a transaction. + +This standard includes all needed information for displaying a transaction and its details, such as `chainId`, `method` signature called, and `events` signatures emitted. + +## Motivation + +Interoperability between ethereum clients, allowing different systems to agree on a standard way of representing submitted transactions hashes, optionally with necessary information for decoding transaction details. + +### Use-cases + +Transaction Receipt URIs embedded in QR-codes, hyperlinks in web-pages, emails or chat messages provide for robust cross-application signaling between very loosely coupled applications. A standardized URI format allows for instant invocation of the user’s preferred transaction explorer application. Such as: + +- In web3 (dapps, mining pools, exchanges), links would automatically open user's preferred transaction explorer; +- In wallets, for users sharing transaction receipts easier; +- In chat applications, as a reply to an [EIP-681] transaction request; +- In crypto vending machines, a QRCode can be displayed when transactions are submitted; +- Anywhere transaction receipts are presented to users. + +## Specification + +### Syntax + +Transaction receipt URLs contain "ethereum" in their schema (protocol) part and are constructed as follows: + + receipt = schema_part transaction_hash [ "@" chain_id ] [ "?" parameters ] + schema_part = "ethereum:tx-" + transaction_hash = "0x" 64*HEXDIG + chain_id = 1*DIGIT + parameters = parameter *( "&" parameter ) + parameter = key "=" value + key = "method" / "events" + value = function_signature / event_list + function_signature = function_name "(" TYPE *( "," TYPE) ")" + function_name = STRING + event_list = event_signature *( ";" event_signature ) + event_signature = event_name "(" event_type *( "," event_type) ")" + event_name = STRING + event_type = ["!"] TYPE + + +Where `TYPE` is a standard ABI type name, as defined in Ethereum Contract ABI specification. `STRING` is a URL-encoded unicode string of arbitrary length. + +The exclamation symbol (`!`), in `event_type`, is used to identify indexed event parameters. + +### Semantics + +`transaction_hash` is mandatory. The hash must be looked up in the corresponding `chain_id` transaction history, if not found it should be looked into the pending transaction queue and rechecked until is found. If not found anequivalent error as "transaction not found error" should be shown instead of the transaction. When the transaction is pending, it should keep checking until the transaction is included in a block and becomes "unrevertable" (usually 12 blocks after transaction is included). + + +`chain_id` is specified by [EIP-155] optional and contains the decimal chain ID, such that transactions on various test and private networks can be represented as well. If no `chain_id` is present, the $ETH/mainnet (`1`) is considered. + +If `method` is not present, this means that the transaction receipt URI does not specify details, or that it was a transaction with no calldata. When present it needs to be validated by comparing the first 4 bytes of transaction calldata with the first 4 bytes of the keccak256 hash of `method`, if invalid, an equivalent error as "method validation error" must be shown instead of the transaction. + +If `events` is not present, this means that the transaction receipt URI does not specify details, or that the transaction did not raised any events. Pending and failed transactions don't validate events, however, when transaction is successful (or changes from pending to success) and events are present in URI, each event in the `event_list` must occur at least once in the transaction receipt event logs, otherwise an equivalent error as "event validation error: {event(s) [$event_signature, ...] not found}" should be shown instead of the transaction. A URI might contain the event signature for all, some or none of the raised events. + +#### Examples + +##### Simple ETH transfer: +`ethereum:tx-0x1143b5e38fe3cf585fb026fb9b5ce35c85a691786397dc8a23a07a62796d8172@1` + +##### Standard Token transfer: + +`ethereum:tx-0x5375e805b0c6afa20daab8d37352bf09a533efb03129ba56dee869e2ce4f2f92@1?method="transfer(address,uint256)"&events="Transfer(!address,!address,uint256)"` + +##### Complex contract transaction: + +`ethereum:tx-0x4465e7cce3c784f264301bfe26fc17609855305213ec74c716c7561154b76fec@1?method="issueAndActivateBounty(address,uint256,string,uint256,address,bool,address,uint256)"&events="Transfer(!address,!address,uint256);BountyIssued(uint256);ContributionAdded(uint256,!address,uint256);BountyActivated(uint256,address)"` + +## Rationale + +The goal of this standard envolves only the transport of submitted transactions, and therefore transaction data must be loaded from blockchain or pending transaction queue, which also serves as a validation of the transaction existence. + +Transaction hash not found is normal in fresh transactions, but can also mean that effectively a transaction was never submitted or have been replaced (through "higher gasPrice" nonce override or through an uncle/fork). + +In order to decode transaction parameters and events, a part of the ABI is required. The transaction signer have to know the ABI to sign a transaction, and is also who is creating a transaction receipt, so the transaction receipt can optionally be shared with the information needed to decode the transaction call data and it's events. + +## Backwards Compatibility + +Future upgrades that are partially or fully incompatible with this proposal must use a prefix other than `tx-` that is separated by a dash (-) character from whatever follows it. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). + +[EIP-155]: ./eip-155.md +[EIP-681]: ./eip-681.md diff --git a/EIPS/eip-2458.md b/EIPS/eip-2458.md new file mode 100644 index 0000000..d4ed2d3 --- /dev/null +++ b/EIPS/eip-2458.md @@ -0,0 +1,75 @@ +--- +eip: 2458 +title: Updates and Updated-by Header +author: Edson Ayllon (@edsonayllon) +discussions-to: https://github.com/ethereum/EIPs/issues/2453 +status: Withdrawn +type: Informational +created: 2020-01-06 +--- + +## Simple Summary + +Adds EIP header options `updates` and `updated-by` to frontmatter of `active` EIPs for use as needed. + +## Abstract + +EIP headers `updates` and `updated-by` are used for updating `active` EIPs. This is to make the improvement process of EIPs more modular, and have updates to existing `active` EIPs receive similar exposures to EIPs which replace existing `final` EIPs. + +## Motivation + +Currently, EIP1 specifies EIP headers: `updated`, `replaces`, and `superseded-by`. Headers `replaces` and `superseded-by` indicates when an entire EIP is being replaced by another EIP, indicating when an EIP is now historical, and is updated by a new standard. + +The header `updated` indicates the date an EIP has received an update by EIP authors and editors, an example EIP being EIP1. `updated` is reserved for EIPs in `draft` or `active` status. + +In the case of `active` status, an EIP may receive an update, but these updates don't operate as with EIPs in `final` status, where a historical EIP is created, and the new EIP is referenced by the historical one. While these updates are not kept immutably, updates to active EIPs can be done modularly by creating a new EIP that goes through the standard discussion and auditing process EIPs undergo. The EIP headers `updates` and `updated-by` are to facilitate this modularity. Creating a new EIP also provides sufficient notification to affected stakeholders of an active EIP before that EIP is `updated`. + +## Specification + +### `updated-by` + +`updated-by` is reserved for EIPs in `active` status. For an EIP in status `active`, updates to that EIP, which update the header `updated`, should be started by opening a new EIP to start vetting for that update. When an `active` EIP receives a new entry to header `updated`, an associated `updated-by` EIP listing should be included, where that newly listed EIP has reached `final` status. + +`updates` should be included as an EIP header, as all EIP headers, and include a reference to an EIP designation. When multiple EIP designations are referenced, each should be separated by a comma. Example: + +``` +--- +updated-by: 9999, 9998, 9997 +--- +``` + +### `updates` + +`updates` is reserved for EIPs updating EIPs in `active` status. An EIP listed as `updates` is implied to also be `requires`; only `updates` is needed for those EIP listings. Having an EIP listing `updates` does not necessarily mean that referenced EIP must reference back with an `updated-by` listing. + +`updates` should be included as an EIP header, as all EIP headers, and include a reference to an EIP designation. When multiple EIP designations are referenced, each should be separated by a comma. Example: + +``` +--- +updates: 1 +--- +``` + +## Rationale + +`updates` and `updated-by` apply only to EIPs in `active` status as updates to EIPs in `final` status are already handled by EIP headers `superseded-by` and `replaces`. + +The syntax should align with previous EIP header syntax, as this EIP is not updating syntax, simply adding header options. + +## Backwards Compatibility + +These EIP headers are optional and do not introduce compatibility issues. + + +## Implementation + +An implementation is adding a header option. + +## Security Considerations + +This standard is informational and does not introduce technical security issues. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). + + diff --git a/EIPS/eip-2464.md b/EIPS/eip-2464.md new file mode 100644 index 0000000..2cdc86d --- /dev/null +++ b/EIPS/eip-2464.md @@ -0,0 +1,81 @@ +--- +eip: 2464 +title: "eth/65: transaction announcements and retrievals" +description: Introduces `NewPooledTransactionHashes`, `GetPooledTransactions`, and `PooledTransactions`. +author: Péter Szilágyi , Péter Szilágyi (@karalabe), Gary Rong , Tim Beiko (@timbeiko) +discussions-to: https://github.com/ethereum/EIPs/issues/2465 +status: Final +type: Standards Track +category: Networking +created: 2020-01-13 +requires: 2364 +--- + +## Abstract + +This EIP introduces three additional message types into the `eth` protocol (releasing a new version, `eth/65`): `NewPooledTransactionHashes (0x08)` to announce a set of transactions without their content; `GetPooledTransactions (0x09)` to request a batch of transactions by their announced hash; and `PooledTransactions (0x0a)` to reply to a transaction request. This permits reducing the bandwidth used for transaction propagation from linear complexity in the number of peers to square root; and also reducing the initial transaction exchange from 10s-100s MB to `len(pool) * 32B ~= 128KB`. + +## Motivation + +The `eth` network protocol has two ways to propagate a newly mined block: it can be broadcast to a peer in its entirety (via `NewBlock (0x07)` in `eth/64` and prior or it can be announced only (via `NewBlockHashes (0x01)`). This duality allows nodes to do the high-bandwidth broadcasting (10s-100s KB) for a square root number of peers; and the low-bandwidth announcing (10s-100s B) for the remaining linear number of peers. The square root broadcast is enough to reach all well connected nodes, but the linear announce is needed to get across degenerate topologies. This works well. + +The `eth` protocol, however, does not have a similar dual mechanism for propagating transactions, so nodes need to rely on broadcasting (via `Transactions (0x02)`). To cater for degenerate topologies, transactions cannot be broadcast square rooted, rather they need to be transferred linearly to all peers. With N peers, each node will transfer the same transaction N times (counting both directions), whereas 1 would be enough in a perfect world. This is a significant waste. + +A similar issue arises when a new network connection is made between two nodes, as they need to sync up their transaction pools, but the pool is just a soup of dangling transactions. Without a way to deduplicate transactions remotely, each node is forced to naively transfer their entire list of transactions to the other side. With pools containing thousands of transactions, a naive transfer amounts to 10s-100s MB, most of which is useless. There is no better way, however. + +This EIP introduces three additional message types into the `eth` protocol (releasing a new version, `eth/65`): `NewPooledTransactionHashes (0x08)` to announce a set of transactions without their content; `GetPooledTransactions (0x09)` to request a batch of transactions by their announced hash; and `PooledTransactions (0x0a)` to reply to a transaction request. This permits reducing the bandwidth used for transaction propagation from linear complexity in the number of peers to square root; and also reducing the initial transaction exchange from 10s-100s MB to `len(pool) * 32B ~= 128KB`. + +With transaction throughput (and size) picking up in Ethereum, transaction propagation is the current dominant component of the used network resources. Most of these resources are however wasted, as the `eth` protocol does not have a mechanism to deduplicate transactions remotely, so the same data is transferred over and over again across all network connections. + +This EIP proposes a tiny extension to the `eth` protocol, which permits nodes to agree on the set of transactions that need to be transferred across a network connection, before doing the costly exchange. This should help reduce the global (operational) bandwidth usage of the Ethereum network by at least an order of magnitude. + +## Specification + +Add three new message types to the `eth` protocol: + * `NewPooledTransactionHashes (0x08): [hash_0: B_32, hash_1: B_32, ...]` + * Specify one or more transactions that have appeared in the network and which have **not yet been included in a block**. To be maximally helpful, nodes should inform peers of all transactions that they may not be aware of. + * There is **no protocol violating hard cap** on the number of hashes a node may announce to a remote peer (apart from the 10MB `devp2p` network packet limit), but 4096 seems a sane chunk (128KB) to avoid a single packet hogging a network connection. + * Nodes should only announce hashes of transactions that the remote peer could reasonably be considered not to know, but it is better to be over zealous than to have a nonce gap in the pool. + * `GetPooledTransactions (0x09): [hash_0: B_32, hash_1: B_32, ...]` + * Specify one or more transactions to retrieve from a remote peer's **transaction pool**. + * There is **no protocol violating hard cap** on the number of transactions a node may request from a remote peer (apart from the 10MB `devp2p` network packet limit), but the recipient may enforce an arbitrary cap on the reply (size or serving time), which **must not** be considered a protocol violation. To keep wasted bandwidth down (unanswered hashes), 256 seems like a sane upper limit. + * `PooledTransactions (0x0a): [[nonce: P, receivingAddress: B_20, value: P, ...], ...]` + * Specify transactions **from the local transaction pool** that the remote node requested via a `GetPooledTransactions (0x09)` message. The items in the list are transactions in the format described in the main Ethereum specification. + * The transactions **must** be in same order as in the request, but it is **ok** to skip transactions that are not available. This way if the response size limit is reached, requesters will know which hashes to request again (everything from the last returned transaction) and which to assume unavailable (all gaps before the last returned transaction). + * A peer may respond with an empty reply **iff** none of the hashes match transactions in its pool. It is allowed to announce a transaction that will not be served later if it gets included in a block in between. + +## Rationale + +**Q: Why limit `GetPooledTransactions (0x09)` to retrieving items from the pool?** + +Apart from the transaction pool, transactions in Ethereum are always bundled together by the hundreds in block bodies and existing network retrievals honor this data layout. Allowing direct access to individual transactions in the database has no actionable use case, but would expose costly database reads into the network. + +For transaction propagation purposes there is no reason to allow disk access, as any transaction finalized to disk will be broadcast inside a block anyway, so at worse there is a few hundred millisecond delay when a node gets the transaction. + +Block propagation may be made a bit more optimal by transferring the contained transactions on demand only, but that is a whole EIP in itself, so better relax the protocol when all the requirements are known and not in advance. It would probably be enough to maintain a set of transactions included in recent blocks in memory. + +**Q: Should `NewPooledTransactionHashes (0x08)` deduplicate from disk?** + +Similarly to `GetPooledTransactions (0x09)`, `NewPooledTransactionHashes (0x08)` should also only operate on the transaction pool and should ignore the disk altogether. During healthy network conditions, a transaction will propagate through much faster than it's included in a block, so it will essentially be non-existent that a newly announced transaction is already on disk. By avoiding disk deduplication, we can avoid a DoS griefing by remote transaction announces. + +If we want to be really correct and avoid even the slightest data race when deduplicating announcements, we can use the same recently-included-transactions trick that we discussed above to discard announcements that have recently become stale. + +**Q: Why not reuse `Transaction (0x02)` instead of a new `PooledTransactions (0x0a)`?** + +Originally this EIP reused the existing `Transaction (0x02)` message as the reply to the `GetPooledTransactions (0x09)` request. This makes client code more complicated, because nodes constantly gossip `Transaction (0x02)` messages to each other as broadcasts, so it's hard to match up which of the many messages is the actual reply to the request. + +By keeping `Transaction (0x02)` and `PooledTransactions (0x0a)` as separate messages, we can also leave the protocol more flexible for future optimizations (e.g. adding request IDs, which are meaningless for gossip broadcasts). + +## Backwards Compatibility + +This EIP extends the `eth` protocol in a backwards incompatible way and requires rolling out a new version, `eth/65`. However, `devp2p` supports running multiple versions of the same wire protocol side-by-side, so rolling out `eth/65` does not require client coordination, since non-updated clients can keep using `eth/64`. + +This EIP does not change the consensus engine, thus does _not_ require a hard fork. + +## Security Considerations + +None. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2470.md b/EIPS/eip-2470.md new file mode 100644 index 0000000..406c749 --- /dev/null +++ b/EIPS/eip-2470.md @@ -0,0 +1,197 @@ +--- +eip: 2470 +title: Singleton Factory +author: Ricardo Guilherme Schmidt (@3esmit) +discussions-to: https://ethereum-magicians.org/t/erc-2470-singleton-factory/3933 +status: Stagnant +type: Standards Track +category: ERC +created: 2020-01-15 +requires: 1014 +--- + +## Simple Summary + +Some DApps needs one, and only one, instance of an contract, which have the same address on any chain. + +A permissionless factory for deploy of keyless deterministic contracts addresses based on its bytecode. + +## Abstract + +Some contracts are designed to be Singletons which have the same address no matter what chain they are, which means that should exist one instance for all, such as [EIP-1820] and [EIP-2429]. These contracts are usually deployed using a method known as [Nick]'s method, so anyone can deploy those contracts on any chain and they have a deterministic address. +This standard proposes the creation of a CREATE2 factory using this method, so other projects requiring this feature can use this factory in any chain with the same setup, even in development chains. + +## Motivation + +Code reuse, using the factory becomes easier to deploy singletons. + +## Specification + +### [ERC-2470] Singleton Factory + +> This is an exact copy of the code of the [ERC2470 factory smart contract]. + +```solidity +pragma solidity 0.6.2; + + +/** + * @title Singleton Factory (EIP-2470) + * @notice Exposes CREATE2 (EIP-1014) to deploy bytecode on deterministic addresses based on initialization code and salt. + * @author Ricardo Guilherme Schmidt (Status Research & Development GmbH) + */ +contract SingletonFactory { + /** + * @notice Deploys `_initCode` using `_salt` for defining the deterministic address. + * @param _initCode Initialization code. + * @param _salt Arbitrary value to modify resulting address. + * @return createdContract Created contract address. + */ + function deploy(bytes memory _initCode, bytes32 _salt) + public + returns (address payable createdContract) + { + assembly { + createdContract := create2(0, add(_initCode, 0x20), mload(_initCode), _salt) + } + } +} +// IV is a value changed to generate the vanity address. +// IV: 6583047 +``` + +### Deployment Transaction + +Below is the raw transaction which MUST be used to deploy the smart contract on any chain. + +``` +0xf9016c8085174876e8008303c4d88080b90154608060405234801561001057600080fd5b50610134806100206000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80634af63f0214602d575b600080fd5b60cf60048036036040811015604157600080fd5b810190602081018135640100000000811115605b57600080fd5b820183602082011115606c57600080fd5b80359060200191846001830284011164010000000083111715608d57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550509135925060eb915050565b604080516001600160a01b039092168252519081900360200190f35b6000818351602085016000f5939250505056fea26469706673582212206b44f8a82cb6b156bfcc3dc6aadd6df4eefd204bc928a4397fd15dacf6d5320564736f6c634300060200331b83247000822470 +``` + +The strings of `2470`'s at the end of the transaction are the `r` and `s` of the signature. +From this deterministic pattern (generated by a human), anyone can deduce that no one knows the private key for the deployment account. + +### Deployment Method + +This contract is going to be deployed using the keyless deployment method---also known as [Nick]'s method---which relies on a single-use address. +(See [Nick's article] for more details). This method works as follows: + +1. Generate a transaction which deploys the contract from a new random account. + - This transaction MUST NOT use [EIP-155] in order to work on any chain. + - This transaction MUST have a relatively high gas price to be deployed on any chain. In this case, it is going to be 100 Gwei. + +2. Forge a transaction with the following parameters: + ```js + { + nonce: 0, + gasPrice: 100000000000, + value: 0, + data: '0x608060405234801561001057600080fd5b50610134806100206000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80634af63f0214602d575b600080fd5b60cf60048036036040811015604157600080fd5b810190602081018135640100000000811115605b57600080fd5b820183602082011115606c57600080fd5b80359060200191846001830284011164010000000083111715608d57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550509135925060eb915050565b604080516001600160a01b039092168252519081900360200190f35b6000818351602085016000f5939250505056fea26469706673582212206b44f8a82cb6b156bfcc3dc6aadd6df4eefd204bc928a4397fd15dacf6d5320564736f6c63430006020033', + gasLimit: 247000, + v: 27, + r: '0x247000', + s: '0x2470' + } + ``` + > The `r` and `s` values, made of starting `2470`, are obviously a human determined value, instead of a real signature. + +3. We recover the sender of this transaction, i.e., the single-use deployment account. + + > Thus we obtain an account that can broadcast that transaction, but we also have the warranty that nobody knows the private key of that account. + +4. Send exactly 0.0247 ether to this single-use deployment account. + +5. Broadcast the deployment transaction. + + > Note: 247000 is the double of gas needed to deploy the smart contract, this ensures that future changes in OPCODE pricing are unlikely to cause this deploy transaction to fail out of gas. A left over will sit in the address of about 0.01 ETH will be forever locked in the single use address. + +The resulting transaction hash is `0x803351deb6d745e91545a6a3e1c0ea3e9a6a02a1a4193b70edfcd2f40f71a01c`. + +This operation can be done on any chain, guaranteeing that the contract address is always the same and nobody can use that address with a different contract. + + +### Single-use Factory Deployment Account + +![](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAB0UlEQVR4nO3asW1CQRBAQdpyCa6CIpxTjgujDGTJNEC2QqvjTbDx33c3P7vL79f1fzLf98dobn8/o5nuP53p/tPzm+5/AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4CMBnH6B0/23L2AbEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ8JYPsCtw+w3g9AvB+AeD8A8X4A4v0AxPsBiPcDEO8HIN4PQLwfgHg/APF+AOL9AMT7AYj3AxDvP/5ByOkApt/PvwgCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgJYDtA9w+gO0fYHsAAGB/CQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAdALYfNExnun+9H4B4PwDxfgDi/QDE+wGI9wMQ7wcg3g9AvB+AeD8A8X4A4v0AxPsBiPcDEO8HIN4/fhCy/aDidADb5wcAAGcHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO8CsH2ApwPY/j4Ah+8PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPB6nlegoDNgrfyiAAAAAElFTkSuQmCC) + +`0xBb6e024b9cFFACB947A71991E386681B1Cd1477D` + +This account is generated by reverse engineering it from its signature for the transaction. +This way no one knows the private key, but it is known that it is the valid signer of the deployment transaction. + +> To deploy the registry, 0.0247 ether MUST be sent to this account *first*. + +### Factory Contract Address +![](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAABn0lEQVR4nO3coRECMRRF0S2GutCUQzd4WqAMLB4qQGWYP+EecXXeZo/OcTrf35Ndbq+l7F/rmB6w+wXuvh+A+H4A4vsBiO8HIL4fgPh+AOL7AYjvByC+H4D4fgDi+wGI7wcgvh+A+H4A4vuXAUxfwPX5GG33+wMAgL0/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgGYHrA9A+cbhoQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wlgesD0+bvvXz0fgM33AwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8ATB9gZoNgHgAxAMgHgDxAIgHQDwA4gEQD4B4AMQDIB4A8QCIB0A8AOId0w8caK3V/wfA5gEQD4B4AMQDIB4A8QCIB0A8AOIBEA+AeADEAyAeAPEAiAdAPADiARAPgHgAxAMgHgDxAIgHQDwA4gEQD4B4AMQDIB4A8QCItwxg+oECDT8QMT1AAAgAASAABIAAEAACQAAIAAEgAASAANAv+gDxVDRR1CVqRAAAAABJRU5ErkJggg==) + +`0xce0042B868300000d44A59004Da54A005ffdcf9f` + +The contract has the address above for every chain on which it is deployed. +### ABI for SingletonFactory: +```json +[ + { + "constant": false, + "inputs": [ + { + "internalType": "bytes", + "name": "_initCode", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "_salt", + "type": "bytes32" + } + ], + "name": "deploy", + "outputs": [ + { + "internalType": "address payable", + "name": "createdContract", + "type": "address" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } +] +``` + +## Rationale + +SingletonFactory does not allow sending value on create2, this was done to prevent different results on the created object. +SingletonFactory allows user defined salt to facilitate the creation of vanity addresses for other projects. If vanity address is not necessary, salt `bytes(0)` should be used. +Contracts that are constructed by the SingletonFactory MUST not use `msg.sender` in their constructor, all variables must came through initialization data. This is intentional, as if allowing a callback after creation to aid initialization state would lead to contracts with same address (but different chains) to have the same address but different initial state. +The resulting address can be calculated in chain by any contract using this formula: `address(keccak256(bytes1(0xff), 0xce0042B868300000d44A59004Da54A005ffdcf9f, _salt, keccak256(_code)) << 96)` or in javascript using https://github.com/ethereumjs/ethereumjs-util/blob/master/docs/README.md#const-generateaddress2. + +## Backwards Compatibility + +Does not apply as there are no past versions of Singleton Factory being used. + +## Test Cases + +TBD + +## Implementation + +https://github.com/3esmit/ERC2470 + +## Security Considerations + +Some contracts can possibly not support being deployed on any chain, or require a different address per chain, that can be safely done by using comparison in [EIP-1344] in constructor. +Account contracts are singletons in the point of view of each user, when wallets want to signal what chain id is intended, [EIP-1191] should be used. +Contracts deployed on factory must not use `msg.sender` in constructor, instead use constructor parameters, otherwise the factory would end up being the controller/only owner of those. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). + +[EIP-155]: ./eip-155.md +[EIP-1191]: ./eip-1191.md +[EIP-1344]: ./eip-1344.md +[EIP-1820]: ./eip-1820.md +[EIP-2429]: https://gitlab.com/status-im/docs/EIPs/blob/secret-multisig-recovery/EIPS/eip-2429.md +[Nick's article]: https://medium.com/@weka/how-to-send-ether-to-11-440-people-187e332566b7 +[Nick]: https://github.com/Arachnid/ + diff --git a/EIPS/eip-2474.md b/EIPS/eip-2474.md new file mode 100644 index 0000000..d3ae13e --- /dev/null +++ b/EIPS/eip-2474.md @@ -0,0 +1,62 @@ +--- +eip: 2474 +title: Coinbase calls +author: Ricardo Guilherme Schmidt (@3esmit) +discussions-to: https://ethresear.ch/t/gas-abstraction-non-signed-block-validator-only-procedures/4388/2 +status: Stagnant +type: Standards Track +category: Core +created: 2020-01-19 +--- + +## Simple Summary + +Allow contracts to be called directly by `block.coinbase` (block validator), without a transaction. + +## Abstract + +_In proof-of-work blockchains, validators are known as miners._ + +The validator might want to execute functions directly, without having to sign a transaction. Some examples might be presenting a proof in a contract for an change which also benefits the validator. + +A notable example would be when a validator want to act as an [EIP-1077](./eip-1077.md) Gas Relayer, incentivized to pick up fees from meta transactions. +Without this change, they can do so by signing from any address a `gasPrice = 0` transaction with the gas relayed call. +However this brings an overhead of a signed transaction by validator that does nothing, as `msg.sender` is never used, and there is no gas cost to EVM charge. + +This proposal makes possible to remove this unused ecrecover. + +## Motivation + +In order to reduce the overhead of calls that don't use `msg.sender` and are being called by validator with `tx.gasPrice = 0`. + +## Specification + +The calls to be executed by `block.coinbase` would be included first at block, and would consume normally the gas of block, however they won't pay/cost gas, instead the call logic would pay the validator in other form. + +Would be valid to execute any calls without a transaction by the block coinbase, except when the validator call tries to read `msg.sender`, which would throw an invalid jump. + +Calls included by the validator would have `tx.origin = block.coinbase` and `gas.price = 0` for the rest of call stack, the rest follows as normal calls. + +## Rationale + +TBD + +## Backwards Compatibility + +`tx.origin = block.coinbase` could cause some issues on bad designed contracts, such as using `tx.origin` to validate a signature, an analysis on how contracts use tx.origin might be useful to decide if this is a good choice. + +## Test Cases + +TBD + +## Implementation + +TBD + +## Security Considerations + +TBD + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2477.md b/EIPS/eip-2477.md new file mode 100644 index 0000000..6a76dc9 --- /dev/null +++ b/EIPS/eip-2477.md @@ -0,0 +1,325 @@ +--- +eip: 2477 +title: Token Metadata Integrity +author: Kristijan Sedlak (@xpepermint), William Entriken , Witek Radomski +discussions-to: https://github.com/ethereum/EIPs/issues/2483 +type: Standards Track +category: ERC +status: Stagnant +created: 2020-01-02 +requires: 165, 721, 1155 +--- + +## Simple Summary + +This specification defines a mechanism by which clients may verify that a fetched token metadata document has been delivered without unexpected manipulation. + +This is the Web3 counterpart of the W3C Subresource Integrity (SRI) specification. + +## Abstract + +An interface `ERC2477` with two functions `tokenURIIntegrity` and `tokenURISchemaIntegrity` are specified for smart contracts and a narrative is provided to explain how this improves the integrity of the token metadata documents. + +## Motivation + +Tokens are being used in many applications to represent, trace and provide access to assets off-chain. These assets include in-game digital items in mobile apps, luxury watches and products in our global supply chain, among many other creative uses. + +Several token standards allow attaching metadata to specific tokens using a URI (RFC 3986) and these are supported by the applications mentioned above. These metadata standards are: + +* ERC-721 metadata extension (`ERC721Metadata`) +* ERC-1155 metadata extension (`ERC1155Metadata_URI`) +* ERC-1046 (DRAFT) ERC-20 Metadata Extension + +Although all these standards allow storing the metadata entirely on-chain (using the "data" URI, RFC 2397), or using a content-addressable system (e.g. IPFS's Content IDentifiers [sic]), nearly every implementation we have found is using Uniform Resource Locators (the exception is The Sandbox which uses IPFS URIs). These URLs provide no guarantees of content correctness or immutability. This standard adds such guarantees. + +## Design + +**Approach A:** A token contract may reference metadata by using its URL. This provides no integrity protection because the referenced metadata and/or schema could change at any time if the hosted content is mutable. This is the world before EIP-2477: + +``` +┌───────────────────────┐ ┌────────┐ ┌────────┐ +│ TokenID │──────▶│Metadata│─────▶│ Schema │ +└───────────────────────┘ └────────┘ └────────┘ +``` + +Note: according to the JSON Schema project, a metadata document referencing a schema using a URI in the `$schema` key is a known approach, but it is not standardized. + +**Approach B:** EIP-2477 provides mechanisms to establish integrity for these references. In one approach, there is integrity for the metadata document. Here, the on-chain data includes a hash of the metadata document. The metadata may or may not reference a schema. In this approach, changing the metadata document will require updating on-chain `tokenURIIntegrity`: + +``` +┌───────────────────────┐ ┌────────┐ ┌ ─ ─ ─ ─ +│ TokenID │──────▶│Metadata│─ ─ ─▶ Schema │ +└───────────────────────┘ └────────┘ └ ─ ─ ─ ─ +┌───────────────────────┐ ▲ +│ tokenURIIntegrity │════════════╝ +└───────────────────────┘ +``` + +**Approach C:** In a stronger approach, the schema is referenced by the metadata using an extension to JSON Schema, providing integrity. In this approach, changing the metadata document or the schema will require updating on-chain `tokenURIIntegrity` and the metadata document, additionally changing the schema requires updating the on-chain `tokenURISchemaIntegrity`: + +``` +┌───────────────────────┐ ┌────────┐ ┌────────┐ +│ TokenID │──────▶│Metadata│═════▶│ Schema │ +└───────────────────────┘ └────────┘ └────────┘ +┌───────────────────────┐ ▲ +│ tokenURIIntegrity │════════════╝ +└───────────────────────┘ +``` + +**Approach D:** Equally strong, the metadata can make a normal reference (no integrity protection) to the schema and on-chain data also includes a hash of the schema document. In this approach, changing the metadata document will require updating on-chain `tokenURIIntegrity` and updating the schema document will require updating the `tokenURISchemaIntegrity`: + +``` +┌───────────────────────┐ ┌────────┐ ┌────────┐ +│ TokenID │──────▶│Metadata│─────▶│ Schema │ +└───────────────────────┘ └────────┘ └────────┘ +┌───────────────────────┐ ▲ ▲ +│ tokenURIIntegrity │════════════╝ ║ +└───────────────────────┘ ║ +┌───────────────────────┐ ║ +│tokenURISchemaIntegrity│════════════════════════════╝ +└───────────────────────┘ +``` + +**Approach E:** Lastly, the schema can be referenced with integrity from the metadata and also using on-chain data. In this approach, changing the metadata document or the schema will require updating on-chain `tokenURIIntegrity` and the metadata document, additionally changing the schema requires updating the on-chain `tokenURISchemaIntegrity`: + +``` +┌───────────────────────┐ ┌────────┐ ┌────────┐ +│ TokenID │──────▶│Metadata│═════▶│ Schema │ +└───────────────────────┘ └────────┘ └────────┘ +┌───────────────────────┐ ▲ ▲ +│ tokenURIIntegrity │════════════╝ ║ +└───────────────────────┘ ║ +┌───────────────────────┐ ║ +│tokenURISchemaIntegrity│════════════════════════════╝ +└───────────────────────┘ +``` + +## Specification + +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. + +### Smart contracts + +**Smart contracts implementing the ERC-2477 standard MUST implement the `ERC2477` interface.** + +```solidity +// SPDX-License-Identifier: CC0-1.0 + +pragma solidity ^0.8.7; + +/// @title ERC-2477 Token Metadata Integrity +/// @dev See https://eips.ethereum.org/EIPS/eip-2477 +/// @dev The ERC-165 identifier for this interface is 0x832a7e0e +interface ERC2477 /* is ERC165 */ { + /// @notice Get the cryptographic hash of the specified tokenID's metadata + /// @param tokenId Identifier for a specific token + /// @return digest Bytes returned from the hash algorithm, or "" if not available + /// @return hashAlgorithm The name of the cryptographic hash algorithm, or "" if not available + function tokenURIIntegrity(uint256 tokenId) external view returns(bytes memory digest, string memory hashAlgorithm); + + /// @notice Get the cryptographic hash for the specified tokenID's metadata schema + /// @param tokenId Identifier for a specific token + /// @return digest Bytes returned from the hash algorithm, or "" if not available + /// @return hashAlgorithm The name of the cryptographic hash algorithm, or "" if not available + function tokenURISchemaIntegrity(uint256 tokenId) external view returns(bytes memory digest, string memory hashAlgorithm); +} +``` + +The returned cryptographic hashes correspond to the token's metadata document and that metadata document's schema, respectively. + +For example, with ERC-721 `tokenURIIntegrity(21)` would correspond to `tokenURI(21)`. With ERC-1155, `tokenURIIntegrity(16)` would correspond to `uri(16)`. In both cases, `tokenURISchemaIntegrity(32)` would correspond to the schema of the document matched by `tokenURIIntegrity(32)`. + +**Smart contracts implementing the ERC-2477 standard MUST implement the ERC-165 standard, including the interface identifiers above.** + +Smart contracts implementing the ERC-2477 standard MAY use any hashing or content integrity scheme. + +Smart contracts implementing the ERC-2477 standard MAY use or omit a mechanism to notify when the integrity is updated (e.g. an Ethereum logging operation). + +Smart contracts implementing the ERC-2477 standard MAY use any mechanism to provide schemas for metadata documents and SHOULD use JSON-LD on the metadata document for this purpose (i.e. `"@schema":...`). + +### Metadata + +A metadata document MAY conform to this schema to provide referential integrity to its schema document. + +```json +{ + "title": "EIP-2477 JSON Object With Refererential Integrity to Schema", + "type": "object", + "properties": { + "$schema": { + "type": "string", + "format": "uri" + }, + "$schemaIntegrity": { + "type": "object", + "properties": { + "digest": { + "type": "string" + }, + "hashAlgorithm": { + "type": "string" + } + }, + "required": ["digest", "hashAlgorithm"] + } + }, + "required": ["$schema", "$schemaIntegrity"] +} +``` + +### Clients + +A client implementing the ERC-2477 standard MUST support at least the `sha256` hash algorithm and MAY support other algorithms. + +### Caveats + +* This EIP metadata lists ERC-721 and ERC-1155 as "required" for implementation, due to a technical limitation of EIP metadata. In actuality, this standard is usable with any token implementation that has a `tokenURI(uint id)` or similar function. + +## Rationale + +**Function and parameter naming** + +The W3C Subresource Integrity (SRI) specification uses the attribute "integrity" to perform integrity verification. This ERC-2477 standard provides a similar mechanism and reuses the integrity name so as to be familiar to people that have seen SRI before. + +**Function return tuple** + +The SRI integrity attribute encodes elements of the tuple $$(cryptographic\ hash\ function, digest, options)$$. This ERC-2477 standard returns a digest and hash function name and omits forward-compatibility options. + +Currently, the SRI specification does not make use of options. So we cannot know what format they might be when implemented. This is the motivation to exclude this parameter. + +The digest return value is first, this is an optimization because we expect on-chain implementations will be more likely to use this return value if they will only be using one of the two. + +**Function return types** + +The digest is a byte array and supports various hash lengths. This is consistent with SRI. Whereas SRI uses base64 encoding to target an HTML document, we use a byte array because Ethereum already allows this encoding. + +The hash function name is a string. Currently there is no universal taxonomy of hash function names. SRI recognizes the names `sha256`, `sha384` and `sha512` with case-insensitive matching. We are aware of two authorities which provide taxonomies and canonical names for hash functions: ETSI Object Identifiers and NIST Computer Security Objects Register. However, SRI's approach is easier to follow and we have adopted this here. + +**Function return type — hash length** + +Clients must support the SHA-256 algorithm and may optionally support others. This is a departure from the SRI specification where SHA-256, SHA-384 and SHA-512 are all required. The rationale for this less-secure requirement is because we expect some clients to be on-chain. Currently SHA-256 is simple and cheap to do on Ethereum whereas SHA-384 and SHA-512 are more expensive and cumbersome. + +The most popular hash function size below 256 bits in current use is SHA-1 at 160 bits. Multiple collisions (the "Shattered" PDF file, the 320 byte file, the chosen prefix) have been published and a recipe is given to generate infinitely more collisions. SHA-1 is broken. The United States National Institute of Standards and Technology (NIST) has first deprecated SHA-1 for certain use cases in November 2015 and has later further expanded this deprecation. + +The most popular hash function size above 256 bits in current use is SHA-384 as specified by NIST. + +The United States National Security Agency requires a hash length of 384 or more bits for the SHA-2 (CNSA Suite Factsheet) algorithm suite for use on TOP SECRET networks. (No unclassified documents are currently available to specify use cases at higher classification networks.) + +We suspect that SHA-256 and the 0xcert Asset Certification will be popular choices to secure token metadata for the foreseeable future. + +**In-band signaling** + +One possible way to achieve strong content integrity with the existing token standards would be to include, for example, a `?integrity=XXXXX` at the end of all URLs. This approach is not used by any existing implementations we know about. There are a few reasons we have not chosen this approach. The strongest reason is that the World Wide Web has the same problem and they chose to use the Sub-Resource Integrity approach, which is a separate data field than the URL. + +Other supplementary reasons are: + +* For on-chain consumers of data, it is easier to parse a direct hash field than to perform string operations. + +* Maybe there are some URIs which are not amenable to being modified in that way, therefore limiting the generalizability of that approach. + +This design justification also applies to `tokenURISchemaIntegrity`. The current JSON-LD specification allows a JSON document to link to a schema document. But it does not provide integrity. Rather than changing how JSON-LD works, or changing JSON Schemas, we have the `tokenURISchemaIntegrity` property to just provide the integrity. + +## Backwards Compatibility + +Both ERC-721 and ERC-1155 provide compatible token metadata specifications that use URIs and JSON schemas. The ERC-2477 standard is compatible with both, and all specifications are additive. Therefore, there are no backward compatibility regressions. + +ERC-1523 Standard for Insurance Policies as ERC-721 Non Fungible Tokens (DRAFT) proposes an extension to ERC-721 which also tightens the requirements on metadata. Because it is wholly an extension of ERC-721, ERC-1523 is automatically supported by ERC-2477 (since this standard already supports ERC-721). + +ERC-1046 (DRAFT) ERC-20 Metadata Extension proposes a comparate extension for ERC-20. Such a concept is outside the scope of this ERC-2477 standard. Should ERC-1046 (DRAFT) be finalized, we will welcome a new ERC which copies ERC-2477 and removes the `tokenId` parameter. + +Similarly, ERC-918 (DRAFT) Mineable Token Standard proposes an extension for ERC-20 and also includes metadata. The same comment applies here as ERC-1046. + +## Test Cases + +Following is a token metadata document which is simultaneously compatible with ERC-721, ERC-1155 and ERC-2477 standards. + +```json +{ + "$schema": "https://URL_TO_SCHEMA_DOCUMENT", + "name": "Asset Name", + "description": "Lorem ipsum...", + "image": "https://s3.amazonaws.com/your-bucket/images/{id}.png" +} +``` + +This above example shows how JSON-LD is employed to reference the schema document (`$schema`). + +Following is a corresponding schema document which is accessible using the URI `"https://URL_TO_SCHEMA_DOCUMENT"` above. + +```json +{ + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Identifies the asset to which this NFT represents" + }, + "description": { + "type": "string", + "description": "Describes the asset to which this NFT represents" + }, + "image": { + "type": "string", + "description": "A URI pointing to a resource with mime type image/* representing the asset to which this NFT represents. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive." + } + } +} +``` + +Assume that the metadata and schema above apply to a token with identifier 1234. (In ERC-721 this would be a specific token, in ERC-1155 this would be a token type.) Then these two function calls MAY have the following output: + +* `function tokenURIIntegrity(1234)` + * `bytes digest `: `3fc58b72faff20684f1925fd379907e22e96b660` + * `string hashAlgorithm`: `sha256` +* `function tokenURISchemaIntegrity(1234)` + * `bytes digest `: `ddb61583d82e87502d5ee94e3f2237f864eeff72` + * `string hashAlgorithm`: `sha256` + +To avoid doubt: the previous paragraph specifies "MAY" have that output because other hash functions are also acceptable. + +## Implementation + +0xcert Framework supports ERC-2477. + +## Reference + +Normative standard references + +1. RFC 2119 Key words for use in RFCs to Indicate Requirement Levels. https://www.ietf.org/rfc/rfc2119.txt +2. ERC-165 Standard Interface Detection. ./eip-165.md +3. ERC-721 Non-Fungible Token Standard. ./eip-721.md +4. ERC-1155 Multi Token Standard. ./eip-1155.md +5. JSON-LD. https://www.w3.org/TR/json-ld/ +6. Secure Hash Standard (SHS). https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf + +Other standards + +1. ERC-1046 ERC-20 Metadata Extension (DRAFT). ./eip-1046.md +2. ERC-918 Mineable Token Standard (DRAFT). ./eip-918.md +3. ERC-1523 Standard for Insurance Policies as ERC-721 Non Fungible Tokens (DRAFT). ./eip-1523.md +4. W3C Subresource Integrity (SRI). https://www.w3.org/TR/SRI/ +5. The "data" URL scheme. https://tools.ietf.org/html/rfc2397 +6. Uniform Resource Identifier (URI): Generic Syntax. https://tools.ietf.org/html/rfc3986 +7. CID [Specification] (DRAFT). https://github.com/multiformats/cid + +Discussion + +1. JSON-LD discussion of referential integrity. https://lists.w3.org/Archives/Public/public-json-ld-wg/2020Feb/0003.html +2. JSON Schema use of `$schema` key for documents. https://github.com/json-schema-org/json-schema-spec/issues/647#issuecomment-417362877 + +Other + +1. [0xcert Framework supports ERC-2477]. https://github.com/0xcert/framework/pull/717 +2. [Shattered] The first collision for full SHA-1. https://shattered.io/static/shattered.pdf +3. [320 byte file] The second SHA Collision. https://privacylog.blogspot.com/2019/12/the-second-sha-collision.html +4. [Chosen prefix] https://sha-mbles.github.io +5. Transitions: Recommendation for Transitioning the Use of Cryptographic Algorithms and Key Lengths. (Rev. 1. Superseded.) https://csrc.nist.gov/publications/detail/sp/800-131a/rev-1/archive/2015-11-06 +6. Commercial National Security Algorithm (CNSA) Suite Factsheet. https://apps.nsa.gov/iaarchive/library/ia-guidance/ia-solutions-for-classified/algorithm-guidance/commercial-national-security-algorithm-suite-factsheet.cfm +7. ETSI Assigned ASN.1 Object Identifiers. https://portal.etsi.org/pnns/oidlist +8. Computer Security Objects Register. https://csrc.nist.gov/projects/computer-security-objects-register/algorithm-registration +9. The Sandbox implementation. https://github.com/pixowl/sandbox-smart-contracts/blob/7022ce38f81363b8b75a64e6457f6923d91960d6/src/Asset/ERC1155ERC721.sol + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). + diff --git a/EIPS/eip-2481.md b/EIPS/eip-2481.md new file mode 100644 index 0000000..c8bc379 --- /dev/null +++ b/EIPS/eip-2481.md @@ -0,0 +1,420 @@ +--- +eip: 2481 +title: eth/66 request identifier +description: Introduces a request id for all requests of the eth protocol +author: Christoph Burgdorf (@cburgdorf) +discussions-to: https://ethereum-magicians.org/t/eip-2481-eth-66-request-identifiers/12132 +status: Final +type: Standards Track +category: Networking +created: 2020-01-17 +requires: 2464 +--- + +## Abstract + +The `eth` protocol defines various request and response commands that are used to exchange data between Ethereum nodes. For example, to ask a peer node for a specific set of headers, a node sends it the [`GetBlockHeaders`](https://github.com/ethereum/devp2p/blob/40ab248bf7e017e83cc9812a4e048446709623e8/caps/eth.md#getblockheaders-0x03) command. + +*Citing from the [`GetBlockHeaders` spec definition](https://github.com/ethereum/devp2p/blob/40ab248bf7e017e83cc9812a4e048446709623e8/caps/eth.md#getblockheaders-0x03):* + +>`[block: {P, B_32}, maxHeaders: P, skip: P, reverse: P in {0, 1}]` + +>Require peer to return a `BlockHeaders` message. Reply must contain a number of block +headers, of rising number when `reverse` is `0`, falling when `1`, `skip` blocks apart, +beginning at block `block` (denoted by either number or hash) in the canonical chain, and +with at most `maxHeaders` items. + +The node that receives the `GetBlockHeaders` command should answer it with the [`BlockHeaders`](https://github.com/ethereum/devp2p/blob/40ab248bf7e017e83cc9812a4e048446709623e8/caps/eth.md#blockheaders-0x04) response command accordingly. + +*Citing from the [`BlockHeaders` spec definition](https://github.com/ethereum/devp2p/blob/40ab248bf7e017e83cc9812a4e048446709623e8/caps/eth.md#blockheaders-0x04):* + +>`[blockHeader_0, blockHeader_1, ...]` + +>Reply to `GetBlockHeaders`. The items in the list (following the message ID) are block +headers in the format described in the main Ethereum specification, previously asked for +in a GetBlockHeaders message. This may validly contain no block headers if none of the +requested block headers were found. The number of headers that can be requested in a +single message may be subject to implementation-defined limits. + +Let's consider a client making many simultaneous requests for `GetBlockHeaders` to one of its peers. By nature it can not be guaranteed that the expected responses arrive in the same order as they were sent. For the client to associate the incoming responses to the correct requests it has to loop through all pending requests trying to match it with the incoming response based on its contents. + +This can be particular tricky for responses that are ambiguous such as empty responses. + +This EIP proposes to change the `GetBlockHeaders` and the `BlockHeaders` command to include a `request_id`. + +The `request_id` is a 64-bit integer set by the client when it makes the request. On the responding side, the exact same `request_id` from the incoming request is put back into the response object. + +This change allows the requesting client to match incoming responses **directly** back to their pending requests without going through all of the pending requests to check if they might match based on the response data. + +The selected request/response pair serves as an example for many similar request/response pairs in the `eth` networking protocol. + +## Motivation + +The lack of request identifiers in the request / response paris of the `eth` protocol puts unnecessary burden of code complexity into every Ethereum client. It also makes the communication slightly less efficient. Another argument can be made that the addition of request identifiers makes the protocol more aligned with the `les` protocol which **does** already defines request identifiers for each request / response pair. + +## Specification + +Change the following message types in the `eth` protocol: + +* `GetBlockHeaders (0x03)` + * **Current (eth/65):** `[block: {P, B_32}, maxHeaders: P, skip: P, reverse: P in {0, 1}]` + * **Then (eth/66)**: `[request_id: P, [block: {P, B_32}, maxHeaders: P, skip: P, reverse: P in {0, 1}]]` +* `BlockHeaders (0x04)` + * **Current (eth/65):** `[blockHeader_0, blockHeader_1, ...]` + * **Then (eth/66)**: `[request_id: P, [blockHeader_0, blockHeader_1, ...]]` +* `GetBlockBodies (0x05)` + * **Current (eth/65):** `[hash_0: B_32, hash_1: B_32, ...]` + * **Then (eth/66)**: `[request_id: P, [hash_0: B_32, hash_1: B_32, ...]]` +* `GetPooledTransactions (0x09)`: + * **Current (eth/65)**`[hash_0: B_32, hash_1: B_32, ...]` + * **Then (eth/66)**`[request_id: P, [hash_0: B_32, hash_1: B_32, ...]]` +* `PooledTransactions (0x0a)`: + * **Current (eth/65)**`[[nonce: P, receivingAddress: B_20, value: P, ...], ...]` + * **Then (eth/66)**`[request_id: P, [[nonce: P, receivingAddress: B_20, value: P, ...], ...]]` +* `BlockBodies (0x06)` + * **Current (eth/65):** `[hash_0: B_32, hash_1: B_32, ...]` + * **Then (eth/66)**: `[request_id: P, [hash_0: B_32, hash_1: B_32, ...]]` +* `GetNodeData (0x0d)` + * **Current (eth/65):** `[hash_0: B_32, hash_1: B_32, ...]` + * **Then (eth/66)**: `[request_id: P, [hash_0: B_32, hash_1: B_32, ...]]` +* `NodeData (0x0e)` + * **Current (eth/65):** `[value_0: B, value_1: B, ...]` + * **Then (eth/66)**: `[request_id: P, [value_0: B, value_1: B, ...]]` +* `GetReceipts (0x0f)` + * **Current (eth/65):** `[blockHash_0: B_32, blockHash_1: B_32, ...]` + * **Then (eth/66)**: `[request_id: P, [blockHash_0: B_32, blockHash_1: B_32, ...]]` +* `Receipts (0x10)` + * **Current (eth/65):** `[[receipt_0, receipt_1], ...]` + * **Then (eth/66)**: `[request_id: P, [[receipt_0, receipt_1], ...]]` + + +To elaborate, each command is altered in the following way: + +1. Create a list with the `request_id` being the first element. +2. Make the second element the list that defines the whole command in the current scheme. + +The ``request_id`` has the following characteristics: + +* 64 bit integer +* Doesn't need to be sequential (can be random) +* Does allow duplicates + +## Rationale + +**Q: The efficiency gains might encourage clients to flood their peers with too many simultaneous requests** + +Peers can always throttle or disconnect if they don't feel treated well. This is the same as today. + +**Q: If `les` already defines the commands like this, why not just use the `les` protocol?** + +In practice, peers that serve the `les` protocol are much harder to find in the network. The reasons for this are varied but might boil down to client defaults, immature implementations or missing incentives. + +**Q: Networking works today, isn't this just adding bloat?** + +This is adding a single integer per command while at the same time reducing code complexity and improving networking efficiency. The addition seems justified. + +**Q: Why not demand request ids to be sequential?** + +Assuming request ids start always to count from `0` upon connection, things will become messy when +connections are lost and clients reconnect and start over with the same request ids that they had used +in the previous session. + +**Q: Why allow duplicate request ids?** + +The main benefit is flexibility and simplicity on the implementation side. Clients could decide to share +the same ids across multiple different request types since they are naturally separated anyway. Clients +could even decide to not rely on request ids at all, therefore using the same constant request id across +all requests. + +**Q: Why choose a 64-bit integer for the request ids** + +64-bit integer were chosen to keep compatibility with the `les` protocol. + +## Backwards Compatibility + +This EIP extends the `eth` protocol in a backwards incompatible way and requires rolling out a new version, `eth/66`. However, `devp2p` supports running multiple versions of the same wire protocol side-by-side, so rolling out `eth/66` does not require client coordination, since non-updated clients can keep using `eth/65`. + +This EIP does not change the consensus engine, thus does *not* require a hard fork. + +## Test Cases + +These testcases cover RLP-encoding of all the redefined messages types, where the `rlp` portion is the rlp-encoding of the message defined in the `data` portion. + + +```json +{ + "type": "GetBlockHeadersPacket66", + "rlp": "0xe8820457e4a000000000000000000000000000000000000000000000000000000000deadc0de050580", + "data": { + "RequestId": 1111, + "Origin": { + "Hash": "0x00000000000000000000000000000000000000000000000000000000deadc0de", + "Number": 0 + }, + "Amount": 5, + "Skip": 5, + "Reverse": false + } +} +``` + +```json +{ + "type": "GetBlockHeadersPacket66", + "rlp": "0xca820457c682270f050580", + "data": { + "RequestId": 1111, + "Origin": { + "Hash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "Number": 9999 + }, + "Amount": 5, + "Skip": 5, + "Reverse": false + } +} +``` + +```json +{ + "type": "BlockHeadersPacket66", + "rlp": "0xf90202820457f901fcf901f9a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008208ae820d0582115c8215b3821a0a827788a00000000000000000000000000000000000000000000000000000000000000000880000000000000000", + "data": { + "RequestId": 1111, + "BlockHeadersPacket": [ + { + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "sha3Uncles": "0x0000000000000000000000000000000000000000000000000000000000000000", + "miner": "0x0000000000000000000000000000000000000000", + "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "transactionsRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "receiptsRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "difficulty": "0x8ae", + "number": "0xd05", + "gasLimit": "0x115c", + "gasUsed": "0x15b3", + "timestamp": "0x1a0a", + "extraData": "0x7788", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0000000000000000", + "hash": "0x8c2f2af15b7b563b6ab1e09bed0e9caade7ed730aec98b70a993597a797579a9" + } + ] + } +} +``` + +```json +{ + "type": "GetBlockBodiesPacket66", + "rlp": "0xf847820457f842a000000000000000000000000000000000000000000000000000000000deadc0dea000000000000000000000000000000000000000000000000000000000feedbeef", + "data": { + "RequestId": 1111, + "GetBlockBodiesPacket": [ + "0x00000000000000000000000000000000000000000000000000000000deadc0de", + "0x00000000000000000000000000000000000000000000000000000000feedbeef" + ] + } +} +``` + +```json +{ + "type": "BlockBodiesPacket66", + "rlp": "0xf902dc820457f902d6f902d3f8d2f867088504a817c8088302e2489435353535353535353535353535353535353535358202008025a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10f867098504a817c809830334509435353535353535353535353535353535353535358202d98025a052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afba052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afbf901fcf901f9a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008208ae820d0582115c8215b3821a0a827788a00000000000000000000000000000000000000000000000000000000000000000880000000000000000", + "data": { + "RequestId": 1111, + "BlockBodiesPacket": [ + { + "Transactions": [ + { + "nonce": "0x8", + "gasPrice": "0x4a817c808", + "gas": "0x2e248", + "to": "0x3535353535353535353535353535353535353535", + "value": "0x200", + "input": "0x", + "v": "0x25", + "r": "0x64b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12", + "s": "0x64b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10", + "hash": "0x588df025c4c2d757d3e314bd3dfbfe352687324e6b8557ad1731585e96928aed" + }, + { + "nonce": "0x9", + "gasPrice": "0x4a817c809", + "gas": "0x33450", + "to": "0x3535353535353535353535353535353535353535", + "value": "0x2d9", + "input": "0x", + "v": "0x25", + "r": "0x52f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb", + "s": "0x52f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb", + "hash": "0xf39c7dac06a9f3abf09faf5e30439a349d3717611b3ed337cd52b0d192bc72da" + } + ], + "Uncles": [ + { + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "sha3Uncles": "0x0000000000000000000000000000000000000000000000000000000000000000", + "miner": "0x0000000000000000000000000000000000000000", + "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "transactionsRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "receiptsRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "difficulty": "0x8ae", + "number": "0xd05", + "gasLimit": "0x115c", + "gasUsed": "0x15b3", + "timestamp": "0x1a0a", + "extraData": "0x7788", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0000000000000000", + "hash": "0x8c2f2af15b7b563b6ab1e09bed0e9caade7ed730aec98b70a993597a797579a9" + } + ] + } + ] + } +} +``` + +```json +{ + "type": "GetNodeDataPacket66", + "rlp": "0xf847820457f842a000000000000000000000000000000000000000000000000000000000deadc0dea000000000000000000000000000000000000000000000000000000000feedbeef", + "data": { + "RequestId": 1111, + "GetNodeDataPacket": [ + "0x00000000000000000000000000000000000000000000000000000000deadc0de", + "0x00000000000000000000000000000000000000000000000000000000feedbeef" + ] + } +} +``` + +```json +{ + "type": "NodeDataPacket66", + "rlp": "0xce820457ca84deadc0de84feedbeef", + "data": { + "RequestId": 1111, + "NodeDataPacket": [ + "0xdeadc0de", + "0xfeedbeef" + ] + } +} +``` + +```json +{ + "type": "GetReceiptsPacket66", + "rlp": "0xf847820457f842a000000000000000000000000000000000000000000000000000000000deadc0dea000000000000000000000000000000000000000000000000000000000feedbeef", + "data": { + "RequestId": 1111, + "GetReceiptsPacket": [ + "0x00000000000000000000000000000000000000000000000000000000deadc0de", + "0x00000000000000000000000000000000000000000000000000000000feedbeef" + ] + } +} +``` + +```json +{ + "type": "ReceiptsPacket66", + "rlp": "0xf90172820457f9016cf90169f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff", + "data": { + "RequestId": 1111, + "ReceiptsPacket": [ + [ + { + "root": "0x", + "status": "0x0", + "cumulativeGasUsed": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "logs": [ + { + "address": "0x0000000000000000000000000000000000000011", + "topics": [ + "0x000000000000000000000000000000000000000000000000000000000000dead", + "0x000000000000000000000000000000000000000000000000000000000000beef" + ], + "data": "0x0100ff", + "blockNumber": "0x0", + "transactionHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "transactionIndex": "0x0", + "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "logIndex": "0x0", + "removed": false + } + ], + "transactionHash": "0x00000000000000000000000000000000000000000000000000000000deadc0de", + "contractAddress": "0x0000000000000000000000000000000000011111", + "gasUsed": "0x1b207", + "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "transactionIndex": "0x0" + } + ] + ] + } +} +``` + +```json +{ + "type": "GetPooledTransactionsPacket66", + "rlp": "0xf847820457f842a000000000000000000000000000000000000000000000000000000000deadc0dea000000000000000000000000000000000000000000000000000000000feedbeef", + "data": { + "RequestId": 1111, + "GetPooledTransactionsPacket": [ + "0x00000000000000000000000000000000000000000000000000000000deadc0de", + "0x00000000000000000000000000000000000000000000000000000000feedbeef" + ] + } +} +``` + +```json +{ + "type": "PooledTransactionsPacket66", + "rlp": "0xf8d7820457f8d2f867088504a817c8088302e2489435353535353535353535353535353535353535358202008025a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10f867098504a817c809830334509435353535353535353535353535353535353535358202d98025a052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afba052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb", + "data": { + "RequestId": 1111, + "PooledTransactionsPacket": [ + { + "nonce": "0x8", + "gasPrice": "0x4a817c808", + "gas": "0x2e248", + "to": "0x3535353535353535353535353535353535353535", + "value": "0x200", + "input": "0x", + "v": "0x25", + "r": "0x64b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12", + "s": "0x64b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10", + "hash": "0x588df025c4c2d757d3e314bd3dfbfe352687324e6b8557ad1731585e96928aed" + }, + { + "nonce": "0x9", + "gasPrice": "0x4a817c809", + "gas": "0x33450", + "to": "0x3535353535353535353535353535353535353535", + "value": "0x2d9", + "input": "0x", + "v": "0x25", + "r": "0x52f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb", + "s": "0x52f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb", + "hash": "0xf39c7dac06a9f3abf09faf5e30439a349d3717611b3ed337cd52b0d192bc72da" + } + ] + } +} +``` + +## Security Considerations + +None + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2488.md b/EIPS/eip-2488.md new file mode 100644 index 0000000..5af41eb --- /dev/null +++ b/EIPS/eip-2488.md @@ -0,0 +1,58 @@ +--- +eip: 2488 +title: Deprecate the CALLCODE opcode +author: Alex Beregszaszi (@axic) +discussions-to: https://ethereum-magicians.org/t/eip-2488-deprecate-the-callcode-opcode/3957 +status: Stagnant +type: Standards Track +category: Core +created: 2019-12-20 +requires: 7 +--- + +## Abstract + +Deprecate `CALLCODE` in a *somewhat* backwards compatible way, by making it always return failure. + +## Motivation + +`CALLCODE` was part of the Frontier release of Ethereum. In the first few weeks/months it became clear +that it cannot accomplish its intended design goal. This was rectified with introducing `DELEGATECALL` +([EIP-7](./eip-7.md)) in the Homestead update (early 2016). + +`CALLCODE` became never utilized, but it still puts a burden on EVM implementations. + +Disabling it will not improve the situation for any client whose goal is to sync from genesis, but would +help light clients or clients planning to sync from a later point in time. + +## Specification + +If `block.number >= FORK_BLOCK`, the `CALLCODE` (`0xf2`) instruction always returns `0`, which signals failure. + +## Rationale + +It would be possible just to remove the opcode and exceptionally abort if it is encountered. +However, by returning failure, the contract has a chance to act on it and potentially recover. + +## Backwards Compatibility + +This is a breaking change and has a potential to break contracts. The author expects no contracts of any value +should be affected. + +TODO: validate this claim. + +## Security Considerations + +TBA + +## Test Cases + +TBA + +## Implementation + +TBA + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2494.md b/EIPS/eip-2494.md new file mode 100644 index 0000000..0fa1a28 --- /dev/null +++ b/EIPS/eip-2494.md @@ -0,0 +1,382 @@ +--- +eip: 2494 +title: Baby Jubjub Elliptic Curve +author: Barry WhiteHat (@barryWhiteHat), Marta Bellés (@bellesmarta), Jordi Baylina (@jbaylina) +discussions-to: https://ethereum-magicians.org/t/eip-2494-baby-jubjub-elliptic-curve/3968 +status: Stagnant +type: Standards Track +category: ERC +created: 2020-01-29 +--- + +## Simple Summary + +This proposal defines Baby Jubjub, an elliptic curve designed to work inside zk-SNARK circuits in Ethereum. + +## Abstract + +Two of the main issues behind why blockchain technology is not broadly used by individuals and industry are scalability and privacy guarantees. With a set of cryptographic tools called zero-knowledge proofs (ZKP) it is possible to address both of these problems. More specifically, the most suitable protocols for blockchain are called zk-SNARKs (zero-knowledge Succinct Non-interactive ARguments of Knowledge), as they are non-interactive, have succinct proof size and sublinear verification time. These types of protocols allow proving generic computational statements that can be modelled with arithmetic circuits defined over a finite field (also called zk-SNARK circuits). + +To verify a zk-SNARK proof, it is necessary to use an elliptic curve. In Ethereum, the curve is alt_bn128 (also referred as BN254), which has primer order `r`. With this curve, it is possible to generate and validate proofs of any `F_r`-arithmetic circuit. This EIP describes *Baby Jubjub*, an elliptic curve defined over the finite field `F_r` which can be used inside any zk-SNARK circuit, allowing for the implementation of cryptographic primitives that make use of elliptic curves, such as the Pedersen Hash or the Edwards Digital Signature Algorithm (EdDSA). + +## Motivation + +A [zero knowledge proof](https://en.wikipedia.org/wiki/Zero-knowledge_proof) (ZKP) is a protocol that enables one party, the prover, to convince another, the verifier, that a statement is true without revealing any information beyond the veracity of the statement. [Non-Interactive ZKPs](https://people.csail.mit.edu/silvio/Selected%20Scientific%20Papers/Zero%20Knowledge/Noninteractive_Zero-Knowkedge.pdf) (NIZK) are a particular type of zero-knowledge proofs in which the prover can generate the proof without interaction with the verifier. NIZK protocols are very suitable for Ethereum applications, because they allow a smart contract to act as a verifier. This way, anyone can generate a proof and send it as part of a transaction to the smart contract, which can perform some action depending on whether the proof is valid or not. In this context, the most preferable NIZK are [zk-SNARK](https://eprint.iacr.org/2013/279.pdf) (Zero-knowledge Succinct Non Interactive ARgument of Knowledge), a set of non-interactive zero-knowledge protocols that have succinct proof size and sublinear verification time. The importance of these protocols is double: on the one hand, they help improve privacy guarantees, and on the other, they are a possible solution to scalability issues (e.g. see [zk-Rollup](https://github.com/barryWhiteHat/roll_up) project). + +Like most ZKPs, zk-SNARKs permit proving computational statements. For example, one can prove things like: the knowledge of a private key associated with a certain public key, the correct computation of a transaction, or the knowledge of the preimage of a particular hash. Importantly, one can do these things without leaking any information about the statements in question. In other words, without leaking any information about the private key, the transaction details, or the value of the preimage. More specifically, zk-SNARKs permit proving any computational statement that can be modelled with an `F_r`-arithmetic circuit, a circuit consisting of set of wires that carry values from the field `F_r` and connect them to addition and multiplication gates `mod r`. This type of circuits are often called zk-SNARK circuits. + +The implementation of most zk-SNARK protocols (e.g. [[Pinnochio]](https://eprint.iacr.org/2013/279.pdf) and [[Groth16]](https://eprint.iacr.org/2016/260.pdf)) make use of an elliptic curve for validating a proof. In Ethereum, the curve used is alt_bn128 (also referred as BN254), which has prime order `r`. While it is possible to generate and validate proofs of `F_r`-arithmetic circuits with BN254, it is not possible to use BN254 to implement elliptic-curve cryptography within these circuits. To implement functions that require the use of elliptic curves inside a zk-SNARK circuit -- such as the [Pedersen Hash](https://github.com/zcash/zips/blob/master/protocol/protocol.pdf) or the [Edwards Digital Signature Algorithm](https://tools.ietf.org/html/rfc8032) (EdDSA) -- a new curve with coordinates in `F_r` is needed. To this end, we propose in this EIP *Baby Jubjub*, an elliptic curve defined over `F_r` that can be used inside any `F_r`-arithmetic circuit. In the next sections we describe in detail the characteristics of the curve, how it was generated, and which security considerations were taken. + +``` + inputs zk-SNARK (alt_bn128) output + +--------------------------------------------+ + | +--------------------+ | + --->| | EdDSA (Baby Jubjub)| | + | +--------------------+ | + --->| |---> + | +-----------------------------+ | + --->| | Pedersen Hash (Baby Jubjub) | | + | +-----------------------------+ | + +--------------------------------------------+ +``` + +## Specification + +### Definitions +Let `F_r` be the prime finite field with `r` elements, where +``` +r = 21888242871839275222246405745257275088548364400416034343698204186575808495617 +``` + +Let `E` be the twisted Edwards elliptic curve defined over `F_r` described by equation +``` +ax^2 + y^2 = 1 + dx^2y^2 +``` +with parameters +``` +a = 168700 +d = 168696 +``` +We call **Baby Jubjub** the curve `E(F_r)`, that is, the subgroup of `F_r`-rational points of `E`. + +### Order + +Baby Jubjub has order + +``` +n = 21888242871839275222246405745257275088614511777268538073601725287587578984328 +``` + +which factors in +``` +n = h x l +``` +where +``` +h = 8 +l = 2736030358979909402780800718157159386076813972158567259200215660948447373041 +``` +The parameter `h` is called *cofactor* and `l` is a prime number of 251 bits. + +### Generator Point + +The point `G = (x,y)` with coordinates +``` +x = 995203441582195749578291179787384436505546430278305826713579947235728471134 +y = 5472060717959818805561601436314318772137091100104008585924551046643952123905 +``` +generates all `n` points of the curve. + +### Base Point + +The point `B = (x,y)` with coordinates + +``` +x = 5299619240641551281634865583518297030282874472190772894086521144482721001553 +y = 16950150798460657717958625567821834550301663161624707787222815936182638968203 +``` +generates the subgroup of points `P` of Baby Jubjub satisfying `l * P = O`. That is, it generates the set of points of order `l` and origin `O`. + +### Arithmetic + +Let `P1 = (x1, y1)` and `P2 = (x2, y2)` be two arbitrary points of Baby Jubjub. Then `P1 + P2 = (x3, y3)` is calculated in the following way: +``` +x3 = (x1*y2 + y1*x2)/(1 + d*x1*x2*y1*y2) +y3 = (y1*y2 - a*x1*x2)/(1 - d*x1*x2*y1*y2) +``` +Note that both addition and doubling of points can be computed using a single formula. + +## Rationale + +The search for Baby Jubjub was motivated by the need for an elliptic curve that allows the implementation of elliptic-curve cryptography in `F_r`-arithmetic circuits. The curve choice was based on three main factors: type of curve, generation process and security criteria. This section describes how these factors were addressed. + +**Form of the Curve** + +Baby Jubjub is a **twisted Edwards** curve birationally equivalent to a **Montgomery** curve. The choice of this form of curve was based on the following facts: +1. The Edwards-curve Digital Signature Scheme is based on twisted Edwards curves. +2. Twisted Edwards curves have a single complete formula for addition of points, which makes the implementation of the group law inside circuits very efficient [[Crypto08/013, Section 6]](https://eprint.iacr.org/2008/013.pdf). +3. As a twisted Edwards curve is generally birationally equivalent to a Montgomery curve [[Crypto08/13,Theorem 3.2]](https://eprint.iacr.org/2008/013.pdf), the curve can be easily converted from one form to another. As addition and doubling of points in a Montgomery curve can be performed very efficiently, computations outside the circuit can be done faster using this form and sped up inside circuits by combining it with twisted Edwards form (see [here](http://hyperelliptic.org/EFD/g1p/index.html)) for more details). + +**Generation of the Curve** + +Baby Jubjub was conceived as a solution to the circuit implementation of cryptographic schemes that require elliptic curves. As with any cryptographic protocol, it is important to reduce the possibility of a backdoor being present. As a result, we designed the generation process to be **transparent** and **deterministic** -- in order to make it clear that no external considerations were taken into account, and to ensure that the process can be reproduced and followed by anyone who wishes to do so. + +The algorithm chosen for generating Baby Jubjub is based in the criteria defined in [[RFC7748, Appendix A.1]](https://tools.ietf.org/html/rfc7748) and can be found in [this github repository](https://github.com/barryWhiteHat/baby_jubjub). Essentially, the algorithm takes a prime number `p = 1 mod 4` and returns the lowest `A>0` such that `A-2` is a multiple of 4 and such that the set of solutions in `F_p` of `y^2 = x^3 + Ax^2 + x` defines a Montgomery curve with cofactor 8. + +Baby Jubjub was generated by running the algorithm with the prime + +`r = 21888242871839275222246405745257275088548364400416034343698204186575808495617`, + +which is the order of alt_bn128, the curve used to verify zk-SNARK proofs in Ethereum. The output of the algorithm was `A=168698`. Afterwards, the corresponding Montgomery curve was transformed into twisted Edwards form. Using SAGE libraries for curves, the order `n` of the curve and its factorization `n = 8*l` was calculated. + +- **Choice of generator** : the generator point `G` is the point of order `n` with smallest positive `x`-coordinate in `F_r`. +- **Choice of base point**: the base point `B` is chosen to be `B = 8*G`, which has order `l`. + +**Security Criteria** + +It is crucial that Baby Jubjub be safe against well-known attacks. To that end, we decided that the curve should pass [SafeCurves](https://safecurves.cr.yp.to/) security tests, as they are known for gathering the best known attacks against elliptic curves. Supporting evidence that Baby Jubjub satisfies the SafeCurves criteria can be found [here](https://github.com/barryWhiteHat/baby_jubjub). + + +## Backwards Compatibility + +Baby Jubjub is a twisted Edwards elliptic curve birational to different curves. So far, the curve has mainly been used in its original form, in Montomgery form, and in another (different representation) twisted Edwards form -- which we call the reduced twisted Edwards form. + +Below are the three representations and the birational maps that make it possible to map points from one form of the curve to another. In all cases, the generator and base points are written in the form **`(x,y)`.** + +### Forms of the Curve + +All generators and base points are written in the form (x,y). + +**Twisted Edwards Form** (standard) + +- Equation: ``ax^2 + y^2 = 1 + dx^2y^2`` +- Parameters: ``a = 168700, d = 168696`` +- Generator point: + ``` + (995203441582195749578291179787384436505546430278305826713579947235728471134, 5472060717959818805561601436314318772137091100104008585924551046643952123905) + ``` +- Base point: + ``` + (5299619240641551281634865583518297030282874472190772894086521144482721001553, 16950150798460657717958625567821834550301663161624707787222815936182638968203) + ``` + +**Montgomery Form** + +- Equation: ``By^2 = x^3 + A x^2 + x`` +- Parameters: ``A = 168698, B = 1`` +- Generator point: + ``` + (7, 4258727773875940690362607550498304598101071202821725296872974770776423442226) + ``` +- Base point: + ``` + (7117928050407583618111176421555214756675765419608405867398403713213306743542, 14577268218881899420966779687690205425227431577728659819975198491127179315626) + ``` + +**Reduced Twisted Edwards Form** + +- Equation: ``a' x^2 + y^2 = 1 + d' x^2y^2`` +- Parameters: + ``` + a' = -1 + d' = 12181644023421730124874158521699555681764249180949974110617291017600649128846 + ``` +- Generator point: + ``` + (4986949742063700372957640167352107234059678269330781000560194578601267663727, 5472060717959818805561601436314318772137091100104008585924551046643952123905) + ``` +- Base point: + ``` + (9671717474070082183213120605117400219616337014328744928644933853176787189663, 16950150798460657717958625567821834550301663161624707787222815936182638968203) + ``` + +### Conversion of Points + +Following formulas allow to convert points from one form of the curve to another. We will denote the coordinates + +* ``(u, v)`` for points in the Montomgery form, +* ``(x, y)`` for points in the Twisted Edwards form and +* ``(x', y')`` for points in reduced Twisted Edwards form. + +Note that in the last conversion -- from Twisted Edwards to Reduced Twisted Edwards and back -- we also use the scaling factor `f`, where: +``` +f = 6360561867910373094066688120553762416144456282423235903351243436111059670888 +``` +In the expressions one can also use directly `-f`, where: +``` +-f = 15527681003928902128179717624703512672403908117992798440346960750464748824729 +``` + +**Montgomery --> Twisted Edwards** +``` +(u, v) --> (x, y) + +x = u/v +y = (u-1)/(u+1) +``` + +**Twisted Edwards --> Montgomery** +``` +(x, y) --> (u, v) + +u = (1+y)/(1-y) +v = (1+y)/((1-y)x) +``` + +**Montgomery --> Reduced Twisted Edwards** +``` +(u, v) --> (x', y') + +x' = u*(-f)/v +y' = (u-1)/(u+1) +``` + +**Reduced Twisted Edwards --> Montgomery** +``` +(x', y') --> (u, v) + +u = (1+y')/(1-y') +v = (-f)*(1+y')/((1-y')*x') +``` + +**Twisted Edwards --> Reduced Twisted Edwards** +``` +(x, y) --> (x', y') + +x' = x*(-f) +y' = y +``` + +**Reduced Twisted Edwards --> Twisted Edwards** +``` +(x', y') --> (x, y) + +x = x'/(-f) +y = y' +``` +## Security Considerations + +This section specifies the safety checks done on Baby Jubjub. The choices of security parameters are based on [SafeCurves criteria](https://safecurves.cr.yp.to), and supporting evidence that Baby Jubjub satisfies the following requisites can be found [here](https://github.com/barryWhiteHat/baby_jubjub). + +**Curve Parameters** + +Check that all parameters in the specification of the curve describe a well-defined elliptic curve over a prime finite field. + +- The number `r` is prime. +- Parameters `a` and `d` define an equation that corresponds to an elliptic curve. +- The product of `h` and `l` results into the order of the curve and the `G` point is a generator. +- The number `l` is prime and the `B` point has order `l`. + +**Elliptic Curve Discrete Logarithm Problem** + +Check that the discrete logarithm problem remains difficult in the given curve. We checked Baby Jubjub is resistant to the following known attacks. + +- *Rho method* [[Blake-Seroussi-Smart, Section V.1]](https://www.cambridge.org/core/books/elliptic-curves-in-cryptography/16A2B60636EFA7EBCC3D5A5D01F28546): we require the cost for the rho method, which takes on average around `0.886*sqrt(l)` additions, to be above `2^100`. +- *Additive and multiplicative transfers* [[Blake-Seroussi-Smart, Section V.2]](https://www.cambridge.org/core/books/elliptic-curves-in-cryptography/16A2B60636EFA7EBCC3D5A5D01F28546): we require the embedding degree to be at least `(l − 1)/100`. +- *High discriminant* [[Blake-Seroussi-Smart, Section IX.3]](https://www.cambridge.org/core/books/elliptic-curves-in-cryptography/16A2B60636EFA7EBCC3D5A5D01F28546): we require the complex-multiplication field discriminant `D` to be larger than `2^100`. + +**Elliptic Curve Cryptography** + +- *Ladders* [[Montgomery]](https://wstein.org/edu/Fall2001/124/misc/montgomery.pdf): check the curve supports the Montgomery ladder. +- *Twists* [[SafeCurves, twist]](https://safecurves.cr.yp.to/twist.html): check it is secure against the small-subgroup attack, invalid-curve attacks and twisted-attacks. +- *Completeness* [[SafeCurves, complete]](https://safecurves.cr.yp.to/complete.html): check if the curve has complete single-scalar and multiple-scalar formulas. +- *Indistinguishability* [[IACR2013/325]](https://eprint.iacr.org/2013/325): check availability of maps that turn elliptic-curve points indistinguishable from uniform random strings. + +## Test Cases + +**Test 1 (Addition)** + +Consider the points ``P1 = (x1, y1)`` and ``P2 = (x2, y2)`` with the following coordinates: +``` +x1 = 17777552123799933955779906779655732241715742912184938656739573121738514868268 +y1 = 2626589144620713026669568689430873010625803728049924121243784502389097019475 + +x2 = 16540640123574156134436876038791482806971768689494387082833631921987005038935 +y2 = 20819045374670962167435360035096875258406992893633759881276124905556507972311 +``` +Then their sum `` P1+P2 = (x3, y3)`` is equal to: +``` +x3 = 7916061937171219682591368294088513039687205273691143098332585753343424131937 +y3 = 14035240266687799601661095864649209771790948434046947201833777492504781204499 +``` + +**Test 2 (Doubling)** + +Consider the points ``P1 = (x1, y1)`` and ``P2 = (x2, y2)`` with the following coordinates: +``` +x1 = 17777552123799933955779906779655732241715742912184938656739573121738514868268, +y1 = 2626589144620713026669568689430873010625803728049924121243784502389097019475 + +x2 = 17777552123799933955779906779655732241715742912184938656739573121738514868268 +y2 = 2626589144620713026669568689430873010625803728049924121243784502389097019475 +``` +Then their sum `` P1+P2 = (x3, y3)`` is equal to: +``` +x3 = 6890855772600357754907169075114257697580319025794532037257385534741338397365 +y3 = 4338620300185947561074059802482547481416142213883829469920100239455078257889 +``` + +**Test 3 (Doubling the identity)** + +Consider the points ``P1 = (x1, y1)`` and ``P2 = (x2, y2)`` with the following coordinates: +``` +x1 = 0 +y1 = 1 + +x2 = 0 +y2 = 1 +``` +Then their sum `` P1+P2 = (x3, y3)`` results in the same point: +``` +x3 = 0 +y3 = 1 +``` + +**Test 4 (Curve membership)** + +Point ``(0,1)`` is a point on Baby Jubjub. + +Point ``(1,0)`` is not a point on Baby Jubjub. + +**Test 5 (Base point choice)** + +Check that the base point `` B = (Bx, By)`` with coordinates + +``` +Bx = 5299619240641551281634865583518297030282874472190772894086521144482721001553 +By = 16950150798460657717958625567821834550301663161624707787222815936182638968203 +``` +is 8 times the generator point ``G = (Gx, Gy)``, where +``` +Gx = 995203441582195749578291179787384436505546430278305826713579947235728471134 +Gy = 5472060717959818805561601436314318772137091100104008585924551046643952123905 +``` +That is, check that ``B = 8 x G``. + +**Test 6 (Base point order)** + +Check that the base point `` B = (Bx, By)`` with coordinates + +``` +Bx = 5299619240641551281634865583518297030282874472190772894086521144482721001553 +By = 16950150798460657717958625567821834550301663161624707787222815936182638968203 +``` +multiplied by `l`, where +``` +l = 2736030358979909402780800718157159386076813972158567259200215660948447373041 +``` +results in the origin point `O = (0, 1)`. This test checks that the base point `B` has order `l`. + +## Implementation + +Arithmetic of Baby Jubjub and some cryptographic primitives using the curve have already been implemented in different languages. Here are a few such implementations: + +- Python: https://github.com/barryWhiteHat/baby_jubjub_ecc +- JavaScript: https://github.com/iden3/circomlib/blob/master/src/babyjub.js +- Circuit (circom): https://github.com/iden3/circomlib/blob/master/circuits/babyjub.circom +- Rust: https://github.com/arnaucube/babyjubjub-rs +- Solidity: https://github.com/yondonfu/sol-baby-jubjub +- Go: https://github.com/iden3/go-iden3-crypto/tree/master/babyjub + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2515.md b/EIPS/eip-2515.md new file mode 100644 index 0000000..7f60b3f --- /dev/null +++ b/EIPS/eip-2515.md @@ -0,0 +1,88 @@ +--- +eip: 2515 +title: Implement Difficulty Freeze +author: James Hancock (@madeoftin) +discussions-to: https://ethereum-magicians.org/t/eip-2515-replace-the-difficulty-bomb-with-a-difficulty-freeze/3995 +status: Stagnant +type: Standards Track +category: Core +created: 2020-02-10 +--- + + +## Simple Summary +The difficulty Freeze is an alternative to the Difficulty Bomb that is implemented within the protocols difficulty adjustment algorithm. The Difficulty Freeze begins at a certain block height, determined in advance, freezes the difficulty and increases by 1% after that block forever. This does not stop the chain, but it incentivizes devs to upgrade at a regular cadence and requires any chain split to address the difficulty freeze. + +## Abstract +The difficulty Freeze is a mechanism that is easy to predict and model, and the pressures of missing it are more readily felt by the core developers and client maintainers. The client maintainers are also positioned as the group that is most able to respond to an incoming Difficulty Freeze. This combined with the predictability is more likely to lead to the timely diffusual of the bomb. + + +## Motivation +The current difficulty bombs' effect on the Block Time Targeting mechanism is rather complex to model, and it has both appeared when it was not expected (Muir Glacier) and negatively affected miners when they are not the target (in the case of delaying forks due to technical difficulties). Miners are affected by a reduction in block rewards due to the increase in block time. Users are affected as a function of the usability of the chain is affected by increased block times. Both of these groups are unable on their own to address the difficulty bomb. In the case of the Difficulty Freeze, the consequences of missing it are more directly felt by the client maintainers and it is more predictiable and so knowing when to make the change is readily apparent. + +## Specification + +Add variable `DIFFICULTY_FREEZE_HEIGHT` + + +The logic of the Difficulty Freeze is defined as follows: + +``` +if (BLOCK_HEIGHT <= DIFFICULTY_FREEZE_HEIGHT): + block_diff = parent_diff + parent_diff // 2048 * max( + 1 - (block_timestamp - parent_timestamp) // 10, -99) + +else: + block_diff = parent_diff + parent_diff * 0.01 +``` + +**Optional Implementation** + +Add the variable `DIFFICULTY_FREEZE_DIFFERENCE` and use the `LAST_FORK_HEIGHT` to calculate when the Difficulty Freeze would occur. + +For example we can set the `DFD = 1,800,000 blocks` or approximately 9 months. The Difficulty Calculation would then be. + +``` +if (BLOCK_HEIGHT <= LAST_FORK_HEIGHT + DIFFICUTLY_FREEZE_DIFFERENCE) : + block_diff = parent_diff + parent_diff // 2048 * max( + 1 - (block_timestamp - parent_timestamp) // 10, -99) + +else: + block_diff = parent_diff + parent_diff * 0.01 +``` + +This approach would have the added benefit that updating the Difficulty Freeze is easier as it happens automatically at the time of every upgrade. The trade-off is that the logic for checking is more complex and would require further analysis and test cases to ensure no consensus bugs arise. + +## Rationale +Block height is very easy to predict and evaluate within the system. This removes the effect of the Difficulty Bomb on block time, simplifying the block time targeting mechanism. The addition of an increase in the difficulty was added after feedback that the game theory of the mechanism did not reliably result in . + +https://twitter.com/quentinc137/status/1227110578235330562 + +## Backwards Compatibility +No backward incompatibilities + +## Test Cases +TBD +## Implementation +TBD + +## Security Considerations +The effect of missing the Difficulty Freeze has a different impact than missing the Difficulty Bomb. At the point of a Difficulty freeze, the protocol is no longer able to adapt to changes in hash power on the network. This can lead to one of three scenarios. + + - The Hash rate Increases: + Block Times would increase on the network for short time until the increase in difficulty is too high for the network to add any more miners. + - The Hash rate decreases: + Block times would increase. + - The Hash rate stays the same: + A consistent increase in blocktimes. + +Clients are motivated to have their client sync fully to the network and so are very motivated to keep this situation from occurring. Simultaneously delaying the Difficulty Freeze is most easily implemented by client teams. Therefore the group that is most negatively affected is also the group that can most efficiently address it. + +## Economic Considerations + +Under the current Difficult, Bomb issuance of ETH is reduced as the Ice Age takes affect. Under the Difficulty Freeze, it is more likely that issuance would increase for a short time; however, clients are motivated to prevent this and keep clients syncing effectively. This means it is much less likely to occur. The increase to the difficulty over time will eventually reduce blocktimes and also issuance. + +It is also easy to predict when this change would happen, and stakeholders who are affected (Eth Holders) can keep client developers accountable by observing when the Difficulty Freeze is approaching and yell at them on twitter. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2520.md b/EIPS/eip-2520.md new file mode 100644 index 0000000..d72c1e9 --- /dev/null +++ b/EIPS/eip-2520.md @@ -0,0 +1,75 @@ +--- +eip: 2520 +title: Multiple contenthash records for ENS +author: Filip Štamcar (@filips123) +discussions-to: https://github.com/ethereum/EIPs/issues/2393 +status: Stagnant +type: Standards Track +category: ERC +created: 2020-02-18 +requires: 1577 +--- + +## Simple Summary +ENS support for multiple `contenthash` records on a single ENS name. + +## Motivation +Many applications are resolving ENS names to content hosted on distributed systems. To do this, they use `contenthash` record from ENS domain to know how to resolve names and which distributed system should be used. + +However, the domain can store only one `contenthash` record which means that the site owner needs to decide which hosting system to use. Because there are many ENS-compatible hosting systems available (IPFS, Swarm, recently Onion and ZeroNet), and there will probably be even more in the future, lack of support for multiple records could become problematic. Instead, domains should be able to store multiple `contenthash` records to allow applications to resolve to multiple hosting systems. + +## Specification +Setting and getting functions **MUST** have the same public interface as specified in EIP 1577. Additionally, they **MUST** also have new public interfaces introduced by this EIP: + +* For setting a `contenthash` record, the `setContenthash` **MUST** provide additional `proto` parameter and use it to save the `contenthash`. When `proto` is not provided, it **MUST** save the record as default record. + + ```solidity + function setContenthash(bytes32 node, bytes calldata proto, bytes calldata hash) external authorised(node); + ``` + +* For getting a `contenthash` record, the `contenthash` **MUST** provide additional `proto` parameter and use it to get the `contenthash` for requested type. When `proto` is not provided, it **MUST** return the default record. + + ```solidity + function contenthash(bytes32 node, bytes calldata proto) external view returns (bytes memory); + ``` + +* Resolver that supports multiple `contenthash` records **MUST** return `true` for `supportsInterface` with interface ID `0x6de03e07`. + +Applications that are using ENS `contenthash` records **SHOULD** handle them in the following way: + +* If the application only supports one hosting system (like directly handling ENS from IPFS/Swarm gateways), it **SHOULD** request `contenthash` with a specific type. The contract **MUST** then return it and application **SHOULD** correctly handle it. + +* If the application supports multiple hosting systems (like MetaMask), it **SHOULD** request `contenthash` without a specific type (like in EIP 1577). The contract **MUST** then return the default `contenthash` record. + +## Rationale +The proposed implementation was chosen because it is simple to implement and supports all important requested features. However, it doesn't support multiple records for the same type and priority order, as they don't give much advantage and are harder to implement properly. + +## Backwards Compatibility +The EIP is backwards-compatible with EIP 1577, the only differences are additional overloaded methods. Old applications will still be able to function correctly, as they will receive the default `contenthash` record. + +## Implementation +```solidity +contract ContentHashResolver { + bytes4 constant private MULTI_CONTENT_HASH_INTERFACE_ID = 0x6de03e07; + mapping(bytes32=>mapping(bytes=>bytes)) hashes; + + function setContenthash(bytes32 node, bytes calldata proto, bytes calldata hash) external { + hashes[node][proto] = hash; + emit ContenthashChanged(node, hash); + } + + function contenthash(bytes32 node, bytes calldata proto) external view returns (bytes memory) { + return hashes[node][proto]; + } + + function supportsInterface(bytes4 interfaceID) public pure returns(bool) { + return interfaceID == MULTI_CONTENT_HASH_INTERFACE_ID; + } +} +``` + +## Security Considerations +TBD + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2525.md b/EIPS/eip-2525.md new file mode 100644 index 0000000..71a2855 --- /dev/null +++ b/EIPS/eip-2525.md @@ -0,0 +1,171 @@ +--- +eip: 2525 +title: ENSLogin +author: Hadrien Croubois (@amxx) +discussions-to: https://ethereum-magicians.org/t/discussion-ens-login/3569 +status: Stagnant +type: Standards Track +category: ERC +created: 2020-02-19 +requires: 137, 634, 1193, 2304 +--- + +## 1. Abstract + +This presents a method to improve a universal method of login to the ethereum blockchain, leveraging the metadata storage provided by the ENS. We consider a user to be logged in when we have an [EIP-1193](./eip-1193.md) provider that can sign transaction and messages on his behalf. This method is inspired by [Alex Van de Sande's work](https://www.youtube.com/watch?v=1LVwWknE-NQ) and [Web3Connect](https://web3connect.com). In the future, the approach described here-after should be extended to work with any blockchain. + +## 2. Motivation + +Multiple wallet solutions can be used to interact with the Ethereum blockchain. Some (metamask, gnosis, ...) are compatible as they inject a standardized wallet object in the browser without requiring any effort from the Dapp developers, but they require an effort on the user side (user has to install the plugin). Other solutions (Portis, Authereum, Torus, Universal Login, ...) propose a more seamless flow to non-crypto-aware users but require an integration effort from the Dapp developers. Hardware wallet (ledger, trezor, keepkey, ...) also require integration effort from the Dapp developers. + +When Dapps integrate login with multiple solutions, they rely on the user choosing the correct wallet-provider. This could prove increasingly difficult as the number of wallet-provider increases, particularly for novice users. Additionally, if decentralized applications pick and choose only a handful of wallets to support, the current incumbent wallets will have a distinct advantage and new wallets will struggle to find adoption. This will create a less competitive environment and stifle innovation. Rather than relying on the user choosing which wallet-provider to connect with (as does Web3Connect), ENSLogin proposes to use user-owned ENS domain as entry points. Metadata attached to these ENS domains is used to detect which wallet-provider if used by the corresponding account. + +That way, ENSLogin would allow any user to connect to any Dapp with any wallet, using a simple domain as a login. + +## 3. Description + +### 3.1. Overview + +The ENSLogin works as follow: + +* Request an ENS domain from the user +* Resolve the ENS domain to retrieve (see [EIP-137](./eip-137.md)) + * An address (see [EIP-137](./eip-137.md)) + * A text entry (see [EIP-634](./eip-634.md)) +* Interpret the text entry and download the file it points to +* Evaluate the content of the downloaded file +* Return the corresponding object to the Dapp + +At this point, the app should process like with any web3 provider. Calling the `enable()` functions should ask the users for wallet specific credentials is needed. + +This workflow is to be implemented by an SDK that Dapp could easily import. The SDK would contain the resolution mechanism and support for both centralized and decentralized storage solution. Wallet-provider specific code should NOT be part of SDK. Wallet-provider specific code should only be present in the external file used to generate the web3 provider. + +### 3.2. Details + +* **Text entry resolution:** A pointer to the code needed to instantiate the wallet-provider is recorded using the ENS support for text entries (see [EIP-634](./eip-634.md)). The corresponding key is `enslogin` (**subject to change**). If no value is associated with the key `enslogin` at the targeted domain, we fallback to metadata store on the parent's node with the key `enslogin-default` (**subject to change**). +**Example:** for the ens domain `username.domain.eth`, the resolution would look for (in order): + * `resolver.at(ens.owner(nodehash("username.domain.eth"))).text(nodehash("username.domain.eth"), 'enslogin')` + * `resolver.at(ens.owner(nodehash("domain.eth"))).text(nodehash("domain.eth"), 'enslogin-default')` + +* **Provider link:** Code for instantiating the wallet-provider must be pointed to in a standardized manner. **This is yet not specified.** The current approach uses a human-readable format `scheme://path` such as: + + * `ipfs://Qm12345678901234567890123456789012345678901234` + * `https://server.com/enslogin-module-someprovider` + + And adds a suffix depending on the targeted blockchain type (see [SLIP 44](https://github.com/satoshilabs/slips/blob/master/slip-0044.md)) and language. Canonical case is a webapp using ethereum so the target would be: + + * `ipfs://Qm12345678901234567890123456789012345678901234/60/js` + * `https://server.com/enslogin-module-someprovider/60/js` + + Note that this suffix mechanism is compatible with http/https as well as IPFS. It is a constraint on the storage layer as some may not be able to do this kind of resolution. + +* **Provider instantiation:** + * [JAVASCRIPT/ETHEREUM] The file containing the wallet-provider's code should inject a function `global.provider: (config) => Promise` that returns a promise to a standardized provider object. For EVM blockchains, the object should follow [EIP-1193](./eip-1193.md). + * Other blockchain types/langages should be detailed in the future. + + +* **Configuration object:** In addition to the username (ENS domain), the Dapp should have the ability to pass a configuration object that could be used by the wallet-provider instantiating function. This configuration should include: + * A body (common to all provider) that specify details about the targeted chain (network name / node, address of the ens entrypoint ...). If any of these are missing, a fallback can be used (mainnet as a default network, bootstrapping an in-browser IPFS node, ...). + * Wallet provider-specific fields (**optional**, starting with one underscore `_`) can be added to pass additional, wallet-provider specific, parameters / debugging flags. + * SDK specific fields (**optional**, starting with two underscores `__`) can be used to pass additional arguments. + + Minimal configuration: + ``` + { + provider: { + network: 'goerli' + } + } + ``` + Example of advanced configuration object: + ``` + { + provider: { + network: 'goerli', + ens: '0x112234455c3a32fd11230c42e7bccd4a84e02010' + }, + ipfs: { + host: 'ipfs.infura.io', + port: 5001, + protocol: 'https' + }, + _authereum: {...}, + _portis: {...}, + _unilogin: {...}, + _torus: {...}, + __callbacks: { + resolved: (username, addr, descr) => { + console.log(`[CALLBACKS] resolved: ${username} ${addr} ${descr}`); + }, + loading: (protocol, path) => { + console.log(`[CALLBACKS] loading: ${protocol} ${path}`); + }, + loaded: (protocol, path) => { + console.log(`[CALLBACKS] loaded: ${protocol} ${path}`); + } + } + } + ``` + +**TODO** *(maybe move that part to section 6.1)*: +Add [SLIP 44](https://github.com/satoshilabs/slips/blob/master/slip-0044.md) compliant blockchain description to the config for better multichain support. This will require a additional field `ENS network` to know which ethereum network to use for resolution when the targeted blockchain/network is not ethereum (could also be used for cross chain resolution on ethereum, for example xDAI login with metadata stored on mainnet) + +### 3.3. Decentralization + +Unlike solution like Web3Connect, ENSLogin proposes a modular approach that is decentralized by nature. +The code needed for a Dapp to use ENSLogin (hereafter referred to as the SDK) only contains lookup mechanism for the ethereum blockchain and the data storages solutions. The solution is limited by the protocols (https / ipfs / ...) that the SDK can interact with. Beyond that, any wallet-provider that follows the expected structure and that is available through one of the supported protocol is automatically compatible with all the Dapps proposing ENSLogin support. There is no need to go through a centralized approval process. Furthermore, deployed SDK do not need to be upgraded to benefit from the latest wallet updates. The only permissioned part of the protocol is in the ENS control of the users over the metadata that describes their wallet-provider implementation. Users could also rely on the fallback mechanism to have the wallet-provider update it for them. + +### 3.4. Incentives + +We believe ENSLogin's biggest strength is the fact that it aligns the incentives of Dapp developers and wallet-providers to follow this standard. + +* A wallet-provider that implements the required file and make them available will ensure the compatibility of its wallet with all Dapps using ENSLogin. This will remove the burden of asking all Dapps to integrate their solutions, which Dapps are unlikely to do until the wallet as strong userbase. Consequently, ENSLogin will improve the competition between wallet-providers and encourage innovation in that space +* A Dapp that uses ENSLogin protocol, either by including the ENSLogin's SDK or by implementing compatible behaviour, will make itself available to all the users of all the compatible wallet. At some point, being compatible with ENSLogin will be the easiest to reach a large user-base. +* ENSLogin should be mostly transparent for the users. Most wallet provider will set up the necessary entries without requiring any effort from the user. Advanced users can take control over the wallet resolution process, which will be simple once the right tooling is available. + +### 3.5. Drawbacks + +While ENSLogin allows dapps to support any wallet for logging in, dapps still must choose which wallets they suggest to users for registration. This can be done through a component like Web3Connect or BlockNative's + +## 4. Prototype + +**TODO** + +## 5. Support by the community + +### 5.1. Adoption + +| Name | Live | Module | Assigns ENS names | support by default | +| -------------- | ---- | ------ | ----------------- | ------------------ | +| Argent | yes | no | yes | no | +| Authereum | yes | yes | yes | no | +| Fortmatic | yes | no | no | no | +| Gnosis Safe | yes | yes\* | no | no | +| Ledger | yes | beta | no | no | +| KeepKey | yes | no | no | no | +| Metamask | yes | yes | no | no | +| Opera | yes | yes\* | no | no | +| Portis | yes | yes | no | no | +| SquareLink | yes | no | no | no | +| Shipl | no | no | no | no | +| Torus | yes | yes | no | no | +| Trezor | yes | no | no | no | +| UniLogin | beta | beta | yes | no | + +\*use the metamask module + +## 6. Possible evolutions + +### 6.1. Multichain support + +**TODO** + +## 7. FAQ + +### 7.1. Can anyone connect with my login? Where are my private keys stored? + +ENSLogin only has access to what is recorded on the ENS, namely your address and the provider you use. Private key management is a is handled by the provider and is outside ENSLogin's scope. Some might store the key on disk. Other might rely on custodial keys stored on a remote (hopefully secure) server. Others might use a dedicated hardware component to handle signature and never directly have access to the private key. + +### 7.2. How do I get an ENS Login? + +**TODO** (this might need a separate ERC) diff --git a/EIPS/eip-2535.md b/EIPS/eip-2535.md new file mode 100644 index 0000000..6cf1d83 --- /dev/null +++ b/EIPS/eip-2535.md @@ -0,0 +1,435 @@ +--- +eip: 2535 +title: Diamonds, Multi-Facet Proxy +description: Create modular smart contract systems that can be extended after deployment. +author: Nick Mudge (@mudgen) +discussions-to: https://ethereum-magicians.org/t/discussion-for-eip2535-diamonds/10459/ +status: Final +type: Standards Track +category: ERC +created: 2020-02-22 +--- + +## Abstract + + + +This proposal standardizes diamonds, which are modular smart contract systems that can be upgraded/extended after deployment, and have virtually no size limit. More technically, a **diamond** is a contract with external functions that are supplied by contracts called **facets**. Facets are separate, independent contracts that can share internal functions, libraries, and state variables. + +## Motivation + +There are a number of different reasons to use diamonds. Here are some of them: + +1. **A single address for unlimited contract functionality.** Using a single address for contract functionality makes deployment, testing and integration with other smart contracts, software and user interfaces easier. +1. **Your contract exceeds the 24KB maximum contract size.** You may have related functionality that it makes sense to keep in a single contract, or at a single contract address. A diamond does not have a max contract size. +1. **A diamond provides a way to organize contract code and data.** You may want to build a contract system with a lot of functionality. A diamond provides a systematic way to isolate different functionality and connect them together and share data between them as needed in a gas-efficient way. +1. **A diamond provides a way to upgrade functionality.** Upgradeable diamonds can be upgraded to add/replace/remove functionality. Because diamonds have no max contract size, there is no limit to the amount of functionality that can be added to diamonds over time. Diamonds can be upgraded without having to redeploy existing functionality. Parts of a diamond can be added/replaced/removed while leaving other parts alone. +1. **A diamond can be immutable.** It is possible to deploy an immutable diamond or make an upgradeable diamond immutable at a later time. +1. **A diamond can reuse deployed contracts.** Instead of deploying contracts to a blockchain, existing already deployed, onchain contracts can be used to create diamonds. Custom diamonds can be created from existing deployed contracts. This enables the creation of on-chain smart contract platforms and libraries. + +This standard is an improvement of [EIP-1538](./eip-1538.md). The same motivations of that standard apply to this standard. + +A deployed facet can be used by any number of diamonds. + +The diagram below shows two diamonds using the same two facets. + +- `FacetA` is used by `Diamond1` +- `FacetA` is used by `Diamond2` +- `FacetB` is used by `Diamond1` +- `FacetB` is used by `Diamond2` + + + +### Upgradeable Diamond vs. Centralized Private Database + +Why have an upgradeable diamond instead of a centralized, private, mutable database? + +1. Decentralized Autonomous Organizations (DAOs) and other governance systems can be used to upgrade diamonds. +1. Wide interaction and integration with the Ethereum ecosystem. +1. With open storage data and verified source code it is possible to show a provable history of trustworthiness. +1. With openness bad behavior can be spotted and reported when it happens. +1. Independent security and domain experts can review the change history of contracts and vouch for their history of trustworthiness. +1. It is possible for an upgradeable diamond to become immutable and trustless. + +### Some Diamond Benefits + +1. A stable contract address that provides needed functionality. +1. A single address with the functionality of multiple contracts (facets) that are independent from each other but can share internal functions, libraries and state variables. +1. Emitting events from a single address can simplify event handling. +1. A way to add, replace and remove multiple external functions atomically (in the same transaction). +1. Fine-grained upgrades, so you can change just the parts of a diamond that need to be changed. +1. Have greater control over when and what functions exist. +1. Decentralized Autonomous Organizations (DAOs), multisig contracts and other governance systems can be used to upgrade diamonds. +1. An event that shows what functions are added, replaced and removed. +1. The ability to show all changes made to a diamond. +1. Increase trust over time by showing all changes made to a diamond. +1. A way to look at a diamond to see its current facets and functions. +1. Have an immutable, trustless diamond. +1. Solves the 24KB maximum contract size limitation. Diamonds can be any size. +1. Separate functionality can be implemented in separate facets and used together in a diamond. +1. Diamonds can be created from already deployed, existing onchain contracts. +1. Larger contracts have to reduce their size by removing error messages and other things. You can keep your full functionality that you need by implementing a diamond. +1. Enables zero, partial or full diamond immutability as desired, and when desired. +1. The ability to develop and improve an application over time with an upgradeable diamond and then make it immutable and trustless if desired. +1. Develop incrementally and let your diamond grow with your application. +1. Upgrade diamonds to fix bugs, add functionality and implement new standards. +1. Organize your code with a diamond and facets. +1. Diamonds can be large (have many functions) but still be modular because they are compartmented with facets. +1. Contract architectures that call multiple contracts in a single transaction can save gas by condensing those contracts into a single diamond and accessing state variables directly. +1. Save gas by converting external functions to internal functions. This done by sharing internal functions between facets. +1. Save gas by creating external functions for gas-optimized specific use cases, such as bulk transfers. +1. Diamonds are designed for tooling and user-interface software. + + +## Specification + +### Terms + +1. A **diamond** is a facade smart contract that `delegatecall`s into its facets to execute function calls. A diamond is stateful. Data is stored in the contract storage of a diamond. +1. A **facet** is a stateless smart contract or Solidity library with external functions. A facet is deployed and one or more of its functions are added to one or more diamonds. A facet does not store data within its own contract storage but it can define state and read and write to the storage of one or more diamonds. The term facet comes from the diamond industry. It is a side, or flat surface of a diamond. +1. A **loupe facet** is a facet that provides introspection functions. In the diamond industry, a loupe is a magnifying glass that is used to look at diamonds. +1. An **immutable function** is an external function that cannot be replaced or removed (because it is defined directly in the diamond, or because the diamond's logic does not allow it to be modified). +1. A **mapping** for the purposes of this EIP is an association between two things and does not refer to a specific implementation. + +The term **contract** is used loosely to mean a smart contract or deployed Solidity library. + +When this EIP uses **function** without specifying internal or external, it means external function. + +In this EIP the information that applies to external functions also applies to public functions. + +### Overview + +A diamond calls functions from its facets using `delegatecall`. + +In the diamond industry diamonds are created and shaped by being cut, creating facets. In this standard diamonds are cut by adding, replacing or removing functions from facets. + +### A Note on Implementing Interfaces + +Because of the nature of diamonds, a diamond can implement an interface in one of two ways: directly (`contract Contract is Interface`), or by adding functions to it from one or more facets. For the purposes of this proposal, when a diamond is said to implement an interface, either method of implementation is permitted. + +### Fallback Function + +When an external function is called on a diamond its fallback function is executed. The fallback function determines which facet to call based on the first four bytes of the call data (known as the function selector) and executes that function from the facet using `delegatecall`. + +A diamond's fallback function and `delegatecall` enable a diamond to execute a facet's function as if it was implemented by the diamond itself. The `msg.sender` and `msg.value` values do not change and only the diamond's storage is read and written to. + +Here is an illustrative example of how a diamond's fallback function might be implemented: + +```solidity +// Find facet for function that is called and execute the +// function if a facet is found and return any value. +fallback() external payable { + // get facet from function selector + address facet = selectorTofacet[msg.sig]; + require(facet != address(0)); + // Execute external function from facet using delegatecall and return any value. + assembly { + // copy function selector and any arguments + calldatacopy(0, 0, calldatasize()) + // execute function call using the facet + let result := delegatecall(gas(), facet, 0, calldatasize(), 0, 0) + // get any return value + returndatacopy(0, 0, returndatasize()) + // return any return value or error back to the caller + switch result + case 0 {revert(0, returndatasize())} + default {return (0, returndatasize())} + } +} +``` + +This diagram shows the structure of a diamond: + + + +### Storage + +A state variable or storage layout organizational pattern is needed because Solidity's builtin storage layout system doesn't support proxy contracts or diamonds. The particular layout of storage is not defined in this EIP, but may be defined by later proposals. Examples of storage layout patterns that work with diamonds are [Diamond Storage](../assets/eip-2535/storage-examples/DiamondStorage.sol) and [AppStorage](../assets/eip-2535/storage-examples/AppStorage.sol). + +Facets can share state variables by using the same structs at the same storage positions. Facets can share internal functions and libraries by inheriting the same contracts or using the same libraries. In these ways facets are separate, independent units but can share state and functionality. + +The diagram below shows facets with their own data and data shared between them. + +Notice that all data is stored in the diamond's storage, but different facets have different access to data. + +In this diagram + +- Only `FacetA` can access `DataA` +- Only `FacetB` can access `DataB` +- Only the diamond's own code can access `DataD`. +- `FacetA` and `FacetB` share access to `DataAB`. +- The diamond's own code, `FacetA` and `FacetB` share access to `DataABD`. + + + +### Solidity Libraries as Facets + +Smart contracts or deployed Solidity libraries can be facets of diamonds. + +Only Solidity libraries that have one or more external functions can be deployed to a blockchain and be a facet. + +Solidity libraries that contain internal functions only cannot be deployed and cannot be a facet. Internal functions from Solidity libraries are included in the bytecode of facets and contracts that use them. Solidity libraries with internal functions only are useful for sharing internal functions between facets. + +Solidity library facets have a few properties that match their use as facets: +* They cannot be deleted. +* They are stateless. They do not have contract storage. +* Their syntax prevents declaring state variables outside Diamond Storage. + +### Adding/Replacing/Removing Functions + +#### `IDiamond` Interface + +All diamonds must implement the `IDiamond` interface. + +During the deployment of a diamond any immutable functions and any external functions added to the diamond must be emitted in the `DiamondCut` event. + +**A `DiamondCut` event must be emitted any time external functions are added, replaced, or removed.** This applies to all upgrades, all functions changes, at any time, whether through `diamondCut` or not. + +```solidity +interface IDiamond { + enum FacetCutAction {Add, Replace, Remove} + // Add=0, Replace=1, Remove=2 + + struct FacetCut { + address facetAddress; + FacetCutAction action; + bytes4[] functionSelectors; + } + + event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata); +} +``` + +The `DiamondCut` event records all function changes to a diamond. + +#### `IDiamondCut` Interface + +A diamond contains within it a mapping of function selectors to facet addresses. Functions are added/replaced/removed by modifying this mapping. + +Diamonds should implement the `IDiamondCut` interface if after their deployment they allow modifications to their function selector mapping. + +The `diamondCut` function updates any number of functions from any number of facets in a single transaction. Executing all changes within a single transaction prevents data corruption which could occur in upgrades done over multiple transactions. + +`diamondCut` is specified for the purpose of interoperability. Diamond tools, software and user-interfaces should expect and use the standard `diamondCut` function. + +```solidity +interface IDiamondCut is IDiamond { + /// @notice Add/replace/remove any number of functions and optionally execute + /// a function with delegatecall + /// @param _diamondCut Contains the facet addresses and function selectors + /// @param _init The address of the contract or facet to execute _calldata + /// @param _calldata A function call, including function selector and arguments + /// _calldata is executed with delegatecall on _init + function diamondCut( + FacetCut[] calldata _diamondCut, + address _init, + bytes calldata _calldata + ) external; +} +``` + +The `_diamondCut` argument is an array of `FacetCut` structs. + +Each `FacetCut` struct contains a facet address and array of function selectors that are updated in a diamond. + +For each `FacetCut` struct: + + * If the `action` is `Add`, update the function selector mapping for each `functionSelectors` item to the `facetAddress`. If any of the `functionSelectors` had a mapped facet, revert instead. + * If the `action` is `Replace`, update the function selector mapping for each `functionSelectors` item to the `facetAddress`. If any of the `functionSelectors` had a value equal to `facetAddress` or the selector was unset, revert instead. + * If the `action` is `Remove`, remove the function selector mapping for each `functionSelectors` item. If any of the `functionSelectors` were previously unset, revert instead. + +Any attempt to replace or remove an immutable function must revert. + +Being intentional and explicit about adding/replacing/removing functions helps catch and prevent upgrade mistakes. + +##### Executing `_calldata` + +After adding/replacing/removing functions the `_calldata` argument is executed with `delegatecall` on `_init`. This execution is done to initialize data or setup or remove anything needed or no longer needed after adding, replacing and/or removing functions. + +If the `_init` value is `address(0)` then `_calldata` execution is skipped. In this case `_calldata` can contain 0 bytes or custom information. + +### Inspecting Facets & Functions + +> A loupe is a small magnifying glass used to look at diamonds. + +Diamonds must support inspecting facets and functions by implementing the `IDiamondLoupe` interface. + +#### `IDiamondLoupe` Interface + +```solidity +// A loupe is a small magnifying glass used to look at diamonds. +// These functions look at diamonds +interface IDiamondLoupe { + struct Facet { + address facetAddress; + bytes4[] functionSelectors; + } + + /// @notice Gets all facet addresses and their four byte function selectors. + /// @return facets_ Facet + function facets() external view returns (Facet[] memory facets_); + + /// @notice Gets all the function selectors supported by a specific facet. + /// @param _facet The facet address. + /// @return facetFunctionSelectors_ + function facetFunctionSelectors(address _facet) external view returns (bytes4[] memory facetFunctionSelectors_); + + /// @notice Get all the facet addresses used by a diamond. + /// @return facetAddresses_ + function facetAddresses() external view returns (address[] memory facetAddresses_); + + /// @notice Gets the facet that supports the given selector. + /// @dev If facet is not found return address(0). + /// @param _functionSelector The function selector. + /// @return facetAddress_ The facet address. + function facetAddress(bytes4 _functionSelector) external view returns (address facetAddress_); +} +``` + +See a [reference implementation](#reference-implementation) to see how this can be implemented. + +The loupe functions can be used in user-interface software. A user interface calls these functions to provide information about and visualize diamonds. + +The loupe functions can be used in deployment functionality, upgrade functionality, testing and other software. + +### Implementation Points + +A diamond must implement the following: + +1. A diamond contains a fallback function and zero or more immutable functions that are defined within it. +1. A diamond associates function selectors with facets. +1. When a function is called on a diamond it executes immediately if it is an "immutable function" defined directly in the diamond. Otherwise the diamond's fallback function is executed. The fallback function finds the facet associated with the function and executes the function using `delegatecall`. If there is no facet for the function then optionally a default function may be executed. If there is no facet for the function and no default function and no other mechanism to handle it then execution reverts. +1. Each time functions are added, replaced or removed a `DiamondCut` event is emitted to record it. +1. A diamond implements the DiamondLoupe interface. +1. All immutable functions must be emitted in the `DiamondCut` event as new functions added. And the loupe functions must return information about immutable functions if they exist. The facet address for an immutable function is the diamond's address. Any attempt to delete or replace an immutable function must revert. + +A diamond may implement the following: + +1. [EIP-165](./eip-165.md)'s `supportsInterface`. If a diamond has the `diamondCut` function then the interface ID used for it is `IDiamondCut.diamondCut.selector`. The interface ID used for the diamond loupe interface is `IDiamondLoupe.facets.selector ^ IDiamondLoupe.facetFunctionSelectors.selector ^ IDiamondLoupe.facetAddresses.selector ^ IDiamondLoupe.facetAddress.selector`. + +The diamond address is the address that users interact with. The diamond address does not change. Only facet addresses can change by using the `diamondCut` function, or other function. + +## Rationale + +### Using Function Selectors + +User interface software can be used to retrieve function selectors and face addresses from a diamond in order show what functions a diamond has. + +This standard is designed to make diamonds work well with user-interface software. Function selectors with the ABI of a contract provide enough information about functions to be useful for user-interface software. + +### Gas Considerations + +Delegating function calls does have some gas overhead. This is mitigated in several ways: + +1. Because diamonds do not have a max size limitation it is possible to add gas optimizing functions for use cases. For example someone could use a diamond to implement the [EIP-721](./eip-721.md) standard and implement batch transfer functions to reduce gas (and make batch transfers more convenient). +1. Some contract architectures require calling multiple contracts in one transaction. Gas savings can be realized by condensing those contracts into a single diamond and accessing contract storage directly. +1. Facets can contain few external functions, reducing gas costs. Because it costs more gas to call a function in a contract with many functions than a contract with few functions. +1. The Solidity optimizer can be set to a high setting causing more bytecode to be generated but the facets will use less gas when executed. + +### Versions of Functions + +Software or a user can verify what version of a function is called by getting the facet address of the function. This can be done by calling the `facetAddress` function from the `IDiamondLoupe` interface. This function takes a function selector as an argument and returns the facet address where it is implemented. + +### Default Function + +Solidity provides the `fallback` function so that specific functionality can be executed when a function is called on a contract that does not exist in the contract. This same behavior can optionally be implemented in a diamond by implementing and using a default function, which is a function that is executed when a function is called on a diamond that does not exist in the diamond. + +A default function can be implemented a number of ways and this standard does not specify how it must be implemented. + +### Loupe Functions & `DiamondCut` Event + +To find out what functions a regular contract has it is only necessary to look at its verified source code. + +The verified source code of a diamond does not include what functions it has so a different mechanism is needed. + +A diamond has four standard functions called the loupe functions that are used to show what functions a diamond has. + +The loupe functions can be used for many things including: +1. To show all functions used by a diamond. +1. To query services like Etherscan or files to retrieve and show all source code used by a diamond. +1. To query services like Etherscan or files to retrieve ABI information for a diamond. +1. To test or verify that a transaction that adds/replaces/removes functions on a diamond succeeded. +1. To find out what functions a diamond has before calling functions on it. +1. To be used by tools and programming libraries to deploy and upgrade diamonds. +1. To be used by user interfaces to show information about diamonds. +1. To be used by user interfaces to enable users to call functions on diamonds. + +Diamonds support another form of transparency which is a historical record of all upgrades on a diamond. This is done with the `DiamondCut` event which is used to record all functions that are added, replaced or removed on a diamond. + +### Sharing Functions Between Facets + +In some cases it might be necessary to call a function defined in a different facet. Here are ways to do this: + +1. Copy internal function code in one facet to the other facet. +1. Put common internal functions in a contract that is inherited by multiple facets. +1. Put common internal functions in a Solidity library and use the library in facets. +1. A type safe way to call an external function defined in another facet is to do this: `MyOtherFacet(address(this)).myFunction(arg1, arg2)` +1. A more gas-efficient way to call an external function defined in another facet is to use delegatecall. Here is an example of doing that: +```solidity +DiamondStorage storage ds = diamondStorage(); +bytes4 functionSelector = bytes4(keccak256("myFunction(uint256)")); +// get facet address of function +address facet = ds.selectorToFacet[functionSelector]; +bytes memory myFunctionCall = abi.encodeWithSelector(functionSelector, 4); +(bool success, bytes memory result) = address(facet).delegatecall(myFunctionCall); +``` +6. Instead of calling an external function defined in another facet you can instead create an internal function version of the external function. Add the internal version of the function to the facet that needs to use it. + +### Facets can be Reusable and Composable + +A deployed facet can be used by any number of diamonds. + +Different combinations of facets can be used with different diamonds. + +It is possible to create and deploy a set of facets that are reused by different diamonds over time. + +The ability to use the same deployed facets for many diamonds reduces deployment costs. + +It is possible to implement facets in a way that makes them usable/composable/compatible with other facets. It is also possible to implement facets in a way that makes them not usable/composable/compatible with other facets. + +A function signature is the name of a function and its parameter types. Example function signature: `myfunction(uint256)`. A limitation is that two external functions with the same function signature can’t be added to the same diamond at the same time because a diamond, or any contract, cannot have two external functions with the same function signature. + +All the functions of a facet do not have to be added to a diamond. Some functions in a facet can be added to a diamond while other functions in the facet are not added to the diamond. + +## Backwards Compatibility + +This standard makes upgradeable diamonds compatible with future standards and functionality because new functions can be added and existing functions can be replaced or removed. + +## Reference Implementation + +All the Solidity code for a complete reference implementation has been put in a single file here: [Diamond.sol](../assets/eip-2535/reference/Diamond.sol) + +The same reference implementation has been organized into multiple files and directories and also includes a deployment script and tests. Download it as a zip file: [`EIP2535-Diamonds-Reference-Implementation.zip`](../assets/eip-2535/reference/EIP2535-Diamonds-Reference-Implementation.zip) + +## Security Considerations + +### Ownership and Authentication + +> **Note:** The design and implementation of diamond ownership/authentication is **not** part of this standard. The examples given in this standard and in the reference implementation are just **examples** of how it could be done. + +It is possible to create many different authentication or ownership schemes with this proposal. Authentication schemes can be very simple or complex, fine grained or coarse. This proposal does not limit it in any way. For example ownership/authentication could be as simple as a single account address having the authority to add/replace/remove functions. Or a decentralized autonomous organization could have the authority to only add/replace/remove certain functions. + +Consensus functionality could be implemented such as an approval function that multiple different people call to approve changes before they are executed with the `diamondCut` function. These are just examples. + +The development of standards and implementations of ownership, control and authentication of diamonds is encouraged. + +### Arbitrary Execution with `diamondCut` + +The `diamondCut` function allows arbitrary execution with access to the diamond's storage (through `delegatecall`). Access to this function must be restricted carefully. + +### Do Not Self Destruct +Use of `selfdestruct` in a facet is heavily discouraged. Misuse of it can delete a diamond or a facet. + +### Function Selector Clash + +A function selector clash occurs when two different function signatures hash to the same four-byte hash. This has the unintended consequence of replacing an existing function in a diamond when the intention was to add a new function. This scenario is not possible with a properly implemented `diamondCut` function because it prevents adding function selectors that already exist. + +### Transparency + +Diamonds emit an event every time one or more functions are added, replaced or removed. All source code can be verified. This enables people and software to monitor changes to a contract. If any bad acting function is added to a diamond then it can be seen. + +Security and domain experts can review the history of change of a diamond to detect any history of foul play. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2537.md b/EIPS/eip-2537.md new file mode 100644 index 0000000..9a80865 --- /dev/null +++ b/EIPS/eip-2537.md @@ -0,0 +1,357 @@ +--- +eip: 2537 +title: Precompile for BLS12-381 curve operations +author: Alex Vlasov (@shamatar), Kelly Olson (@ineffectualproperty) +discussions-to: https://ethereum-magicians.org/t/eip2537-bls12-precompile-discussion-thread/4187 +status: Stagnant +type: Standards Track +category: Core +created: 2020-02-21 +--- + +## Simple Summary + +This precompile adds operation on BLS12-381 curve as a precompile in a set necessary to *efficiently* perform operations such as BLS signature verification and perform SNARKs verifications. + +## Abstract + +If `block.number >= X` we introduce *nine* separate precompiles to perform the following operations: + +- BLS12_G1ADD - to perform point addition in G1 (curve over base prime field) with a gas cost of `500` gas +- BLS12_G1MUL - to perform point multiplication in G1 (curve over base prime field) with a gas cost of `12000` gas +- BLS12_G1MULTIEXP - to perform multiexponentiation in G1 (curve over base prime field) with a gas cost formula defined in the corresponding section +- BLS12_G2ADD - to perform point addition in G2 (curve over quadratic extension of the base prime field) with a gas cost of `800` gas +- BLS12_G2MUL - to perform point multiplication in G2 (curve over quadratic extension of the base prime field) with a gas cost of `45000` gas +- BLS12_G2MULTIEXP - to perform multiexponentiation in G2 (curve over quadratic extension of the base prime field) with a gas cost formula defined in the corresponding section +- BLS12_PAIRING - to perform a pairing operations between a set of *pairs* of (G1, G2) points a gas cost formula defined in the corresponding section +- BLS12_MAP_FP_TO_G1 - maps base field element into the G1 point with a gast cost of `5500` gas +- BLS12_MAP_FP2_TO_G2 - maps extension field element into the G2 point with a gas cost of `75000` gas + +Mapping functions specification is included as a separate [document](../assets/eip-2537/field_to_curve.md). Mapping function does NOT perform mapping of the byte string into field element (as it can be implemented in many different ways and can be efficiently performed in EVM), but only does field arithmetic to map field element into curve point. Such functionality is required for signature schemes. + +Multiexponentiation operation is included to efficiently aggregate public keys or individual signer's signatures during BLS signature verification. + +### Proposed addresses table + +|Precompile |Address | +|---|---| +|BLS12_G1ADD | 0x0a | +|BLS12_G1MUL | 0x0b | +|BLS12_G1MULTIEXP | 0x0c | +|BLS12_G2ADD | 0x0d | +|BLS12_G2MUL | 0x0e | +|BLS12_G2MULTIEXP | 0x0f | +|BLS12_PAIRING | 0x10 | +|BLS12_MAP_FP_TO_G1 | 0x11 | +|BLS12_MAP_FP2_TO_G2 | 0x12 | + +## Motivation + +Motivation of this precompile is to add a cryptographic primitive that allows to get 120+ bits of security for operations over pairing friendly curve compared to the existing BN254 precompile that only provides 80 bits of security. + +## Specification + +Curve parameters: + +BLS12 curve is fully defined by the following set of parameters (coefficient `A=0` for all BLS12 curves): + +``` +Base field modulus = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab +B coefficient = 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004 +Main subgroup order = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 +Extension tower +Fp2 construction: +Fp quadratic non-residue = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa +Fp6/Fp12 construction: +Fp2 cubic non-residue c0 = 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 +Fp2 cubic non-residue c1 = 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 +Twist parameters: +Twist type: M +B coefficient for twist c0 = 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004 +B coefficient for twist c1 = 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004 +Generators: +G1: +X = 0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb +Y = 0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1 +G2: +X c0 = 0x024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8 +X c1 = 0x13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e +Y c0 = 0x0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801 +Y c1 = 0x0606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be +Pairing parameters: +|x| (miller loop scalar) = 0xd201000000010000 +x is negative = true +``` + +One should note that base field modulus is equal to `3 mod 4` that allows an efficient square root extraction, although as described below gas cost of decompression is larger than gas cost of supplying decompressed point data in `calldata`. + +### Fine points and encoding of base elements + +#### Field elements encoding: + +To encode points involved in the operation one has to encode elements of the base field and the extension field. + +Base field element (Fp) is encoded as `64` bytes by performing BigEndian encoding of the corresponding (unsigned) integer (top `16` bytes are always zeroes). `64` bytes are chosen to have `32` byte aligned ABI (representable as e.g. `bytes32[2]` or `uint256[2]`). Corresponding integer **must** be less than field modulus. + +For elements of the quadratic extension field (Fp2) encoding is byte concatenation of individual encoding of the coefficients totaling in `128` bytes for a total encoding. For an Fp2 element in a form `el = c0 + c1 * v` where `v` is formal quadratic non-residue and `c0` and `c1` are Fp elements the corresponding byte encoding will be `encode(c0) || encode(c1)` where `||` means byte concatenation (or one can use `bytes32[4]` or `uint256[4]` in terms of Solidity types). + +*Note on the top `16` bytes being zero*: it's required that the encoded element is "in a field" that means strictly `< modulus`. In BigEndian encoding it automatically means that for a modulus that is just `381` bit long top `16` bytes in `64` bytes encoding are zeroes and it **must** be checked if only a subslice of input data is used for actual decoding. + +If encodings do not follow this spec anywhere during parsing in the precompile the precompile *must* return an error. + +#### Encoding of points in G1/G2: + +Points in either G1 (in base field) or in G2 (in extension field) are encoded as byte concatenation of encodings of the `x` and `y` affine coordinates. Total encoding length for G1 point is thus `128` bytes and for G2 point is `256` bytes. + +#### Point of infinity encoding: + +Also referred to as "zero point". For BLS12 curves point with coordinates `(0, 0)` (formal zeroes in Fp or Fp2) is *not* on the curve, so encoding of such point `(0, 0)` is used as a convention to encode point of infinity. + +#### Encoding of scalars for multiplication operation: + +Scalar for multiplication operation is encoded as `32` bytes by performing BigEndian encoding of the corresponding (unsigned) integer. Corresponding integer is **not** required to be less than or equal than main subgroup size. + +#### Behavior on empty inputs: + +Certain operations have variable length input, such as multiexponentiations (takes a list of pairs `(point, scalar)`), or pairing (takes a list of `(G1, G2)` points). While their behavior is well-defined (from arithmetic perspective) on empty inputs, this EIP discourages such use cases and variable input length operations must return an error if input is empty. + +### ABI for operations + +#### ABI for G1 addition + +G1 addition call expects `256` bytes as an input that is interpreted as byte concatenation of two G1 points (`128` bytes each). Output is an encoding of addition operation result - single G1 point (`128` bytes). + +Error cases: +- Either of points being not on the curve must result in error +- Field elements encoding rules apply (obviously) +- Input has invalid length + +#### ABI for G1 multiplication + +G1 multiplication call expects `160` bytes as an input that is interpreted as byte concatenation of encoding of G1 point (`128` bytes) and encoding of a scalar value (`32` bytes). Output is an encoding of multiplication operation result - single G1 point (`128` bytes). + +Error cases: +- Point being not on the curve must result in error +- Field elements encoding rules apply (obviously) +- Input has invalid length + +#### ABI for G1 multiexponentiation + +G1 multiexponentiation call expects `160*k` bytes as an input that is interpreted as byte concatenation of `k` slices each of them being a byte concatenation of encoding of G1 point (`128` bytes) and encoding of a scalar value (`32` bytes). Output is an encoding of multiexponentiation operation result - single G1 point (`128` bytes). + +Error cases: +- Any of G1 points being not on the curve must result in error +- Field elements encoding rules apply (obviously) +- Input has invalid length +- Input is empty + +#### ABI for G2 addition + +G2 addition call expects `512` bytes as an input that is interpreted as byte concatenation of two G2 points (`256` bytes each). Output is an encoding of addition operation result - single G2 point (`256` bytes). + +Error cases: +- Either of points being not on the curve must result in error +- Field elements encoding rules apply (obviously) +- Input has invalid length + +#### ABI for G2 multiplication + +G2 multiplication call expects `288` bytes as an input that is interpreted as byte concatenation of encoding of G2 point (`256` bytes) and encoding of a scalar value (`32` bytes). Output is an encoding of multiplication operation result - single G2 point (`256` bytes). + +Error cases: +- Point being not on the curve must result in error +- Field elements encoding rules apply (obviously) +- Input has invalid length + +#### ABI for G2 multiexponentiation + +G2 multiexponentiation call expects `288*k` bytes as an input that is interpreted as byte concatenation of `k` slices each of them being a byte concatenation of encoding of G2 point (`256` bytes) and encoding of a scalar value (`32` bytes). Output is an encoding of multiexponentiation operation result - single G2 point (`256` bytes). + +Error cases: +- Any of G2 points being not on the curve must result in error +- Field elements encoding rules apply (obviously) +- Input has invalid length +- Input is empty + +#### ABI for pairing + +Pairing call expects `384*k` bytes as an inputs that is interpreted as byte concatenation of `k` slices. Each slice has the following structure: +- `128` bytes of G1 point encoding +- `256` bytes of G2 point encoding + +Output is a `32` bytes where first `31` bytes are equal to `0x00` and the last byte is `0x01` if pairing result is equal to multiplicative identity in a pairing target field and `0x00` otherwise. + +Error cases: +- Any of G1 or G2 points being not on the curve must result in error +- Any of G1 or G2 points are not in the correct subgroup +- Field elements encoding rules apply (obviously) +- Input has invalid length +- Input is empty + +#### ABI for mapping Fp element to G1 point + +Field-to-curve call expects `64` bytes an an input that is interpreted as a an element of the base field. Output of this call is `128` bytes and is G1 point following respective encoding rules. + +Error cases: +- Input has invalid length +- Input is not a valid field element + +#### ABI for mapping Fp2 element to G2 point + +Field-to-curve call expects `128` bytes an an input that is interpreted as a an element of the quadratic extension field. Output of this call is `256` bytes and is G2 point following respective encoding rules. + +Error cases: +- Input has invalid length +- Input is not a valid field element + +### Gas burinig on error + +Following the current state of all other precompiles if call to one of the precompiles in this EIP results in an error then all the gas supplied along with a `CALL` or `STATICCALL` is burned. + +### DDoS protection + +Sane implementation of this EIP *should not* contain infinite cycles (it is possible and not even hard to implement all the functionality without `while` cycles) and gas schedule accurately reflects a time spent on computations of the corresponding function (precompiles pricing reflects an amount of gas consumed in the worst case where such case exists). + +### Gas schedule + +Assuming a constant `30 MGas/second` following prices are suggested. + +#### G1 addition + +`500` gas + +#### G1 multiplication + +`12000` gas + +#### G2 addition + +`800` gas + +#### G2 multiplication + +`45000` gas + +#### G1/G2 Multiexponentiation + +Multiexponentiations are expected to be performed by the Peppinger algorithm (we can also say that is **must** be performed by Peppinger algorithm to have a speedup that results in a discount over naive implementation by multiplying each pair separately and adding the results). For this case there was a table prepared for discount in case of `k <= 128` points in the multiexponentiation with a discount cup `max_discount` for `k > 128`. + +To avoid non-integer arithmetic call cost is calculated as `(k * multiplication_cost * discount) / multiplier` where `multiplier = 1000`, `k` is a number of (scalar, point) pairs for the call, `multiplication_cost` is a corresponding single multiplication call cost for G1/G2. + +Discounts table as a vector of pairs `[k, discount]`: + +``` +[[1, 1200], [2, 888], [3, 764], [4, 641], [5, 594], [6, 547], [7, 500], [8, 453], [9, 438], [10, 423], [11, 408], [12, 394], [13, 379], [14, 364], [15, 349], [16, 334], [17, 330], [18, 326], [19, 322], [20, 318], [21, 314], [22, 310], [23, 306], [24, 302], [25, 298], [26, 294], [27, 289], [28, 285], [29, 281], [30, 277], [31, 273], [32, 269], [33, 268], [34, 266], [35, 265], [36, 263], [37, 262], [38, 260], [39, 259], [40, 257], [41, 256], [42, 254], [43, 253], [44, 251], [45, 250], [46, 248], [47, 247], [48, 245], [49, 244], [50, 242], [51, 241], [52, 239], [53, 238], [54, 236], [55, 235], [56, 233], [57, 232], [58, 231], [59, 229], [60, 228], [61, 226], [62, 225], [63, 223], [64, 222], [65, 221], [66, 220], [67, 219], [68, 219], [69, 218], [70, 217], [71, 216], [72, 216], [73, 215], [74, 214], [75, 213], [76, 213], [77, 212], [78, 211], [79, 211], [80, 210], [81, 209], [82, 208], [83, 208], [84, 207], [85, 206], [86, 205], [87, 205], [88, 204], [89, 203], [90, 202], [91, 202], [92, 201], [93, 200], [94, 199], [95, 199], [96, 198], [97, 197], [98, 196], [99, 196], [100, 195], [101, 194], [102, 193], [103, 193], [104, 192], [105, 191], [106, 191], [107, 190], [108, 189], [109, 188], [110, 188], [111, 187], [112, 186], [113, 185], [114, 185], [115, 184], [116, 183], [117, 182], [118, 182], [119, 181], [120, 180], [121, 179], [122, 179], [123, 178], [124, 177], [125, 176], [126, 176], [127, 175], [128, 174]] +``` + +`max_discount = 174` + +#### Pairing operation + +Cost of the pairing operation is `43000*k + 65000` where `k` is a number of pairs. + +#### Fp-to-G1 mapping operation + +Fp -> G1 mapping is `5500` gas. + +#### Fp2-to-G2 mapping operation + +Fp2 -> G2 mapping is `75000` gas + +#### Gas schedule clarifications for the variable-length input + +For multiexponentiation and pairing functions gas cost depends on the input length. The current state of how gas schedule is implemented in major clients (at the time of writing) is that gas cost function does *not* perform any validation of the length of the input and never returns an error. So we present a list of rules how gas cost functions **must** be implemented to ensure consistency between clients and safety. + +##### Gas schedule clarifications for G1/G2 Multiexponentiation + +Define a constant `LEN_PER_PAIR` that is equal to `160` for G1 operation and to `288` for G2 operation. Define a function `discount(k)` following the rules in the corresponding section, where `k` is number of pairs. + +The following pseudofunction reflects how gas should be calculated: +``` + k = floor(len(input) / LEN_PER_PAIR); + if k == 0 { + return 0; + } + + gas_cost = k * multiplication_cost * discount(k) / multiplier; + + return gas_cost; + +``` + +We use floor division to get number of pairs. If length of the input is not divisible by `LEN_PER_PAIR` we still produce *some* result, but later on precompile will return an error. Also, case when `k = 0` is safe: `CALL` or `STATICCALL` cost is non-zero, and case with formal zero gas cost is already used in `Blake2f` precompile. In any case, main precompile routine **must** produce an error on such an input because it violated encoding rules. + +##### Gas schedule clarifications for pairing + +Define a constant `LEN_PER_PAIR = 384`; + +The following pseudofunction reflects how gas should be calculated: +``` + k = floor(len(input) / LEN_PER_PAIR); + + gas_cost = 23000*k + 115000; + + return gas_cost; + +``` + +We use floor division to get number of pairs. If length of the input is not divisible by `LEN_PER_PAIR` we still produce *some* result, but later on precompile will return an error (precompile routine **must** produce an error on such an input because it violated encoding rules). + +## Rationale + +Motivation section covers a total motivation to have operations over BLS12-381 curve available. We also extend a rationale for move specific fine points. + +### Multiexponentiation as a separate call + +Explicit separate multiexponentiation operation that allows one to save execution time (so gas) by both the algorithm used (namely Peppinger algorithm) and (usually forgotten) by the fact that `CALL` operation in Ethereum is expensive (at the time of writing), so one would have to pay non-negigible overhead if e.g. for multiexponentiation of `100` points would have to call the multipication precompile `100` times and addition for `99` times (roughly `138600` would be saved). + +## Backwards Compatibility + +There are no backward compatibility questions. + +## Important notes + +### Subgroup checks + +Subgroup check **is mandatory** during the pairing call. Implementations *should* use fast subgroup checks: at the time of writing multiplication gas cost is based on `double-and-add` multiplication method that has a clear "worst case" (all bits are equal to one). For pairing operation it's expected that implementation uses faster subgroup check, e.g. by using wNAF multiplication method for elliptic curves that is ~ `40%` cheaper with windows size equal to 4. (Tested empirically. Savings are due to lower hamming weight of the group order and even lower hamming weight for wNAF. Concretely, subgroup check for both G1 and G2 points in a pair are around `35000` combined). + +### Field to curve mapping + +Algorithms and set of parameters for SWU mapping method is provided by a separate [document](../assets/eip-2537/field_to_curve.md) + +## Test Cases + +Due to the large test parameters space we first provide properties that various operations must satisfy. We use additive notation for point operations, capital letters (`P`, `Q`) for points, small letters (`a`, `b`) for scalars. Generator for G1 is labeled as `G`, generator for G2 is labeled as `H`, otherwise we assume random point on a curve in a correct subgroup. `0` means either scalar zero or point of infinity. `1` means either scalar one or multiplicative identity. `group_order` is a main subgroup order. `e(P, Q)` means pairing operation where `P` is in G1, `Q` is in G2. + +Requeired properties for basic ops (add/multiply): + +- Commutativity: `P + Q = Q + P` +- Additive negation: `P + (-P) = 0` +- Doubling `P + P = 2*P` +- Subgroup check: `group_order * P = 0` +- Trivial multiplication check: `1 * P = P` +- Multiplication by zero: `0 * P = 0` +- Multiplication by the unnormalized scalar `(scalar + group_order) * P = scalar * P` + +Required properties for pairing operation: +- Degeneracy `e(P, 0*Q) = e(0*P, Q) = 1` +- Bilinearity `e(a*P, b*Q) = e(a*b*P, Q) = e(P, a*b*Q)` (internal test, not visible through ABI) + +### Benchmarking test cases + +A set of test vectors for quick benchmarking on new implementations is located in a separate [file](../assets/eip-2537/bench_vectors.md) + +## Reference Implementation + +There are two fully spec compatible implementations on the day of writing: +- One in Rust language that is based on the EIP1962 code and integrated with OpenEthereum for this library +- One implemented specifically for Geth as a part of the current codebase + +## Security Considerations + +Strictly following the spec will eliminate security implications or consensus implications in a contrast to the previous BN254 precompile. + +Important topic is a "constant time" property for performed operations. We explicitly state that this precompile **IS NOT REQUIRED** to perform all the operations using constant time algorithms. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2539.md b/EIPS/eip-2539.md new file mode 100644 index 0000000..d84fa69 --- /dev/null +++ b/EIPS/eip-2539.md @@ -0,0 +1,266 @@ +--- +eip: 2539 +title: BLS12-377 curve operations +author: Alex Vlasov (@shamatar) +discussions-to: https://ethereum-magicians.org/t/eip-2539-bls12-377-precompile-discussion-thread/4659 +status: Stagnant +type: Standards Track +category: Core +created: 2020-02-26 +requires: 1109, 2046 +--- + +## Simple Summary +This precompile adds operation on BLS12-377 curve (from Zexe paper) as a precompile in a set necessary to *efficiently* perform operations such as BLS signature verification and perform SNARKs verifications. Unique properties of BLS12-377 also later allow to have SNARKs that check BLS12-377 pairing in an efficient way and allow e.g. constant-size BLS signature aggregation. + +## Abstract + +If `block.number >= X` we introduce *nine* separate precompiles to perform the following operations: + +- BLS12_377_G1ADD - to perform point addition on a curve defined over prime field +- BLS12_377_G1MUL - to perform point multiplication on a curve defined over prime field +- BLS12_377_G1MULTIEXP - to perform multiexponentiation on a curve defined over prime field +- BLS12_377_G2ADD - to perform point addition on a curve twist defined over quadratic extension of the base field +- BLS12_377_G2MUL - to perform point multiplication on a curve twist defined over quadratic extension of the base field +- BLS12_377_G2MULTIEXP - to perform multiexponentiation on a curve twist defined over quadratic extension of the base field +- BLS12_377_PAIRING - to perform a pairing operations between a set of *pairs* of (G1, G2) points + +Multiexponentiation operation is included to efficiently aggregate public keys or individual signer's signatures during BLS signature verification. + +### Proposed addresses table + +|Precompile |Address | +|---|---| +|BLS12_377_G1ADD | 0x13 | +|BLS12_377_G1MUL | 0x14 | +|BLS12_377_G1MULTIEXP | 0x15 | +|BLS12_377_G2ADD | 0x16 | +|BLS12_377_G2MUL | 0x17 | +|BLS12_377_G2MULTIEXP | 0x18 | +|BLS12_377_PAIRING | 0x19 | + +## Motivation +Motivation of this precompile is to add a cryptographic primitive that allows to get 120+ bits of security for operations over pairing friendly curve compared to the existing BN254 precompile that only provides 80 bits of security. In addition it allows efficient one-time recursive proof aggregations, e.g. proofs about existence of BLS12-377 based signature. + +## Specification + +Curve parameters: + +BLS12-377 curve is fully defined by the following set of parameters (coefficient `A=0` for all BLS12 curves): + +``` +Base field modulus = 0x01ae3a4617c510eac63b05c06ca1493b1a22d9f300f5138f1ef3622fba094800170b5d44300000008508c00000000001 +B coefficient = 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 +Main subgroup order = 0x12ab655e9a2ca55660b44d1e5c37b00159aa76fed00000010a11800000000001 +Extension tower: +Fp2 construction: +Fp quadratic non-residue = 0x01ae3a4617c510eac63b05c06ca1493b1a22d9f300f5138f1ef3622fba094800170b5d44300000008508bffffffffffc +Fp6/Fp12 construction: +Fp2 cubic non-residue c0 = 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +Fp2 cubic non-residue c1 = 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 +Twist parameters: +Twist type: D +B coefficient for twist c0 = 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +B coefficient for twist c1 = 0x010222f6db0fd6f343bd03737460c589dc7b4f91cd5fd889129207b63c6bf8000dd39e5c1ccccccd1c9ed9999999999a +Generators: +G1: +X = 0x008848defe740a67c8fc6225bf87ff5485951e2caa9d41bb188282c8bd37cb5cd5481512ffcd394eeab9b16eb21be9ef +Y = 0x01914a69c5102eff1f674f5d30afeec4bd7fb348ca3e52d96d182ad44fb82305c2fe3d3634a9591afd82de55559c8ea6 +G2: +X c0 = 0x018480be71c785fec89630a2a3841d01c565f071203e50317ea501f557db6b9b71889f52bb53540274e3e48f7c005196 +X c1 = 0x00ea6040e700403170dc5a51b1b140d5532777ee6651cecbe7223ece0799c9de5cf89984bff76fe6b26bfefa6ea16afe +Y c0 = 0x00690d665d446f7bd960736bcbb2efb4de03ed7274b49a58e458c282f832d204f2cf88886d8c7c2ef094094409fd4ddf +Y c1 = 0x00f8169fd28355189e549da3151a70aa61ef11ac3d591bf12463b01acee304c24279b83f5e52270bd9a1cdd185eb8f93 +Pairing parameters: +|x| (miller loop scalar) = 0x8508c00000000001 +x is negative = false +``` + +#### Fine points and encoding of base elements + +##### Field elements encoding: + +To encode points involved in the operation one has to encode elements of the base field and the extension field. + +Base field element (Fp) is encoded as `64` bytes by performing BigEndian encoding of the corresponding (unsigned) integer (top `16` bytes are always zeroes). `64` bytes are chosen to have `32` byte aligned ABI (representable as e.g. `bytes32[2]` or `uint256[2]`). Corresponding integer **must** be less than field modulus. + +For elements of the quadratic extension field (Fp2) encoding is byte concatenation of individual encoding of the coefficients totaling in `128` bytes for a total encoding. For an Fp2 element in a form `el = c0 + c1 * v` where `v` is formal quadratic non-residue and `c0` and `c1` are Fp elements the corresponding byte encoding will be `encode(c0) || encode(c1)` where `||` means byte concatenation (or one can use `bytes32[4]` or `uint256[4]` in terms of Solidity types). + +If encodings do not follow this spec anywhere during parsing in the precompile the precompile *must* return an error. + +##### Encoding of points in G1/G2: + +Points in either G1 (in base field) or in G2 (in extension field) are encoded as byte concatenation of encodings of the `x` and `y` affine coordinates. Total encoding length for G1 point is thus `128` bytes and for G2 point is `256` bytes. + +##### Point of infinity encoding: + +Also referred as "zero point". For BLS12 curves point with coordinates `(0, 0)` (formal zeroes in Fp or Fp2) is *not* on the curve, so encoding of such point `(0, 0)` is used as a convention to encode point of infinity. + +##### Encoding of scalars for multiplication operation: + +Scalar for multiplication operation is encoded as `32` bytes by performing BigEndian encoding of the corresponding (unsigned) integer. Corresponding integer is **not** required to be less than or equal than main subgroup size. + +#### ABI for operations + +##### ABI for G1 addition + +G1 addition call expects `256` bytes as an input that is interpreted as byte concatenation of two G1 points (`128` bytes each). Output is an encoding of addition operation result - single G1 point (`128` bytes). + +Error cases: +- Either of points being not on the curve must result in error +- Field elements encoding rules apply (obviously) +- Input has invalid length + +##### ABI for G1 multiplication + +G1 multiplication call expects `160` bytes as an input that is interpreted as byte concatenation of encoding of G1 point (`128` bytes) and encoding of a scalar value (`32` bytes). Output is an encoding of multiplication operation result - single G1 point (`128` bytes). + +Error cases: +- Point being not on the curve must result in error +- Field elements encoding rules apply (obviously) +- Input has invalid length + +##### ABI for G1 multiexponentiation + +G1 multiexponentiation call expects `160*k` bytes as an input that is interpreted as byte concatenation of `k` slices each of them being a byte concatenation of encoding of G1 point (`128` bytes) and encoding of a scalar value (`32` bytes). Output is an encoding of multiexponentiation operation result - single G1 point (`128` bytes). + +Error cases: +- Any of G1 points being not on the curve must result in error +- Field elements encoding rules apply (obviously) +- Input has invalid length + +##### ABI for G2 addition + +G2 addition call expects `512` bytes as an input that is interpreted as byte concatenation of two G2 points (`256` bytes each). Output is an encoding of addition operation result - single G2 point (`256` bytes). + +Error cases: +- Either of points being not on the curve must result in error +- Field elements encoding rules apply (obviously) +- Input has invalid length + +##### ABI for G2 multiplication + +G2 multiplication call expects `288` bytes as an input that is interpreted as byte concatenation of encoding of G2 point (`256` bytes) and encoding of a scalar value (`32` bytes). Output is an encoding of multiplication operation result - single G2 point (`256` bytes). + +Error cases: +- Point being not on the curve must result in error +- Field elements encoding rules apply (obviously) +- Input has invalid length + +##### ABI for G2 multiexponentiation + +G2 multiexponentiation call expects `288*k` bytes as an input that is interpreted as byte concatenation of `k` slices each of them being a byte concatenation of encoding of G2 point (`256` bytes) and encoding of a scalar value (`32` bytes). Output is an encoding of multiexponentiation operation result - single G2 point (`256` bytes). + +Error cases: +- Any of G2 points being not on the curve must result in error +- Field elements encoding rules apply (obviously) +- Input has invalid length + +##### ABI for pairing + +Pairing call expects `384*k` bytes as an inputs that is interpreted as byte concatenation of `k` slices. Each slice has the following structure: +- `128` bytes of G1 point encoding +- `256` bytes of G2 point encoding + +Output is a `32` bytes where first `31` bytes are equal to `0x00` and the last byte is `0x01` if pairing result is equal to multiplicative identity in a pairing target field and `0x00` otherwise. + +Error cases: +- Invalid encoding of any boolean variable must result in error +- Any of G1 or G2 points being not on the curve must result in error +- Any of G1 or G2 points are not in the correct subgroup +- Field elements encoding rules apply (obviously) +- Input has invalid length + +#### Prevention of DDoS on error handling + +This precompile performs extensive computations and in case of any errors during execution it MUST consume all gas from the the gas schedule for the corresponding operation. + +#### Gas schedule + +Assuming a constant `30 MGas/second` following prices are suggested. + +##### G1 addition + +`600` gas + +##### G1 multiplication + +`12000` gas + +##### G2 addition + +`4500` gas + +##### G2 multiplication + +`55000` gas + +##### G1/G2 Multiexponentiation + +Multiexponentiations are expected to be performed by the Peppinger algorithm (we can also say that is **must** be performed by Peppinger algorithm to have a speedup that results in a discount over naive implementation by multiplying each pair separately and adding the results). For this case there was a table prepared for discount in case of `k <= 128` points in the multiexponentiation with a discount cup `max_discount` for `k > 128`. + +To avoid non-integer arithmetic call cost is calculated as `k * multiplication_cost * discount / multiplier` where `multiplier = 1000`, `k` is a number of (scalar, point) pairs for the call, `multiplication_cost` is a corresponding single multiplication call cost for G1/G2. + +Discounts table as a vector of pairs `[k, discount]`: + +``` +[[1, 1200], [2, 888], [3, 764], [4, 641], [5, 594], [6, 547], [7, 500], [8, 453], [9, 438], [10, 423], [11, 408], [12, 394], [13, 379], [14, 364], [15, 349], [16, 334], [17, 330], [18, 326], [19, 322], [20, 318], [21, 314], [22, 310], [23, 306], [24, 302], [25, 298], [26, 294], [27, 289], [28, 285], [29, 281], [30, 277], [31, 273], [32, 269], [33, 268], [34, 266], [35, 265], [36, 263], [37, 262], [38, 260], [39, 259], [40, 257], [41, 256], [42, 254], [43, 253], [44, 251], [45, 250], [46, 248], [47, 247], [48, 245], [49, 244], [50, 242], [51, 241], [52, 239], [53, 238], [54, 236], [55, 235], [56, 233], [57, 232], [58, 231], [59, 229], [60, 228], [61, 226], [62, 225], [63, 223], [64, 222], [65, 221], [66, 220], [67, 219], [68, 219], [69, 218], [70, 217], [71, 216], [72, 216], [73, 215], [74, 214], [75, 213], [76, 213], [77, 212], [78, 211], [79, 211], [80, 210], [81, 209], [82, 208], [83, 208], [84, 207], [85, 206], [86, 205], [87, 205], [88, 204], [89, 203], [90, 202], [91, 202], [92, 201], [93, 200], [94, 199], [95, 199], [96, 198], [97, 197], [98, 196], [99, 196], [100, 195], [101, 194], [102, 193], [103, 193], [104, 192], [105, 191], [106, 191], [107, 190], [108, 189], [109, 188], [110, 188], [111, 187], [112, 186], [113, 185], [114, 185], [115, 184], [116, 183], [117, 182], [118, 182], [119, 181], [120, 180], [121, 179], [122, 179], [123, 178], [124, 177], [125, 176], [126, 176], [127, 175], [128, 174]] +``` + +`max_discount = 174` + +##### Pairing operation + +Cost of the pairing operation is `55000*k + 65000` where `k` is a number of pairs. + +## Rationale +Motivation section covers a total motivation to have operations over BLS12-377 curve available. We also extend a rationale for move specific fine points. + +#### Multiexponentiation as a separate call + +Explicit separate multiexponentiation operation that allows one to save execution time (so gas) by both the algorithm used (namely Peppinger algorithm) and (usually forgotten) by the fact that `CALL` operation in Ethereum is expensive (at the time of writing), so one would have to pay non-negigible overhead if e.g. for multiexponentiation of `100` points would have to call the multipication precompile `100` times and addition for `99` times (roughly `138600` would be saved). + +## Backwards Compatibility +There are no backward compatibility questions. + +## Important notes + +### Subgroup checks + +Subgroup check **is mandatory** during the pairing call. Implementations *should* use fast subgroup checks: at the time of writing multiplication gas cost is based on `double-and-add` multiplication method that has a clear "worst case" (all bits are equal to one). For pairing operation it's expected that implementation uses faster subgroup check, e.g. by using wNAF multiplication method for elliptic curves that is ~ `40%` cheaper with windows size equal to 4. (Tested empirically. Savings are due to lower hamming weight of the group order and even lower hamming weight for wNAF. Concretely, subgroup check for both G1 and G2 points in a pair are around `35000` combined). + +## Test Cases + +Due to the large test parameters space we first provide properties that various operations must satisfy. We use additive notation for point operations, capital letters (`P`, `Q`) for points, small letters (`a`, `b`) for scalars. Generator for G1 is labeled as `G`, generator for G2 is labeled as `H`, otherwise we assume random point on a curve in a correct subgroup. `0` means either scalar zero or point of infinity. `1` means either scalar one or multiplicative identity. `group_order` is a main subgroup order. `e(P, Q)` means pairing operation where `P` is in G1, `Q` is in G2. + +Requeired properties for basic ops (add/multiply): + +- Commutativity: `P + Q = Q + P` +- Additive negation: `P + (-P) = 0` +- Doubling `P + P = 2*P` +- Subgroup check: `group_order * P = 0` +- Trivial multiplication check: `1 * P = P` +- Multiplication by zero: `0 * P = 0` +- Multiplication by the unnormalized scalar `(scalar + group_order) * P = scalar * P` + +Required properties for pairing operation: +- Degeneracy `e(P, 0*Q) = e(0*P, Q) = 1` +- Bilinearity `e(a*P, b*Q) = e(a*b*P, Q) = e(P, a*b*Q)` (internal test, not visible through ABI) + +Test vector for all operations are expanded in this `csv` files in [repo](https://github.com/matter-labs/eip1962/tree/master/src/test/test_vectors/eip2537). + +## Implementation +There is a various choice of existing implementations of the curve operations. It may require extra work to add an ABI: +- EIP1962 code bases with fixed parameters + - [Rust](https://github.com/matter-labs/eip1962) + - [C++](https://github.com/matter-labs/eip1962_cpp) +- Original implementation linked in Zexe paper in [Rust](https://github.com/scipr-lab/zexe) +- Standalone in [Go](https://github.com/kilic/bls12-377) + +## Security Considerations +Strictly following the spec will eliminate security implications or consensus implications in a contrast to the previous BN254 precompile. + +Important topic is a "constant time" property for performed operations. We explicitly state that this precompile **IS NOT REQUIRED** to perform all the operations using constant time algorithms. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). \ No newline at end of file diff --git a/EIPS/eip-2542.md b/EIPS/eip-2542.md new file mode 100644 index 0000000..5580dca --- /dev/null +++ b/EIPS/eip-2542.md @@ -0,0 +1,87 @@ +--- +eip: 2542 +title: New opcodes TXGASLIMIT and CALLGASLIMIT +author: Alex Forshtat +discussions-to: https://ethereum-magicians.org/t/eip-2542-add-txgaslimit-callgaslimit-txgasrefund-opcodes +status: Stagnant +type: Standards Track +category: Core +created: 2020-02-29 +--- + +## Simple Summary +A mechanism to allow smart contracts to access information on gas limits for the current transaction and execution frame. + +## Abstract +Currently, there is an existing opcode `0x45 GASLIMIT` that provides access to the block gas limit. While this information may be useful in some cases, it is probably not a value that smart contract developers may be concerned about. The opcode `0x5a GAS` provides the remaining gas, not the initial one. Also, it is worth noting how existing `0x32 ORIGIN`, `0x33 CALLER`, `0x34 CALLVALUE` and `0x3a GASPRICE` opcodes set a pattern of having access to both the transaction and current execution frame state. +TBD: as 0x30 opcode range is exhausted, the proposed opcodes can be added to 0x50 range, or a new range can be added. + +## Motivation +As concepts of relaying, meta-transactions, gas fees, and account abstraction gain popularity, it becomes critical for some contracts to be able to track gas expenditure with absolute precision. Without access to this data on an EVM level, such contracts resort to approximation, mimicking EVM logic on-chain, and some use-cases even become infeasible. + +## Specification +If block.number >= TBD, add three new opcodes: + +TXGASLIMIT: 0x5c + +Pushes the gas limit of the entire transaction onto the stack. This is a value of the 'startgas' parameter signed by the externally owned account. +Gas costs: 2 (same as `GASLIMIT`) + +CALLGASLIMIT: 0x5d + +Pushes the gas limit of the current execution frame onto the stack. This is the 'callGas' value that was obtained after the application of the EIP-150 “all but one 64th” rule. +Gas costs: 2 (same as `GASLIMIT`) + +Also, consider renaming `0x45 GASLIMIT` to `BLOCKGASLIMIT` to avoid confusion. + +## Rationale +Consider a solidity smart contract that wants to know how much gas the entire transaction or a part of it had consumed. It is not entirely possible with the current EVM. With proposed changes, using a pseudo-Solidity syntax, this information would be easily available: +``` +function keepTrackOfGas(string memory message, uint256 number) public { + ... + uint gasUsed = msg.gasLimit - gasleft(); +} +``` +This is an extremely common use case, and multiple implementations suffer from not taking the non-accessible expenses into consideration. The state-of-the-art solution for the `gasUsed` problem is to access 'gasleft()' as the first line of your smart contract. +Note how variable transaction input size means the gas used by the transaction depends on the number of zero and non-zero bytes of input, as well `GTXDATANONZERO`. Another issue is that Solidity handles `public` methods by loading the entire input from `calldata` to `memory`, spending an unpredictable amount of gas. + +Another application is for a method to have a requirement for a gas limit given to it. This situation arises quite commonly in the context of meta-transactions, where the msg.sender's account holder may not be too interested in the inner transaction's success. Exaggerated pseudocode: + +``` +function verifyGasLimit(uint256 desiredGasLimit, bytes memory signature, address signer, bytes memory someOtherData) public { + require(ecrecover(abi.encodePacked(desiredGasLimit, someOtherData), signature) == signer, "Signature does not match"); + require(tx.gasLimit == desiredGasLimit, "Transaction limit does not match the signed value. The signer did not authorize that."); + ... +} +``` +In this situation it is not possible to rely on 'gasleft()' value, because it is dynamic, depends on opcode and calldata pricing, and cannot be signed. + + +## Backwards Compatibility +This proposal introduces two new opcodes and renames an existing one, but stays fully backwards compatible apart from that. + +## Forwards Compatibility +A major consideration for this proposal is its alignment with one or many possible future modifications to the EVM: + +1. EIP-2489 Deprecate the GAS opcode (a.k.a. 39-UNGAS proposal) + There is a sentiment that the ability of smart contracts to perform "gas introspection" leads to the contracts being dependent on current opcode pricing. + While criticizing said misconception is beyond the scope of this EIP, in case there is a need to make a breaking change to the behavior of the existing `0x5a GAS` opcode, the same considerations will apply to the proposed opcodes. This means this EIP does not add any new restraints on EMV evolution. + +2. Stateless Ethereum + The UNGAS proposal is said to be related to the ongoing project of Stateless Ethereum. It’s not strictly necessary for stateless Ethereum, but it is an idea for how to make future breaking changes to gas schedules easier. + As long as the concept of 'gas limit' is part of the EVM, the author sees no reason proposed opcodes would conflict with Stateless Ethereum. Gas schedules are not exposed by this proposal. + +3. Comparison with other controversial opcodes + There are opcodes that are not proposed for deprecation but face criticism. Examples include `0x32 ORIGIN` being misused by smart contract developers, or `0x46 CHAINID` making some smart-contracts 'unforkable'. + This EIP neither encourages nor enables any bad security practices, and does not introduce any concepts that are new for EVM either. + +## Security considerations + +Existing smart contracts are not affected by this change. +Smart contracts that will use proposed opcodes must not use them for the core of any security features, but only as a source of information about their execution environment. + +## 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. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2544.md b/EIPS/eip-2544.md new file mode 100644 index 0000000..4df16b5 --- /dev/null +++ b/EIPS/eip-2544.md @@ -0,0 +1,126 @@ +--- +eip: 2544 +title: ENS Wildcard Resolution +description: Adds support for "wildcard" resolution of subdomains in ENS. +author: Nick Johnson (@arachnid), 0age (@0age) +discussions-to: https://ethereum-magicians.org/t/eip-2544-ens-wildcard-resolution +status: Stagnant +type: Standards Track +category: ERC +created: 2020-02-28 +requires: 137 +--- + +## Abstract + +The Ethereum Name Service Specification (EIP-137) establishes a two-step name resolution process. First, an ENS client performs the namehash algorithm on the name to determine the associated "node", and supplies that node to the ENS Registry contract to determine the resolver. Then, if a resolver has been set on the Registry, the client supplies that same node to the resolver contract, which will return the associated address or other record. + +As currently specified, this process terminates if a resolver is not set on the ENS Registry for a given node. This EIP changes the name resolution process by adding an additional step if a resolver is not set for a domain. This step strips out the leftmost label from the name, derives the node of the new fragment, and supplies that node to the ENS Registry. If a resolver is located for that node, the client supplies the original, complete node to that resolver contract to derive the relevant records. This step is repeated until a node with a resolver is found. + +Further, this specification defines a new way for resolvers to resolve names, using a unified `resolve()` method that permits more flexible handling of name resolution. + +## Motivation + +Many applications such as wallet providers, exchanges, and dapps have expressed a desire to issue ENS names for their users via custom subdomains on a shared parent domain. However, the cost of doing so is currently prohibitive for large user bases, as a distinct record must be set on the ENS Registry for each subdomain. + +Furthermore, users cannot immediately utilize these subdomains upon account creation, as the transaction to assign a resolver for the node of the subdomain must first be submitted and mined on-chain. This adds unnecessary friction when onboarding new users, who coincidentally would often benefit greatly from the usability improvements afforded by an ENS name. + +Enabling wildcard support allows for the design of more advanced resolvers that deterministically generate addresses and other records for unassigned subdomains. The generated addresses could map to counterfactual contract deployment addresses (i.e. `CREATE2` addresses), to designated "fallback" addresses, or other schemes. Additionally, individual resolvers would still be assignable to any given subdomain, which would supersede the wildcard resolution using the parent resolver. + +Another critical motivation with EIP-2544 is to enable wildcard resolution in a backwards-compatible fashion. It does not require modifying the current ENS Registry contract or any existing resolvers, and continues to support existing ENS records — legacy ENS clients would simply fail to resolve wildcard records. + +## Specification + +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. + +Let: + - `namehash` be the algorithm defined in EIP 137. + - `dnsencode` be the process for encoding DNS names specified in section 3.1 of RFC1035, with the exception that there is no limit on the total length of the encoded name. The empty string is encoded identically to the name '.', as a single 0-octet. + - `parent` be a function that removes the first label from a name (eg, `parent('foo.eth') = 'eth'`). `parent('tld')` is defined as the empty string ''. + - `ens` is the ENS registry contract for the current network. + +EIP-2544-compliant ENS resolvers MAY implement the following function interface: + +``` +interface ExtendedResolver { + function resolve(bytes calldata name, bytes calldata data) external view returns(bytes); +} +``` + +If a resolver implements this function, it MUST return true when `supportsInterface()` is called on it with the interface's ID, 0xTBD. + +ENS clients will call `resolve` with the DNS-encoded name to resolve and the encoded calldata for a resolver function (as specified in EIP-137 and elsewhere); the function MUST either return valid return data for that function, or revert if it is not supported. + +EIP-2544-compliant ENS clients MUST perform the following procedure when determining the resolver for a given name: + +1. Set `currentname = name` +2. Set `resolver = ens.resolver(namehash(currentname))` +3. If `resolver` is not the zero address, halt and return `resolver`. +4. If `name` is the empty name ('' or '.'), halt and return null. +5. Otherwise, set `currentname = parent(currentname)` and go to 2. + +If the procedure above returns null, name resolution MUST terminate unsuccessfully. Otherwise, EIP-2544-compliant ENS clients MUST perform the following procedure when resolving a record: + +1. Set `calldata` to the ABI-encoded call data for the resolution function required - for example, the ABI encoding of `addr(namehash(name))` when resolving the `addr` record. +2. Set `supports2544 = resolver.supportsInterface(0xTBD)`. +3. If `supports2544` is true, set `result = resolver.resolve(dnsencode(name), calldata)` +4. Otherwise, set `result` to the result of calling `resolver` with `calldata`. +5. Return `result` after decoding it using the return data ABI of the corresponding resolution function (eg, for `addr()`, ABI-decode the result of `resolver.resolve()` as an `address`). + +Note that in all cases the resolution function (`addr()` etc) and the `resolve` function are supplied the original `name`, *not* the `currentname` found in the first stage of resolution. + +### Pseudocode +``` +function getResolver(name) { + for(let currentname = name; currentname !== ''; currentname = parent(currentname)) { + const node = namehash(currentname); + const resolver = ens.resolver(node); + if(resolver != '0x0000000000000000000000000000000000000000') { + return resolver; + } + } + return null; +} + +function resolve(name, func, ...args) { + const resolver = getResolver(name); + if(resolver === null) { + return null; + } + const supports2544 = resolver.supportsInterface('0xTBD'); + let result; + if(supports2544) { + const calldata = resolver[func].encodeFunctionCall(namehash(name), ...args); + result = resolver.resolve(dnsencode(name), calldata); + return resolver[func].decodeReturnData(result); + } else { + return resolver[func](...args); + } +} +``` + +## Rationale + +The proposed implementation supports wildcard resolution in a manner that minimizes the impact to existing systems. It also reuses existing algorithms and procedures to the greatest possible extent, thereby easing the burden placed on authors and maintainers of various ENS clients. + +It also recognizes an existing consensus concerning the desirability of wildcard resolution for ENS, enabling more widespread adoption of the original specification by solving for a key scalability obstacle. + +While introducing an optional `resolve` function for resolvers, taking the unhashed name and calldata for a resolution function increases implementation complexity, it provides a means for resolvers to obtain plaintext labels and act accordingly, which enables many wildcard-related use-cases that would otherwise not be possible - for example, a wildcard resolver could resolve `id.nifty.eth` to the owner of the NFT with id `id` in some collection. With only namehashes to work with, this is not possible. Resolvers with simpler requirements can continue to simply implement resolution functions directly and omit support for the `resolve` function entirely. + +The DNS wire format is used for encoding names as it permits quick and gas-efficient hashing of names, as well as other common operations such as fetching or removing individual labels; in contrast, dot-separated names require iterating over every character in the name to find the delimiter. + +## Backwards Compatibility + +Existing ENS clients that are compliant with EIP-137 will fail to resolve wildcard records and refuse to interact with them, while those compliant with EIP-2544 will continue to correctly resolve, or reject, existing ENS records. Resolvers wishing to implement the new `resolve` function for non-wildcard use-cases (eg, where the resolver is set directly on the name being resolved) should consider what to return to legacy clients that call the individual resolution functions for maximum compatibility. + +## Security Considerations + +While compliant ENS clients will continue to refuse to resolve records without a resolver, there is still the risk that an improperly-configured client will refer to an incorrect resolver, or will not reject interactions with the null address when a resolver cannot be located. + +Additionally, resolvers supporting completely arbitrary wildcard subdomain resolution will increase the likelihood of funds being sent to unintended recipients as a result of typos. Applications that implement such resolvers should consider making additional name validation available to clients depending on the context, or implementing features that support recoverability of funds. + +There is also the possibility that some applications might require that no resolver be set for certain subdomains. For this to be problematic, the parent domain would need to successfully resolve the given subdomain node — to the knowledge of the authors, no application currently supports this feature or expects that subdomains should not resolve to a record. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2565.md b/EIPS/eip-2565.md new file mode 100644 index 0000000..bfd8331 --- /dev/null +++ b/EIPS/eip-2565.md @@ -0,0 +1,104 @@ +--- +eip: 2565 +title: ModExp Gas Cost +author: Kelly Olson (@ineffectualproperty), Sean Gulley (@sean-sn), Simon Peffers (@simonatsn), Justin Drake (@justindrake), Dankrad Feist (@dankrad) +discussions-to: https://ethereum-magicians.org/t/big-integer-modular-exponentiation-eip-198-gas-cost/4150 +status: Final +type: Standards Track +category: Core +created: 2020-03-20 +requires: 198 +--- + +## Simple Summary +Defines the gas cost of the `ModExp` (`0x00..05`) precompile. + +## Abstract +To accurately reflect the real world operational cost of the `ModExp` precompile, this EIP specifies an algorithm for calculating the gas cost. This algorithm approximates the multiplication complexity cost and multiplies that by an approximation of the iterations required to execute the exponentiation. + +## Motivation +Modular exponentiation is a foundational arithmetic operation for many cryptographic functions including signatures, VDFs, SNARKs, accumulators, and more. Unfortunately, the ModExp precompile is currently over-priced, making these operations inefficient and expensive. By reducing the cost of this precompile, these cryptographic functions become more practical, enabling improved security, stronger randomness (VDFs), and more. + +## Specification +As of `FORK_BLOCK_NUMBER`, the gas cost of calling the precompile at address `0x0000000000000000000000000000000000000005` will be calculated as follows: +``` +def calculate_multiplication_complexity(base_length, modulus_length): + max_length = max(base_length, modulus_length) + words = math.ceil(max_length / 8) + return words**2 + +def calculate_iteration_count(exponent_length, exponent): + iteration_count = 0 + if exponent_length <= 32 and exponent == 0: iteration_count = 0 + elif exponent_length <= 32: iteration_count = exponent.bit_length() - 1 + elif exponent_length > 32: iteration_count = (8 * (exponent_length - 32)) + ((exponent & (2**256 - 1)).bit_length() - 1) + return max(iteration_count, 1) + +def calculate_gas_cost(base_length, modulus_length, exponent_length, exponent): + multiplication_complexity = calculate_multiplication_complexity(base_length, modulus_length) + iteration_count = calculate_iteration_count(exponent_length, exponent) + return max(200, math.floor(multiplication_complexity * iteration_count / 3)) +``` + +## Rationale +After benchmarking the ModExp precompile, we discovered that it is ‘overpriced’ relative to other precompiles. We also discovered that the current gas pricing formula could be improved to better estimate the computational complexity of various ModExp input variables. The following changes improve the accuracy of the `ModExp` pricing: + +### 1. Modify ‘computational complexity’ formula to better reflect the computational complexity +The complexity function defined in [EIP-198](./eip-198.md) is as follow: + +``` +def mult_complexity(x): + if x <= 64: return x ** 2 + elif x <= 1024: return x ** 2 // 4 + 96 * x - 3072 + else: return x ** 2 // 16 + 480 * x - 199680 +``` +where is `x` is `max(length_of_MODULUS, length_of_BASE)` + +The complexity formula in [EIP-198](./eip-198.md) was meant to approximate the difficulty of Karatsuba multiplication. However, we found a better approximation for modelling modular exponentiation. In the complexity formula defined in this EIP, `x` is divided by 8 to account for the number of limbs in multiprecision arithmetic. A comparison of the current ‘complexity’ function and the proposed function against the execution time can be seen below: + +![Option 1 Graph](../assets/eip-2565/Complexity_Regression.png) + +The complexity function defined here has a better fit vs. the execution time when compared to the [EIP-198](./eip-198.md) complexity function. This better fit is because this complexity formula accounts for the use of binary exponentiation algorithms that are used by ‘bigint’ libraries for large exponents. You may also notice the regression line of the proposed complexity function bisects the test vector data points. This is because the run time varies depending on if the modulus is even or odd. + +### 2. Change the value of GQUADDIVISOR +After changing the 'computational complexity' formula in [EIP-198](./eip-198.md) to the one defined here it is necessary to change `QGUADDIVSOR` to bring the gas costs inline with their runtime. By setting the `QGUADDIVISOR` to `3` the cost of the ModExp precompile will have a higher cost (gas/second) than other precompiles such as ECRecover. + +![Option 2 Graph](../assets/eip-2565/GQuad_Change.png) + +### 3. Set a minimum gas cost to prevent abuse +This prevents the precompile from underpricing small input values. + +## Test Cases +There are no changes to the underlying interface or arithmetic algorithms, so the existing test vectors can be reused. Below is a table with the updated test vectors: + +| Test Case | EIP-198 Pricing | EIP-2565 Pricing | +| ------------- | ------------- | ------------- | +| modexp_nagydani_1_square | 204 | 200 | +| modexp_nagydani_1_qube | 204 | 200 | +| modexp_nagydani_1_pow0x10001 | 3276 | 341 | +| modexp_nagydani_2_square | 665 | 200 | +| modexp_nagydani_2_qube | 665 | 200 | +| modexp_nagydani_2_pow0x10001 | 10649 | 1365 | +| modexp_nagydani_3_square | 1894 | 341 | +| modexp_nagydani_3_qube | 1894 | 341 | +| modexp_nagydani_3_pow0x10001 | 30310 | 5461 | +| modexp_nagydani_4_square | 5580 | 1365 | +| modexp_nagydani_4_qube | 5580 | 1365 | +| modexp_nagydani_4_pow0x10001 | 89292 | 21845 | +| modexp_nagydani_5_square | 17868 | 5461 | +| modexp_nagydani_5_qube | 17868 | 5461 | +| modexp_nagydani_5_pow0x10001 | 285900 | 87381 | + +## Implementations +[Geth](https://github.com/ethereum/go-ethereum/pull/21607) + +[Python](https://gist.github.com/ineffectualproperty/60e34f15c31850c5b60c8cf3a28cd423) + +## Security Considerations +The biggest security consideration for this EIP is creating a potential DoS vector by making ModExp operations too inexpensive relative to their computation time. + +## References +[EIP-198](./eip-198.md) + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2566.md b/EIPS/eip-2566.md new file mode 100644 index 0000000..27ad65a --- /dev/null +++ b/EIPS/eip-2566.md @@ -0,0 +1,106 @@ +--- +eip: 2566 +title: Human Readable Parameters for Contract Function Execution +author: Joseph Stockermans (@jstoxrocky) +discussions-to: https://ethereum-magicians.org/t/human-readable-parameters-for-contract-function-execution/4154 +status: Stagnant +type: Standards Track +category: Interface +created: 2020-03-23 +--- + +## Simple Summary +New Ethereum RPC method `eth_sendTransactionToContractFunction` that parallels `eth_sendTransaction` but allows for human-readable contract function execution data to be displayed to users. + +## Abstract +When a dapp prompts a user to execute a smart contract function via a ProviderWallet, confirmation screens displayed in the ProviderWallet layer cannot display the human readable details of the function to be called and the arguments to be passed. This is because the Ethereum RPC method used for contract function execution (`eth_sendTransaction`) accepts information about what function to call in a non-human readable (and non-recoverable) format. As such, when a ProviderWallet receives this non-human readable information from a dapp, they are unable to display a human readable version since they never received one and cannot recover one from the data. + +This creates a poor and potentially dangerous user experience. For example, a malicious dapp could swap out the `address` argument in a token contract's `transfer(address,uint256)` function and reroute the tokens intended for someone else to themselves. This sleight-of-hand would be quiet and unlikely to be picked up by a casual user glancing over the non-human readable data. By adding a new Ethereum RPC method (`eth_sendTransactionToContractFunction`) that accepts the function ABI, ProviderWallets can recreate and display the human readable details of contract function execution to users. + +## Motivation +### ProviderWallet Definition +ProviderWallets like Metamask and Geth are hybrid software that combine an Ethereum API provider with an Ethereum wallet. This allows them to sign transactions on behalf of their users and also broadcast those signed transactions to the Ethereum network. ProviderWallets are used for both convenience and for the protection they give users through human readable confirmation prompts. + +### Existing Solutions +Much discussion has been made in the past few years on the topic of human readable Ethereum transaction data. Aragon's [Radspec](https://github.com/aragon/radspec) addresses this issue by requiring contract developers to amend their contract functions with human readable comments. ProviderWallets can then use Aragon's Radspec software to parse these comments from the contract code and display them to the end user - substituting in argument values where necessary. Unfortunately, this approach cannot work with contracts that do not have Radspec comments (and may require integration with IPFS). + +[EIP 1138](https://github.com/ethereum/EIPs/issues/1138) also addresses this issue directly but contains serious security issues - allowing untrusted dapps to generate the human readable message displayed to users. In a similar train of thought, [Geth's #2940 PR](https://github.com/ethereum/go-ethereum/pull/2940) and [EIPs 191](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-191.md), [712](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md) all highlight the Ethereum community's desire for ProviderWallets to better inform users about what data they are actually acting upon. + +Finally, the ProviderWallet Metamask already includes some built-in magic for interactions with ERC20 contracts that allows confirmation prompts to display the intended *token* recipient and *token* value. Although this is accomplished in an ad hoc fashion for ERC20-like contracts only, the motivation is the same: users deserve better information about the execution of contract functions they are relying on ProviderWallets to perform. + +### Background +At one point or another, a dapp will ask a user to interact with a contract. The interaction between dapps and contracts is a large part of the Ethereum ecosystem and is most commonly brokered by a ProviderWallet. When a dapp asks a user to interact with a contract, it will do so by sending the `eth_sendTransaction` method name to the Ethereum API exposed by a ProviderWallet along with the relevant transaction data. The `data` field of the transaction data contains the information necessary for the Ethereum virtual machine to identify and execute the contract's function. This field has a specific formatting that is both non-human readable and non-recoverable to its human readable state. + +The accepted format for `eth_sendTransaction`'s `data` field is the hexadecimal encoding of the first four bytes of the keccak256 digest of the function signature. This abbreviated hash is then concatenated with the ABI encoded arguments to the function. Since the keccak256 digest of the function signature cannot be converted back into the function signature, the `data` field is not only non-human readable, its non-recoverable as well. On top of this, additional insight into the concatenated argument values is further obfuscated as information about their data types are held in the function signature preimage. + +## Specification +This EIP proposes increasing the set of Ethereum RPC methods to include a new method - `eth_sendTransactionToContractFunction`. This method parallels `eth_sendTransaction` with the only difference being the inclusion of the contract function's `abi` field. + +Parameters + +1. `Object` - The transaction object + * `from`: `DATA`, 20 Bytes - The address the transaction is sent from. + * `to`: `DATA`, 20 Bytes - (optional when creating new contract) The address the transaction is directed to. + * `gas`: `QUANTITY` - (optional, default: 90000) Integer of the gas provided for the transaction execution. It will return unused gas. + * `gasPrice`: `QUANTITY` - (optional, default: To-Be-Determined) Integer of the gasPrice used for each paid gas + * `value`: `QUANTITY` - (optional) Integer of the value sent with this transaction + * `data`: `DATA` - The hash of the invoked method signature and encoded parameters + * `abi`: `DATA` - The function ABI + * `nonce`: `QUANTITY` - (optional) Integer of a nonce. This allows to overwrite your own pending transactions that use the same nonce. + +Example Parameters +``` + params: [{ + "from": "0x69e6F1b01f34A702Ce63bA6EF83c64fAEC37a227", + "to": "0xe44127f6fA8A00ee0228730a630fc1F3162C4d52", + "gas": "0x76c0", // 30400 + "gasPrice": "0x9184e72a000", // 10000000000000 + "value": "0x9184e72a", // 2441406250 + "abi": "{ + "inputs": [{ + "name": "_address", + "type": "address" + }, { + "name": "_value", + "type": "uint256" + }], + "name": "transferTokens", + "outputs": [{ + "name": "success", + "type": "bool" + }], + "stateMutability": "nonpayable", + "type": "function" + }", + "data": "0xbec3fa170000000000000000000000006Aa89e52c9a826496A8f311c1a9db62fd477E256000000000000000000000000000000000000000000000000000000174876E800" + }] +``` + +Returns +DATA, 32 Bytes - the transaction hash, or the zero hash if the transaction is not yet available. + +Example +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"eth_sendTransactionToContractFunction","params":[{see above}],"id":1}' + +// Result +{ + "id":1, + "jsonrpc": "2.0", + "result": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331" +} + +## Rationale +This EIP's proposed `eth_sendTransactionToContractFunction` method is intended to parallel `eth_sendTransaction` as much as possible since both methods result in the same behaviour when executing a contract function. The newly introduced `abi` field is an element of the contract's ABI that corresponds to the intended function. The `data` field is the same `data` field from `eth_sendTransaction`. The `abi` field can be combined with values parsed from the `data` field to recreate human readable contract function execution information. + +## Implementation +The `data` field in `eth_sendTransactionToContractFunction` is the same as that required for `eth_sendTransaction` allowing the transaction to be completed via the existing mechanisms used for `eth_sendTransaction`. The input argument values can be parsed from the `data` field and since we know their types from the `abi` field, the provider wallet can use this info to encode and display the values in an appropriate human readable format. Furthermore, the hashed and truncated function signature in the `data` field can be reconstructed using the information provided in the `abi` field providing an additional check to ensure that the supplied ABI matches the `data` field. + +## Backwards Compatibility +With backwards compatibility in mind, this EIP proposes augmenting the set of Ethereum RPC methods with an additional method instead of mutating the existing method. Precedent for adding a new RPC method comes from [EIP 712](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md) in which adding the method `eth_signTypedData` is proposed for confirmation prompt security. As an alternate approach, the `eth_sendTransaction` method could be changed to accept an additional `abi` argument, but this would break all existing code attempting to execute a contract function. + +## Security Considerations +Displaying the contract address, function name, and argument values can provide additional security to users, but it is not a guarantee that a function will execute as the user expects. A poorly implemented contract can still name its function `transfer` and accept `address` and `uint256` arguments - but there is nothing short of contract examination that will let a user know that this contract is indeed a valid ERC20 contract. This EIP does not intend to solve the larger problem around trust in a contract's code, but instead intends to give users better tools to understand exactly what is contained within the data they are broadcasting to the Ethereum network. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2569.md b/EIPS/eip-2569.md new file mode 100644 index 0000000..8ed6b11 --- /dev/null +++ b/EIPS/eip-2569.md @@ -0,0 +1,353 @@ +--- +eip: 2569 +title: Saving and Displaying Image Onchain for Universal Tokens +description: A set of interfaces to save an SVG image in Ethereum, and to retrieve the image file from Ethereum for universal tokens. +author: Hua Zhang (@dgczhh), Yuefei Tan (@whtyfhas), Derek Zhou (@zhous), Ran Xing (@lemontreeran) +discussions-to: https://ethereum-magicians.org/t/erc-2569-saving-and-displaying-image-onchain-for-universal-tokens/4167 +status: Stagnant +type: Standards Track +category: ERC +created: 2020-03-28 +--- + +## Abstract +This set of interfaces allow a smart contract to save an SVG image in Ethereum and to retrieve an SVG image from Ethereum for fungible tokens, non-fungible tokens and tokens based on standards that will be developed in the future. + +The interface set has two interfaces: one to save an SVG file in Ethereum and the other to retrieve an SVG file from Ethereum. + +Typical applications include but not limited to: +* A solution for storage of a fungible token's icon. +* A solution for storage of a non-fungible token's icon. +* A solution for storage of the icon/logo of a DAO's reputation token. + +## Motivation +The ERC-721 token standard is a popular standard to define a non-fungible token in Ethereum. This standard is widely used to specify a crypto gift, crypto medal, crypto collectible etc. The most famous use case is the [cryptokitty](https://www.cryptokitties.co/). + +In most of these applications an image is attached to an ERC-721 token. For example, in the cryptokitty case each kitty has a unique image. While the token's code is saved in Ethereum permanently, the image attached to the token is not. + +The existing solutions still keep such an image in a centralized server instead of Ethereum. When these applications display an image for a token they retrieve the token's information from Ethereum and search the centralized server for the token's associated image by using the token's information. + +Although this is an applicable way to display an image for a token, the image is still vulnerable to risks of being damaged or lost when saved in a centralized server. + +Hence we propose a set of interfaces to save an image for a universal token in Ethereum to keep the image permanent and tamper-resistant, and to retrieve an image for a universal token from Ethereum. + +## Specification + +An EIP-2569 compatible contract MUST have a method with the signature getTokenImageSvg(uint256) view returns (string memory) and a method with the signature setTokenImageSvg(uint256 tokenId, string memory imagesvg) internal. + +These methods define how a smart contract saves an image for a universal token in Ethereum which keeps the image permanent and tamper-resistant, and how a smart contract retrieves an image from Ethereum for a universal token. + +By calling the methods users should access an SVG image. + +* getTokenImageSvg(uint256 tokenId) external view returns (string memory): for an ERC-721 or ERC-1155 token or a token implemented by a contract which has a member "ID" to specify its token type or token index we define an interface to get an SVG image by using the token's ID number. For an ERC-20 token or a token implemented by a contract which doesn't have a member "ID" to specify its token type or token index we define an interface to get an SVG image for it if the token has a member variable string to save the image. + +It has the following parameter: + +tokenId: for a non-fungible token such as an ERC-721 token or a multi-token such as an ERC-1155 token which has a member "ID" to specify its token type or token index our proposed interface assigns an SVG image's file content to a string variable of the token's contract and associates the SVG image to this "ID" number. This unique ID is used to access its SVG image in both a "set" operation and a "get" operation. +For a fungible token such as an ERC-20 token no such an ID is needed and our proposed interface just assigns an SVG image's file content to a string variable of the token's contract. + +* setTokenImageSvg(uint256 tokenId, string memory imagesvg) internal: for an ERC-721 or ERC-1155 token or a token implemented by a contract which has a member "ID" to specify its token type or token index we define an interface to associate an SVG image to the token's ID number. For an ERC-20 token or a token implemented by a contract which doesn't have a member "ID" to specify its token type or token index we define an interface to assign an SVG image to a member variable string of this token's contract. + +It has the following two parameters: + +tokenId: for a non-fungible token such as an ERC-721 token or a multi-token such as an ERC-1155 token which has a member "ID" to specify its token type or token index our proposed interface assigns an SVG image's file content to a string variable of the token's contract and associates the SVG image to this "ID" number. This unique ID is used to access its SVG image in both a "set" operation and a "get" operation. +For a fungible token such as an ERC-20 token no such an ID is needed and our proposed interface just assigns an SVG image's file content to a string variable of the token's contract. + +imageSvg: we use a string variable to save an SVG image file's content. +An SVG image that will be saved in the imageSvg string should include at least two attributes:"name", "desc"(description). + +The procedure to save an image for a token in Ethereum is as follows: + +**Step1:** define a string variable or an array of strings to hold an image or an array of images. + +**Step 2:** define a function to set an (SVG) image's file content or an array of image file's contents to the string variable or the array of strings. + +Step 1: for a token such as an ERC-721 or ERC-1155 token which has a member variable "ID" to specify a token type or index and a member variable string to keep an (SVG) image associated with the "ID", retrieve the (SVG) image from Ethereum by calling our proposed "get" interface with the token's ID; +for a token which doesn't have a member variable "ID" to specify a token type of index but has a member variable string to keep an (SVG) image, retrieve the (SVG) image from Ethereum by calling our proposed "get" without an "ID". + +## Rationale +After Bitcoin was created people have found ways to keep information permanent and tamper-resistant by encoding text messages they want to preserve permanently and tamper-resistantly in blockchain transactions. However existing applications only do this for text information and there are no solutions to keep an image permanent and tamper-resistant. + +One of the most significant reasons for not doing so is that in general the size of an image is much bigger than the size of a text file, thus the gas needed to save an image in Ethereum would exceed a block's gas limit. + +However this changed a lot after the SVG(Scalable Vector Graphics) specification was developed by W3C since 1999. + +The SVG specification offers several advantages (for more details about the advantages please refer to a reference link:https://en.wikipedia.org/wiki/Scalable_Vector_Graphics) over raster images. One of these advantages is its compact file-size. + +"Compact file-size – Pixel-based images are saved at a large size from the start because you can only retain the quality when you make the image smaller, but not when you make it larger. This can impact a site’s download speed. Since SVGs are scalable, they can be saved at a minimal file size". + +This feature well fixes the painpoint of saving an image file in Ethereum, therefore we think saving an SVG image in Ethereum is a good solution for keep the image permanent and tamper-resistant. + +In most ERC-721 related DAPPs they display an image for a non-fungible token. In most ERC-20 related DAPPs they don't have an image for a fungible token. We think displaying an image for a token either based on existing token standards such as ERC-20, ERC-721, ERC-1155 or based on future standards is needed in many use cases. Therefore those DAPPs which currently don't display an image for a token will eventually need such a function. + +However with regard to most of the existing DAPPs which can display an image for a token they save such an image in a centralized server which, we think, is just a compromised solution. By utilizing the SVG specification we think converting a token's image to an SVG image and saving it in Ethereum provides a better solution for DAPPs to access an image for a token. + +This solution not only works for tokens based on ERC-721, ERC-1155 and ERC-20 but will work for tokens based on future standards. + +## Backwards Compatibility +There are no backward compatibility issues. + +## Reference Implementation +`tokenId`: a token index in an ERC-721 token or a token type/index in an ERC-1155 token. It is a uint256 variable. + +`imageSvg`: an SVG image's file content. It is a string variable. Note: the SVG image should include at least three attributes:"name", "description" and "issuer". + +`setTokenImageSvg`: interface to set an SVG image to a token with or without an ID number. + +`getTokenImageSvg`: interface to get an SVG image for a token with or without an ID number. + +We propose to add three sol files in the existing ERC-721 implementation. +Here are the details for the proposed sol files. + +```solidity +// ----- IERC721GetImageSvg.sol ------------------------- + +pragma solidity ^0.5.0; + +import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; + +/** + * @title ERC-721 Non-Fungible Token Standard, optional retrieving SVG image extension + * @dev See https://eips.ethereum.org/EIPS/eip-721 + */ +contract IERC721GetImageSvg is IERC721 { + function getTokenImageSvg(uint256 tokenId) external view returns (string memory); +} + + +// ----- ERC721GetImageSvg.sol ------------------------- + +pragma solidity ^0.5.0; + +import "@openzeppelin/contracts/GSN/Context.sol"; +import "@openzeppelin/contracts/token/ERC721/./ERC721.sol"; +import "@openzeppelin/contracts/introspection/ERC165.sol"; +import "./IERC721GetImageSvg.sol"; + +contract ERC721GetImageSvg is Context, ERC165, ERC721, IERC721GetImageSvg { + // Mapping for token Images + mapping(uint256 => string) private _tokenImageSvgs; + + /* + * bytes4(keccak256('getTokenImageSvg(uint256)')) == 0x87d2f48c + * + * => 0x87d2f48c == 0x87d2f48c + */ + bytes4 private constant _INTERFACE_ID_ERC721_GET_TOKEN_IMAGE_SVG = 0x87d2f48c; + + /** + * @dev Constructor function + */ + constructor () public { + // register the supported interfaces to conform to ERC721 via ERC165 + _registerInterface(_INTERFACE_ID_ERC721_GET_TOKEN_IMAGE_SVG); + } + + /** + * @dev Returns an SVG Image for a given token ID. + * Throws if the token ID does not exist. May return an empty string. + * @param tokenId uint256 ID of the token to query + */ + function getTokenImageSvg(uint256 tokenId) external view returns (string memory) { + require(_exists(tokenId), "ERC721GetImageSvg: SVG Image query for nonexistent token"); + return _tokenImageSvgs[tokenId]; + } + + /** + * @dev Internal function to set the token SVG image for a given token. + * Reverts if the token ID does not exist. + * @param tokenId uint256 ID of the token to set its SVG image + * @param imagesvg string SVG to assign + */ + function setTokenImageSvg(uint256 tokenId, string memory imagesvg) internal { + require(_exists(tokenId), "ERC721GetImageSvg: SVG image set of nonexistent token"); + _tokenImageSvgs[tokenId] = imagesvg; + } + +} + + +// ----- ERC721ImageSvgMintable.sol ------------------------- + +pragma solidity ^0.5.0; + +import "@openzeppelin/contracts/token/ERC721/ERC721Metadata.sol"; +import "@openzeppelin/contracts/access/roles/MinterRole.sol"; +import "./ERC721GetImageSvg.sol"; + +/** + * @title ERC721ImageSvgMintable + * @dev ERC721 minting logic with imagesvg. + */ +contract ERC721ImageSvgMintable is ERC721, ERC721Metadata, ERC721GetImageSvg, MinterRole { + /** + * @dev Function to mint tokens. + * @param to The address that will receive the minted tokens. + * @param tokenId The token id to mint. + * @param tokenImageSvg The token SVG image of the minted token. + * @return A boolean that indicates if the operation was successful. + */ + function mintWithTokenImageSvg(address to, uint256 tokenId, string memory tokenImageSvg) public onlyMinter returns (bool) { + _mint(to, tokenId); + setTokenImageSvg(tokenId, tokenImageSvg); + return true; + } +} + + +We propose to add three sol files in the existing ERC-1155 implementation. +Here are the details for the proposed sol files. + +// ----- IERC1155GetImageSvg.sol ------------------------- + +pragma solidity ^0.5.0; + +import "./IERC1155.sol"; + +/** + * @title ERC-1155 Multi Token Standard, retrieving SVG image for a token + * @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1155.md + */ +contract IERC1155GetImageSvg is IERC1155 { + function getTokenImageSvg(uint256 tokenId) external view returns (string memory); +} + + +// ----- ERC1155GetImageSvg.sol ------------------------- + +pragma solidity ^0.5.0; + +import "./ERC1155.sol"; +import "./IERC1155GetImageSvg.sol"; + +contract ERC1155GetImageSvg is ERC165, ERC1155, IERC1155GetImageSvg { + // Mapping for token Images + mapping(uint256 => string) private _tokenImageSvgs; + + /* + * bytes4(keccak256('getTokenImageSvg(uint256)')) == 0x87d2f48c + * + * => 0x87d2f48c == 0x87d2f48c + */ + bytes4 private constant _INTERFACE_ID_ERC1155_GET_TOKEN_IMAGE_SVG = 0x87d2f48c; + + /** + * @dev Constructor function + */ + constructor () public { + // register the supported interfaces to conform to ERC1155 via ERC165 + _registerInterface(_INTERFACE_ID_ERC1155_GET_TOKEN_IMAGE_SVG); + } + + + /** + * @dev Returns an SVG Image for a given token ID. + * Throws if the token ID does not exist. May return an empty string. + * @param tokenId uint256 ID of the token to query + */ + function getTokenImageSvg(uint256 tokenId) external view returns (string memory) { + require(_exists(tokenId), "ERC1155GetImageSvg: SVG Image query for nonexistent token"); + return _tokenImageSvgs[tokenId]; + } + + /** + * @dev Internal function to set the token SVG image for a given token. + * Reverts if the token ID does not exist. + * @param tokenId uint256 ID of the token to set its SVG image + * @param imagesvg string SVG to assign + */ + function setTokenImageSvg(uint256 tokenId, string memory imagesvg) internal { + require(_exists(tokenId), "ERC1155GetImageSvg: SVG image set of nonexistent token"); + _tokenImageSvgs[tokenId] = imagesvg; + } + +} + + + +// ----- ERC1155MixedFungibleWithSvgMintable.sol ------------------------- + +pragma solidity ^0.5.0; + +import "./ERC1155MixedFungibleMintable.sol"; +import "./ERC1155GetImageSvg.sol"; + +/** + @dev Mintable form of ERC1155 with SVG images + Shows how easy it is to mint new items with SVG images +*/ + +contract ERC1155MixedFungibleWithSvgMintable is ERC1155, ERC1155MixedFungibleMintable, ERC1155GetImageSvg { + /** + * @dev Function to mint non-fungible tokens. + * @param _to The address that will receive the minted tokens. + * @param _type The token type to mint. + * @param tokenImageSvg The token SVG image of the minted token. + */ + function mintNonFungibleWithImageSvg(uint256 _type, address[] calldata _to, string memory tokenImageSvg) external creatorOnly(_type) { + mintNonFungible(_type, _to); + setTokenImageSvg(_type, tokenImageSvg); + } + + + /** + * @dev Function to mint fungible tokens. + * @param _to The address that will receive the minted tokens. + * @param _id The token type to mint. + * @param _quantities The number of tokens for a type to mint. + * @param tokenImageSvg The token SVG image of the minted token. + */ + function mintFungibleWithImageSvg(uint256 _id, address[] calldata _to, uint256[] calldata _quantities, string memory tokenImageSvg) external creatorOnly(_id) { + mintFungible(_id, _to, _quantities, tokenImageSvg) { + setTokenImageSvg(_id, tokenImageSvg); + } +} + + + +We propose to add three sol files in the existing ERC-20 implementation. +Here are the details for the proposed sol files. + + +// ----- IERC20GetImageSvg.sol ------------------------- + +pragma solidity ^0.5.0; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +/** + * @title ERC-20 Fungible Token Standard, retrieving SVG image for a token + * @dev See https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol + */ +contract IERC20GetImageSvg is IERC20 { + function getTokenImageSvg() external view returns (string memory); +} + + +// ----- ERC20GetImageSvg.sol ------------------------- + +pragma solidity ^0.5.0; +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import "./IERC20GetImageSvg.sol"; + +contract ERC20GetImageSvg is ERC20, IERC20GetImageSvg { + string private _tokenImageSvg; +//将图片实现写在构造器中 + constructor(string calldata svgCode) public { +_tokenImageSvg = svgCode +} + + /** + * @dev Returns an SVG Image. + */ + function getTokenImageSvg() external view returns (string memory) { + return _tokenImageSvg; + } + +} + + +``` + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). + diff --git a/EIPS/eip-2583.md b/EIPS/eip-2583.md new file mode 100644 index 0000000..12689ee --- /dev/null +++ b/EIPS/eip-2583.md @@ -0,0 +1,294 @@ +--- +eip: 2583 +title: Penalty for account trie misses +author: Martin Holst Swende (@holiman) +discussions-to: https://ethereum-magicians.org/t/eip-2583-penalties-for-trie-misses/4190 +status: Stagnant +type: Standards Track +category: Core +created: 2020-02-21 +--- + + +## Simple Summary + +This EIP introduces a gas penalty for opcodes which access the account for trie non-existent accounts. + +## Abstract + +This EIP adds a gas penalty for accesses to the account trie, where the address being looked up does not exist. Non-existing accounts can be used in +DoS attacks, since they bypass cache mechanisms, thus creating a large discrepancy between 'normal' mode of execution and 'worst-case' execution of an opcode. + +## Motivation + +As the ethereum trie becomes more and more saturated, the number of disk lookups that a node is required to do in order to access a piece of state increases too. This means that checking e.g. `EXTCODEHASH` of an account at block `5` was _inherently_ a cheaper operation that it is at, say `8.5M`. + +From an implementation perspective, a node can (and does) use various caching mechanisms to cope with the problem, but there's an inherent problem with caches: when they yield a 'hit', they're great, but when they 'miss', they're useless. + +This is attackable. By forcing a node to lookup non-existent keys, an attacker can maximize the number of disk lookups. +Sidenote: even if the 'non-existence' is cached, it's trivial to use a new non-existent key the next time, and never hit the same non-existent key again. Thus, caching 'non-existence' might be dangerous, since it will evict 'good' entries. + +So far, the attempts to handle this problem has been in raising the gas cost, e.g. [EIP-150](./eip-150.md), [EIP-1884](./eip-1884.md). + +However, when determining gas-costs, a secondary problem that arises due to the large discrepancy between 'happy-path' and 'notorious path' -- how do we determine the pricing? + +- The 'happy-path', assuming all items are cached? + - Doing so would that would underprice all trie-accesses, and could be DoS-attacked. +- The 'normal' usage, based on benchmarks of actual usage? + - This is basically what we do now, but that means that intentionally notorious executions are underpriced -- which constitutes a DoS vulnerability. +- The 'paranoid' case: price everything as if caching did not exist? + - This would severely harm basically every contract due to the gas-cost increase. Also, if the gas limits were raised in order to allow the same amount of computation as before, the notorious case could again be used for DoS attacks. + +From an engineering point of view, a node implementor is left with few options: + +- Implement bloom filters for existence. This is difficult, not least because of the problems of reorgs, and the fact that it's difficult to undo bloom filter modifications. +- Implement flattened account databases. This is also difficult, both because of reorgs and also because it needs to be an _additional_ data structure aside from the `trie` -- we need the `trie` for consensus. So it's an extra data structure of around `15G` that needs to be kept in check. This is currently being pursued by the Geth-team. + +This EIP proposes a mechanism to alleviate the situation. + +## Specification + +We define the constant `penalty` as `TBD` (suggested `2000` gas). + +For opcodes which access the account trie, whenever the operation is invoked targeting an `address` which does not exist in the trie, then `penalty` gas is deducted from the available `gas`. + +### Detailed specification + +These are the opcodes which triggers lookup into the main account trie: + +| Opcode | Affected | Comment | +| ----- | ---------| ----------| +| BALANCE| Yes | `balance(nonexistent_addr)` would incur `penalty`| +| EXTCODEHASH| Yes | `extcodehash(nonexistent_addr)` would incur `penalty`| +| EXTCODECOPY| Yes | `extcodecopy(nonexistent_addr)` would incur `penalty`| +| EXTCODESIZE| Yes | `extcodesize(nonexistent_addr)` would incur `penalty`| +| CALL | Yes| See details below about call variants| +| CALLCODE | Yes| See details below about call variants| +| DELEGATECALL | Yes| See details below about call variants| +| STATICCALL | Yes| See details below about call variants| +| SELFDESTRUCT | No| See details below. | +| CREATE | No | Create destination not explicitly settable, and assumed to be nonexistent already.| +| CREATE2 | No | Create destination not explicitly settable, and assumed to be nonexistent already.| + + +### Notes on Call-derivatives + +A `CALL` triggers a lookup of the `CALL` destination address. The base cost for `CALL` is at `700` gas. A few other characteristics determine the actual gas cost of a call: + +1. If the `CALL` (or `CALLCODE`) transfers value, an additional `9K` is added as cost. + 1.1 If the `CALL` destination did not previously exist, an additional `25K` gas is added to the cost. + +This EIP adds a second rule in the following way: + +2. If the call does _not_ transfer value and the callee does _not_ exist, then `penalty` gas is added to the cost. + +In the table below, +- `value` means non-zero value transfer, +- `!value` means zero value transfer, +- `dest` means destination already exists, or is a `precompile` +- `!dest` means destination does not exist and is not a `precompile` + +| Op | value,dest| value, !dest |!value, dest| !value, !dest| +| -- | --------- | -- | --| -- | +|CALL | no change | no change| no change| `penalty`| +|CALLCODE | no change | no change| no change| `penalty`| +|DELEGATECALL | N/A | N/A| no change| `penalty` | +|STATICCALL | N/A | N/A| no change| `penalty` | + +Whether the rules of this EIP is to be applied for regular ether-sends in `transactions` is TBD. See the 'Backwards Compatibility'-section for some more discussion on that topic. + +### Note on `SELFDESTRUCT` + +The `SELFDESTRUCT` opcode also triggers an account trie lookup of the `beneficiary`. However, due to the following reasons, it has been omitted from having a `penalty` since it already costs `5K` gas. + +### Clarifications: + +- The `base` costs of any opcodes are not modified by the EIP. +- The opcode `SELFBALANCE` is not modified by this EIP, regardless of whether the `self` address exists or not. + + +## Rationale + +With this scheme, we could continue to price these operations based on the 'normal' usage, but gain protection from attacks that try to maximize disk lookups/cache misses. +This EIP does not modify anything regarding storage trie accesses, which might be relevant for a future EIP. However, there are a few crucial differences. + + +1. Storage tries are typically small, and there's a high cost to populate a storage trie with sufficient density for it to be in the same league as the account trie. +2. If an attacker wants to use an existing large storage trie, e.g. some popular token, he would typically have to make a `CALL` to cause a lookup in that token -- something like `token.balanceOf()`. + That adds quite a lot of extra gas-impediments, as each `CALL` is another `700` gas, plus gas for arguments to the `CALL`. + +### Determining the `penalty` + +A transaction with `10M` gas can today cause ~`14K` trie lookups. + +- A `penalty` of `1000`would lower the number to ~`5800` lookups, `41%` of the original. +- A `penalty` of `2000`would lower the number to ~`3700` lookups, `26%` of the original. +- A `penalty` of `3000`would lower the number to ~`2700` lookups, `20%` of the original. +- A `penalty` of `4000`would lower the number to ~`2100` lookups, `15%` of the original. + +There exists a roofing function for the `penalty`. Since the `penalty` is deducted from `gas`, that means that a malicious contract can always invoke a malicious relay to perform the trie lookup. Let's refer to this as the 'shielded relay' attack. + +In such a scenario, the `malicious` would spend `~750` gas each call to `relay`, and would need to provide the `relay` with at least `700` gas to do a trie access. + +Thus, the effective `cost` would be on the order of `1500`. It can thus be argued that `penalty` above `~800` would not achieve better protection against trie-miss attacks. + + +## Backwards Compatibility + +This EIP requires a hard-fork. + +### Ether transfers + +A regular `transaction` from one EOA to another, with value, is not affected. + +A `transaction` with `0` value, to a destination which does not exist, would be. This scenario is highly unlikely to matter, since such a `transaction` is useless -- even during success, all it would accomplish would be to spend some `gas`. With this EIP, it would potentially spend some more gas. + +### Layer 2 + +Regarding layer-2 backward compatibility, this EIP is a lot less disruptive than EIPs which modify the `base` cost of an opcode. For state accesses, there are +seldom legitimate scenarios where + +1. A contract checks `BALANCE`/`EXTCODEHASH`/`EXTCODECOPY`/`EXTCODESIZE` of another contract `b`, _and_, +2. If such `b` does not exist, continues the execution + +#### Solidity remote calls +Example: When a remote call is made in Solidity: +``` + recipient.invokeMethod(1) +``` + +- Solidity does a pre-flight `EXTCODESIZE` on `recipient`. +- If the pre-flight check returns `0`, then `revert(0,0)` is executed, to stop the execution. +- If the pre-flight check returns non-zero, then the execution continues and the `CALL` is made. + +With this EIP in place, the 'happy-path' would work as previously, and the 'notorious'-path where `recipient` does not exist would cost an extra `penalty` gas, but the actual execution-flow would be unchanged. + +#### ERC223 + +[ERC223 Token Standard](https://github.com/ethereum/EIPs/issues/223) is, at the time of writing, marked as 'Draft', but is deployed and in use on mainnet today. + +The ERC specifies that when a token `transfer(_to,...)` method is invoked, then: + +> This function must transfer tokens and invoke the function `tokenFallback (address, uint256, bytes)` in `_to`, if `_to` is a contract. +> ... +> NOTE: The recommended way to check whether the `_to` is a contract or an address is to assemble the code of `_to`. If there is no code in `_to`, then this is an externally owned address, otherwise it's a contract. + +The reference implementations from [Dexaran](https://github.com/Dexaran/ERC223-token-standard/tree/development/token/ERC223) and [OpenZeppelin](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/1bc923b6a222e79a90f20305a459b0ee779eb918/contracts/token/ERC721/ERC721.sol#L499) both implement the `isContract` check using an `EXTCODESIZE` invocation. + +This scenario _could_ be affected, but in practice should not be. Let's consider the possibilities: + +1. The `_to` is a contract: Then `ERC223` specifies that the function `tokenFallback(...)` is invoked. + - The gas expenditure for that call is at least`700` gas. + - In order for the `callee` to be able to perform any action, best practice it to ensure that it has at least `2300` gas along with the call. + - In summary: this path requires there to be least `3000` extra gas available (which is not due to any `penalty`) +2. The `_to` exists, but is no contract. The flow exits here, and is not affected by this EIP +2. The `_to` does not exist: A `penalty` is deducted. + +In summary, it would seem that `ERC223` should not be affected, as long as the `penalty` does not go above around `3000` gas. + + +### Other + +The contract [`Dentacoin`](https://etherscan.io/address/0x08d32b0da63e2c3bcf8019c9c5d849d7a9d791e6#code) would be affected. + +``` + function transfer(address _to, uint256 _value) returns (bool success) { + ... // omitted for brevity + if (balances[msg.sender] >= _value && balances[_to] + _value > balances[_to]) { // Check if sender has enough and for overflows + balances[msg.sender] = safeSub(balances[msg.sender], _value); // Subtract DCN from the sender + + if (msg.sender.balance >= minBalanceForAccounts && _to.balance >= minBalanceForAccounts) { // Check if sender can pay gas and if recipient could + balances[_to] = safeAdd(balances[_to], _value); // Add the same amount of DCN to the recipient + Transfer(msg.sender, _to, _value); // Notify anyone listening that this transfer took place + return true; + } else { + balances[this] = safeAdd(balances[this], DCNForGas); // Pay DCNForGas to the contract + balances[_to] = safeAdd(balances[_to], safeSub(_value, DCNForGas)); // Recipient balance -DCNForGas + Transfer(msg.sender, _to, safeSub(_value, DCNForGas)); // Notify anyone listening that this transfer took place + + if(msg.sender.balance < minBalanceForAccounts) { + if(!msg.sender.send(gasForDCN)) throw; // Send eth to sender + } + if(_to.balance < minBalanceForAccounts) { + if(!_to.send(gasForDCN)) throw; // Send eth to recipient + } + } + } else { throw; } + } +``` + +The contract checks `_to.balance >= minBalanceForAccounts`, and if the `balance` is too low, some `DCN` is converted to `ether` and sent to the `_to`. This is a mechanism to ease on-boarding, whereby a new user who has received some `DCN` can immediately create a transaction. + +Before this EIP: + +- When sending `DCN` to a non-existing address, the additional `gas` expenditure would be: + - `9000` for an ether-transfer + - `25000` for a new account-creation + - (`2300` would be refunded to the caller later) + - A total runtime `gas`-cost of `34K` gas would be required to handle this case. + +After this EIP: + +- In addition to the `34K` an additional `penalty` would be added. + - Possibly two, since the reference implementation does the balance-check twice, but it's unclear whether the compiled code would indeed perform the check twice. +- A total runtime `gas`-cost of `34K+penalty` (or `34K + 2 * penalty`) would be required to handle this case. + +It can be argued that the extra penalty of `2-3K` gas can be considered marginal in relation to the other `34K` gas already required to handle this. + +## Test Cases + +The following cases need to be considered and tested: + +- That during creation of a brand new contract, within the constructor, the `penalty` should not be applied for calls concerning the self-address. +- TBD: How the `penalty` is applied in the case of a contract which has performed a `selfdestruct` + - a) previously in the same call-context, + - b) previously in the same transaction, + - c) previously in the same block, + For any variant of `EXTCODEHASH(destructed)`, `CALL(destructed)`, `CALLCODE(destructed)` etc. +- The effects on a `transaction` with `0` value going to a non-existent account. + +## Security Considerations + +See 'Backwards Compatibility' + +## Implementation + +Not yet available. + +## Alternative variants + +### Alt 1: Insta-refunds + +Bump all trie accesses with `penalty`. `EXTCODEHASH` becomes `2700` instead of `700`. +- If a trie access hit an existing item, immediately refund penalty (`2K` ) + +Upside: + +- This eliminates the 'shielded relay' attack + +Downside: + +- This increases the up-front cost of many ops (CALL/EXTCODEHASH/EXTCODESIZE/STATICCALL/EXTCODESIZE etc) + - Which may break many contracts. + +### Alt 2: Parent bail + +Use `penalty` as described, but if a child context goes OOG on the `penalty`, then the remainder is subtracted from the +parent context (recursively). + +Upside: + +- This eliminates the 'shielded relay' attack + +Downside: + +- This breaks the current invariant that a child context is limited by whatever `gas` was allocated for it. + - However, the invariant is not _totally_ thrown out, the new invariant becomes that it is limited to `gas + penalty`. +- This can be seen as 'messy' -- since only _some_ types of OOG (penalties) becomes passed up the call chain, but not others, e.g. OOG due to trying + to allocate too much memory. There is a distinction, however: + - Gas-costs which arise due to not-yet-consumed resources do not get passed to parent. For example: a huge allocation is not actually performed if there is insufficient gas. + - Whereas gas-costs which arise due to already-consumed resources _do_ get passed to parent; in this case the penalty is paid post-facto for a trie iteration. + + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2584.md b/EIPS/eip-2584.md new file mode 100644 index 0000000..e2a6293 --- /dev/null +++ b/EIPS/eip-2584.md @@ -0,0 +1,114 @@ +--- +eip: 2584 +title: Trie format transition with overlay trees +author: Guillaume Ballet (@gballet) +discussions-to: https://ethresear.ch/t/overlay-method-for-hex-bin-tree-conversion/7104 +status: Stagnant +type: Standards Track +category: Core +created: 2020-04-03 +--- + +## Simple Summary + +This EIP proposes a method to convert the state trie format from hexary to binary: new values are directly stored in a binary trie “laid over” the hexary trie. Meanwhile, the hexary trie is converted to a binary trie in the background. When the process is finished, both layers are merged. + +## Abstract + +This EIP describes a four phase process to complete the conversion. + + * In the first phase, all new state writes are made to an overlay binary trie, while the hexary trie is being converted to binary. The block format is changed to have two storage roots: the root of the hexary trie (hereafter called the "base" trie) and the root of the overlay binary trie. + * After enough time has been given to miners to perform the conversion, the second phase begins. The overlay tree is progressively merged back into the newly converted binary base trie. A constant number of entries are deleted from the overlay and inserted into the base trie. + * The third and final phase begins when the overlay trie is empty. The field holding its root is removed from the block header. + +## Motivation + +There is a long running interest in switching the state trie from a hexary format to a binary format, for reasons pertaining to proof and storage sizes. The conversion process poses a catch-up issue, caused by the sheer size of the full state: it can not be translated in a reasonable time (i.e. on the same order of magnitude as the block time). + +## Specification + +This specification follows the notation introduced by the [Yellow Paper](https://ethereum.github.io/yellowpaper). Prior to reading it is advisable to be familiar with the Yellow Paper. + +### Binary tries + +This EIP assumes that a binary trie is defined like the MPT, except that: + + * The series of bytes in I₀ is seen as a series of _bits_ and so ∀i≤256, I₀[i] is the ith bit in key I₀ + * The first item of an **extension** or a **leaf** is replacing nibbles with bits; + * A **branch** is a 2 item structure in which both items correspond to each of the two possible bit values for the keys at this point in their traversal; + * c(𝕴,i) ≡ RLP((u(0), u(1)) at a branch, where u(j) = n({I : I ∈ 𝕴 ⋀ I₀[i] = j}, i+1) + +Let ß be the function that, given a hexary trie, computes the equivalent representation of that trie in the aforementioned binary trie format. + +### Phase 1 + +Let _h₁_ be the previously agreed-upon block height at which phase 1 starts, and _h₂_ the block at which phase 2 starts. For each block of height h₁ ≤ _h_ < h₂: + + 0. A conversion process is started in the background, to turn the hexary trie into its binary equivalent. The end goal of this process is the calculation of the _root hash of the converted binary trie_, denoted Hᵣ². The root of the hexary trie is hereafter called Hᵣ¹⁶. Formally, this process is written as Hᵣ² ≡ ß(Hᵣ¹⁶). + 1. Block headers contain a new Hₒ field, which is the _root of the overlay binary trie_; + 2. Hᵣ ≡ P(H)ᵣ¹⁶, i.e. as long as the conversion from hexary to binary is not complete, the hexary trie root is the same as that of its parent block. + +The following is changed in the execution environment: + + * Upon executing a _state read_, ϒ first searches for the address in the overlay trie. If the key can not be found there, ϒ then searches the base trie as it did at block heights h' < h₁; + * Upon executing a _state write_, ϒ inserts or updates the value into the overlay tree. The base tree is left untouched; + * If an account is deleted, ϒ inserts the empty hash H(∅) at the address of that account in order to mark its deletion; reads from such an address behave the same as a missing account, regardless of what is present in the base trie. + +Phase 1 ends at block height h₂, which is set far enough from h₁ to offer miners enough time to perform the conversion. + +### Phase 2 + + This phase differs from the previous one on the following points: + + * At block height h₂, before the execution of ϒ, Hᵣ ≡ Hᵣ², i.e. the value before the execution of the transition function is set to the root of the converted _binary base trie_; + * Hₒ is still present in the block tree, however the overlay trie content can only be _read from or deleted_; + * At block height h ≥ h₂, before the execution of ϒ, N accounts are being deleted from the binary overlay trie and inserted into the binary base trie; + * Upon executing a _state write_, ϒ will insert or update the value into the _base_ trie. If the search key exists in the overlay trie, it is deleted. + +Account deletion is performed according to the following rules: + + * The first N accounts (in lexicographic order) remaining in the overlay tree are selected; For each of these accounts: + * If the value is the empty hash, remove the account at the same address from the binary base trie; + * Otherwise, insert/update the account at the corresponding address in the base trie with its overlay trie value. + +When the overlay trie is empty, phase 2 ends and phase 3 begins. + +### Phase 3 + +Phase 3 is the same as phase 2, except for the following change: + + * Hₒ is dropped from the block header + +## Rationale + +Methods that have been discussed until now include a "stop the world" approach, in which the chain is stopped for the significant amount of time that is required by the conversion, and a "copy on write" approach, in which branches are converted upon being accessed. +The approach suggested here has the advantage that the chain continues to operate normally during the conversion process, and that the tree is fully converted to a binary format, in a predictable time. + +## Backwards Compatibility + +This requires a fork and will break backwards compatibility, as the hashes and block formats will necessarily be different. This will cause a fork in clients that don't implement the overlay tree, and those that do not accept the new binary root. No mitigation is proposed, as this is a hard fork. + +## Test Cases + + * For testing phase 1, it suffices to check that every key in the hexary trie is also available in the binary trie. A looser but faster test picks 1% of keys in the hexary trie at random, and checks that they are present in the binary trie; + * TBD for phase 2 & 3 + +## Implementation + +A prototype version of the conversion process (phase 1) is available for `geth` in [this PR](https://github.com/holiman/go-ethereum/pull/12). + +## Security Considerations + +There are three attack vectors that I can foresee: + + * A targeted attack that would cause the overlay trie to be unreasonably large. Since gas costs will likely increase during the transition process, lengthening phase 2 will make Ethereum more expensive during an extended period of time. This could be solved by increasing the cost of `SSTORE` during phase 1. + * Conversely, if h₂ comes too soon, a majority of miners might not be able to produce the correct value for Hᵣ² in time. + * If a group of miners representing more than 51% of the network are reporting an invalid value, they could be stealing funds without anyone having a say. + +## Community feedback + + * Preliminary tests indicate that a fast machine can perform the conversion in roughly 30 minutes. + * The initial version of this EIP expected miners to vote on the value of the binary base root. This has been removed because of the complexity of this process, and because this functionality is already guaranteed by the "longest chain" rule. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2593.md b/EIPS/eip-2593.md new file mode 100644 index 0000000..c5e1cc6 --- /dev/null +++ b/EIPS/eip-2593.md @@ -0,0 +1,131 @@ +--- +eip: 2593 +title: Escalator fee market change for ETH 1.0 chain +author: Dan Finlay +discussions-to: https://ethresear.ch/t/another-simple-gas-fee-model-the-escalator-algorithm-from-the-agoric-papers/6399 +status: Stagnant +type: Standards Track +category: Core +created: 2020-03-13 +--- + +## Simple Summary +The current "first price auction" fee model in Ethereum is inefficient and needlessly costly to users. This EIP proposes a way to replace this with a mechanism that allows dynamically priced transaction fees and efficient transaction price discovery. + +## Abstract + +Based on [The Agoric Papers](https://agoric.com/papers/incentive-engineering-for-computational-resource-management/full-text/). + +Each transaction would have the option of providing parameters that specify an "escalating" bid, creating a time-based auction for validators to include that transaction. + +This creates highly efficient price discovery, where the price will always immediately fall to the highest bid price, which is not necessarily that user's highest price they would pay. + +![escalator algorithm price chart](https://ethresear.ch/uploads/default/original/2X/0/042795efa4c2680d644bc66386cd2984a70293f8.gif) + +## Motivation +Ethereum currently prices transaction fees using a simple first-price auction, which leads to well documented inefficiencies (some of which are documented in [EIP-1559](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md)) when users are trying to estimate what price will get a transaction included in a block, especially during times of price volatility and full blocks. + +EIP 1559 is currently being championed as an improvement for the Ethereum protocol, and while I agree that the gas market is very inefficient, since a change like this will affect all client and wallet implementations, the Ethereum community should make sure to make a selection based on solid reasoning and justifications, which I believe 1559 is currently lacking. + +To facilitate a more productive and concrete discussion about the gas fee market, I felt it was important to present an alternative that is clearly superior to the status quo, so that any claimed properties of EIP-1559 can be compared to a plausible alternative improvement. + +I suggest the three gas payment algorithms be compared under all combinations of these conditions: + +- Blocks that are regularly half full, Blocks that are regularly less than half full, and blocks that repeatedly full in a surprising ("black swan") series. +- Users that are willing to wait for a price that may be below the market rate, vs users who value inclusion urgently and are willing to pay above market rate. + +We should then ask: +- Is the user willing to pay the most in a given scenario also likely to have their transaction processed in a time period they find acceptable? +- Are users who want a good price likely to get included in a reasonable period of time? (Ideally within one block) + +I believe that under this analysis we will find that the escalator algorithm outperforms EIP-1559 in both normal and volatile conditions, for both high-stakes transactions and more casual users looking for a good price. + +While I think a deeper simulation/analysis should be completed, I will share my expected results under these conditions. + +Furthermore, by introducing tx fee payment related to the current time, we create an incentive for miners to more honestly report the current time. + +### User Strategies Under Various Conditions and Algorithms + +First I will suggest a likely optimal strategy for different players under the conditions of the different algorithms being considered. + +| Gas Strategy | Current Single-Price | EIP 1559 | Escalator | +|---------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Blocks regularly half full, user wants urgent inclusion. | User bids within the range of prices that have been recently accepted, likely over-pays slightly. | User bids one price tier over the current rate, and is likely included. | User bids a range from the low end of recently included to the high end, and is likely included at the lowest rate possible. | +| Blocks regularly half full, user willing to wait for a good price. | User bids below or near the low end of the recently accepted prices, may need to wait for a while. If waiting too long, user may need to re-submit with a higher price. | User bids under or at the current price tier, and may wait for the price to fall. If waiting too long, user may need to re-submit with a higher price. | User bids as low as they'd like, but set an upper bound on how long they're willing to wait before increasing price. | +| Blocks regularly full, user wants urgent inclusion. | User bids over the price of all recently accepted transactions, almost definitely over-paying significantly. | User bids over the current price tier, and needs to increase their `tip` parameter to be competitive on the next block, recreating the single-price auction price problem. | User bids over a price that has been accepted consistently, with an escalating price in case that price is not high enough. | +| Blocks regularly full, user willing to wait for a good price. | User bids below the low end of the recently accepted prices, may need to wait for a while. If waiting too long, user may need to re-submit with a higher price. | User bids under or at the current price tier, and may wait for the price to fall. If waiting too long, user may need to re-submit with a higher price. | User bids as low as they'd like, but set an upper bound on how long they're willing to wait before increasing price. | +| Blocks regularly under-full, user wants urgent inclusion. | User bids within or over the range of prices that have been recently accepted, likely over-pays slightly, and is likely included in the next block. | User bids at or over the current price tier, and is likely included in the next block. | User submits bid starting within recently accepted prices, is likely accepted in the next block. | +| Blocks regularly under-full, user willing to wait for a good price. | User bids below the low end of the recently accepted prices, may need to wait for a while. If waiting too long, user may need to re-submit with a higher price. | User bids at or under the current price tier, and is likely included in the next block. If bidding under and waiting too long, user may need to re-submit with a higher price. | User bids as low as they'd like, but set an upper bound on how long they're willing to wait before increasing price, is likely included in the next few blocks at the lowest possible price. | + +### User Results Under Various Conditions and Algorithms + +Now I will consider the ultimate results of the strategies listed above. Are users happy under these conditions? Did we save users money? Were users who wanted urgent inclusion able to secure it? + +| Gas Strategy | Current Single-Price | EIP 1559 | Escalator | +|---------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------| +| Blocks regularly half full, user wants urgent inclusion. | User pays an expected amount, and gets transaction mined reliably. | User pays an expected amount, and gets transaction mined reliably. | User pays an expected amount, and gets transaction mined reliably. | +| Blocks regularly half full, user willing to wait for a good price. | User can wait for a better price, but may need to resubmit re-signed transactions. | User can wait for a better price, but may need to resubmit re-signed transactions. | User can discover the lowest price within their time preference with a single signature. | +| Blocks regularly full, user wants urgent inclusion. | User over-pays, but reliably gets transaction included. | Due to `tip` parameter "breaking tie" within a block, user over-pays for reliable inclusion. | User is able to balance the amount of overpayment they risk with the urgency they require. | +| Blocks regularly full, user willing to wait for a good price. | User chooses their price, and waits for it, or manually re-submits. | User chooses their price, and waits for it, or manually re-submits. | User chooses their lowest price, but also their highest price and maximum wait time, so no resubmission is needed. | +| Blocks regularly under-full, user wants urgent inclusion. | User over-pays, but reliably gets transaction included. | User bids at or over current price tier, gets transaction mined reliably. | User pays an expected amount, and gets transaction mined reliably. | +| Blocks regularly under-full, user willing to wait for a good price. | User bids below the low end of the recently accepted prices, may need to wait for a while. If waiting too long, user may need to re-submit with a higher price. | User chooses their price, and waits for it, or manually re-submits. | User chooses their lowest price, but also their highest price and maximum wait time, so no resubmission is needed. | + +In all cases, the escalator algorithm as I have described is able to perform optimally. + +The current gas auction model works well under half-full and less conditions, but for users with urgent needs, has the downside of overpayment. For users seeking a low price, the current model has the downside of requiring re-submission, but has the benefit of always giving users a path towards reliable block inclusion. + +EIP-1559 also performs well under normal conditions, but under conditions where blocks are regularly full, the price discovery mechanism breaks, and miners will fall back to the `TIP` parameter to choose the transactions to include, meaning that under network congestion, EIP-1559 forces users to _either_ choose efficient prices or certainty of next-block inclusion. + +EIP-1559 also has all the re-submission issues of the current model in situations where a user would like to pay under the current market rate, but has certain time constraints limiting their patience. The Escalator algorithm is the only strategy listed here that allows users to discover the lowest possible price given the network conditions and their time constraints. + +## Specification +**Client-Wide Parameters** +* `INITIAL_FORK_BLKNUM`: TBD + +**Transaction Parameters** +The transaction `gasPrice` parameter is now optional, and if excluded can be replaced by these parameters instead: + +* `START_PRICE`: The lowest price that the user would like to pay for the transaction. +* `START_TIME`: The first time that this transaction is valid at. +* `MAX_PRICE`: The maximum price the sender would be willing to pay to have this transaction processed. +* `MAX_TIME`: The time at which point the user's `MAX_PRICE` is achieved. The transaction remains valid after this time at that price. + +**Proposal** + +For all blocks where `block.number >= INITIAL_FORK_BLKNUM`: + +When processing a transaction with the new pricing parameters, miners now receive a fee based off of the following linear function, where `BLOCK` is the current block number: + +* IF `BLOCK > MAX_TIME` then `TX_FEE = MAX_PRICE`. +* `TX_FEE = START_PRICE + ((MAX_PRICE - START_PRICE) / (MAX_TIME - START_TIME) * (BLOCK - START_TIME))` + +As a JavaScript function: +```javascript +function txFee (startTime, startPrice, maxTime, maxPrice, currentTime) { + + if (currentTime >= maxTime) return maxPrice + + const priceRange = maxPrice - startPrice + const blockRange = maxTime - startTime + const slope = priceRange / blockRange + + return startPrice + (slope * (currentTime - startTime)) +} +``` + +## Backwards Compatibility + +Since a current `gasPrice` transaction is effectively a flat-escalated transaction bid, it is entirely compatible with this model, and so there is no concrete requirement to deprecate current transaction processing logic, allowing cold wallets and hardware wallets to continue working for the foreseeable future. + +## Test Cases +TBD + +## Implementation +TBD + +## Security Considerations +The security considerations for this EIP are: +- None currently known. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2612.md b/EIPS/eip-2612.md new file mode 100644 index 0000000..eecb1f7 --- /dev/null +++ b/EIPS/eip-2612.md @@ -0,0 +1,188 @@ +--- +eip: 2612 +title: Permit Extension for EIP-20 Signed Approvals +description: EIP-20 approvals via EIP-712 secp256k1 signatures +author: Martin Lundfall (@Mrchico) +discussions-to: https://github.com/ethereum/EIPs/issues/2613 +status: Final +type: Standards Track +category: ERC +created: 2020-04-13 +requires: 20, 712 +--- + +## Abstract +Arguably one of the main reasons for the success of [EIP-20](./eip-20.md) tokens lies in the interplay between `approve` and `transferFrom`, which allows for tokens to not only be transferred between externally owned accounts (EOA), but to be used in other contracts under application specific conditions by abstracting away `msg.sender` as the defining mechanism for token access control. + +However, a limiting factor in this design stems from the fact that the EIP-20 `approve` function itself is defined in terms of `msg.sender`. This means that user's _initial action_ involving EIP-20 tokens must be performed by an EOA (_but see Note below_). If the user needs to interact with a smart contract, then they need to make 2 transactions (`approve` and the smart contract call which will internally call `transferFrom`). Even in the simple use case of paying another person, they need to hold ETH to pay for transaction gas costs. + +This ERC extends the EIP-20 standard with a new function `permit`, which allows users to modify the `allowance` mapping using a signed message, instead of through `msg.sender`. + +For an improved user experience, the signed data is structured following [EIP-712](./eip-712.md), which already has wide spread adoption in major RPC providers. + +**_Note:_** EIP-20 must be performed by an EOA unless the address owning the token is actually a contract wallet. Although contract wallets solves many of the same problems that motivates this EIP, they are currently only scarcely adopted in the ecosystem. Contract wallets suffer from a UX problem -- since they separate the EOA `owner` of the contract wallet from the contract wallet itself (which is meant to carry out actions on the `owner`s behalf and holds all of their funds), user interfaces need to be specifically designed to support them. The `permit` pattern reaps many of the same benefits while requiring little to no change in user interfaces. + +## Motivation +While EIP-20 tokens have become ubiquitous in the Ethereum ecosystem, their status remains that of second class tokens from the perspective of the protocol. The ability for users to interact with Ethereum without holding any ETH has been a long outstanding goal and the subject of many EIPs. + +So far, many of these proposals have seen very little adoption, and the ones that have been adopted (such as [EIP-777](./eip-777.md)), introduce a lot of additional functionality, causing unexpected behavior in mainstream contracts. + +This ERC proposes an alternative solution which is designed to be as minimal as possible and to only address _one problem_: the lack of abstraction in the EIP-20 `approve` method. + +While it may be tempting to introduce `*_by_signature` counterparts for every EIP-20 function, they are intentionally left out of this EIP-20 for two reasons: + + - the desired specifics of such functions, such as decision regarding fees for `transfer_by_signature`, possible batching algorithms, varies depending on the use case, and, + - they can be implemented using a combination of `permit` and additional helper contracts without loss of generality. + +## Specification +Compliant contracts must implement 3 new functions in addition to EIP-20: +```sol +function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external +function nonces(address owner) external view returns (uint) +function DOMAIN_SEPARATOR() external view returns (bytes32) +``` +The semantics of which are as follows: + +For all addresses `owner`, `spender`, uint256s `value`, `deadline` and `nonce`, uint8 `v`, bytes32 `r` and `s`, +a call to `permit(owner, spender, value, deadline, v, r, s)` will set +`approval[owner][spender]` to `value`, +increment `nonces[owner]` by 1, +and emit a corresponding `Approval` event, +if and only if the following conditions are met: + + +- The current blocktime is less than or equal to `deadline`. +- `owner` is not the zero address. +- `nonces[owner]` (before the state update) is equal to `nonce`. +- `r`, `s` and `v` is a valid `secp256k1` signature from `owner` of the message: + +If any of these conditions are not met, the `permit` call must revert. + +```sol +keccak256(abi.encodePacked( + hex"1901", + DOMAIN_SEPARATOR, + keccak256(abi.encode( + keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"), + owner, + spender, + value, + nonce, + deadline)) +)) +``` +where `DOMAIN_SEPARATOR` is defined according to EIP-712. The `DOMAIN_SEPARATOR` should be unique to the contract and chain to prevent replay attacks from other domains, +and satisfy the requirements of EIP-712, but is otherwise unconstrained. +A common choice for `DOMAIN_SEPARATOR` is: +```solidity +DOMAIN_SEPARATOR = keccak256( + abi.encode( + keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'), + keccak256(bytes(name)), + keccak256(bytes(version)), + chainid, + address(this) +)); +``` + +In other words, the message is the EIP-712 typed structure: + +```js +{ + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Permit": [{ + "name": "owner", + "type": "address" + }, + { + "name": "spender", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + }, + { + "name": "nonce", + "type": "uint256" + }, + { + "name": "deadline", + "type": "uint256" + } + ], + "primaryType": "Permit", + "domain": { + "name": erc20name, + "version": version, + "chainId": chainid, + "verifyingContract": tokenAddress + }, + "message": { + "owner": owner, + "spender": spender, + "value": value, + "nonce": nonce, + "deadline": deadline + } +}} +``` + +Note that nowhere in this definition we refer to `msg.sender`. The caller of the `permit` function can be any address. + + +## Rationale +The `permit` function is sufficient for enabling any operation involving EIP-20 tokens to be paid for using the token itself, rather than using ETH. + +The `nonces` mapping is given for replay protection. + +A common use case of `permit` has a relayer submit a `Permit` on behalf of the `owner`. In this scenario, the relaying party is essentially given a free option to submit or withhold the `Permit`. If this is a cause of concern, the `owner` can limit the time a `Permit` is valid for by setting `deadline` to a value in the near future. The `deadline` argument can be set to `uint(-1)` to create `Permit`s that effectively never expire. + +EIP-712 typed messages are included because of its wide spread adoption in many wallet providers. + + +## Backwards Compatibility +There are already a couple of `permit` functions in token contracts implemented in contracts in the wild, most notably the one introduced in the `dai.sol`. + +Its implementation differs slightly from the presentation here in that: +- instead of taking a `value` argument, it takes a bool `allowed`, setting approval to 0 or `uint(-1)`. +- the `deadline` argument is instead called `expiry`. This is not just a syntactic change, as it effects the contents of the signed message. + +There is also an implementation in the token `Stake` (Ethereum address `0x0Ae055097C6d159879521C384F1D2123D1f195e6`) with the same ABI as `dai` but with different semantics: it lets users issue "expiring approvals", that only allow `transferFrom` to occur while `expiry >= block.timestamp`. + +The specification presented here is in line with the implementation in Uniswap V2. + +The requirement to revert if the permit is invalid was added when the EIP was already widely deployed, but at the moment it was consistent with all found implementations. + +## Security Considerations + +Though the signer of a `Permit` may have a certain party in mind to submit their transaction, another party can always front run this transaction and call `permit` before the intended party. The end result is the same for the `Permit` signer, however. + +Since the ecrecover precompile fails silently and just returns the zero address as `signer` when given malformed messages, it is important to ensure `owner != address(0)` to avoid `permit` from creating an approval to spend "zombie funds" belong to the zero address. + +Signed `Permit` messages are censorable. The relaying party can always choose to not submit the `Permit` after having received it, withholding the option to submit it. The `deadline` parameter is one mitigation to this. If the signing party holds ETH they can also just submit the `Permit` themselves, which can render previously signed `Permit`s invalid. + +The standard EIP-20 race condition for approvals (SWC-114) applies to `permit` as well. + +If the `DOMAIN_SEPARATOR` contains the `chainId` and is defined at contract deployment instead of reconstructed for every signature, there is a risk of possible replay attacks between chains in the event of a future chain split. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2615.md b/EIPS/eip-2615.md new file mode 100644 index 0000000..9191c80 --- /dev/null +++ b/EIPS/eip-2615.md @@ -0,0 +1,241 @@ +--- +eip: 2615 +title: Non-Fungible Token with mortgage and rental functions +author: Kohshi Shiba +discussions-to: https://github.com/ethereum/EIPs/issues/2616 +status: Stagnant +type: Standards Track +category: ERC +created: 2020-04-25 +requires: 165, 721 +--- + +## Simple Summary + +This standard proposes an extension to ERC721 Non-Fungible Tokens (NFTs) to support rental and mortgage functions. These functions are necessary for NFTs to emulate real property, just like those in the real world. + +## Abstract + +This standard is an extension of ERC721. It proposes additional roles, the right of tenants to enable rentals, and the right of lien. + +With ERC2615, NFT owners will be able to rent out their NFTs and take out a mortgage by collateralizing their NFTs. For example, this standard can apply to: + +- Virtual items (in-game assets, virtual artwork, etc.) +- Physical items (houses, automobiles, etc.) +- Intellectual property rights +- DAO membership tokens + +NFT developers are also able to easily integrate ERC2615 since it is fully backwards-compatible with the ERC721 standard. + +One notable point is that the person who has the right to use an application is not the owner but the user (i.e. tenant). Application developers must implement this specification into their applications. + +## Motivation + +It has been challenging to implement rental and mortgage functions with the ERC721 standard because it only has one role defined (which is the Owner). + +Currently, a security deposit is needed for trustless renting with ERC721, and ownership lockup within a contract is necessary whenever one chooses to mortgage their ERC721 property. The tracking and facilitation of these relationships must be done separately from the ERC721 standard. + +This proposal eliminates these requirements by integrating basic rights of tenantship and liens. By standardizing these functions, developers can more easily integrate rental and mortgage functions for their applications. + +## Specification + +This standard proposes three user roles: the **Lien Holder**, the **Owner**, and the **User**. Their rights are as follows: + +- A **Lien Holder** has the right to: + + 1. Transfer the **Owner** role + 2. Transfer the **User** role + +- An **Owner** has the right to: + + 1. Transfer the **Owner** role + 2. Transfer the **User** role + +- A **User** has the right to: + 1. Transfer the **User** role + +### ERC-2615 Interface + +```solidity +event TransferUser(address indexed from, address indexed to, uint256 indexed itemId, address operator); +event ApprovalForUser(address indexed user, address indexed approved, uint256 itemId); +event TransferOwner(address indexed from, address indexed to, uint256 indexed itemId, address operator); +event ApprovalForOwner(address indexed owner, address indexed approved, uint256 itemId); +event ApprovalForAll(address indexed owner, address indexed operator, bool approved); +event LienApproval(address indexed to, uint256 indexed itemId); +event TenantRightApproval(address indexed to, uint256 indexed itemId); +event LienSet(address indexed to, uint256 indexed itemId, bool status); +event TenantRightSet(address indexed to, uint256 indexed itemId,bool status); + +function balanceOfOwner(address owner) public view returns (uint256); +function balanceOfUser(address user) public view returns (uint256); +function userOf(uint256 itemId) public view returns (address); +function ownerOf(uint256 itemId) public view returns (address); + +function safeTransferOwner(address from, address to, uint256 itemId) public; +function safeTransferOwner(address from, address to, uint256 itemId, bytes memory data) public; +function safeTransferUser(address from, address to, uint256 itemId) public; +function safeTransferUser(address from, address to, uint256 itemId, bytes memory data) public; + +function approveForOwner(address to, uint256 itemId) public; +function getApprovedForOwner(uint256 itemId) public view returns (address); +function approveForUser(address to, uint256 itemId) public; +function getApprovedForUser(uint256 itemId) public view returns (address); +function setApprovalForAll(address operator, bool approved) public; +function isApprovedForAll(address requester, address operator) public view returns (bool); + +function approveLien(address to, uint256 itemId) public; +function getApprovedLien(uint256 itemId) public view returns (address); +function setLien(uint256 itemId) public; +function getCurrentLien(uint256 itemId) public view returns (address); +function revokeLien(uint256 itemId) public; + +function approveTenantRight(address to, uint256 itemId) public; +function getApprovedTenantRight(uint256 itemId) public view returns (address); +function setTenantRight(uint256 itemId) public; +function getCurrentTenantRight(uint256 itemId) public view returns (address); +function revokeTenantRight(uint256 itemId) public; +``` + +### ERC-2615 Receiver + +```solidity +function onERCXReceived(address operator, address from, uint256 itemId, uint256 layer, bytes memory data) public returns(bytes4); +``` + +### ERC-2615 Extensions + +Extensions here are provided to help developers build with this standard. + +#### 1. ERC721 Compatible functions + +This extension makes this standard compatible with ERC721. By adding the following functions, developers can take advantage of the existing tools for ERC721. + +Transfer functions in this extension will transfer both the **Owner** and **User** roles when the tenant right has not been set. Conversely, when the tenant right has been set, only the **Owner** role will be transferred. + +```solidity +function balanceOf(address owner) public view returns (uint256) +function ownerOf(uint256 itemId) public view returns (address) +function approve(address to, uint256 itemId) public +function getApproved(uint256 itemId) public view returns (address) +function transferFrom(address from, address to, uint256 itemId) public +function safeTransferFrom(address from, address to, uint256 itemId) public +function safeTransferFrom(address from, address to, uint256 itemId, bytes memory data) pubic +``` + +#### 2. Enumerable + +This extension is analogous to the enumerable extension of the ERC721 standard. + +```solidity +function totalNumberOfItems() public view returns (uint256); +function itemOfOwnerByIndex(address owner, uint256 index, uint256 layer)public view returns (uint256 itemId); +function itemByIndex(uint256 index) public view returns (uint256); +``` + +#### 3. Metadata + +This extension is analogous to the metadata extension of the ERC721 standard. + +```solidity +function itemURI(uint256 itemId) public view returns (string memory); +function name() external view returns (string memory); +function symbol() external view returns (string memory); +``` + +## How rentals and mortgages work + +This standard does not deal with token or value transfer. Other logic (outside the scope of this standard) must be used to orchestrate these transfers and to implement validation of payment. + +### Mortgage functions + +The following diagram demonstrates the mortgaging functionality. + +![concept image](../assets/eip-2615/mortgage-sequential.jpg "mortgage") + +Suppose Alice owns an NFT and wants to take out a mortgage, and Bob wants to earn interest by lending tokens to Alice. + +1. Alice approves the setting of a lien for the NFT Alice owns. +2. Alice sends a loan request to the mortgage contract. +3. Bob fills the loan request and transfers tokens to the mortgage contract. The lien is then set on the NFT by the mortgage contract. +4. Alice can now withdraw the borrowed tokens from the mortgage contract. +5. Alice registers repayment (anyone can pay the repayment). +6. Bob can finish the agreement if the agreement period ends and the agreement is kept (i.e. repayment is paid without delay). +7. Bob can revoke the agreement if the agreement is breached (e.g. repayment is not paid on time) and execute the lien and take over the ownership of the NFT. + +### Rental functions + +The following diagram demonstrates the rental functionality. + +![concept image](../assets/eip-2615/rental-sequential.jpg "rental") + +Suppose Alice owns NFTs and wants to rent out a NFT, and Bob wants to lease a NFT. + +1. Alice approves the setting of a tenant-right for the NFT Alice owns. +2. Alice sends a rental listing to the rental contract. +3. Bob fills the rental request, and the right to use the NFT is transferred to Bob. At the same time, the tenant-right is set, and Alice becomes not able to transfer the right to use the NFT. +4. Bob registers rent (anyone can pay the rent). +5. Alice can withdraw the rent from the rental contract. +6. Alice can finish the agreement if the agreement period has ended and the agreement is kept (i.e. rent is paid without delay). +7. Alice can revoke the agreement if the agreement is breached (e.g. rent is not paid on time) and revoke the tenant-right and take over the right to use the NFT. + +## Rationale + +There have been some attempts to achieve rentals or mortgages with ERC721. However, as I noted before, it has been challenging to achieve. I will explain the reasons and advantages of this standard below. + +### No security lockup for rentals + +To achieve trustless rental of NFTs with ERC721, it has been necessary to deposit funds as security. This is required to prevent malicious activity from tenants, as it is impossible to take back ownership once it is transferred. + +With this standard, security deposits are no longer needed since the standard natively supports rental and tenantship functions. + +### No ownership escrow when taking out a mortgage + +In order to take out a mortgage on NFTs, it has been necessary to transfer the NFTs to a contract as collateral. This is required to prevent the potential default risk of the mortgage. + +However, secured collateral with ERC721 hurts the utility of the NFT. Since most NFT applications provide services to the canonical owner of a NFT, the NFT essentially cannot be utilized under escrow. + +With ERC2615, it is possible to collateralize NFTs and use them at the same time. + +### Easy integration + +Because of the above reasons, a great deal of effort is required to implement rental and mortgage functions with ERC721. Adopting this standard is a much easier way to integrate rental and mortgage functionality. + +### No money/token transactions within tokens + +A NFT itself does not handle lending or rental functions directly. This standard is open-source, and there is no platform lockup. Developers can integrate it without having to worry about those risks. + +## Backward compatibility + +As mentioned in the specifications section, this standard can be fully ERC721 compatible by adding an extension function set. + +In addition, new functions introduced in this standard have many similarities with the existing functions in ERC721. This allows developers to easily adopt the standard quickly. + +## Test Cases + +When running the tests, you need to create a test network with Ganache-CLI: + +``` +ganache-cli -a 15 --gasLimit=0x1fffffffffffff -e 1000000000 +``` + +And then run the tests using Truffle: + +``` +truffle test -e development +``` + +Powered by Truffle and Openzeppelin test helper. + +## Implementation + +[Github Reposotory](https://github.com/kohshiba/ERC-X). + +## Security Considerations + +Since the external contract will control lien or tenant rights, flaws within the external contract directly lead to the standard's unexpected behavior. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). + diff --git a/EIPS/eip-2645.md b/EIPS/eip-2645.md new file mode 100644 index 0000000..9cba919 --- /dev/null +++ b/EIPS/eip-2645.md @@ -0,0 +1,71 @@ +--- +eip: 2645 +title: Hierarchical Deterministic Wallet for Layer-2 +author: Tom Brand , Louis Guthmann +discussions-to: https://ethereum-magicians.org/t/hierarchical-deterministic-wallet-for-computation-integrity-proof-cip-layer-2/4286 +status: Stagnant +type: Standards Track +category: ERC +created: 2020-05-13 +--- + +## Simple Summary +In the context of Computation Integrity Proof (CIP) Layer-2 solutions such as ZK-Rollups, users are required to sign messages on new elliptic curves optimized for those environnements. We leverage existing work on Key Derivation ([BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki), [BIP39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki) and [BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki)) to define an efficient way to securely produce CIP L2s private keys, as well as creating domain separation between Layer-2 applications. + +## Abstract +We provide a Derivation Path allowing a user to derive hierarchical keys for Layer-2 solutions depending on the zk-technology, the application, the user’s Layer-1 address, as well as an efficient grinding method to enforce the private key distribution within the curve domain. The propose Derivation Path is defined as follow +``` +m / purpose' / layer' / application' / eth_address_1' / eth_address_2' / index +``` + +## Motivation +In the context of Computation Integrity Proof (CIP) Layer-2 solutions such as ZK-Rollups, users are required to sign messages on new elliptic curves optimized for those environnements. Extensive work has been done to make it secure on Bitcoin via [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki), [BIP39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki) and [BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki). These protocols are the standard for wallets in the entire industry, independent of the underlying blockchain. As Layer-2 solutions are taking off, it is a necessary requirement to maintain the same standard and security in this new space. + +## Specification +Starkware keys are derived with the following [BIP43](https://github.com/bitcoin/bips/blob/master/bip-0043.mediawiki)-compatible derivation path, with direct inspiration from [BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki): +``` +m / purpose' / layer' / application' / eth_address_1' / eth_address_2' / index +``` +where: +* `m` - the seed. +* `purpose` - `2645` (the number of this EIP). +* `layer` - the 31 lowest bits of sha256 on the layer name. Serve as a domain separator between different technologies. In the context of `starkex`, the value would be `579218131`. +* `application` - the 31 lowest bits of sha256 of the application name. Serve as a domain separator between different applications. In the context of DeversiFi in June 2020, it is the 31 lowest bits of sha256(starkexdvf) and the value would be `1393043894`. +* `eth_address_1 / eth_address_2` - the first and second 31 lowest bits of the corresponding eth_address. +* `index` - to allow multiple keys per eth_address. + +As example, the expected path for address 0x0000....0000 assuming seed `m` and index 0 in the context of DeversiFi in June 2020: `m/2645'/579218131'/1393043894'/0'/0'/0` + +The key derivation should follow the following algorithm +``` +N = 2**256 +n = Layer2 curve order +path = stark derivation path +BIP32() = Official BIP-0032 derivation function on secp256k1 +hash = SHA256 +i = 0 +root_key = BIP32(path) +while True: + key = hash(root_key|i) + if (key < (N - (N % n))): + return key % n + i++ +``` +This algorithm has been defined to maintain efficiency on existing restricted devices. + +Nota Bene: At each round, the probability for a key to be greater than (N - (N % n)) is < 2^(-5). + +## Rationale +This EIP specifies two aspects of keys derivation in the context of Hierarchical Wallets: +- Derivation Path +- Grinding Algorithm to enforce a uniform distribution over the elliptic curve. +The derivation path is defined to allow efficient keys separation based on technology and application while maintaining a 1-1 relation with the Layer-1 wallet. In such a way, losing EIP-2645 wallets falls back to losing the Layer-1 wallet. + +## Backwards Compatibility +This standard complies with BIP43. + +## Security Considerations +This EIP has been defined to maintain separation of keys while providing foolproof logic on key derivation. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2657.md b/EIPS/eip-2657.md new file mode 100644 index 0000000..83b1c21 --- /dev/null +++ b/EIPS/eip-2657.md @@ -0,0 +1,66 @@ +--- +eip: 2657 +title: Ephemeral Testnet Yolo +author: James Hancock (@madeoftin) +discussions-to: https://gitter.im/ethereum/AllCoreDevs +status: Stagnant +type: Meta +created: 2020-04-19 +--- + +**Disclaimer: This is for testing basic infrastructure. It will be nuked. It is not for deploying dapps, nor does it define what will go into mainnet. For information on network upgrades, please follow the relevant meta EIPs and ongoing discussion on Ethereum/pm.** + +## Abstract + +The specification for Ephemeral Testnet Yolo. Clients who wish to sync need to implement the following features into their client. It is for testing basic infrastructure and will be nuked. + +## Specification + +Name: Yolo +ID: `YOLO-v1` + + - [x] EIP 2537 Commit Hash - [5edff4ae6ff62c7e0bbfad624fc3d0ba7dc84392](https://github.com/ethereum/EIPs/commit/5edff4ae6ff62c7e0bbfad624fc3d0ba7dc84392) + - [x] EIP 2315 Commit Hash - [e8accf22cdc5562d6982c560080c6cd6b7f94867](https://github.com/ethereum/EIPs/commit/e8accf22cdc5562d6982c560080c6cd6b7f94867) + +*[ ] Proposed - [x] Consensus to include.* +## Timeline + + - Deployed: June 3rd 2020 + +## Client Consensus -> Implementation + +YOLO-v1 +| **Client** | Signal | Spec | Merged | Syncing | +|--------------|--------|------|--------|---------| +| Besu | x | x | | | +| EthereumJS | x | | | | +| Geth | x | x | x | x | +| Nethermind | x | x | | | +| OpenEthereum | x | x | | | +| Trinity | | | | | + +**Signal** - +Client intends to participate. *(You are on the bus)* + +**Spec** - +Client is satisfied with the proposed specification. *(You agree with the direction)* + +**Merge** - +Changes are implemented in the client and configurable for YOLO. *(You are ready to hit the gas and go)* + +**Syncing** +Client syncs with the network + + +## Syncing Instructions + +**Geth** +- Yolo V1 testnet is up and running https://yolonet.xyz/ +- Support is baked into Geth master branch via --yolov1 +- Genesis config json is at https://yolonet.xyz/yolo.json +- EF bootnode at enode://9e1096aa59862a6f164994cb5cb16f5124d6c992cdbf4535ff7dea43ea1512afe5448dca9df1b7ab0726129603f1a3336b631e4d7a1a44c94daddd03241587f9@35.178.210.161:30303 +- Stats page secret is YOLOv1, with geth you can --ethstats='yournode:YOLOv1@stats.yolonet.xyz' +- Faucet is unauthenticated, you can reach it from the dashboard + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2666.md b/EIPS/eip-2666.md new file mode 100644 index 0000000..3bd458a --- /dev/null +++ b/EIPS/eip-2666.md @@ -0,0 +1,133 @@ +--- +eip: 2666 +title: Repricing of precompiles and Keccak256 function +author: Alex Vlasov (@shamatar) +discussions-to: https://ethereum-magicians.org/t/eip2666-global-precompiles-repricing-and-many-more-discussion-thread/4332 +status: Stagnant +type: Standards Track +category: Core +created: 2020-05-22 +requires: 1352, 2046, 2565 +--- + +## Simple Summary + +This EIP tries to set prices of certain precompiles and built-in EVM function to be in line with their performance, consumed resources and newer changes in EVM itself. + +New price formulas are proposed for: +- SHA256 precompile (`0x02`) +- RIPEMD precompile (`0x03`) +- KECCAK256 opcode (`0x20`) + +## Abstract + +Costs of many precompiles and built-in functions are invalid at the current state of the clients. This EIP contains a list of changes to the pricing formulas to better reflect underlying computations' structure. + +## Motivation + +Historical pricing for these functions in EVM does not reflect inner structure of the underlying computations (inner structure of the hash functions). + +- EIP-2046 changes a `STATICCALL (0xfa)` cost to precompile and it may be necessary to adjust costs of some precompiles that *may* have taken old large cost (`700` gas) into account and tried to compensate for it +- Some precompiles are overpriced and their pricing formulas do not reflect the structure of underlying functions +- Keccak256 built-in function (opcode) in EVM has pricing that does not reflect underlying hash function structure + +## Specification + +If `block_number >= X` set the gas cost of the following precompiles and Keccak256 opcode: +- SHA256 (precompile `0x02`): `10 + ((len(input) + 8)/64 + 1) * 9` +- RIPEMD (precompile `0x03`): `6 + ((len(input) + 8)/64 + 1) * 12` +- KECCAK256 (`0x20`): `13 + (len(input)/136 + 1)*15` + +This EIP *ideally* requires that `MODEXP` repricing is [implemented](./eip-2565.md) to also accurately reflect that there is no implicit compensation for an old `STATICCALL (0xfa)` cost (pre-2046). + +## Rationale +Cost of functions being executed must accurately reflect real CPU time spent on computations, so benchmarking was performed for current precompiles and Keccak256 function to measure running time versus input parameters. + +### Detailed summary of repricing approach + +This EIP relies on two facts: +- apriori knowledge of the inner strucute of the hash functions +- benchmarks provided by the client teams for some reasonable range of input lengths for random inputs (random byte strings of a given length) + +### Benchmarks on the most popular clients + +Necessary benchmarks for EIP-2666 were provided by the clients and raw form is assembled in [here](https://docs.google.com/spreadsheets/d/1aCQnk7prrp3Mbcf011BE5zZnkbc3Iw7QAixn6mLbKS0/edit?usp=sharing) + +- SHA256 precompile + +Currently it's `60` gas + `12` gas per `32` byte word (number of words is `ceil(len(input)/word_len)` here and in similar places. If there is no `floor` or `ceil` specifier all divisions below are integer divisions (floor divisions)). Proposed formula is `A * ((len(input) + 8) / 64 + 1) + B`, with coefficients below + +| | | A | B | +|---|---|---|---| +| Geth | | 5 | 3 | +| OE | | 9 | 4 | +| Besu | | 5 | 10 | +| Nethermind | | 10 | 5 | + +EIP-2666 proposes `A = 9`, `B = 10`. There are no large one-off costs in this precompile, so it's EIP-2046 - safe. + +- RIPEMD precompile + +Currently it's `600` gas + `120` gas per `32` byte word. Proposed formula is `A * ((len(input) + 8) / 64 + 1) + B`, with coefficients below + +| | | A | B | +|---|---|---|---| +| Geth | | 12 | 6 | +| OE | | 8 | 2 | +| Besu | | 29 | 16 | +| Nethermind | | 10 | 6 | + +EIP-2666 proposes `A = 12`, `B = 6`. There are no large one-off costs in this precompile, so it's EIP-2046 - safe. Besu expects to have performance improvements by the end of the year. + +- Keccak256 performance + +Currently it's `30` gas + `6` gas per `32` byte word. Proposed formula is `A * (len(input) / 136 + 1) + B`, with coefficients below + +| | | A | B | +|---|---|---|---| +| Geth | | 13 | 13 | +| OE | | 15 | 2 | +| Besu | | 19 | 28 | +| Nethermind | | 16 | 3 | + +EIP-2666 proposes `A = 15`, `B = 13`. There are no large one-off costs in this precompile, so it's EIP-2046 - safe. Besu expects to have performance improvements by the end of the year. + +### Tooling and data + +Reference material (from benchmarks of different clients) with raw data can be found [here](https://docs.google.com/spreadsheets/d/1aCQnk7prrp3Mbcf011BE5zZnkbc3Iw7QAixn6mLbKS0/edit?usp=sharing). + +There is a repository available with inputs for benchmarking and precompiles testing [here](https://github.com/shamatar/bench_precompiles) that can be used by client teams to perform all the necessary measurements. + +Raw Besu [benchmarks](https://gist.github.com/shemnon/0ddba91be501fa23291bdec9107fe99a). + +### Note on formulas structure + +There are terms in formulas that look like `A * 1` and those are explicitly not combined to the `B` coefficient to reflect that hash of an empty byte array requires to perform a round of hashing anyway. + +## Backwards Compatibility +Precompile repricings has happened in a past and can be considered standard procedure. Gas costs of many contracts is expected to reduce that may break re-entrancy protection measures based on fixed gas costs. In any case, such protection should have never been considered good and final. + +## Test Cases + +Let's consider a simple example of Keccak256 hash of `0`, `64` and `160` bytes that can is a simple sanity check for implementation. + +- Hash `0` bytes: + - Old price: `30 + 6 * ceil(0 / 32) = 30` gas + - New price: `15 * (0/136 + 1) + 13 = 28` gas +- Hash `64` bytes + - Old price: `30 + 6 * ceil(64 / 32) = 42` gas + - New price: `15 * (64/136 + 1) + 13 = 28` gas +- Hash `160` bytes + - Old price: `30 + 6 * ceil(160 / 32) = 60` gas + - New price: `15 * (160/136 + 1) + 13 = 43` gas + +## Implementation + +There is no reference implementation at the time of writing as it requires just a simple change of constants in major clients. + +## Security Considerations + +As described in backward compatibility section in some cases reduction of cost may allow e.g. re-entrancy that was not expected before, but we think that re-entrancy protection based on fixed gas costs is anyway flawed design decision. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2677.md b/EIPS/eip-2677.md new file mode 100644 index 0000000..e0d136c --- /dev/null +++ b/EIPS/eip-2677.md @@ -0,0 +1,76 @@ +--- +eip: 2677 +title: Limit size of `initcode` +author: Martin Holst Swende (@holiman), Paweł Bylica (@chfast), Alex Beregszaszi (@axic) +discussions-to: https://ethereum-magicians.org/t/eip-2677-limit-size-of-initcode/4550 +status: Stagnant +type: Standards Track +category: Core +created: 2020-05-18 +--- + +## Simple Summary + +Enforce a maximum size limit (`max_initcode_size`) of `49152` (`0xc000`) for `initcode`. + +## Abstract + +Enforce a maximum size limit (`max_initcode_size`) for `initcode`. If the size of `initcode` exceeds `max_initcode_size`, then contract creation fails with an out of gas error. + +Since [EIP-170](./eip-170.md) was implemented, there has been a size limit of `24576` (`0x6000`) on contract code. We propose to also limit the size of executable code to `2x` the above limit, i.e. `49152` (`0xc000`). + +This also leads to two nice properties: + +- instruction offset in code fits 16-bit value, +- code size fits 16-bit value. + +## Motivation + +When a client executes `initcode`, the client has to perform a jumpdest analysis. In some cases, the client also performs a `hash` of the code: + +* To use as a key in a mapping containing result of a jumpdest analysis +* To use for address calculation within `CREATE2`. + +The work performed during a jumpdest analysis scales linearly with the size of the code. Currently, a transaction can expand the memory once, and reuse the same memory segment (with minor modifications) to force the client to perform a lot of analysis/hashing, leading to slow block processing. + +Historically, this was exploited in June 2017, precipitating the 1.6.5-patch release of [geth](https://github.com/ethereum/go-ethereum/releases/tag/v1.6.5) + +The work performed during address calculation within `CREATE2` is charged in proportion with size of the code. + +## Specification + +There are three situations where this is applicable: + +* `CREATE`, +* `CREATE2`, +* creation using a transaction with empty receiver. + +In all these (and future) cases, the EVM should fail with Out Of Gas error if the code has a length more than `max_initcode_size`. + +## Rationale + +TBA + +## Backwards Compatibility + +This EIP requires a "network upgrade", since it modifies consenus-rules. + +## Security Considerations + +For client implementations, this EIP makes attacks based on jumpdest-analysis or hashing of code less problematic, so should increase the robustness of clients. + +For layer 2, this EIP introduces failure-modes where there previously were none. There _could_ exist factory-contracts which deploy multi-level contract hierarchies, such that the code for multiple contracts are included in the initcode of the first contract. The author(s) of this EIP are not aware of any such contracts. + +## Test Cases + +Test cases should include the following cases, + +- `CREATE`/`CREATE2`/`tx create` with `initcode_size` at `max_initcode_size` +- `CREATE`/`CREATE2`/`tx create` with `initcode_size` at `max_initcode_size+1` + +## Implementation + +TBA + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2678.md b/EIPS/eip-2678.md new file mode 100644 index 0000000..5daf0ac --- /dev/null +++ b/EIPS/eip-2678.md @@ -0,0 +1,1042 @@ +--- +eip: 2678 +title: Revised Ethereum Smart Contract Packaging Standard (EthPM v3) +author: g. nicholas d’andrea (@gnidan), Piper Merriam (@pipermerriam), Nick Gheorghita (@njgheorghita), Christian Reitwiessner (@chriseth), Ben Hauser (@iamdefinitelyahuman), Bryant Eisenbach (@fubuloubu) +discussions-to: https://ethereum-magicians.org/t/ethpm-v3-specification-working-group/4086 +status: Final +type: Standards Track +category: ERC +created: 2020-05-26 +--- + + +## Simple Summary + +A data format describing a smart contract software package. + + +## Abstract + +This EIP defines a data format for *package manifest* documents, +representing a package of one or more smart contracts, optionally +including source code and any/all deployed instances across multiple +networks. Package manifests are minified JSON objects, to be distributed +via content addressable storage networks, such as IPFS. Packages +are then published to on-chain EthPM registries, defined in +[EIP-1319](./eip-1319.md), from where they can be freely accessed. + +This document presents a natural language description of a formal +specification for version **3** of this format. + + +## Motivation + +This standard aims to encourage the Ethereum development ecosystem +towards software best practices around code reuse. By defining an open, +community-driven package data format standard, this effort seeks to +provide support for package management tools development by offering a +general-purpose solution that has been designed with observed common +practices in mind. + +- Updates the schema for a *package manifest* to be compatible with + the [metadata](https://solidity.readthedocs.io/en/latest/metadata.html) output for compilers. +- Updates the `"sources"` object definition to support a wider range of source file types and serve as [JSON input](https://solidity.readthedocs.io/en/latest/using-the-compiler.html#compiler-input-and-output-json-description) for a compiler. +- Moves compiler definitions to a top-level `"compilers"` array in order to: + - Simplify the links between a compiler version, sources, and the + compiled assets. + - Simplify packages that use multiple compiler versions. +- Updates key formatting from `snake_case` to `camelCase` to be + more consistent with [JSON convention](https://google.github.io/styleguide/jsoncstyleguide.xml?showone=Property_Name_Format#Property_Name_Format). + +### Guiding Principles + +This specification makes the following assumptions about the document +lifecycle. + +1. Package manifests are intended to be generated programmatically by + package management software as part of the release process. + +2. Package manifests will be consumed by package managers during tasks + like installing package dependencies or building and deploying new + releases. + +3. Package manifests will typically **not** be stored alongside the + source, but rather by package registries *or* referenced by package + registries and stored in something akin to IPFS. + +4. Package manifests can be used to verify public deployments of source + contracts. + +### Use Cases + +The following use cases were considered during the creation of this +specification. + +* **owned**: A package which contains contracts which are not meant to be used by themselves but rather as base contracts to provide functionality to other contracts through inheritance. +* **transferable**: A package which has a single dependency. +* **standard-token**: A package which contains a reusable contract. +* **safe-math-lib**: A package which contains deployed instance of one of the package contracts. +* **piper-coin**: A package which contains a deployed instance of a reusable contract from a dependency. +* **escrow**: A package which contains a deployed instance of a local contract which is linked against a deployed instance of a local library. +* **wallet**: A package with a deployed instance of a local contract which is linked against a deployed instance of a library from a dependency. +* **wallet-with-send**: A package with a deployed instance which links against a deep dependency. +* **simple-auction**: Compiler `"metadata"` field output. + +## Package Specification + +### Conventions + +#### RFC2119 + +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. + +- + + +#### Prefixed vs Unprefixed + +A [prefixed](#prefixed) hexadecimal value begins with `0x`. +[Unprefixed](#unprefixed) values have no prefix. Unless otherwise +specified, all hexadecimal values **should** be represented with the +`0x` prefix. + +* **Prefixed**: `0xdeadbeef` +* **Unprefixed**: `deadbeef` + +### Document Format + +The canonical format is a single JSON object. Packages **must** conform +to the following serialization rules. + +- The document **must** be tightly packed, meaning no linebreaks or + extra whitespace. + +- The keys in all objects **must** be sorted alphabetically. + +- Duplicate keys in the same object are invalid. + +- The document **must** use + [UTF-8](https://en.wikipedia.org/wiki/UTF-8) + encoding. + +- The document **must** not have a trailing newline. + +- To ensure backwards compatibility, `manifest_version` is a forbidden + top-level key. + + +### Document Specification + +The following fields are defined for the package. Custom fields **may** +be included. Custom fields **should** be prefixed with `x-` to prevent +name collisions with future versions of the specification. + +* **See Also**: Formalized ([JSON-Schema](https://json-schema.org)) version of this specification: [package.spec.json](../assets/eip-2678/package.spec.json) +* **Jump To**: [Definitions](#object-definitions) + +### EthPM Manifest Version + +The `manifest` field defines the specification version that this +document conforms to. + +- Packages **must** include this field. + +* **Required**: Yes +* **Key**: `manifest` +* **Type**: String +* **Allowed Values**: `ethpm/3` + +### Package Name + +The `name` field defines a human readable name for this package. + +- Packages **should** include this field to be released on an EthPM + registry. + +- Package names **must** begin with a lowercase letter and be + comprised of only the lowercase letters `a-z`, numeric characters `0-9`, and the + dash character `-`. + +- Package names **must** not exceed 255 characters in length. + +* **Required**: If `version` is included. +* **Key**: `name` +* **Type**: String +* **Format**: **must** match the regular expression `^[a-z][-a-z0-9]{0,255}$` + +### Package Version + +The `version` field declares the version number of this release. + +- Packages **should** include this field to be released on an EthPM + registry. + +- This value **should** conform to the + [semver](http://semver.org/) version numbering + specification. + +* **Required**: If `name` is included. +* **Key**: `version` +* **Type**: String + +### Package Metadata + +The `meta` field defines a location for metadata about the package which +is not integral in nature for package installation, but may be important +or convenient to have on-hand for other reasons. + +- This field **should** be included in all Packages. + +* **Required**: No +* **Key**: `meta` +* **Type**: [Package Meta Object](#the-package-meta-object) + +### Sources + +The `sources` field defines a source tree that **should** comprise the +full source tree necessary to recompile the contracts contained in this +release. + +* **Required**: No +* **Key**: `sources` +* **Type**: Object (String: [Sources Object](#the-source-object)) + +### Contract Types + +The `contractTypes` field hosts the [Contract +Types](#contract-type) which have been included in this release. + +- Packages **should** only include contract types that can be found in + the source files for this package. + +- Packages **should not** include contract types from dependencies. + +- Packages **should not** include abstract contracts in the contract + types section of a release. + +* **Required**: No +* **Key**: `contractTypes` +* **Type**: Object (String: [Contract Type Object](#the-contract-type-object)) +* **Format**: Keys **must** be valid [Contract Aliases](#contract-alias).
Values **must** conform to the [Contract Type Object](#the-contract-type-object) definition. + +### Compilers + +The `compilers` field holds the information about the compilers and +their settings that have been used to generate the various +`contractTypes` included in this release. + +* **Required**: No +* **Key**: `compilers` +* **Type**: Array ([Compiler Information Object](#the-compiler-information-object)) + +### Deployments + +The `deployments` field holds the information for the chains on which +this release has [Contract Instances](#contract-instance) as well +as the [Contract Types](#contract-type) and other deployment +details for those deployed contract instances. The set of chains defined +by the [BIP122 URI](#bip122-uri) keys for this object **must** be +unique. There cannot be two different URI keys in a deployments field +representing the same blockchain. + +* **Required**: No +* **Key**: `deployments` +* **Type**: Object (String: Object(String: [Contract Instance Object](#the-contract-instance-object))) +* **Format**: Keys **must** be a valid BIP122 URI chain definition.
Values **must** be objects which conform to the following format:
- Keys **must** be valid [Contract Instance Names](#contract-instance-name)
- Values **must** be a valid [Contract Instance Object](#the-contract-instance-object) + +### Build Dependencies + +The `buildDependencies` field defines a key/value mapping of EthPM +packages that this project depends on. + +* **Required**: No +* **Key**: `buildDependencies` +* **Type**: Object (String: String) +* **Format**: Keys **must** be valid [package names](#package-name).
Values **must** be a [Content Addressable URI](#content-addressable-uri) which resolves to a valid package that conforms the same EthPM manifest version as its parent. + +### Object Definitions + +Definitions for different objects used within the Package. All objects +allow custom fields to be included. Custom fields **should** be prefixed +with `x-` to prevent name collisions with future versions of the +specification. + + +### The *Link Reference* Object + +A [Link Reference](#link-reference) object has the following +key/value pairs. All link references are assumed to be associated with +some corresponding [Bytecode](#bytecode). + +#### Offsets: `offsets` + +The `offsets` field is an array of integers, corresponding to each of +the start positions where the link reference appears in the bytecode. +Locations are 0-indexed from the beginning of the bytes representation +of the corresponding bytecode. This field is invalid if it references a +position that is beyond the end of the bytecode. + +* **Required**: Yes +* **Type**: Array + +#### Length: `length` + +The `length` field is an integer which defines the length in bytes of +the link reference. This field is invalid if the end of the defined link +reference exceeds the end of the bytecode. + +* **Required**: Yes +* **Type**: Integer + +#### Name: `name` + +The `name` field is a string which **must** be a valid +[Identifier](#identifier). Any link references which **should** be +linked with the same link value **should** be given the same name. + +* **Required**: No +* **Type**: String +* **Format**: **must** conform to the [Identifier](#identifier) format. + +### The *Link Value* Object + +Describes a single [Link Value](#link-value). + +A **Link Value object** is defined to have the following key/value +pairs. + + +#### Offsets: `offsets` + +The `offsets` field defines the locations within the corresponding +bytecode where the `value` for this link value was written. These +locations are 0-indexed from the beginning of the bytes representation +of the corresponding bytecode. + +* **Required**: Yes +* **Type**: Integer +* **Format**: See below. + +Format + +Array of integers, where each integer **must** conform to all of the +following. + +- greater than or equal to zero + +- strictly less than the length of the unprefixed hexadecimal + representation of the corresponding bytecode. + +#### Type: `type` + +The `type` field defines the `value` type for determining what is +encoded when [linking](#linking) the corresponding bytecode. + +* **Required**: Yes +* **Type**: String +* **Allowed Values**: `"literal"` for bytecode literals.
`"reference"` for named references to a particular [Contract Instance](#contract-instance) + +#### Value: `value` + +The `value` field defines the value which should be written when [linking](#linking) the corresponding bytecode. + +* **Required**: Yes +* **Type**: String +* **Format**: Determined based on `type`, see below. + +Format + +For static value *literals* (e.g. address), value **must** be a 0x-prefixed +hexadecimal string representing bytes. + + +To reference the address of a [Contract +Instance](#contract-instance) from the current package the value +should be the name of that contract instance. + +- This value **must** be a valid [Contract Instance + Name](#contract-instance-name). + +- The chain definition under which the contract instance that this + link value belongs to must contain this value within its keys. + +- This value **may not** reference the same contract instance that + this link value belongs to. + +To reference a contract instance from a [Package](#package) from +somewhere within the dependency tree the value is constructed as +follows. + +- Let `[p1, p2, .. pn]` define a path down the dependency tree. + +- Each of `p1, p2, pn` **must** be valid package names. + +- `p1` **must** be present in keys of the `buildDependencies` for the + current package. + +- For every `pn` where `n > 1`, `pn` **must** be present in the keys + of the `buildDependencies` of the package for `pn-1`. + +- The value is represented by the string + `::<...>::` where all of ``, + ``, `` are valid package names and `` is + a valid [Contract Name](#contract-name). + +- The `` value **must** be a valid [Contract + Instance Name](#contract-instance-name). + +- Within the package of the dependency defined by ``, all of the + following must be satisfiable: + + - There **must** be *exactly* one chain defined under the + `deployments` key which matches the chain definition that this + link value is nested under. + + - The `` value **must** be present in the keys + of the matching chain. + +### The *Bytecode* Object + +A bytecode object has the following key/value pairs. + +#### Bytecode: `bytecode` + +The `bytecode` field is a string containing the `0x` prefixed +hexadecimal representation of the bytecode. + +* **Required**: Yes +* **Type**: String +* **Format**: `0x` prefixed hexadecimal. + +#### Link References: `linkReferences` + +The `linkReferences` field defines the locations in the corresponding +bytecode which require [linking](#linking). + +* **Required**: No +* **Type**: Array +* **Format**: All values **must** be valid [Link Reference objects](#the-link-reference-object). See also below. + +Format + +This field is considered invalid if *any* of the [Link +References](#link-reference) are invalid when applied to the +corresponding `bytecode` field, *or* if any of the link references +intersect. + +Intersection is defined as two link references which overlap. + +#### Link Dependencies: `linkDependencies` + +The `linkDependencies` defines the [Link Values](#link-value) that +have been used to link the corresponding bytecode. + +* **Required**: No +* **Type**: Array +* **Format**: All values **must** be valid [Link Value objects](#the-link-value-object). See also below. + +Format + +Validation of this field includes the following: + +- Two link value objects **must not** contain any of the same values + for `offsets`. + +- Each [link value object](#the-link-value-object) **must** have a + corresponding [link reference object](#the-link-reference-object) under + the `linkReferences` field. + +- The length of the resolved `value` **must** be equal to the `length` + of the corresponding [Link Reference](#link-reference). + + +### The *Package Meta* Object + +The *Package Meta* object is defined to have the following key/value +pairs. + +#### Authors + +The `authors` field defines a list of human readable names for the +authors of this package. Packages **may** include this field. + +* **Required**: No +* **Key**: `authors` +* **Type**: Array(String) + +#### License + +The `license` field declares the license associated with this package. +This value **should** conform to the +[SPDX](https://spdx.org/licenses/) +format. Packages **should** include this field. If a file [Source +Object](#the-source-object) defines its own license, that license takes +precedence for that particular file over this package-scoped `meta` +license. + +* **Required**: No +* **Key**: `license` +* **Type**: String + +#### Description + +The `description` field provides additional detail that may be relevant +for the package. Packages **may** include this field. + +* **Required**: No +* **Key**: `description` +* **Type**: String + +#### Keywords + +The `keywords` field provides relevant keywords related to this package. + +* **Required**: No +* **Key**: `keywords` +* **Type**: Array(String) + +#### Links + +The `links` field provides URIs to relevant resources associated with +this package. When possible, authors **should** use the following keys +for the following common resources. + +- `website`: Primary website for the package. + +- `documentation`: Package Documentation + +- `repository`: Location of the project source code. + +* **Required**: No +* **Key**: `links` +* **Type**: Object (String: String) + +### The *Sources* Object + +A *Sources* object is defined to have the following fields. + +* **Key**: A unique identifier for the source file. (String) +* **Value**: [Source Object](#the-source-object) + +### The *Source* Object + +#### Checksum: `checksum` + +Hash of the source file. + +* **Required**: Only **if** the `content` field is missing and none of the provided URLs contain a content hash. +* **Key**: `checksum` +* **Value**: [Checksum Object](#the-checksum-object) + +#### URLS: `urls` + +Array of urls that resolve to the same source file. +- Urls **should** be stored on a content-addressable filesystem. + **If** they are not, then either `content` or `checksum` **must** be + included. + +- Urls **must** be prefixed with a scheme. + +- If the resulting document is a directory the key **should** be + interpreted as a directory path. + +- If the resulting document is a file the key **should** be + interpreted as a file path. + +* **Required**: If `content` is not included. +* **Key**: `urls` +* **Value**: Array(String) + +#### Content: `content` + +Inlined contract source. If both `urls` and `content` are provided, the `content` value +**must** match the content of the files identified in `urls`. + +* **Required**: If `urls` is not included. +* **Key**: `content` +* **Value**: String + +#### Install Path: `installPath` + +Filesystem path of source file. +- **Must** be a relative filesystem path that begins with a `./`. + +- **Must** resolve to a path that is within the current virtual + working directory. + +- **Must** be unique across all included sources. + +- **Must not** contain `../` to avoid accessing files outside of + the source folder in improper implementations. + +* **Required**: This field **must** be included for the package to be writable to disk. +* **Key**: `installPath` +* **Value**: String + +#### Type: `type` + +The `type` field declares the type of the source file. The field +**should** be one of the following values: `solidity`, `vyper`, +`abi-json`, `solidity-ast-json`. + +* **Required**: No +* **Key**: `type` +* **Value**: String + +#### License: `license` + +The `license` field declares the type of license associated with +this source file. When defined, this license overrides the +package-scoped [meta license](#license). + +* **Required**: No +* **Key**: `license` +* **Value**: String + +### The *Checksum* Object + +A *Checksum* object is defined to have the following key/value pairs. + +#### Algorithm: `algorithm` + +The `algorithm` used to generate the corresponding hash. Possible +algorithms include, but are not limited to `sha3`, `sha256`, `md5`, +`keccak256`. + +* **Required**: Yes +* **Type**: String + +#### Hash: `hash` + +The `hash` of a source files contents generated with the corresponding +algorithm. + +* **Required**: Yes +* **Type**: String + +### The *Contract Type* Object + +A *Contract Type* object is defined to have the following key/value +pairs. + +#### Contract Name: `contractName` + +The `contractName` field defines the [Contract +Name](#contract-name) for this [Contract +Type](#contract-type). + +* **Required**: If the [Contract Name](#contract-name) and [Contract Alias](#contract-alias) are not the same. +* **Type**: String +* **Format**: **Must** be a valid [Contract Name](#contract-name) + +#### Source ID: `sourceId` + +The global source identifier for the source file from which this +contract type was generated. + +* **Required**: No +* **Type**: String +* **Format**: **Must** match a unique source ID included in the [Sources Object](#the-sources-object) for this package. + +#### Deployment Bytecode: `deploymentBytecode` + +The `deploymentBytecode` field defines the bytecode for this [Contract +Type](#contract-type). + +* **Required**: No +* **Type**: Object +* **Format**: **Must** conform to the [Bytecode object](#the-bytecode-object) format. + +#### Runtime Bytecode: `runtimeBytecode` + +The `runtimeBytecode` field defines the unlinked `0x`-prefixed runtime +portion of [Bytecode](#bytecode) for this [Contract +Type](#contract-type). + +* **Required**: No +* **Type**: Object +* **Format**: **Must** conform to the [Bytecode object](#the-bytecode-object) format. + +#### ABI: `abi` + +* **Required**: No +* **Type**: Array +* **Format**: **Must** conform to the [Ethereum Contract ABI JSON](https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI#json) format. + +#### UserDoc: `userdoc` + +* **Required**: No +* **Type**: Object +* **Format**: **Must** conform to the [UserDoc](https://github.com/ethereum/wiki/wiki/Ethereum-Natural-Specification-Format#user-documentation) format. + +#### DevDoc: `devdoc` + +* **Required**: No +* **Type**: Object +* **Format**: **Must** conform to the [DevDoc](https://github.com/ethereum/wiki/wiki/Ethereum-Natural-Specification-Format#developer-documentation) format. + +### The *Contract Instance* Object + +A **Contract Instance Object** represents a single deployed [Contract +Instance](#contract-instance) and is defined to have the following +key/value pairs. + +#### Contract Type: `contractType` + +The `contractType` field defines the [Contract +Type](#contract-type) for this [Contract +Instance](#contract-instance). This can reference any of the +contract types included in this [Package](#package) *or* any of the +contract types found in any of the package dependencies from the +`buildDependencies` section of the [Package +Manifest](#package-manifest). + +* **Required**: Yes +* **Type**: String +* **Format**: See below. + +Format + +Values for this field **must** conform to *one of* the two formats +herein. + +To reference a contract type from this Package, use the format +``. + +- The `` value **must** be a valid [Contract + Alias](#contract-alias). + +- The value **must** be present in the keys of the `contractTypes` + section of this Package. + +To reference a contract type from a dependency, use the format +`:`. + +- The `` value **must** be present in the keys of the + `buildDependencies` of this Package. + +- The `` value **must** be be a valid [Contract + Alias](#contract-alias). + +- The resolved package for `` must contain the + `` value in the keys of the `contractTypes` section. + +#### Address: `address` + +The `address` field defines the [Address](#address) of the +[Contract Instance](#contract-instance). + +* **Required**: Yes +* **Type**: String +* **Format**: Hex encoded `0x` prefixed Ethereum address matching the regular expression `^0x[0-9a-fA-F]{40}$`. + +#### Transaction: `transaction` + +The `transaction` field defines the transaction hash in which this +[Contract Instance](#contract-instance) was created. + +* **Required**: No +* **Type**: String +* **Format**: `0x` prefixed hex encoded transaction hash. + +#### Block: `block` + +The `block` field defines the block hash in which this the transaction +which created this *contract instance* was mined. + +* **Required**: No +* **Type**: String +* **Format**: `0x` prefixed hex encoded block hash. + +#### Runtime Bytecode: `runtimeBytecode` + +The `runtimeBytecode` field defines the runtime portion of bytecode for +this [Contract Instance](#contract-instance). When present, the +value from this field supersedes the `runtimeBytecode` from the +[Contract Type](#contract-type) for this [Contract +Instance](#contract-instance). + +* **Required**: No +* **Type**: Object +* **Format**: **Must** conform to the [Bytecode Object](#the-bytecode-object) format. + +Every entry in the `linkReferences` for this bytecode **must** have a +corresponding entry in the `linkDependencies` section. + +### The *Compiler Information* Object + +The `compilers` field defines the various compilers and settings used +during compilation of any [Contract Types](#contract-type) or +[Contract Instance](#contract-instance) included in this package. + +A *Compiler Information* object is defined to have the following +key/value pairs. + +#### Name: `name` + +The `name` field defines which compiler was used in compilation. + +* **Required**: Yes +* **Key**: `name` +* **Type**: String + +#### Version: `version` + +The `version` field defines the version of the compiler. The field +**should** be OS agnostic (OS not included in the string) and take the +form of either the stable version in +[semver](http://semver.org/) format or if built on a +nightly should be denoted in the form of `-` ex: +`0.4.8-commit.60cc1668`. + +* **Required**: Yes +* **Key**: `version` +* **Type**: String + +#### Settings: `settings` + +The `settings` field defines any settings or configuration that was used +in compilation. For the `"solc"` compiler, this **should** conform to +the [Compiler Input and Output +Description](http://solidity.readthedocs.io/en/latest/using-the-compiler.html#compiler-input-and-output-json-description). + +* **Required**: No +* **Key**: `settings` +* **Type**: Object + +#### Contract Types: `contractTypes` + +A list of the [Contract Alias](#contract-alias) or [Contract Types](#contract-type) in this package +that used this compiler to generate its outputs. + +- All `contractTypes` that locally declare `runtimeBytecode` + **should** be attributed for by a compiler object. + +- A single `contractTypes` **must** not be attributed to more than one + compiler. + +* **Required**: No +* **Key**: `contractTypes` +* **Type**: Array([Contract Alias](#contract-alias)) + + +### BIP122 URI + +BIP122 URIs are used to define a blockchain via a subset of the +[BIP-122](https://github.com/bitcoin/bips/blob/master/bip-0122.mediawiki) +spec. + + blockchain:///block/ + +The `` represents the blockhash of the first block on the +chain, and `` represents the hash of the +latest block that’s been reliably confirmed (package managers should be +free to choose their desired level of confirmations). + +### Glossary + +The terms in this glossary have been updated to reflect the changes made +in V3. + +#### ABI +The JSON representation of the application binary interface. See the +official +[specification](https://solidity.readthedocs.io/en/develop/abi-spec.html) +for more information. + +#### Address +A public identifier for an account on a particular chain + +#### Bytecode +The set of EVM instructions as produced by a compiler. Unless otherwise +specified this should be assumed to be hexadecimal encoded, representing +a whole number of bytes, and [prefixed](#prefixed) with `0x`. + +Bytecode can either be linked or unlinked. (see +[Linking](#linking)) + +* **Unlinked Bytecode**: The hexadecimal representation of a contract’s EVM instructions that contains sections of code that requires [linking](#linking) for the contract to be functional.
The sections of code which are unlinked **must** be filled in with zero bytes.
**Example**: `0x606060405260e06000730000000000000000000000000000000000000000634d536f` +* **Linked Bytecode**: The hexadecimal representation of a contract’s EVM instructions which has had all [Link References](#link-reference) replaced with the desired [Link Values](#link-value). **Example**: `0x606060405260e06000736fe36000604051602001526040518160e060020a634d536f` + +#### Chain Definition +This definition originates from [BIP122 +URI](https://github.com/bitcoin/bips/blob/master/bip-0122.mediawiki). + +A URI in the format `blockchain:///block/` + +- `chain_id` is the unprefixed hexadecimal representation of the + genesis hash for the chain. + +- `block_hash` is the unprefixed hexadecimal representation of the + hash of a block on the chain. + +A chain is considered to match a chain definition if the the genesis +block hash matches the `chain_id` and the block defined by `block_hash` +can be found on that chain. It is possible for multiple chains to match +a single URI, in which case all chains are considered valid matches + +#### Content Addressable URI +Any URI which contains a cryptographic hash which can be used to verify +the integrity of the content found at the URI. + +The URI format is defined in RFC3986 + +It is **recommended** that tools support IPFS and Swarm. + +#### Contract Alias +This is a name used to reference a specific [Contract +Type](#contract-type). Contract aliases **must** be unique within a +single [Package](#package). + +The contract alias **must** use *one of* the following naming schemes: + +- `` + +- `` + +The `` portion **must** be the same as the [Contract +Name](#contract-name) for this contract type. + +The `` portion **must** match the regular expression +`^[-a-zA-Z0-9]{1,256}$`. + +#### Contract Instance +A contract instance a specific deployed version of a [Contract +Type](#contract-type). + +All contract instances have an [Address](#address) on some specific +chain. + +#### Contract Instance Name +A name which refers to a specific [Contract +Instance](#contract-instance) on a specific chain from the +deployments of a single [Package](#package). This name **must** be +unique across all other contract instances for the given chain. The name +must conform to the regular expression +`^[a-zA-Z_$][a-zA-Z0-9_$]{0,255}$` + +In cases where there is a single deployed instance of a given [Contract +Type](#contract-type), package managers **should** use the +[Contract Alias](#contract-alias) for that contract type for this +name. + +In cases where there are multiple deployed instances of a given contract +type, package managers **should** use a name which provides some added +semantic information as to help differentiate the two deployed instances +in a meaningful way. + +#### Contract Name +The name found in the source code that defines a specific [Contract +Type](#contract-type). These names **must** conform to the regular +expression `^[a-zA-Z_$][a-zA-Z0-9_$]{0,255}$`. + +There can be multiple contracts with the same contract name in a +projects source files. + +#### Contract Type +Refers to a specific contract in the package source. This term can be +used to refer to an abstract contract, a normal contract, or a library. +Two contracts are of the same contract type if they have the same +bytecode. + +Example: + + contract Wallet { + ... + } + +A deployed instance of the `Wallet` contract would be of of type +`Wallet`. + +#### Identifier +Refers generally to a named entity in the [Package](#package). + +A string matching the regular expression +`^[a-zA-Z][-_a-zA-Z0-9]{0,255}$` + +#### Link Reference +A location within a contract’s bytecode which needs to be linked. A link +reference has the following properties. + +* **`offset`**: Defines the location within the bytecode where the link reference begins. +* **`length`**: Defines the length of the reference. +* **`name`**: (optional) A string to identify the reference. + +#### Link Value +A link value is the value which can be inserted in place of a [Link +Reference](#link-reference) + +#### Linking +The act of replacing [Link References](#link-reference) with [Link +Values](#link-value) within some [Bytecode](#bytecode). + +#### Package +Distribution of an application’s source or compiled bytecode along with +metadata related to authorship, license, versioning, et al. + +For brevity, the term **Package** is often used metonymously to mean +[Package Manifest](#package-manifest). + +#### Package Manifest +A machine-readable description of a package. + +#### Prefixed +[Bytecode](#bytecode) string with leading `0x`. + +* **Example**: `0xdeadbeef` + +#### Unprefixed +Not [Prefixed](#prefixed). + +* **Example**: `deadbeef` + +## Rationale + +### Minification + +EthPM packages are distributed as alphabetically-ordered & minified JSON to ensure consistency. +Since packages are published on content-addressable filesystems (eg. IPFS), this restriction +guarantees that any given set of contract assets will always resolve to the same content-addressed URI. + +### Package Names + +Package names are restricted to lower-case characters, numbers, and `-` to improve the readability +of the package name, in turn improving the security properties for a package. A user is more likely +to accurately identify their target package with this restricted set of characters, and not confuse +a malicious package that disguises itself as a trusted package with similar but different +characters (e.g. `O` and `0`). + +### BIP122 + +The BIP-122 standard has been used since EthPM v1 since it is an industry standard URI scheme for +identifying different blockchains and distinguishing between forks. + +### Compilers + +Compilers are now defined in a top-level array, simplifying the task for tooling to identify the compiler types +needed to interact with or validate the contract assets. This also removes unnecessarily duplicated +information, should multiple `contractTypes` share the same compiler type. + +## Backwards Compatibility + +To improve understanding and readability of the EthPM spec, the +`manifest_version` field was updated to `manifest` in v3. To ensure +backwards compatibility, v3 packages **must** define a top-level +`"manifest"` with a value of `"ethpm/3"`. Additionally, +`"manifest_version"` is a forbidden top-level key in v3 packages. + + +## Security Considerations + +Using EthPM packages implicitly requires importing &/or executing code written by others. The EthPM spec +guarantees that when using a properly constructed and released EthPM package, the user will have the exact same +code that was included in the package by the package author. However, it is impossible to guarantee that this code +is safe to interact with. Therefore, it is critical that end users only interact with EthPM packages authored and +released by individuals or organizations that they trust to include non-malicious code. + + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2680.md b/EIPS/eip-2680.md new file mode 100644 index 0000000..497cac5 --- /dev/null +++ b/EIPS/eip-2680.md @@ -0,0 +1,136 @@ +--- +eip: 2680 +title: Ethereum 2 wallet layout +author: Jim McDonald +discussions-to: https://ethereum-magicians.org/t/eip-2680-ethereum-2-wallet-layout/4323 +status: Stagnant +type: Standards Track +category: ERC +created: 2020-05-29 +--- + +## Simple Summary + +A standard layout and naming format for walletstore and keystore for both hierarchical (e.g. filesystem, Amazon S3) and non-hierarchical (key/value) storage systems. + +## Abstract + +Ethereum wallets have no standards for their layout in persistent storage, making different wallet implementations incompatible. This defines a standard for the placement of Ethereum walletstores and keystores, making it possible for different software to work with the same wallets and keys. + +## Motivation + +A standard layout for wallets and accounts allows interoperability between validators. This benefits users, as they can move from one validator software to another (and back) without requiring movement of files. This is important because any movement of files containing keys involves danger of either deleting them or duplicating them, both of which could cause loss of access to funds. + +## Specification + +There are four elements for a wallet that need to be addressed. These are defined below. + +### Base location +The base location is required to be well-known, either pre-defined or defined by the storage system's connection parameters. + +For filesystems the pre-defined base location for different operating systems is as follows: + + - Windows: `%APPDATA%\ethereum2\wallets` + - MacOSX: `${HOME}/Library/Application Support/ethereum2/wallets` + - Linux: `${HOME}/.config/ethereum2/wallets` + +For other hierarchical stores, for example Amazon S3, the base location MUST be the lower-case hex string representing the [SHA-256](../assets/eip-2680/sha256-384-512.pdf) hash of the string "Ethereum 2 wallet:" appended with the identifier for the hierarchical store. For example, if the account ID for a user's Amazon S3 account is "AbC0438EB" then: + + - string would be `Ethereum 2 wallet:AbC0438EB` + - SHA-256 hash of string would be the byte array `0x991ec14a8d13836b10d8c3039c9e30876491cb8aa9c9c16967578afc815c9229` + - base location would be the string `991ec14a8d13836b10d8c3039c9e30876491cb8aa9c9c16967578afc815c9229` + +For non-hierarchical stores there is no base location. + +### Wallet container +The wallet container holds the walletstore and related keystores. + +The wallet container is identified by the wallet's UUID. It MUST be a string following the syntactic structure as laid out in [section 3 of RFC 4122](https://tools.ietf.org/html/rfc4122#section-3). + +### Walletstore +The walletstore element contains the walletstore and is held within the wallet container. It is identified by the wallet's UUID. It MUST be a string following the syntactic structure as laid out in [section 3 of RFC 4122](https://tools.ietf.org/html/rfc4122#section-3). + +### Keystore +The keystore element contains the keystore for a given key and is held within the wallet container. It is identified by the key's UUID. It MUST be a string following the syntactic structure as laid out in [section 3 of RFC 4122](https://tools.ietf.org/html/rfc4122#section-3). + +## Hierarchical store example +Hierarchical stores are a common way to store and organize information. The most common example is the filesystem, but a number of object-based stores such as Amazon S3 also provide hierarchical naming. + +Putting these elements together for a sample wallet with wallet UUID `1f031fff-c51d-44fc-8baf-d6b304cb70a7` and key UUIDs `1302106c-8441-4e2e-b687-6c77f49fc624` and `4a320100-83fd-4db7-8126-6d6d205ba834` gives the following layout: + +``` +- 1f031fff-c51d-44fc-8baf-d6b304cb70a7 ++- 1302106c-8441-4e2e-b687-6c77f49fc624 ++- 1f031fff-c51d-44fc-8baf-d6b304cb70a7 ++- 4a320100-83fd-4db7-8126-6d6d205ba834 +``` + +### Non-hierarchical store example +Non-hierarchical stores use a simplified approach where the wallet UUID and key UUIDs are concatenated using the ':' character. Using the same example wallet and key UUIDs as above would result in objects with the following keys: + +``` +1f031fff-c51d-44fc-8baf-d6b304cb70a7:1302106c-8441-4e2e-b687-6c77f49fc624 +1f031fff-c51d-44fc-8baf-d6b304cb70a7:1f031fff-c51d-44fc-8baf-d6b304cb70a7 +1f031fff-c51d-44fc-8baf-d6b304cb70a7:4a320100-83fd-4db7-8126-6d6d205ba834 +``` + +### Protecting against concurrent write access +TBD + +### Iterating over wallets +In the case of hierarchical stores and iteration-capable non-hierarchical stores iteration over wallets is a matter of iterating over the files in the root container. + +An implementer MAY include an index in the base location. If so then it MUST follow the structure as specified in the following "Index format" section. + +### Iterating over accounts +In the case of hierarchical stores iteration over accounts is a matter of iterating over the files in the wallet container. + +An implementer MAY include an index within a wallet container for accounts within that wallet. If so then it MUST follow the structure as specified in the following "Index format" section. + +### Index format +The index format is the same for both wallets and accounts, following a standard JSON schema. + +```json +{ + "type": "array", + "items": { + "type": "object", + "properties": { + "uuid": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "uuid", + "name" + ] + } +} +``` + +The index MUST use the identifier 'index'. + +Public keys must NOT be stored in the index. + +## Rationale + +A standard for walletstores, similar to that for keystores, provides a higher level of compatibility between wallets and allows for simpler wallet and key interchange between them. + +## Implementation + +A Go implementation of the filesystem layout can be found at [https://github.com/wealdtech/go-eth2-wallet-filesystem](https://github.com/wealdtech/go-eth2-wallet-filesystem). + +A Go implementation of the Amazon S3 layout can be found at [https://github.com/wealdtech/go-eth2-wallet-s3](https://github.com/wealdtech/go-eth2-wallet-s3). + +## Security Considerations + +Locations for wallet stores are defined to be within each user's personal space, reducing the possibility of accidental exposure of information. It is, however, still possible for permissions to be set such that this data is world-readable, and applications implementing this EIP should attempt to set, and reset, permissions to ensure that only the relevant user has access to the information. + +The names for both wallet and key stores are UUIDs, ensuring that no data is leaked from the metadata. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2681.md b/EIPS/eip-2681.md new file mode 100644 index 0000000..db9e1ff --- /dev/null +++ b/EIPS/eip-2681.md @@ -0,0 +1,56 @@ +--- +eip: 2681 +title: Limit account nonce to 2^64-1 +author: Alex Beregszaszi (@axic) +discussions-to: https://ethereum-magicians.org/t/eip-2681-limit-account-nonce-to-2-64-1/4324 +status: Final +type: Standards Track +category: Core +created: 2020-04-25 +--- + +## Abstract + +Limit account nonce to be between `0` and `2^64-1`. + +## Motivation + +Account nonces are currently specified to be arbitrarily long unsigned integers. Dealing with arbitrary length data in the state witnesses is not optimal, therefore this EIP will allow proofs to represent the nonce in a more optimized way. + +Additionally it could prove beneficial to transaction formats, where some improvements are potentially sought by at least three other proposals. + +Lastly, this facilitates a minor optimisation in clients, because the nonce no longer needs to be kept as a 256-bit number. + +## Specification + +Introduce two new restrictions retroactively from genesis: + +1. Consider any transaction invalid, where the nonce exceeds or equals to `2^64-1`. +2. The `CREATE` and `CREATE2` instructions' execution ends with the result `0` pushed on stack, where the account nonce is `2^64-1`. Gas for initcode execution is not deducted in this case. + +## Rationale + +1. It is unlikely for any nonce to reach or exceed the proposed limit. If one would want to reach that limit via external transactions, it would cost at least `21000 * (2^64-1) = 387_381_625_547_900_583_915_000` gas. + +2. It must be noted that in the past, in the Morden testnet, each new account had a starting nonce of `2^20` in order to differentiate transactions from mainnet transactions. +This mode of replay protection is out of fashion since [EIP-155](./eip-155.md) introduced a more elegant way using chain identifiers. + +3. Most clients already consider the nonce field to be 64-bit, such as go-ethereum. + +4. The reason a transaction with nonce `2^64-1` is invalid, because otherwise after inclusion the sender account's nonce would exceed `2^64-1`. + +## Backwards Compatibility + +While this is a breaking change, no actual effect should be visible: + +1. There is no account in the state currently which would have a nonce exceeding that value. As of November 2020, the account `0xea674fdde714fd979de3edf0f56aa9716b898ec8` is responsible for the highest account nonce at approximately 29 million. + +2. go-ethereum already has this restriction partially in place (`state.Account.Nonce` and `types.txdata.AccountNonce` it as a 64-bit number). + +## Security Considerations + +None. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2696.md b/EIPS/eip-2696.md new file mode 100644 index 0000000..555d517 --- /dev/null +++ b/EIPS/eip-2696.md @@ -0,0 +1,100 @@ +--- +eip: 2696 +title: JavaScript `request` method RPC transport +author: Micah Zoltu (@MicahZoltu), Erik Marks (@rekmarks) +discussions-to: https://github.com/ethereum/EIPs/issues/2697 +status: Final +type: Standards Track +category: Interface +created: 2020-06-04 +--- + +## Simple Summary +A standard for remote procedure calls between an Ethereum Provider and an Ethereum Client when both are able to interface with each other via a shared JavaScript object. + +## Abstract +This standard provides the description of an object that is made available to JavaScript applications which they can use to communicate with the Ethereum blockchain through. This standard only describes the transport mechanism, it does not specify the payloads that are valid nor does it specify how the client or the provider will discover or agree on payload content. + +How/where this Ethereum object is exposed is left to future standards. + +## Motivation +When working within a JavaScript runtime (such as NodeJS, Electron, Browser, etc.) it may be possible for the runtime or a runtime plugin to inject objects into the runtime. Someone authoring a runtime or a runtime plugin may choose to expose an Ethereum Provider to any JavaScript apps or scripts running within that runtime in order to provide indirect access to an Ethereum-like blockchain and potentially signing tools. In order to achieve maximum compatibility between the provider and the client, a standard is necessary for what the shape of that object is. + +## Specification + +### RFC-2119 + +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). + +### Interface + +TypeScript interface definition: +```ts +interface RequestArguments { + readonly method: string; + readonly params?: readonly unknown[] | object; +} + +interface EthereumProvider { + request(args: RequestArguments): Promise +} +``` +The Provider **MUST** implement implement a `request` method on the exposed `EthereumProvider` object. The `request` method **MUST** be callable with a single parameter which contains the arguments for the request as defined in the TypeScript `interface` above. + +If the Provider supports a JSON-RPC (https://www.jsonrpc.org/specification) request as specified elsewhere, then it **MUST** accept a `request` call for that JSON-RPC method with the `RequestArguments.method` argument matching the JSON-RPC `method` string for the RPC call and the `RequestArguments.params` matching the `params` object of the RPC call. The `RequestArguments.params` should be encoded as a JavaScript object matching the specified JSON-RPC type, not encoded as a JSON string as would normally be the case when transporting JSON-RPC. + +#### Example +If the JSON-RPC request would contain a payload like: +```typescript +'{ "jsonrpc": "2.0", "id": 1, "method": "do_work", "params": [ 5, "hello" ] }' +``` +Then the matching `EthereumProvider.request` call would be: +```typescript +declare const provider: EthereumProvider +provider.request({ method: 'method', params: [ 5, 'hello' ] }) +``` + +### Results +If the Provider supports a JSON-RPC request as specified elsewhere, then it **MUST** return an object that matches the expected `result` definition for the associated JSON-RPC request. + +#### Example +If the JSON-RPC response would contain a payload like: +```typescript +'{ "jsonrpc": "2.0", "id": 1, "result": { "color": "red", "value": 5 } }' +``` +Then the matching `EthereumProvider.request` response would be: +```typescript +{ color: 'red', value: 5 } +``` + +### Errors +```ts +interface ProviderRpcError extends Error { + message: string; + code: number; + data?: unknown; +} +``` + +| code | message | meaning | +| -----| --------------------- | ------------------------------------------------------------------------ | +| 4001 | User Rejected Request | The user rejected the request. | +| 4100 | Unauthorized | The requested method and/or account has not been authorized by the user. | +| 4200 | Unsupported Method | The Provider does not support the requested method. | +| 4900 | Disconnected | The Provider is disconnected from all chains. | +| 4901 | Chain Disconnected | The Provider is not connected to the requested chain. | + +If the Provider is unable to fulfill a request for any reason, it **MUST** resolve the promise as an error. The resolved error **MUST** be shaped as a `ProviderRpcError` defined above whenever possible. _While it is impossible to guaranteed that a JavaScript application will never throw an out of memory or stack overflow error, care should be taken to ensure that promise rejections conform to the above shape whenever possible._ + +If a `code` is provided that is listed in the list above, or in the JSON-RPC specification (https://www.jsonrpc.org/specification#error_object), or in the associated JSON-RPC request standard being followed, then the error reason **MUST** align with the established meaning of that code and the `message` **MUST** match the provided `message` + +The `data` field **MAY** contain any data that is relevant to the error or would help the user understand or troubleshoot the error. + +## Rationale +While this standard is perhaps not the greatest mechanism for communicating between an application and a blockchain, it is closely aligned with established practices within the community so migration from existing systems to this one should be relatively easy. Most communication is currently done via JSON-RPC, so aligning with the JSON-RPC standard was desired to enable quick integration with existing systems. + +## Security Considerations +The relationship between Ethereum Provider and client is a trusted one, where it is assumed that the user implicitly trusts the Ethereum Provider which is how it managed to get injected into the client, or the client expressly pulled in a connection to it. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2700.md b/EIPS/eip-2700.md new file mode 100644 index 0000000..bd7c590 --- /dev/null +++ b/EIPS/eip-2700.md @@ -0,0 +1,47 @@ +--- +eip: 2700 +title: JavaScript Provider Event Emitter +author: Micah Zoltu (@MicahZoltu), Erik Marks (@rekmarks) +discussions-to: https://github.com/ethereum/EIPs/issues/2701 +status: Final +type: Standards Track +category: Interface +created: 2020-06-05 +--- + +## Simple Summary +A standard mechanism for JavaScript Ethereum Providers to notify clients about chain state changes when both are able to interface with each other via a shared JavaScript object. + +## Abstract +This standard provides the description of an object that is made available to JavaScript applications which they can use to receive notifications from an Ethereum Provider. This standard only describes the notification mechanism, it does not specify the payloads that are valid nor does it specify how the client or the provider will discover or agree on payload content. + +How/where this Ethereum Provider object is exposed is left to future standards. + +## Motivation +When working within a JavaScript runtime (such as NodeJS, Electron, Browser, etc.) it may be possible for the runtime or a runtime plugin to inject objects into the runtime. Someone authoring a runtime or a runtime plugin may choose to expose an Ethereum Provider to any JavaScript apps or scripts running within that runtime in order to provide notifications of blockchain state changes. In order to achieve maximum compatibility between the provider and the client, a standard is necessary for what the shape of that object is. + +## Specification +### RFC-2119 +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). + +### Interface +```ts +interface EthereumProvider { + on(eventName: string, listener: (...params: unknown[]) => void): void + removeListener(eventName: string, listener: (...params: unknown[]) => void): void +} +``` +The specific events that can be listened to and the shape of their listener callback functions is left to be defined in separate standards. + +If `on` is called with an `eventName` that the provider is familiar with then the provider **MUST** call the provided `listener` when the named event occurs. If the same `listener` is added multiple times to the same event via `on`, the provider **MAY** choose to either callback the listener one time or one time per call to `on`. + +If `removeListener` is called with an `eventName` and `listener` that was previously added via `on` then the provider **MUST** decrease the number of times it calls the `listener` per event by one. + +## Rationale +This EIP is mostly a retrospective EIP meaning it codifies an already existing specification so there isn't a lot of room for improving things such as by using a discriminated union object for listener parameters or having a tighter definition of `on`. The specific events are intentionally left out of this specification as that set will be an ever-evolving collection and having the first few listed here doesn't add value to this specification (especially if, over time, the first few end up deprecated or unused). + +## Security Considerations +The relationship between Ethereum Provider and client is a trusted one, where it is assumed that the user implicitly trusts the Ethereum Provider which is how it managed to get injected into the client, or the client expressly pulled in a connection to it. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2711.md b/EIPS/eip-2711.md new file mode 100644 index 0000000..1f4d513 --- /dev/null +++ b/EIPS/eip-2711.md @@ -0,0 +1,136 @@ +--- +eip: 2711 +title: Sponsored, expiring and batch transactions. +author: Micah Zoltu (@MicahZoltu) +discussions-to: https://ethereum-magicians.org/t/eip-2711-separate-gas-payer-from-msg-sender/4353 +status: Withdrawn +type: Standards Track +category: Core +created: 2020-06-11 +requires: 2718 +--- + +## Simple Summary +Creates a new transaction type that supports sponsored transactions (separate gas payer from sender), batch transactions (multiple transactions executed in sequence), and expiring transactions (transactions which are not valid after a certain timestamp). + +## Abstract +An EIP-2718 transaction with the type number `2` is a new type of transaction that includes support for: +1. **Sponsored Transactions**: an optional additional signature from which the account that will pay for gas (`GAS_PAYER`) can be recovered +2. **Batch Transactions**: multiple transactions from the same sender that will be executed in sequence +3. **Expiring Transactions**: an optional `validUntil` field that makes the transaction invalid after a certain point in time + +## Motivation +### Sponsored Transactions +With the advent of tokens and especially stable coins, it has become common for users to not hold ETH in an account while they may have other assets of value in that account. Some users don't want to be exposed to the perceived volatility of ETH and instead would prefer to transact using other assets. Unfortunately, since gas **MUST** be paid for with ETH, this prevents the user from transacting with their assets without first acquiring some ETH using some other means, and then using that ETH to pay fees. + +This EIP proposes a mechanism by which we can allow people to transact without ever having to own any ETH by allowing someone else to cover gas costs. The arrangements that enable the covering of gas costs is out of scope for this EIP but it could be an extra-protocol monthly subscription, payment could occur as part of the transaction being submitted, the recpient may be willing to cover gas costs, or it could be a free service offered as a value-add by a company that you are working with. + +While it is possible to implement these sort of mechanisms at the individual contract layer, such solutions require integration by just about every contract and those solutions also end up depending on gas costs being stable with time in order to appropriately bake them into contracts without putting either party at risk of malicious participants in the system. For this reason, it is believed that separating out `GAS_PAYER` from `msg.sender` at the protocol layer is valuable. + +### Batch Transactions +Often times an EOA may want to execute a series of transactions with a strong guarantee that they happen in order with nothing occurring between them. For example, one may want to send some tokens to a contract and then follow that up with another transaction that makes a contract call on the destination address that causes those tokens to be registered to them. By supporting transaction batching at layer 1, we can ensure that the user can get strong guarantees at signing time of cross-transaction atomicity. + +### Expiring Transactions +* If any form of dust-account clearing is introduced, e.g. (https://github.com/ethereum/EIPs/issues/168), it will be necessary to introduce a replay protection, such as https://github.com/ethereum/EIPs/issues/169 . Having temporal replay protection removes the need to change nonce-behaviour in the state, since transactions would not be replayable at a later date than explicitly set by the user. +* In many cases, such as during ICOs, a lot of people want their transactions to either become included soon (within a couple of hours) or not at all. Currently, transactions are queued and may not execute for several days, at a cost for both the user (who ends up paying gas for a failing purchase) and the network, dealing with the large transaction queues. +* Node implementations have no commonly agreed metric for which transactions to keep, discard or propagate. Having a TTL on transactions would make it easier to remove stale transactions from the system. + +## Specification +### Definitions +**`TransactionType`** 2. See [EIP-2718](./eip-2718.md) + +**`TransactionSubtype`** is either 1, 2, 3, or 4. + +**`ChainId`** The transaction is valid if this value is `0` or it is included in a block on a chain whose ID is equal to this value. + +**`ValidUntil`** The transaction is valid if this value is `0` or it is included in a block whose `timestamp` is less than or equal to this value. + +**`YParity`** The parity (0 for even, 1 for odd) of the y-value of a secp256k1 signature. + +**`ChildTransaction`** A nested transaction consisting of `[to, value, data]`. + +**`SenderPayload`** Defined based on the `TransactionSubtype` as follows: +1. `[1, ChildTransaction[], nonce, ChainId, ValidUntil, gasLimit, gasPrice]` +2. `[2, ChildTransaction[], nonce, ChainId, ValidUntil, gasLimit, gasPrice]` +3. `[3, ChildTransaction[], nonce, ChainId, ValidUntil, gasLimit]` +4. `[4, ChildTransaction[], nonce, ChainId, ValidUntil]` + +**`SenderSignature`** `[YParity, r, s]` of `secp256k1(keccak256(rlp([TransactionType, SenderPayload])))` + +**`GasPayerPayload`** Defined based on the `TransactionSubtype` as follows: +1. `[]` +2. `[]` +3. `[gasPrice]` +4. `[gasLimit, gasPrice]` + +**`GasPayerSignature`** is `[]` for `TransactionSubType` `1` or `[YParity, r, s]` of `secp256k1(keccak256(rlp([SenderPayload, SenderSignature, GasPayerPayload])))` for others. + +### New Transaction Type + +As of `FORK_BLOCK_NUMBER` an [EIP-2718](./eip-2718.md) transaction with a `TransactionType` of `2` will have its `Payload` interpreted as an RLP encoded tuple of: +``` +[...SenderPayload, ...SenderSignature, ...GasPayerPayload, ...GasPayerSignature] +``` + +The address recovered from `SenderSignature` is the address... +1. ...returned by the `CALLER` opcode (0x33, aka `msg.sender`) during the first call frame of the transaction +2. ...returned by the `ORIGIN` opcode (0x32, aka `tx.origin`) +3. ...whose `nonce` is used +4. ...whose ETH balance is deducted if any value is attached to the transaction +5. ...whose ETH balance is deducted to pay for gas if `GasPayerSignature` is not present + +If `GasPayerSignature` is present, then the address recovered from it is the address... +1. ...whose ETH balance is deducted to pay for gas + +The base gas cost of transactions of this type will be `TRANSACTION_TYPE_2_BASE_GAS_PRICE` + `TRANSACTION_TYPE_2_CHILD_GAS_PRICE` * `n`, rather than the cost associated with transactions of type `0` and legacy transactions. + +### New Transaction Receipt + +As of `FORK_BLOCK_NUMBER` an [EIP-2718](./eip-2718.md) transaction receipt with a `TransactionType` of `2` will have its `Payload` interpreted as a `rlp([status, cumulativeGasUsed, logsBloom, logs][])` where each item of the array corresponds to the child-transaction at matching offset in the transaction type 2 `Payload`. + +## Rationale +### One Monolithic EIP +This EIP could be split up into multiple EIPs, one for each of the subtypes and one for the meta-type. Alternatively, each of the subtypes could be a unique TransactionType. The reason we chose to go with a single EIP with subtypes is because these 4 transactions all have a *lot* in common and each separate EIP would be almost identical to the previous. We felt that in this case, splitting into multiple EIPs wasn't worth the duplication of EIP content. +### ChainID not encoded with `v` +While we could save one byte in the common case by bundling the y-parity bit of the signature with the Chain ID like in EIP-155, this adds complexity to signing tools that the authors deem not worth it given the size of the transaction overall. +### Optionality of ChainID +Sometimes it is useful to have a transaction that *can* be replayed on multiple chains. An example of this is when you construct a vanity signature for a transaction and have the `from` be whatever address that signature recovers to. With the ability to have someone else be a gas payer (setting both the gas limit and the gas price), one can have transactions that deploy contracts which live at the same address on every chain. While this can be accomplished with CREATE2 using legacy transactions, we have the opportunity here to simplify the process and enable potentially other future uses of deterministic transactions by making ChainID optional. +### Optionality of ValidUntil +A user can set `ValidUntil` to a very large number which effectively makes it non-expiring. By making `ValidUntil` optional, we can save some bytes on the wire by allowing such transcations to simply have a `0` (1 byte in RLP) value for this field. +### `SENDER` sets `gasLimit` and `gasPrice` +This type of transaction is useful when the transaction may execute differently depending on what these values are set to. By having the `SENDER` set both, we ensure that the `SENDER` has full control over the transaction details. +### `SENDER` sets `gasLimit`, `GAS_PAYER` sets `gasPrice` +This type of transaction is useful when the transaction may execute differently depending on how much gas it is allowed (e.g., number of loops) but where the `SENDER` would like to give the `GAS_PAYER` the ability to price the transaction to maximize chances of inclusion. +### `GAS_PAYER` sets `gasLimit` and `gasPrice` +This type of transaction allows the `SENDER` to define what they want to do, and leaves all worry about gas to the `GAS_PAYER`. This is useful for transactions where the sender doesn't care how much gas is used or the price that is paid and also either trusts the `GAS_PAYER` to be non-malicious or doesn't care if the `SENDER`'s nonce is increased. Such situations are useful when you have extra-protocol trust between the `SENDER` and `GAS_PAYER` and you want to separate concerns (what to do vs how to get included) for security or complexity reasons. +### Nonces +The inner transaction needs a nonce to protect themselves from replay attacks. Since the inner transaction has a nonce, we get replay protection on the outer transaction as well, so it is not critical for security to have multiple parties provide a nonce. + +We could have the `GAS_PAYER` provide a second nonce, but this would increase the payload size and require `GAS_PAYER` to do replace-by-fee (noisy for gossip) if they want to slip in a new (different inner) transaction with a higher gas price. It would also create the possibility of a deadlock if the `SENDER` nonces aren't ordered the same as the `GAS_PAYER` nonces, and if the `SENDER` nonce isn't the lowest valid nonce for the `SENDER` then the `GAS_PAYER` can't sign and submit yet. Finally, client complexity increases slightly if a transaction has two nonces because you have to protect yourself from deadlocks and do more work to determine validity. +### ValidUntil +For the dust-account clearing usecase, +- This change is much less invasive in the consensus engine. + - No need to maintain a consensus-field of 'highest-known-nonce' or cap the number of transactions from a sender in a block. + - Only touches the transaction validation part of the consensus engine + - Other schemas which uses the `nonce` can have unintended side-effects, + - such as inability to create contracts at certain addresses. + - more difficult to integrate with offline signers, since more elaborate nonce-schemes requires state access to determine. + - More intricate schemes like `highest-nonce` are a lot more difficult, since highest-known-nonce will be a consensus-struct that is incremented and possibly reverted during transaction execution, requiring one more journalled field. + +### ValidUntil as timestamp instead of block number +- The unix time is generally available in most settings, even on a computer which is offline. This means that even a setup where blockchain information is unavailable, the party signing a transaction can generate a transaction with the desired properties. +- The correlation between time and block number is not fixed; even though a 13s blocktime is 'desired', this varies due to both network hashrate and difficulty bomb progression. +- The block number is even more unreliable as a timestamp for testnets and private networks. +- unix time is more user-friendly, a user can more easily decide on reasonable end-date for a transaction, rather than a suitalbe number of valid blocks. + +## Backwards Compatibility +No known issues. + +## Test Cases + +## Implementation + +## Security Considerations + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2718.md b/EIPS/eip-2718.md new file mode 100644 index 0000000..83a19b0 --- /dev/null +++ b/EIPS/eip-2718.md @@ -0,0 +1,79 @@ +--- +eip: 2718 +title: Typed Transaction Envelope +description: Defines a new transaction type that is an envelope for future transaction types. +author: Micah Zoltu (@MicahZoltu) +discussions-to: https://ethereum-magicians.org/t/eip-2718-typed-transaction-envelope/4355 +status: Final +type: Standards Track +category: Core +created: 2020-06-13 +--- + +## Abstract +`TransactionType || TransactionPayload` is a valid transaction and `TransactionType || ReceiptPayload` is a valid transaction receipt where `TransactionType` identifies the format of the transaction and `*Payload` is the transaction/receipt contents, which are defined in future EIPs. + +## Motivation +In the past, when we have wanted to add new transaction types we have had to ensure they were backward compatible with all other transactions, meaning that you could differentiate them based only on the encoded payload, and it was not possible to have a transaction that matched both types. +This was seen in [EIP-155](./eip-155.md) where the new value was bit-packed into one of the encoded fields. +There are multiple proposals in discussion that define new transaction types such as one that allows EOA accounts to execute code directly within their context, one that enables someone besides `msg.sender` to pay for gas, and proposals related to layer 1 multi-sig transactions. +These all need to be defined in a way that is mutually compatible, which quickly becomes burdensome to EIP authors and to clients who now have to follow complex rules for differentiating transaction type. + +By introducing an envelope transaction type, we only need to ensure backward compatibility with existing transactions and from then on we just need to solve the much simpler problem of ensuring there is no numbering conflict between `TransactionType`s. + +## Specification +### Definitions +* `||` is the byte/byte-array concatenation operator. + +### Transactions +As of `FORK_BLOCK_NUMBER`, the transaction root in the block header **MUST** be the root hash of `patriciaTrie(rlp(Index) => Transaction)` where: +* `Index` is the index in the block of this transaction +* `Transaction` is either `TransactionType || TransactionPayload` or `LegacyTransaction` +* `TransactionType` is a positive unsigned 8-bit number between `0` and `0x7f` that represents the type of the transaction +* `TransactionPayload` is an opaque byte array whose interpretation is dependent on the `TransactionType` and defined in future EIPs +* `LegacyTransaction` is `rlp([nonce, gasPrice, gasLimit, to, value, data, v, r, s])` + +All signatures for future transaction types **SHOULD** include the `TransactionType` as the first byte of the signed data. +This makes it so we do not have to worry about signatures for one transaction type being used as signatures for a different transaction type. + +### Receipts +As of `FORK_BLOCK_NUMBER`, the receipt root in the block header **MUST** be the root hash of `patriciaTrie(rlp(Index) => Receipt)` where: +* `Index` is the index in the block of the transaction this receipt is for +* `Receipt` is either `TransactionType || ReceiptPayload` or `LegacyReceipt` +* `TransactionType` is a positive unsigned 8-bit number between `0` and `0x7f` that represents the type of the transaction +* `ReceiptPayload` is an opaque byte array whose interpretation is dependent on the `TransactionType` and defined in future EIPs +* `LegacyReceipt` is `rlp([status, cumulativeGasUsed, logsBloom, logs])` + +The `TransactionType` of the receipt **MUST** match the `TransactionType` of the transaction with a matching `Index`. + +## Rationale +### TransactionType only goes up to 0x7f +For the forseable future, 0x7f is plenty and it leaves open a number of options for extending the range such as using the high bit as a continuation bit. +This also prevents us from colliding with legacy transaction types, which always start with a byte `>= 0xc0`. +### **SHOULD** instead of **MUST** for the TransactionType being first byte of signed data +While it is strongly recommended that all future transactions sign the first byte to ensure that there is no problem with signature reuse, the authors acknowledge that this may not always make sense or be possible. +One example where this isn't possible is wrapped legacy transactions that are signature compatible with the legacy signing scheme. +Another potential situation is one where transactions don't have a signature in the traditional sense and instead have some other mechanism for determining validity. +### TransactionType selection algorithm +There was discussion about defining the `TransactionType` identifier assignment/selection algorithm in this standard. +While it would be nice to have a standardized mechanism for assignment, at the time of writing of this standard there is not a strong need for it so it was deemed out of scope. +A future EIP may introduce a standard for TransactionType identifier assignment if it is deemed necessary. +### Opaque byte array rather than an RLP array +By having the second byte on be opaque bytes, rather than an RLP (or other encoding) list, we can support different encoding formats for the transaction payload in the future such as SSZ, LEB128, or a fixed width format. +### ORIGIN and CALLER +There was discussion about having ORIGIN and CALLER opcodes become dependent on the transaction type, so that each transaction type could define what those opcodes returned. +However, there is a desire to make transaction type opaque to the contracts to discourage contracts treating different types of transactions differently. +There also were concerns over backward compatibility with existing contracts which make assumptions about ORIGIN and CALLER opcodes. +Going forward, we will assume that all transaction types will have an address that reasonably represents a `CALLER` of the first EVM frame and `ORIGIN` will be the same address in all cases. +If a transaction type needs to supply additional information to contracts, they will need a new opcode. + +## Backwards Compatibility +Clients can differentiate between the legacy transactions and typed transactions by looking at the first byte. +If it starts with a value in the range `[0, 0x7f]` then it is a new transaction type, if it starts with a value in the range `[0xc0, 0xfe]` then it is a legacy transaction type. +`0xff` is not realistic for an RLP encoded transaction, so it is reserved for future use as an extension sentinel value. + +## Security Considerations +When designing a new 2718 transaction type, it is **STRONGLY** recommended to include the transaction type as the first byte of the signed payload. If you fail to do this, it is possible that your transaction may be signature compatible with transactions of another type which can introduce security vulnerabilities for users. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2733.md b/EIPS/eip-2733.md new file mode 100644 index 0000000..04b276e --- /dev/null +++ b/EIPS/eip-2733.md @@ -0,0 +1,292 @@ +--- +eip: 2733 +title: Transaction Package +author: Matt Garnett (@lightclient) +discussions-to: https://ethereum-magicians.org/t/eip-transaction-package/4365 +status: Withdrawn +type: Standards Track +category: Core +created: 2020-06-16 +requires: 2718 +withdrawal-reason: I have decided to pursue EIP-3074 as the preferred solution to transaction packages. +--- + +## Simple Summary +Creates a new transaction type which executes a package of one or more +transactions, while passing status information to subsequent transactions. + +## Abstract +Introduce a new transaction type which includes a list of transactions that +must be executed serially by clients. Execution information (e.g. success, +gas_used, etc.) will be propagated forward to the next transaction. + +## Motivation +Onboarding new users to Ethereum has been notoriously difficult due to the need +for new users to acquire enough ether to pay for their transactions. This +hurdle has seen a significant allocation of resources over the years to solve. +Today, that solution is meta-transactions. This is, unfortunately, a brittle +solution that requires signatures to be recovered within a smart contract to +authenticate the message. This EIP aims to provide a flexible framework for +relayers to "sponsor" many transactions at once, trustlessly. + +Meta-transactions often use relay contracts to maintain nonces and allow users +to pay for gas using alternative assets. They have historically been designed +to catch reversions in their inner transactions by only passing a portion of +the available gas to the subcall. This allows them to be certain the outer call +will have enough gas to complete any required account, like processing a gas +payment. This type of subcall has been considered bad practice for a long time, +but in the case of where you don't trust the subcalls, it is the only available +solution. + +Transaction packages are an alternative that allow multiple transactions to be +bundled into one package and executed atomically, similarly to how relay +contracts operate. Transactions are able to pass their result to subsequent +transactions. This allows for conditional workflows based on the outcome of +previous transactions. Although this functionality is already possible as +described above, workflows using transaction packages are more robust, because +they are protected from future changes to the gas schedule. + +An important byproduct of this EIP is that it also facilitates bundling +transactions for single users. + +## Specification +Introduce a new [EIP-2718](./eip-2718.md) transaction type where `id = 2`. + +#### Structure +``` +struct TransactionPackage { + chain_id: u256, + children: [ChildPackage], + nonce: u64, + gas_price: u256, + v: u256, + r: u256, + s: u256 +} +``` + +##### Hash +`keccak256(rlp([2, chain_id, children, nonce, gas_price, v, r, s])` + +##### Signature Hash +`keccak256(rlp([2, chain_id, children, nonce, gas_price])` + +##### Receipt +Each `ChildTransaction` transaction will generate a `ChildReceipt` after execution. Each +of these receipts will be aggregated into a `Receipt`. + +``` +type Receipt = [ChildReceipt] +``` + +``` +struct ChildReceipt { + status: u256, + cumulative_gas_used: u256, + logs_bloom: [u8; 256], + logs: [u8] +} +``` + +#### Child Transaction +Let `ChildPackage` be interpreted as follows. + +``` +struct ChildPackage { + type: u8, + nonce: u64, + transactions: [ChildTransaction], + max_gas_price: u256, + v: u256, + r: u256, + s: u256 +} +``` + +``` +struct ChildTransaction { + flags: u8, + to: Address, + value: u256, + data: [u8], + extra: [u8], + gas_limit: u256 +} +``` + +##### Types +The `type` field is used to denote whether the `Child` signer wishes to +delegate the `max_gas_price` and `gas_limit` choice to the `TransactionPackage` +signer. + +| type | signature hash | +|---|---| +| `0x00` | `keccak256(rlp([0, nonce, transactions, max_gas_price])` | +| `0x01` | `keccak256(rlp([1, nonce, transactions_without_gas_limit])` | + +### Validity + +A `TransactionPackage` can be deemed valid or invalid as follows. + +```rust +fn is_valid(config: &Config, state: &State, tx: TransactionPackage) bool { + if ( + config.chain_id() != tx.chain_id || + tx.children.len() == 0 || + state.nonce(tx.from()) + 1 != tx.nonce + ) { + return false; + } + + let cum_limit = tx.children.map(|x| x.gas_limit).sum(); + if state.balance(tx.from()) < cum_limit * tx.gas_price + intrinsic_gas(tx) { + return false; + } + + for child in tx.children { + if ( + child.nonce != state.nonce(child.from()) + 1 || + child.value > state.balance(child.from()) || + child.max_gas_price < tx.gas_price + ) { + return false; + } + + for tx in child.txs { + if ( + tx.flags != 0 || + tx.extra.len() != 0 || + tx.gas_limit < intrinsic_gas(tx) + ) { + return false; + } + } + } + + true +} +``` + +### Results + +Subsequent `ChildTransaction`s will be able to receive the result of the +previous `ChildTransaction` via `RETURNDATACOPY (0x3E)` in first frame of +execution, before making any subcalls. Each element, except the last, will be +`0`-padded left to 32 bytes. + +``` +struct Result { + // Status of the previous transaction + success: bool, + + // Total gas used by the previous transaction + gas_used: u256, + + // Cumulative gas used by previous transactions + cum_gas_used: u256, + + // The size of the return value + return_size: u256, + + // The return value of the previous transaction + return_value: [u8] +} +``` + +### Intrinsic Cost +Let the intrinsic cost of the transaction package be defined as follows: + +``` +fn intrinsic_gas(tx: TransactionPackage) u256 { + let data_gas = tx.children.map(|c| c.txs.map(|t| data_cost(&c.data)).sum()).sum(); + 17000 + 8000 * tx.children.len() + data_gas +} +``` + +### Execution +Transaction packages should be executed as follows: +1. Deduct the cumulative cost from the outer signer's balance. +2. Load the first child package, and execute the first child transaction. +3. Record all state changes, logs, the receipt, and refund any unused gas. +4. If there are no more child transactions, goto `8`. +5. Compute `Result` for the previously executed transaction. +6. Prepare `Result` to be available via return opcodes in the next + transaction's first frame. +7. Execute the next transaction, then goto `3`. +8. Load the next child package, then goto `7`. + +## Rationale + +### Each `Child` has its own signature +For simplicity, the author has chosen to require each child package to specify +its own signature, even if the signer is the same as the package signer. This +choice is made to allow for maximum flexibility, with minimal client changes. +This transaction can still be used by a single user at the cost of only one +additional signature recovery. + +### `ChildPackage` specifies `max_gas_price` instead of `gas_price` +Allowing child packages to specify a range of acceptable gas prices is +strictly more versatile than a static price. It gives relayers more flexibility +in terms of building transaction bundles, and it makes it possible for relayers +to try and achieve the best price for the transaction sender. With a fixed +price, the relayer may require the user to sign multiple different +transactions, with varying prices. This can be avoided by specifying a max +price, and communicating out-of-band how the urgency of the transaction (e.g. +the relayer should package it with the max price immediately vs. slowly +increasing the gas price). +A future transaction type can be specified with only a single +signature, if such an optimization is desired. + +### `ChildPackage` is also typed +The type element serves a modest role in the transaction type, denoting whether +the transaction signer wishes to delegate control of the gas price and gas +limit to the outer signer. This is a useful UX improvement when interacting +with a trusted relayer, as once the user decides to make a transaction the +relayer can ensure it is included on chain by choosing the best gas price and +limit. + +### The `flags` and `extra` fields aren't used +These fields are included to better support future changes to the transaction +type. This would likely be used in conjunction with the `flags` and `type` +fields. A benefit of explicitly defining them is that specialized serialization +of RLP can be avoided, simplifing clients and downstream infrastructure. The +author believe the cost of 2 bytes per transaction is acceptable for smoother +integration of future features. + +## Backwards Compatibility +Contracts which rely on `ORIGIN (0x32) == CALLER (0x33) && RETURNDATASIZE +(0x3D) == 0x00` will now always fail in transaction packages, unless they are +the first executed transaction. It’s unknown if any contracts conduct this +check. + +## Test Cases +TBD + +## Implementation +TBD + +## Security Considerations +### Managing packages efficiently in the mempool +The introduction of a new transaction type brings along new concerns regarding +the mempool. Done naively, it could turn into a DDoS vector for clients. This +EIP has been written to reduce as much validation complexity as possible. + +An existing invariant in the mempool that is desirable for new transactions to +maintain, is that transactions can be validated in constant time. This is also +possible for packaged transactions. There is an inherent 10Mb limit for RLPx +frames, so that would be the upper bound on transactions that could be included +in a package. On the other hand, clients can also just configure their own +bound locally (e.g. packages must be less than 1Mb). Validity can then be +determined by using the function above. + +Once a package has been validated, it must continuously be monitored for nonce +invalidations within its package. One potential way to achieve this efficiently +is to modify the mempool to operate on thin pointers to the underlying +transaction. This will allow packages to ingest as many "single" transactions, +simplifying the facilities for monitoring changes. These "parts" of the package +can maintain a pointer to a structure with pointers to all the parts of the +package. This way, as soon as one part becomes invalid, it can request the +parent to invalidate all outstanding parts of the package. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2746.md b/EIPS/eip-2746.md new file mode 100644 index 0000000..22aa20e --- /dev/null +++ b/EIPS/eip-2746.md @@ -0,0 +1,220 @@ +--- +eip: 2746 +title: Rules Engine Standard +author: Aaron Kendall (@jaerith), Juan Blanco (@juanfranblanco) +discussions-to: https://ethereum-magicians.org/t/eip-2746-rules-engine-interface/4435 +status: Stagnant +type: Standards Track +category: ERC +created: 2020-06-20 +--- + +## Simple Summary +An interface for using a smart contract as a rules engine. A single deployed contract can register a data domain, create sets of rules that perform actions on that domain, and then invoke a set as an atomic transaction. + +## Abstract +This standard proposes an interface that will allow the creation of hierarchal sets of rules (i.e., RuleTrees) that can be invoked to evaluate and manipulate a registered data domain. At the time of this draft, all intentions to insert additional functionality onto the blockchain requires the coding and creation of a newly deployed contract. However, this standard will allow users to deploy a contract just once, one which will then allow them to create (and invoke) pipelines of commands within that contract. + +## Motivation +At the time of this draft, all development for Ethereum requires writing the code that forms smart contracts and then deploying those contracts to Ethereum. In order to create a proper contract, many considerations must be taken into account when designing and implementing the code, especially in terms of efficiency (i.e., gas cost) and security. Even the simplest contracts require a certain amount of vigilance and examination, before and after deployment. These requirements pertain to all cases, even for simple cases of examining a value and/or altering it. + +These technical challenges might form an obstacle for many others who might wish to create software around Ethereum. Less technical companies and users might also want to configure and deploy simple functionality onto the chain, without knowing the relevant languages or details necessary. By having the data domain and the predefined actions (i.e., types of rules) implemented along with this interface, a deployed instance of such a rules engine contract can provide efficient and safe functionality to no-code or little-code clients, allowing more users of various technical proficiency to interact with the Ethereum ecosystem. + +## Specification +For the clarification of terminology, an Attribute is a registered data point within the data domain, representing data that exists either in the rules engine contract or elsewhere. A Rule is an predefined action that occurs upon a single data point (i.e., Attribute) in the predefined data domain. For example, a Rule could check whether the Attribute 'TokenAmt' has a value less than the RHL (i.e., right-hand value) of 10. A RuleSet is a collection of Rules, where their collection invocation creates a boolean result that determines the navigational flow of execution between RuleSets. A RuleTree is a collection of RuleSets that are organized within a hierarchy, where RuleSets can contain other RuleSets. + +```solidity +pragma solidity ^0.6.0; + +/** + @title ERC-2746 Rules Engine Standard + @dev See https://eips.ethereum.org/EIPS/eip-2746 + */ + interface ERCRulesEngine { + + /** + @dev Should emit when a RuleTree is invoked. + The `ruler` is the ID and owner of the RuleTree being invoked. It is also likely msg.sender. + */ + event CallRuleTree( + address indexed ruler + ); + + /** + @dev Should emit when a RuleSet is invoked. + The `ruler` is the ID and owner of the RuleTree in which the RuleSet is stored. It is also likely msg.sender. + The 'ruleSetId' is the ID of the RuleSet being invoked. + */ + event CallRuleSet( + address indexed ruler, + bytes32 indexed tmpRuleSetId + ); + + /** + @dev Should emit when a Rule is invoked. + The `ruler` is the ID and owner of the RuleTree in which the RuleSet is stored. It is also likely msg.sender. + The 'ruleSetId' is the ID of the RuleSet being invoked. + The 'ruleId' is the ID of the Rule being invoked. + The 'ruleType' is the type of the rule being invoked. + */ + event CallRule( + address indexed ruler, + bytes32 indexed ruleSetId, + bytes32 indexed ruleId, + uint ruleType + ); + + /** + @dev Should emit when a RuleSet fails. + The `ruler` is the ID and owner of the RuleTree in which the RuleSet is stored. It is also likely msg.sender. + The 'ruleSetId' is the ID of the RuleSet being invoked. + The 'severeFailure' is the indicator of whether or not the RuleSet is a leaf with a 'severe' error flag. + */ + event RuleSetError ( + address indexed ruler, + bytes32 indexed ruleSetId, + bool severeFailure + ); + + /** + @notice Adds a new Attribute to the data domain. + @dev Caller should be the deployer/owner of the rules engine contract. An Attribute value can be an optional alternative if it's not a string or numeric. + @param _attrName Name/ID of the Attribute + @param _maxLen Maximum length of the Attribute (if it is a string) + @param _maxNumVal Maximum numeric value of the Attribute (if it is numeric) + @param _defaultVal The default value for the Attribute (if one is not found from the source) + @param _isString Indicator of whether or not the Attribute is a string + @param _isNumeric Indicator of whether or not the Attribute is numeric + */ + function addAttribute(bytes32 _attrName, uint _maxLen, uint _maxNumVal, string calldata _defaultVal, bool _isString, bool _isNumeric) external; + + /** + @notice Adds a new RuleTree. + @param _owner Owner/ID of the RuleTree + @param _ruleTreeName Name of the RuleTree + @param _desc Verbose description of the RuleTree's purpose + */ + function addRuleTree(address _owner, bytes32 _ruleTreeName, string calldata _desc) external; + + /** + @notice Adds a new RuleSet onto the hierarchy of a RuleTree. + @dev RuleSets can have child RuleSets, but they will only be called if the parent's Rules execute to create boolean 'true'. + @param _owner Owner/ID of the RuleTree + @param _ruleSetName ID/Name of the RuleSet + @param _desc Verbose description of the RuleSet + @param _parentRSName ID/Name of the parent RuleSet, to which this will be added as a child + @param _severalFailFlag Indicator of whether or not the RuleSet's execution (as failure) will result in a failure of the RuleTree. (This flag only applies to leaves in the RuleTree.) + @param _useAndOp Indicator of whether or not the rules in the RuleSet will execute with 'AND' between them. (Otherwise, it will be 'OR'.) + @param _failQuickFlag Indicator of whether or not the RuleSet's execution (as failure) should immediately stop the RuleTree. + */ + function addRuleSet(address _owner, bytes32 _ruleSetName, string calldata _desc, bytes32 _parentRSName, bool _severalFailFlag, bool _useAndOp, bool _failQuickFlag) external; + + /** + @notice Adds a new Rule into a RuleSet. + @dev Rule types can be implemented as any type of action (greater than, less than, etc.) + @param _owner Owner/ID of the RuleTree + @param _ruleSetName ID/Name of the RuleSet to which the Rule will be added + @param _ruleName ID/Name of the Rule being added + @param _attrName ID/Name of the Attribute upon which the Rule is invoked + @param _ruleType ID of the type of Rule + @param _rightHandValue The registered value to be used by the Rule when performing its action upon the Attribute + @param _notFlag Indicator of whether or not the NOT operator should be performed on this Rule. + */ + function addRule(address _owner, bytes32 _ruleSetName, bytes32 _ruleName, bytes32 _attrName, uint _ruleType, string calldata _rightHandValue, bool _notFlag) external; + + /** + @notice Executes a RuleTree. + @param _owner Owner/ID of the RuleTree + */ + function executeRuleTree(address _owner) external returns (bool); + + /** + @notice Retrieves the properties of a Rule. + @param _owner Owner/ID of the RuleTree + @param _ruleSetName ID/Name of the RuleSet where the Rule resides + @param _ruleIdx Index of the rule in the RuleSet's listing + @return bytes32 ID/Name of Rule + @return uint Type of Rule + @return bytes32 Target Attribute of Rule + @return string Value mentioned in Rule + @return bool Flag for NOT operator in Rule + @return bytes32[] Values that should be provided in delegated call (if Rule is custom operator) + */ + function getRuleProps(address _owner, bytes32 _ruleSetName, uint _ruleIdx) external returns (bytes32, uint, bytes32, string memory, bool, bytes32[] memory); + + /** + @notice Retrieves the properties of a RuleSet + @param _owner Owner/ID of the RuleTree + @param _ruleSetName ID/Name of the RuleSet + @return string Verbose description of the RuleSet + @return bool Flag that indicates whether this RuleSet's failure (if a leaf) will cause the RuleTree to fail + @return bool Flag that indicates whether this RuleSet uses the AND operator when executing rules collectively + @return uint Indicates the number of rules hosted by this RuleSet + @return bytes32[] The list of RuleSets that are children of this RuleSet + */ + function getRuleSetProps(address _owner, bytes32 _ruleSetName) external returns (string memory, bool, bool, uint, uint, bytes32[] memory); + + /** + @notice Retrieves the properties of a RuleSet + @param _owner Owner/ID of the RuleTree + @return bytes32 Name of the RuleTree + @return string Verbose description of the RuleTree + @return bytes32 ID/Name of the RuleSet that serves as the root node for the RuleTree + */ + function getRuleTreeProps(address _owner) external returns (bytes32, string memory, bytes32); + + /** + @notice Removes a RuleTree. + @param _owner Owner/ID of the RuleTree + */ + function removeRuleTree(address _owner) external returns (bool); +} +``` + +### Considerations + +An argument could be made for interface functions that allow a RuleTree's owner to include others users as executors of the RuleTree. + +Another argument could be made for interface functions that allow an administrator to configure the origin point of an Attribute, such as whether the Attribute's value comes from a data structure (internal to the rules engine contract) or from calling a contract method (like an implementation of the [Diamond Standard](https://github.com/ethereum/EIPs/issues/2535)). + +Yet another argument could be made for interface functions that allow an administrator to extend the functionality catalog provided by the rules engine, by allowing other contracts' methods to be added as a rule operation. + +Also, an argument could be made for functions that calculate and report the range of potential cost for invoking a RuleTree. Unlike the normal execution of a contract method, the Ethereum transaction costs of invoking a RuleTree are more dynamic, depending on its depth/breadth and the navigational flow during invocation. Since the general cost of a RuleTree is unknown until the time of invocation, these functions could report the minimal amount of gas for a transaction (i.e., none of the Rules in a RuleTree are invoked) and the maximum amount for a transaction (i.e., all Rules in a RuleTree are invoked). + +### Example + +A company wishes to deploy a contract with data points and functionality that are predefined and/or under the control of an administrator, and it aims to build a no-code client that will allow less-technical users to define actions within the rules engine contract. In this example, the company wants one of its users to write the rules in a proprietary markup language, in order for the calculation of a VAT to be determined. For the sake of transparency, [these rules](https://ipfs.infura.io/ipfs/QmPrZ9959c7SzzqdLkVgX28xM7ZrqLeT3ydvRAHCaL1Hsn) are published onto IPFS, so that they are accessible to auditors and possibly government officials. The no-code client will then know how to parse the rules from the markup and communicate with the rules engine contract, establishing the RuleTree to be invoked later by the company's user(s) or off-chain programs. + +In order to calculate the value of the VAT, these provided rules invoke simple mathematical operations that can perform the calculation. However, the implementation of the rules engine contract could possess other functionality called by rules, ones that could execute more complicated logic or call the methods of other contracts. + +## Rationale + +### Attributes + +The data points are abstracted in order to let the implementation provide the mechanism for retrieving/populating the data. Data can be held by an internal data structure, another contract's method, or any number of other options. + +### Events + +The events specified will help the caller of the RuleTree after execution, so that they may ascertain the navigational flow of RuleSet execution within the RuleTree and so that they may understand which RuleSets failed. + +### Right-Hand Value + +In the function addRule(), the data type for the right-hand value is 'string' since the rule's action depends on its type, meaning that the value must be provided in a generic form. In the case of a Rule that performs numerical operations, the provided value could be transformed into a number when stored in the Rule. + +## Implementation +- [Wonka](https://github.com/Nethereum/Wonka/tree/master/Solidity/WonkaEngine) +- [Wonka Rules Editor](https://github.com/jaerith/WonkaRulesBlazorEditor) + +The Wonka implementation supports this proposed interface and also implements all of the additional considerations mentioned above. + +## Security Considerations + +The deployer of the contract should be the owner and administrator, allowing for the addition of Attributes and RuleTrees. Since a RuleTree is owned by a particular EOA (or contract address), the only accounts that should be able to execute the RuleTree should be its owner or the contract's owner/administrator. If Attributes are defined to exist as data within other contracts, the implementation must take into account the possibility that RuleTree owners must have the security to access the data in those contracts. + +## References + +**Standards** +- [EIP-2535 Diamond Standard](./eip-2535.md) + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2767.md b/EIPS/eip-2767.md new file mode 100644 index 0000000..87ac14f --- /dev/null +++ b/EIPS/eip-2767.md @@ -0,0 +1,123 @@ +--- +eip: 2767 +title: Contract Ownership Governance +author: Soham Zemse (@zemse), Nick Mudge (@mudgen) +discussions-to: https://github.com/ethereum/EIPs/issues/2766 +status: Stagnant +type: Standards Track +category: ERC +created: 2020-07-04 +requires: 20, 165, 173 +--- + +## Simple Summary + +A standard for Governance contracts that holds the administrative ownership of other smart contracts with voting power distributed as `ERC-20` tokens. + +## Abstract + +The following standard defines the implementation of a standard API for a Governance smart contract based on `ERC-20`. Existing `ERC-173` compatible contracts can upgrade from private key wallet ownership to a Governance smart contract. Adhering to a standard API enables general tools to populate governance information of various projects, thus increasing transparency. + +## Motivation + +Traditionally, many contracts that require that they be owned or controlled in some way use `ERC-173` which standardized the use of ownership in the smart contracts. For example to withdraw funds or perform administrative actions. + +```solidity +contract dApp { + function doSomethingAdministrative() external onlyOwner { + // admin logic that can be performed by a single wallet + } +} +``` + +Often, such administrative rights for a contract are written for maintenance purpose but users need to trust the owner. Rescue operations by an owner have raised questions on decentralised nature of the projects. Also, there is a possibility of compromise of an owner's private key. + +At present, many governance implementations by ambitious projects need users to visit a specific UI to see governance information about their project. Some examples of live implementations having different API that does the same thing are [Compound Governance](https://github.com/compound-finance/compound-protocol/blob/master/contracts/Governance/GovernorAlpha.sol#L27), [Uniswap Governance](https://github.com/Uniswap/governance/blob/master/contracts/GovernorAlpha.sol#L27) and [Sushiswap Governance](https://github.com/sushiswap/sushiswap/blob/master/contracts/GovernorAlpha.sol#L45). It's just like if the ERC-20 standard wasn't finalized, then token projects would have their own block explorer. Adhering to a standard API would enable general tools (like Etherscan) to populate governance information, thus increasing transparency to users. Using widely popular `ERC-20` token as a governance token, existing tools built to work with `ERC-20` can already display voters. This can result in a wide adoption for contract governance over private key based ownership. + +## Specification + +A Governance contract that is compliant with `ERC-2767` shall implement the following interfaces: + +```solidity +/// @title ERC-2767 Governance +/// @dev ERC-165 InterfaceID: 0xd8b04e0e +interface ERC2767 is ERC165 { + /// @notice Gets number votes required for achieving consensus + /// @dev Should cost less than 30000 gas + /// @return Required number of votes for achieving consensus + function quorumVotes() external view returns (uint256); + + /// @notice The address of the Governance ERC20 token + function token() external view returns (address); +} +``` + +### `ERC-20` Governance Token + +An `ERC-2767` Governance Contract should reference an address through `token()` that implements `ERC-20` interface. `token()` is allowed to return self address (`address(this)`), if `ERC-20` functionalities are implemented in the same contract (one can consider checking out Diamond Standard [`ERC-2535`](https://eips.ethereum.org/EIPS/eip-2535) to optimise contract size). + +Implementations are allowed to have varying `ERC-20`'s `totalSupply()` (through any standard of minting or burning). But having a fixed `quorumVotes()` return value in this case would cause required votes consensus in `%` with respect to `totalSupply()` to change. To automatically account for this, any custom logic under `quorumVotes()` is allowed to return for e.g. `51%` of `totalSupply()`. + +### `ERC-165` Interface Identification + +An `ERC-2767` Governance Contract should also implement `ERC-165`. This helps general tools to identify whether a contract is a `ERC-2767` Governance contract. + +```solidity +interface ERC165 { + /// @notice Query if a contract implements an interface + /// @param interfaceID The interface identifier, as specified in ERC-165 + /// @dev Interface identification is specified in ERC-165. This function + /// uses less than 30,000 gas. + /// @return `true` if the contract implements `interfaceID` and + /// `interfaceID` is not 0xffffffff, `false` otherwise + function supportsInterface(bytes4 interfaceID) external view returns (bool); +} +``` + +## Rationale + +The goals of this EIP have been the following: + +- Standardize API of Governance contracts to make it easy for analysis tools to be built. +- Encourage use of `ERC-20` based weighted governance over existing multi-sig (_generally limited to 50 max owners_) for big projects. +- Encourage existing `ERC-173` ownership smart contracts / projects to move to Governance based ownership by removing the effort needed to host custom UI for their project. +- Encourage availability of publicly audited governance contracts, just like `ERC-20` which anyone can use. +- Make it possible to utilize existing `ERC-20` tools for owners of governance token analysis. +- Make future protocols possible that need to interact with governances of multiple projects. +- Keep this EIP minimal and allow another EIPs to standardize any specific functionalities. + +## Backwards Compatibility + +Smart contracts that are `ERC-173` compliant can transfer their ownership to a Governance contract. This enables such contracts to become compatible with `ERC-2767` Governance. + +However, there are some existing projects with governance implementations and most of them have custom APIs ([Compound Governance](https://github.com/compound-finance/compound-protocol/blob/master/contracts/Governance/GovernorAlpha.sol#L27), [Uniswap Governance](https://github.com/Uniswap/governance/blob/master/contracts/GovernorAlpha.sol#L27) and [Sushiswap Governance](https://github.com/sushiswap/sushiswap/blob/master/contracts/GovernorAlpha.sol#L45)), since a standard did not exist. Not having an `ERC-2767` compatible governance contract means only that general tools might not be able to populate their governance information without including some special code for the project. + +For existing governance contracts to get compatible with `ERC-2767`: + +1. Projects can deploy a new governance contract and transfer ownership to it to be `ERC-2767` compatible. This is suitable for those who use Multi-sig wallets for Governance. +2. It is understood that redeploying governance contracts would be a troublesome task, and contracts who already have functionality similar to `ERC-20` based (weighted votes) have a bit advanced way to avoid it. Basically, they can create a forwarder contract implements `ERC-2767` and forwards all calls to the actual non-standard methods. Projects can list the forwarder contract to display the information project's governance info without requiring any custom code in analysys tool, but this might have certain limitations depending on the project's existing governance implementation. Specification of forwarder contract is out of scope for this EIP and it may be addressed in another EIP if required. + + + +## Implementation + +The reference implementations are available in this [repository](https://github.com/zemse/contract-ownership-governance). Publicly audited implementations will be included in future. + +## Security Considerations + +Implementers are free to choose between On-chain and Off-chain consensus. Exact specification is out of scope for this standard (open for other EIPs to standardize). However, this section mentions points that implementers can consider. + +#### On-chain + +In such implementations, community can create transaction proposals and vote on it by sending on-chain transactions. + +- OpenZeppelin Snapshots can be used to prevent double voting. + +#### Off-chain + +- The signatures in off-chain governance implementation can follow recommendations of `ERC-191` or `ERC-712`. +- To prevent replaying signatures, it'd be best if executer is required to sort the signatures based on increasing addresses. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2770.md b/EIPS/eip-2770.md new file mode 100644 index 0000000..db0b5d7 --- /dev/null +++ b/EIPS/eip-2770.md @@ -0,0 +1,207 @@ +--- +eip: 2770 +title: Meta-Transactions Forwarder Contract +author: Alex Forshtat (@forshtat), Dror Tirosh (@drortirosh) +discussions-to: https://ethereum-magicians.org/t/erc-2770-meta-transactions-forwarder-contract/5391 +status: Stagnant +type: Standards Track +category: ERC +created: 2020-07-01 +requires: 712, 2771 +--- + +## Simple Summary +Standardized contract interface for extensible meta-transaction forwarding. + +## Abstract + +This proposal defines an external API of an extensible Forwarder whose responsibility is to validate transaction +signatures on-chain and expose the signer to the destination contract, that is expected to accommodate all use-cases. +The ERC-712 structure of the forwarding request can be extended allowing wallets to display readable data even +for types not known during the Forwarder contract deployment. + +## Motivation + +There is a growing interest in making it possible for Ethereum contracts to +accept calls from externally owned accounts that do not have ETH to pay for +gas. + +This can be accomplished with meta-transactions, which are transactions that have been signed as plain data by one +externally owned account first and then wrapped into an Ethereum transaction by a different account. + +`msg.sender` is a transaction parameter that can be inspected by a contract to +determine who signed the transaction. The integrity of this parameter is +guaranteed by the Ethereum EVM, but for a meta-transaction verifying +`msg.sender` is insufficient, and signer address must be recovered as well. + +The Forwarder contract described here allows multiple Gas Relays and Relay Recipient contracts to rely +on a single instance of the signature verifying code, improving reliability and security +of any participating meta-transaction framework, as well as avoiding on-chain code duplication. + +## Specification +The Forwarder contract operates by accepting a signed typed data together with it's ERC-712 signature, +performing signature verification of incoming data, appending the signer address to the data field and +performing a call to the target. + +### Forwarder data type registration +Request struct MUST contain the following fields in this exact order: +``` +struct ForwardRequest { + address from; + address to; + uint256 value; + uint256 gas; + uint256 nonce; + bytes data; + uint256 validUntil; +} +``` +`from` - an externally-owned account making the request \ +`to` - a destination address, normally a smart-contract\ +`value` - an amount of Ether to transfer to the destination\ +`gas` - an amount of gas limit to set for the execution\ +`nonce` - an on-chain tracked nonce of a transaction\ +`data` - the data to be sent to the destination\ +`validUntil` - the highest block number the request can be forwarded in, or 0 if request validity is not time-limited + +The request struct MAY include any other fields, including nested structs, if necessary. +In order for the Forwarder to be able to enforce the names of the fields of this struct, only registered types are allowed. + +Registration MUST be performed in advance by a call to the following method: +``` +function registerRequestType(string typeName, string typeSuffix) +``` +`typeName` - a name of a type being registered\ +`typeSuffix` - an ERC-712 compatible description of a type + +For example, after calling +``` +registerRequestType("ExtendedRequest", "uint256 x,bytes z,ExtraData extraData)ExtraData(uint256 a,uint256 b,uint256 c)") +``` +the following ERC-712 type will be registered with forwarder: +``` +/* primary type */ +struct ExtendedRequest { + address from; + address to; + uint256 value; + uint256 gas; + uint256 nonce; + bytes data; + uint256 validUntil; + uint256 x; + bytes z; + ExtraData extraData; +} + +/* subtype */ +struct ExtraData { + uint256 a; + uint256 b; + uint256 c; +} +``` + +### Signature verification + +The following method performs an ERC-712 signature check on a request: +``` +function verify( + ForwardRequest forwardRequest, + bytes32 domainSeparator, + bytes32 requestTypeHash, + bytes suffixData, + bytes signature +) view; +``` +`forwardRequest` - an instance of the `ForwardRequest` struct +`domainSeparator` - caller-provided domain separator to prevent signature reuse across dapps (refer to ERC-712) +`requestTypeHash` - hash of the registered relay request type +`suffixData` - RLP-encoding of the remainder of the request struct +`signature` - an ERC-712 signature on the concatenation of `forwardRequest` and `suffixData` + +### Command execution + +In order for the Forwarder to perform an operation, the following method is to be called: +``` +function execute( + ForwardRequest forwardRequest, + bytes32 domainSeparator, + bytes32 requestTypeHash, + bytes suffixData, + bytes signature +) +public +payable +returns ( + bool success, + bytes memory ret +) +``` + +Performs the ‘verify’ internally and if it succeeds performs the following call: +``` +bytes memory data = abi.encodePacked(forwardRequest.data, forwardRequest.from); +... +(success, ret) = forwardRequest.to.call{gas: forwardRequest.gas, value: forwardRequest.value}(data); +``` +Regardless of whether the inner call succeeds or reverts, the nonce is incremented, invalidating the signature and preventing a replay of the request. + +Note that `gas` parameter behaves according to EVM rules, specifically EIP-150. The forwarder validates internally that +there is enough gas for the inner call. In case the `forwardRequest` specifies non-zero value, extra `40000 gas` is +reserved in case inner call reverts or there is a remaining Ether so there is a need to transfer value from the `Forwarder`: +```solidity +uint gasForTransfer = 0; +if ( req.value != 0 ) { + gasForTransfer = 40000; // buffer in case we need to move Ether after the transaction. +} +... +require(gasleft()*63/64 >= req.gas + gasForTransfer, "FWD: insufficient gas"); +``` +In case there is not enough `value` in the Forwarder the execution of the inner call fails.\ +Be aware that if the inner call ends up transferring Ether to the `Forwarder` in a call that did not originally have `value`, this +Ether will remain inside `Forwarder` after the transaction is complete. + +### ERC-712 and 'suffixData' parameter +`suffixData` field must provide a valid 'tail' of an ERC-712 typed data. +For instance, in order to sign on the `ExtendedRequest` struct, the data will be a concatenation of the following chunks: +* `forwardRequest` fields will be RLP-encoded as-is, and variable-length `data` field will be hashed +* `uint256 x` will be appended entirely as-is +* `bytes z` will be hashed first +* `ExtraData extraData` will be hashed as a typed data + +So a valid `suffixData` is calculated as following: +``` +function calculateSuffixData(ExtendedRequest request) internal pure returns (bytes) { + return abi.encode(request.x, keccak256(request.z), hashExtraData(request.extraData)); +} + +function hashExtraData(ExtraData extraData) internal pure returns (bytes32) { + return keccak256(abi.encode( + keccak256("ExtraData(uint256 a,uint256 b,uint256 c)"), + extraData.a, + extraData.b, + extraData.c + )); +} +``` + +### Accepting Forwarded calls +In order to support calls performed via the Forwarder, the Recipient contract must read the signer address from the +last 20 bytes of `msg.data`, as described in ERC-2771. + +## Rationale +Further relying on `msg.sender` to authenticate end users by their externally-owned accounts is taking the Ethereum dapp ecosystem to a dead end. + +A need for users to own Ether before they can interact with any contract has made a huge portion of use-cases for smart contracts non-viable, +which in turn limits the mass adoption and enforces this vicious cycle. + +`validUntil` field uses a block number instead of timestamp in order to allow for better precision and integration +with other common block-based timers. + +## Security Considerations +All contracts introducing support for the Forwarded requests thereby authorize this contract to perform any operation under any account. +It is critical that this contract has no vulnerabilities or centralization issues. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2771.md b/EIPS/eip-2771.md new file mode 100644 index 0000000..8fadf23 --- /dev/null +++ b/EIPS/eip-2771.md @@ -0,0 +1,140 @@ +--- +eip: 2771 +title: Secure Protocol for Native Meta Transactions +description: A contract interface for receiving meta transactions through a trusted forwarder +author: Ronan Sandford (@wighawag), Liraz Siri (@lirazsiri), Dror Tirosh (@drortirosh), Yoav Weiss (@yoavw), Alex Forshtat (@forshtat), Hadrien Croubois (@Amxx), Sachin Tomar (@tomarsachin2271), Patrick McCorry (@stonecoldpat), Nicolas Venturo (@nventuro), Fabian Vogelsteller (@frozeman), Gavin John (@Pandapip1) +discussions-to: https://ethereum-magicians.org/t/erc-2771-secure-protocol-for-native-meta-transactions/4488 +status: Final +type: Standards Track +category: ERC +created: 2020-07-01 +--- + +## Abstract + +This EIP defines a contract-level protocol for `Recipient` contracts to accept meta-transactions through trusted `Forwarder` contracts. No protocol changes are made. `Recipient` contracts are sent the effective `msg.sender` (referred to as `_msgSender()`) and `msg.data` (referred to as `_msgData()`) by appending additional calldata. + +## Motivation + +There is a growing interest in making it possible for Ethereum contracts to accept calls from externally owned accounts that do not have ETH to pay for gas. Solutions that allow for third parties to pay for gas costs are called meta transactions. For the purposes of this EIP, meta transactions are transactions that have been authorized by a **Transaction Signer** and relayed by an untrusted third party that pays for the gas (the **Gas Relay**). + +## Specification + +The keywords "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. + +### Definitions + +**Transaction Signer**: Signs & sends transactions to a Gas Relay + +**Gas Relay**: Receives signed requests off-chain from Transaction Signers and pays gas to turn it into a valid transaction that goes through a Trusted Forwarder + +**Trusted Forwarder**: A contract trusted by the `Recipient` to correctly verify signatures and nonces before forwarding the request from Transaction Signers + +**Recipient**: A contract that accepts meta-transactions through a Trusted Forwarder + +### Example Flow + +![Example flow](../assets/eip-2771/example-flow.png) + +### Extracting The Transaction Signer address + +The **Trusted Forwarder** is responsible for calling the **Recipient** contract and MUST append the address of the **Transaction Signer** (20 bytes of data) to the end of the call data. + +For example : + +```solidity +(bool success, bytes memory returnData) = to.call.value(value)(abi.encodePacked(data, from)); +``` + +The **Recipient** contract can then extract the **Transaction Signer** address by performing 3 operations: + +1. Check that the **Forwarder** is trusted. How this is implemented is out of the scope of this proposal. +2. Extract the **Transaction Signer** address from the last 20 bytes of the call data and use that as the original `sender` of the transaction (instead of `msg.sender`) +3. If the `msg.sender` is not a trusted forwarder (or if the `msg.data` is shorter than 20 bytes), then return the original `msg.sender` as it is. + +The **Recipient** MUST check that it trusts the Forwarder to prevent it from +extracting address data appended from an untrusted contract. This could result +in a forged address. + +### Protocol Support Discovery Mechanism + +Unless a **Recipient** contract is being used by a particular frontend that knows that this contract has support for native meta transactions, it would not be possible to offer the user the choice of using meta-transaction to interact with the contract. We thus need a mechanism by which the **Recipient** can let the world know that it supports meta transactions. + +This is especially important for meta transactions to be supported at the Web3 wallet level. Such wallets may not necessarily know anything about the **Recipient** contract users may wish to interact with. + +As a **Recipient** could trust forwarders with different interfaces and capabilities (e.g., transaction batching, different message signing formats), we need to allow wallets to discover which Forwarder is trusted. + +To provide this discovery mechanism a **Recipient** contract MUST implement this function: + +```solidity +function isTrustedForwarder(address forwarder) external view returns(bool); +``` + +`isTrustedForwarder` MUST return `true` if the forwarder is trusted by the Recipient, otherwise it MUST return `false`. `isTrustedForwarder` MUST NOT revert. + +Internally, the **Recipient** MUST then accept a request from forwarder. + +`isTrustedForwarder` function MAY be called on-chain, and as such gas restrictions MUST be put in place. It SHOULD NOT consume more than 50,000 gas + +## Rationale + +* Make it easy for contract developers to add support for meta + transactions by standardizing the simplest viable contract interface. +* Without support for meta transactions in the recipient contract, an externally owned + account can not use meta transactions to interact with the recipient contract. +* Without a standard contract interface, there is no standard way for a client + to discover whether a recipient supports meta transactions. +* Without a standard contract interface, there is no standard way to send a + meta transaction to a recipient. +* Without the ability to leverage a trusted forwarder every recipient contract + has to internally implement the logic required to accept meta transactions securely. +* Without a discovery protocol, there is no mechanism for a client to discover + whether a recipient supports a specific forwarder. +* Making the contract interface agnostic to the internal implementation + details of the trusted forwarder, makes it possible for a recipient contract + to support multiple forwarders with no change to code. +* `msg.sender` is a transaction parameter that can be inspected by a contract to determine who signed the transaction. The integrity of this parameter is guaranteed by the Ethereum EVM, but for a meta transaction securing `msg.sender` is insufficient. + * The problem is that for a contract that is not natively aware of meta transactions, the `msg.sender` of the transaction will make it appear to be coming from the **Gas Relay** and not the **Transaction Signer**. A secure protocol for a contract to accept meta transactions needs to prevent the **Gas Relay** from forging, modifying or duplicating requests by the **Transaction Signer**. + +## Reference Implementation + +### Recipient Example + +```solidity +contract RecipientExample { + + function purchaseItem(uint256 itemId) external { + address sender = _msgSender(); + // ... perform the purchase for sender + } + + address immutable _trustedForwarder; + constructor(address trustedForwarder) internal { + _trustedForwarder = trustedForwarder; + } + + function isTrustedForwarder(address forwarder) public returns(bool) { + return forwarder == _trustedForwarder; + } + + function _msgSender() internal view returns (address payable signer) { + signer = msg.sender; + if (msg.data.length>=20 && isTrustedForwarder(signer)) { + assembly { + signer := shr(96,calldataload(sub(calldatasize(),20))) + } + } + } + +} +``` + +## Security Considerations + +A malicious forwarder may forge the value of `_msgSender()` and effectively send transactions from any address. Therefore, `Recipient` contracts must be very careful in trusting forwarders. If a forwarder is upgradeable, then one must also trust that the contract won't perform a malicious upgrade. + +In addition, modifying which forwarders are trusted must be restricted, since an attacker could "trust" their own address to forward transactions, and therefore be able to forge transactions. It is recommended to have the list of trusted forwarders be immutable, and if this is not feasible, then only trusted contract owners should be able to modify it. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2780.md b/EIPS/eip-2780.md new file mode 100644 index 0000000..3c75c91 --- /dev/null +++ b/EIPS/eip-2780.md @@ -0,0 +1,116 @@ +--- +eip: 2780 +title: Reduce intrinsic transaction gas +author: Matt Garnett (@lightclient), Uri Klarman (@uriklarman) +discussions-to: https://ethereum-magicians.org/t/eip-2780-reduce-intrinsic-cost-of-transactions/4413 +status: Withdrawn +type: Standards Track +category: Core +created: 2020-07-11 +--- + +## Abstract +Reduce the intrinsic cost of a transaction from `21,000` to `7,000` gas. + +## Motivation +The current `21,000` gas intrinsic cost of a transaction makes sending ETH very costly, often times prohibitively costly for small amounts (tens of USD). +While broad changes to the gas price and first price auction are being considerted in other EIPs (like EIP-1559), +substantially reducing the cost of sending ETH and enabling higher volumes of such transactions would be a net positive if done in a safe manner, +and without imposing negative externalities, as outlined below. + + +## Specification +After `block.number >= FORK_BLOCK`, enforce an intrinsic gas cost of `7,000`. + +## Rationale + +The proliferation of calls to DeFi smart-contracts had substantially increased the cost of making transactions, +since such calls are usually both time sensitive and involve large sums of money. +While the utilization of the chain's capacity by the most valuable transactions is by design, +the high cost of sending transactionsz is limiting Ethereum's the use-cases, +create a terribel user experience, +decreases the actual value created (and captured) by ETH, +and pushes users to seek alternatives in other, less-congested chains. + + +Note on Layer-2: It is true that the high cost might push users to try using L2 solutions, +however forcing users to utilize L2 by allowing the UX of using L1 to deteriorate is a losing strategy (see BTC / Lightning), +and the safety of many L2 solutions hinges on the ability to use L1 if needed. +If L2 are to gain significant traction they must achieve it by providing superior properties (e.g., finality, cost, UX), +not by imposing artificial restrictions to encourage it. + + +Reducing the intrinsic cost of a transaction from `21,000` to `7,000` gas will make sending transactions cheaper, +is easily achievable, and does not incur technical debt. +However, such a change should only be made after it is determined that it does not impose non-negligble externalities, specifically: + +* Increases uncle-rate. + +* Increases the pace at which the state-size of Ethereum grows. + +* Enhance gas-manipulation technices (gas-token). + + +## Backwards Compatibility +This EIP is backward compatible as well as compatible with other approaches pursued in other EIPs. + +## Test Cases +While the benefits of reducing transactions' intrinsic cost are appearant, +such a change should be applied if it impose no negative externalities, +or if such effects are negligible. + + +#### Increased Uncle Rate + +Historically, periods of high transaction counts has correlated with +higher-than-average uncle blocks being mined. It must be determined that the +new intrinsic cost `7,000` will not adversely affect uncle rate. + +A testnet applying this change should be deployed to test the effect of this change on uncle rate. + +details TBD. + + +## Implementation +TBD + +## Security Considerations + + +#### Increased State Size Growth + +The growth of Ethereum's state size continues to raise concerns among members of the community. +However, an analysis of Ethereum accounts shows that their effect on stat size is negligible. + +Looking at the first half of 2020, the number of accounts on the Ethereum chain had grown from 84,127,955 to 103,485,373 - an increase of 19,357,418. Since the *creation* of each new account adds 20 bytes to the chain state, these new accounts had added ~369 MB to the state. +At the same time, the chain had grown from ~117 GB to ~147 GB - an increase of 30 GB. +The creation of new accounts had therefore accounted for only a very small percentage (1.2%) of the chain’s growth. + +Even under the very aggressive assumption that reducing the intrinsic cost of transactions from `21,000` to `7,000` gas would translate to x3 more new accounts being created, if this change was implemented on 1/1/2020, the state size would have only been 0.49% larger than it is today (see below) + +While the sate-size remains an open issue which needs solving - reducing the intrinsic cost of transactions would hardly affect the rate at which the state-size grows, and would significantly improve the chain’s usability. + + +#### Enhancing Gas-Manipulation (gas-token) + +Gas Token (https://gastoken.io/) is an Ethereum smart-contracts which leverages the storage refund mechanism by storing data (V.1) or creating accounts (V.2) using a low gas price, and then free (V.1) or self-destruct (V.2) them in a later transaction which utilizes a higher gas price. This mechanism is only economical when the high gas price redeeming the tokens is more than twice as high as the low gas price used to mint them. +Gas Tokens do not actually increase the state-size lon-term, as they release all the data they store in order to benefit from their gas boost. +However, they do manipulate the gas price auction. + +There had been concerns that reducing the intrinsic cost of transactions from `21,000` to `7,000` would boost the savings achiieved using gas tokens, however these concerns are unfounded. +Due to some overhead of using the smart contract, minting and freeing a single gas-token is uneconomical, but the effect of the overhead diminishes the more tokens are minted and freed. +This is also the reason why their efficiency is hardly affected by the intrinsic cost of transactions - the gas token is designed to spread the transaction cost among many tokens. + +The creators of gas tokens outline the maximal potential savings when minting very large number of tokens (up to x2.97 for V.1, and up to 3.49 for V.2). These numbers are *unaffected* by the proposed change. In a more realistic scenario where 100 gas tokens are minted, the proposed change increases the saving multiplier by a minuscule amount, generally smaller than the increase achieved by minting 200 tokens instead of 100. +The table below captures the effect of this proposal on the savings multiplier in a + +| Version | free_gas_price / mint_gas_price | old savings multiplier | new savings multiplier | saving multiplier of 200 tokens | +|---|---|---|---|---| +| V.1 | 10 | 2.075 | 2.077 | 2.1 | +| V.1 | 100 | 2.780 | 2.781 | 2.819 | +| V.2 | 10 | 2.243 | 2.275 | 2.261 | +| V.2 | 100 | 3.251 | 3.315 | 3.316 | + + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2786.md b/EIPS/eip-2786.md new file mode 100644 index 0000000..2c8d944 --- /dev/null +++ b/EIPS/eip-2786.md @@ -0,0 +1,70 @@ +--- +eip: 2786 +title: Ethereum Provider Connect/Disconnect Events +author: Micah Zoltu (@MicahZoltu), Erik Marks (@rekmarks) +discussions-to: https://github.com/ethereum/EIPs/issues/2787 +status: Withdrawn +type: Standards Track +category: Interface +created: 2020-07-15 +requires: 2700 +--- + +## Simple Summary + +When an Ethereum Provider becomes connected or disconnected, it will emit a `connect`/`disconnect` event. + +## Abstract + +The Provider is said to be “connected” when it can service RPC requests to at least one chain. +The Provider is said to be “disconnected” when it cannot service RPC requests to any chain at all. +When the Provider switches from a "connected" state to a "disconnected" state, it will emit a `connect` event. +When the Provider switches from a "disconnected" state to a "connected" state, it will emit a `disconnect` event. + +## Motivation + +When an application is hooked up to an Ethereum provider, there is value in having the application be alerted of connect/disconnect events that may occur so the application can appropriately inform the user of the situation. +It is left up to the application to decide whether to listen in on these events, and how to handle them. + +## Specification + +### Definitions + +#### Connected + +The Provider is considered `connected` when it is able to service RPC requests to at least one chain. + +#### Disconnected + +The Provider is considered `disconnected` when it is unable to service RPC requests to any chain. + +### Events + +#### `connect` + +The Provider **MUST** emit a `connect` event to all attached [EIP-2700](./eip-2700.md) listeners if it transitions from a `disconnected` state to a `connected` state. +All attached listeners **MUST** be called with the parameter `{ chainId }`. +`chainId` **MUST** specify the integer ID of the connected chain encoded as a hexadecimal string. +If the Provider supports the `eth_chainId` JSON-RPC method or a derivation of it, then the `chainId` **MUST** match the return value of `eth_chainId`. +The Provider **MAY** call the attached listeners in any order. + +## Rationale + +This EIP is mostly a retrospective EIP meaning it codifies an already existing specification so there isn’t a lot of room for improving things such as by having a connect/disconnect event per chain. + +## Security Considerations + +The relationship between Ethereum Provider and client is a trusted one, where it is assumed that the user implicitly trusts the Ethereum Provider which is how it managed to get injected into the client, or the client expressly pulled in a connection to it. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). + +## Appendix I: Examples + +```javascript +// connect +provider.on('connect', ({ chainId }) => { + console.log(`Provider connected to: ${chainId}`); +}); +``` diff --git a/EIPS/eip-2803.md b/EIPS/eip-2803.md new file mode 100644 index 0000000..4804c8d --- /dev/null +++ b/EIPS/eip-2803.md @@ -0,0 +1,50 @@ +--- +eip: 2803 +title: Rich Transactions +description: Support 'rich transactions' by allowing transactions from externally owned accounts to execute bytecode directly. +author: Micah Zoltu (@MicahZoltu) +discussions-to: https://ethereum-magicians.org/t/rich-transactions-via-evm-bytecode-execution-from-externally-owned-accounts/4025 +status: Stagnant +type: Standards Track +category: Core +created: 2020-07-18 +--- + +## Abstract +If a transaction has a `to` of address `x`, then the `data` of the transaction will be treated as EVM bytecode and it will be executed from the context of the `CALLER` of the transaction (aka: the transaction signer). + +## Motivation +Many Ethereum DApps presently require users to approve multiple transactions in order to produce one effect - for example, the common pattern of first approving a contract to spend a token, then calling that contract. This results in a poor user-experience, and complicates the experience of interacting with DApps. + +Making it possible for externally owned accounts to execute EVM bytecode directly allows a single transaction to execute multiple contract calls, allowing DApps to provide a streamlined experience, where every interaction results in at most one transaction. + +While this is in principle possible today using contract wallets, other UX issues, such as the need to fund a sending account with gas money, lack of support for contract wallets in browser integrations, and lack of a consistent API for contract wallets has led to poor adoption of these.This EIP is a way of enhancing the utility of existing EOAs, in the spirit of "don't let the perfect be the enemy of the good". + +## Specification +A new reserved address is specified at `x`, in the range used for precompiles. When a transaction is sent to this address from an externally owned account, the payload of the transaction is treated as EVM bytecode, and executed with the signer of the transaction as the current account. For clarity: + - The `ADDRESS` opcode returns the address of the EOA that signed the transaction. + - The `BALANCE` opcode returns the balance of the EOA that signed the transaction. + - Any `CALL` operations that send value take their value from the EOA that signed the transaction. + - `CALL` will set the `CALLER` to the EOA (not `x`). + - `DELEGATECALL` preserves the EOA as the owning account. + - The `CALLER` and `ORIGIN` opcodes both return the address of the EOA that signed the transaction. + - There is no code associated with the precompile address. `CODE*` and `EXTCODE*` opcodes behave the same as they do for any empty address. + - `CALLDATA*` opcodes operate on the transaction payload as expected. + - `SLOAD` and `SSTORE` operate on the storage of the EOA. As a result, an EOA can have data in storage, that persists between transactions. + - The `SELFDESTRUCT` opcode does nothing. + - All other opcodes behave as expected for a call to a contract address. + - The transaction is invalid if there is any value attached. + - A call to the precompile address from a contract has no special effect and is equivalent to a call to a nonexistent precompile or an empty address. + +## Rationale +The intent of this EIP is for the new precompile to act in all ways possible like a `DELEGATECALL` from an externally owned account. Some changes are required to reflect the fact that the code being executed is not stored on chain, and for special cases such as `SELFDESTRUCT`, to prevent introducing new edge-cases such as the ability to zero-out an EOA's nonce. + +A precompile was used rather than a new EIP-2718 transaction type because a precompile allows us to have a rich transaction with any type of EIP-2718 transaction. + +## Backwards Compatibility +This EIP introduces a new feature that will need to be implemented in a future hard fork. No backwards compatibility issues with existing code are expected. + +Contracts or DApps that assume that an EOA cannot atomically perform multiple operations may be affected by this change, as this now makes it possible for EOAs to execute multiple atomic operations together. The authors do not believe this is a significant use-case, as this 'protection' is already trivially defeated by miners. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2831.md b/EIPS/eip-2831.md new file mode 100644 index 0000000..3a07fba --- /dev/null +++ b/EIPS/eip-2831.md @@ -0,0 +1,169 @@ +--- +eip: 2831 +title: Transaction Replacement Message Type +author: Gregory Markou (@GregTheGreek) +discussions-to: https://ethereum-magicians.org/t/eip-2831-transaction-replacement-message-type/4448 +status: Stagnant +type: Standards Track +category: Interface +created: 2020-07-26 +requires: 1193 +--- + +## Summary + +An extension to the JavaScript Ethereum Provider API ([EIP-1193](./eip-1193.md)) this creates a new message type in the event a transaction replacement occurs. + +## Abstract + +The current communication between providers and consumers of providers are fundamentally broken in the event that a transaction in the mempool has been superseded by a newer transactions. Providers currently have no way of communicating a transaction replacement, and consumers are required to poll block by block for the resulting transaction. + +## Motivation + +Exert from EIP-1193 +> A common convention in the Ethereum web application ("dapp") ecosystem is for key management software ("wallets") to expose their API via a JavaScript object in the web page. +This object is called "the Provider". + +Many ingenious developments have been made by wallet developers to improve the overall user experience while interacting with the Ethereum blockchain. One specific innovation was transaction replacement, offering users the ability to effectively cancel a previously sent transaction. + +Transaction replacement is not a new concept, but unfortunately causes major user experience problems for dapp developers as the replaced transaction is near impossible to track. + +This EIP formalizes a way for both providers and dapp developers to track transaction replacements seamlessly. + +## Specification + +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). + +### Definitions + +_This section is non-normative._ + +- Provider + - A JavaScript object made available to a consumer, that provides access to Ethereum by means of a Client. +- Wallet + - An end-user application that manages private keys, performs signing operations, and acts as a middleware between the Provider and the Client. +- Transaction Replacement + - Submitting a transaction with both: the same nonce and a 10% increase in the gas price, of a previous transaction which a user no longer wishes to send. This must occur before the original transaction is included in the blockchain. + +### Events + +These methods **MUST** be implemented per the Node.js [`EventEmitter` API](https://nodejs.org/api/events.html). + +The following three events must be implemented: `tx_replacement`, `tx_speedup` and `tx_cancel`. + +A `tx_speedup` is defined as a transaction replacement in which the user wishes to adjust the `gasPrice`, to potentially receive a fast block inclusion. For a `tx_speedup` to be considered valid, the replacement tx must contain the **same** following properties as the one it supersedes: +- Nonce +- To +- Value +- Data + +```typescript +interface txSpeedupInfo { + readonly oldTx: string; + readonly newTx: string; + readonly nonce: string; + readonly from: string; +} + +Provider.on('tx_speedup', listener: (txSpeedupInfo: txSpeedupInfo) => void): Provider; +``` +This event emits the old transaction hash (`oldTx`), the new transaction hash (`newTx`), the nonce used for both transactions (`nonce`), and the signing address for the transaction (`from`). + +A `tx_cancel` is defined as a transaction replacement in which the user wishes to nullify a previous transaction before its inclusion. For a `tx_cancel` to be considered valid, the replacement tx must contain the following properties: +- The same nonce as the superseded transaction +- The same From and To +- Zero value +- No data + +```typescript +interface txCancelInfo { + readonly oldTx: string; + readonly newTx: string; + readonly nonce: string; + readonly from: string; +} + +Provider.on('tx_cancel', listener: (txCancelInfo: txCancelInfo) => void): Provider; +``` +This event emits the old transaction hash (`oldTx`), the new transaction hash (`newTx`), the nonce used for both transactions (`nonce`), and the signing address for the transaction (`from`). + +A `tx_replacement` is defined as a transaction replacement in which a user has completely replaced a previous transaction with a completely brand new one. The replacement tx must contain the following properties: +- The same nonce as the superseded transaction + +```typescript +interface txReplacementInfo { + readonly oldTx: string; + readonly newTx: string; + readonly nonce: string; + readonly from: string; +} + +Provider.on('tx_replacement', listener: (txReplacementInfo: txReplacementInfo) => void): Provider; +``` +This event emits the old transaction hash (`oldTx`), the new transaction hash (`newTx`), the nonce used for both transactions (`nonce`), and the signing address for the transaction (`from`). + +## Rationale + +The implementation was chosen to help the ease of implementation for both providers and dapp developers. Since `ProviderMessage` is widely used by dapp developers already it means that the implementation path would be as trivial as adding and additional `if` clause to their existing message listener. This also provides a benefit to dapps in the event that a provider has not yet implemented the events, it will not cause the dapp panic with `undefined` should it be implemented natively (eg: `ethereum.txCancel(...)` which would error with `ethereum.txReplacement()` is not a function). + +## Backwards Compatibility + +Many Providers adopted EIP-1193, as this EIP extends the same event logic, there should be no breaking changes. All providers that do not support the new events should either I) Ignore the subscription or II) Provide some error to the user. + +## Implementations + +- [Web3.js](https://github.com/ethereum/web3.js/issues/3723) +- [MetaMask](https://github.com/MetaMask/metamask-extension/issues/9174) + +## Security Considerations + +None at the current time. + +## References + +- [Web3.js issue with metamask tx cancel](https://github.com/ethereum/web3.js/issues/3585) +- [Browser doesn't know when a transaction is replace](https://github.com/MetaMask/metamask-extension/issues/3347) + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). + +## Appendix I: Examples + +These examples assume a web browser environment. + +```javascript +// Most Providers are available as window.ethereum on page load. +// This is only a convention, not a standard, and may not be the case in practice. +// Please consult the Provider implementation's documentation. +const ethereum = window.ethereum; + +const transactionParameters = { ... } // Fill in parameters + +ethereum + .request({ + method: 'eth_sendTransaction', + params: [transactionParameters], + }) + .then((txHash) => { + ethereum.on('tx_cancel', (info) => { + const { oldTx, newTx, nonce, from } = message.data; + console.log(`Tx ${oldTx} with nonce ${nonce} from ${from} was cancelled, the new hash is ${newTx}`) + }); + ethereum.on('tx_speedup', (info) => { + const { oldTx, newTx, nonce, from } = message.data; + console.log(`Tx ${oldTx} with nonce ${nonce} from ${from} was sped up, the new hash is ${newTx}`) + }); + ethereum.on('tx_replacement', (info) => { + const { oldTx, newTx, nonce, from } = message.data; + console.log(`Tx ${oldTx} with nonce ${nonce} from ${from} was replaced, the new hash is ${newTx}`) + }); + + + console.log(`Transaction hash ${txHash}`) + }) + .catch((error) => { + console.error(`Error sending transaction: ${error.code}: ${error.message}`); + }); + +``` diff --git a/EIPS/eip-2844.md b/EIPS/eip-2844.md new file mode 100644 index 0000000..6546ba3 --- /dev/null +++ b/EIPS/eip-2844.md @@ -0,0 +1,132 @@ +--- +eip: 2844 +title: Add DID related methods to the JSON-RPC +author: Joel Thorstensson (@oed) +discussions-to: https://github.com/ethereum/EIPs/issues/2845 +status: Stagnant +type: Standards Track +category: Interface +created: 2020-08-01 +--- + +## Simple Summary +Add new methods to the JSON-RPC for signing and decrypting JOSE objects under a new `did_*` prefix. + +## Abstract +This EIP describes three new methods to add to the JSON-RPC that enables wallets to support *Decentralized Identifiers* (DIDs) as well as *JSON Object Signing and Encryption* (JOSE). These standards enables wallets to support data decryption as well as authenticated data, both in standard formats using JOSE. With these new methods apps can request the DID from a users wallet, from which a DID document can be resolved. The DID document contains public keys that can be used for encryption and signature verification. This enables Alice to discover Bobs public keys by only knowing Bobs DID. This EIP does not enforce the user of any particular DID method or JOSE algorithms, wallets are free to implement these however they wish. + +## Motivation +There has been one main previous effort ([#130](https://github.com/ethereum/EIPs/issues/130), [#1098](https://github.com/ethereum/EIPs/pull/1098)) to add decryption to Ethereum wallets in a standard way. This previous approach used a non standard way to encode and represent data encrypted using `x25519-xsalsa20-poly1305`. While this approach does provide a functional way to add encryption support to wallets, it does not take into account similar work that has gone into standardizing the way encrypted data is represented, namely using [JOSE](https://datatracker.ietf.org/wg/jose/documents/). This is a standard from IETF for representing signed and encrypted objects. Another shortcoming of the previous approach is that it's impossible to retrieve the `x25519` public key from another user if only an Ethereum address is known. Public key discoverability is at the core of the work that is happening with the [W3C DID standard](https://w3c.github.io/did-core), where given a DID a document which contains public keys can always be discovered. Implementations of this standard already exist and are adopted within the Ethereum community, e.g. [`did:ethr`](https://github.com/decentralized-identity/ethr-did-resolver/) and [`did:3`](https://github.com/3box/3id-resolver). Interoperability between JOSE and DIDs [already exists](https://github.com/decentralized-identity/did-jwt), and work is being done to [strengthen it](https://github.com/decentralized-identity/did-jose-extensions). Adding support for JOSE and DIDs will enable Ethereum wallets to support a wide range of new use cases such as more traditional authentication using JWTs, as well as new emerging technologies such as [Secure Data Stores](https://identity.foundation/secure-data-store/) and [encrypted data in IPFS](https://github.com/ipld/specs/pull/269). + +## Specification +Three new JSON-RPC methods are specified under the new `did_*` prefix. + +### Auth + +Authenticate the current rpc connection to the DID methods. + +Prompt the user to give permission to the current connection to access the user DID and the given `paths`. + +##### Method: + +`did_authenticate` + +##### Params: + +* `nonce` - a random string used as a challenge +* `aud` - the intended audience of the authentication response +* `paths` - an array of strings + +##### Returns: + +A JWS with general serialization containing the following properties: + +* `nonce ` - the random string which was given as a challenge + +* `did` - the DID which authentication was given for +* `paths` - the paths which was given permission for +* `exp` - a unix timestamp after which the JWS should be considered invalid +* `aud` - optional audience for the JWS, should match the domain which made the request + +An additional property `kid` with the value which represents the DID, and the `keyFragment` that was used to sign the JWS should be added to the protected header ([details](https://github.com/decentralized-identity/did-jose-extensions/issues/2)). + + +#### CreateJWS + +Creates a JSON Web Signature (JWS). + +An additional property `kid` with the value which represents the DID, and the `keyFragment` that was used to sign the JWS should be added to the protected header ([details](https://github.com/decentralized-identity/did-jose-extensions/issues/2)). When `revocable` is set to false the JWS signature should not be possible to revoke. For some DID methods like. `did:key` this is always the case. For other methods which support key revocation it is necessary to include the `version-id` in the `kid` to refer to a specific version of the DID document. When `revocable` is set to true `version-id` must not be included in the `kid` for DID methods that support key revocation. + +##### Method: + +`did_createJWS` + +##### Params: + +* `payload` - the payload to sign, json object or `base64url` encoded string +* `protected` - the protected header, json object +* `did` - the DID that should sign the message, may include the key fragment, string +* `revocable` - makes the JWS revocable when rotating keys, boolean default to `false` + +##### Returns: + +An object containing a JWS with general serialization on the `jws` property. + +##### Recommendation: + +Use `secp256k1` for signing, alternatively `ed25519`. + + + +#### DecryptJWE + +Decrypt the given JWE. + +If the cleartext object contains a property `paths` that contains an array of strings and one of the paths in there are already authenticated using `did_authenticate` the decryption should happen without user confirmation. + +##### Method: + +`did_decryptJWE` + +##### Params: + +* `jwe` - a JWE with general serialization, string +* `did` - the DID that should try to decrypt the JWE, string + +##### Returns: + +An object containing the cleartext, encoded using [`base64pad`](https://github.com/multiformats/multibase), assigned to the `cleartext` property. + +##### Recommendation: + +Implement decryption using `xchacha20poly1305` and `x25519` for key agreement. + + + +## Rationale +This EIP chooses to rely on DIDs and JOSE since there is already support for these standards in many places, by current systems and new systems. By using DIDs and JOSE wallet implementers can also choose which signing and encryption algorithms that they want to support, since these formats are fairly agnostic to specific crypto implementations. + +### Permission system + +A simple permission system is proposed where clients can request permissions though path prefixes, e.g. `/some/permission`. When decryption of a JWE is requested the wallet should check if the decrypted payload contains a `paths` property. If this property doesn't exist the user may be prompted to confirm that the given rpc connection (app) is allowed to read the decrypted data. If the `paths` property is present in the decrypted data it should contain an array of string paths. If one of the these path prefixes matches with one of the path prefixes the user has already granted permission for then decryption should happen automatically without any user confirmation. + +This simple permission system was inspired by some previous comments ([1](https://github.com/ethereum/EIPs/issues/130#issuecomment-329770999), [2](https://medium.com/@wighawag/3-proposals-for-making-web3-a-better-experience-974f97765700)) but avoids data lock in around origins. + +## Implementation + +[IdentityWallet](https://github.com/3box/identity-wallet-js/): An implementation of the wallet side `did_*` methods using the 3ID DID. + +[key-did-provider-ed25519](https://github.com/ceramicnetwork/key-did-provider-ed25519): An implementation of the wallet side `did_*` methods using the `did:key` method. + +[js-did](https://github.com/ceramicnetwork/js-did): A small library which consumes the `did_*` methods. + +[MinimalCipher](https://github.com/digitalbazaar/minimal-cipher): An implementation of DID related encryption for JWE. + +## Security Considerations + +Both JOSE and DIDs are standards that have gone though a lot of scrutiny. Their security will not be considered in this document. In the specification section, recommendations are given for which algorithms to use. For signatures `secp256k1` is already used by ethereum and for decryption `xchacha20poly1305` is widely available, very performant, and already used in TLS. + +The main security consideration of this EIP is the suggested permission system. Here various threat models could be considered. However, this EIP does not go into details about how it should work other than suggesting an approach. In the end it is up to wallet implementations to choose how to ask their users for consent. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2848.md b/EIPS/eip-2848.md new file mode 100644 index 0000000..5212f67 --- /dev/null +++ b/EIPS/eip-2848.md @@ -0,0 +1,202 @@ +--- +eip: 2848 +title: My Own Messages (MOM) +author: Giuseppe Bertone (@Neurone) +discussions-to: https://github.com/InternetOfPeers/EIPs/issues/1 +status: Stagnant +type: Standards Track +category: ERC +created: 2020-08-02 +--- + +## Simple Summary + +My Own Messages (MOM) is a standard to create your very own public, always updated, unstoppable, verifiable, message board. + +## Abstract + +My Own Messages (MOM) use Ethereum as a certification layer for commands and multihash of your messages. It don't use smart contracts but simple self-send transactions with specific payload attached. + +To ge more insights, you can test a [live client](http://internetofpeers.org/mom-client/), watch a [full video overview and demo](https://www.youtube.com/watch?v=z1SnoQkQYkU) and read a [brief presentation](../assets/eip-2848/presentation.pdf). + +## Motivation + +As a _developer_ or _pool's owner_, I'd like to send messages to my users in a decentralized way. They must be able to easily verify my role in the smart contract context (owner, user, and so on) and they must be able to do it without relying on external, insecure and hackable social media sites (Facebook, Twitter, you name it). Also, I'd like to read messages from my userbase, in the same secure and verifiable manner. + +As a _user_, I want a method to easily share my thoughts and idea, publish content, send messages, receive feedback, receive tips, and so on, without dealing with any complexity: just write a message, send it and it's done. Also, I want to write to some smart contract's owner or to the sender of some transaction. + +As an _explorer service_, I want to give my users an effective way to read information by smart contract owners and a place to share ideas and information without using third party services (i.e. Etherscan uses Disqus, and so on) + +And in _any role_, I want a method that does not allow scams - transactions without values, no smart contract's address to remember or to fake - and it does not allow spam - it's cheap but not free, and even if you can link/refer other accounts, you cannot send them messages directly, and others must explicitly follow and listen to your transactions if they want to read your messages. + +Main advantages: + +- You can send messages to users of your ÐApp or Smart Contract, and they always know it is a voice reliable as the smart contract is. +- Create your Ethereum account dedicated to your personal messages, say something only once and it can be seen on every social platform (no more reply of the same post/opinion on dozens of sites like Reddit, Twitter, Facebook, Medium, Disqus, and so on...) +- Small fee to be free: pay just few cents of dollar to notarize your messages, and distribute them with IPFS, Swarm or any other storage you prefer. Because the multihash of the content is notarized, you can always check the integrity of the message you download even from centralized storage services. +- Finally, you can ask and get tips for your words directly into your wallet. + +I know, My Own Messages (MOM) sounds like _mom_. And yes, pun intended :) + +## Specification + +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) when, and only when, they appear in all capitals as shown here. + +Clients following MOM standard **MUST** allow users to send and to read MOM transaction, creating an _updated message list_ for each address the users are interested in. + +Reading MOM transactions, MOM clients **MUST** be able to show the current and updated message list, and they **SHOULD** be able to show also all the message history if users ask for it. + +Apart from message list, MOM clients **SHOULD** be able to download the content of the messages and to show them to the user. + +Clients **SHOULD** allow users to choose and set the source to download content from, and they **SHOULD** be able to use common Content Addressable Networks - i.e. IPFS or Swarm - or HTTP servers. If content is downloaded from HTTP servers, clients **MUST** check the content against the declared multihash. + +As the default setting, clients **MUST** consider `text/markdown` ([RFC 7763](https://www.ietf.org/rfc/rfc7763.txt)) as the media type of the content represented by a multihash, and in particular [Markdown](https://en.wikipedia.org/wiki/Markdown) text in [UTF-8](https://en.wikipedia.org/wiki/UTF-8) without [BOM](https://en.wikipedia.org/wiki/Byte_order_mark). + +Clients **MAY** let users choose to parse messages considering other content types. In this case they **SHOULD** cast a warning to users stating that a content type other than `text/markdown` is used while processing messages. + +It's **RECOMMENDED** that clients inform users about the actual setting of the default content type. + +### MOM transactions + +Clients **MUST** assume that **invalid MOM transactions don't exist**. If a transaction does not strictly follow the MOM standard, clients **MUST** ignore it and they **MUST NOT** consider it a MOM transaction at all. + +Because there can be security implications parsing data sent by users, clients **SHOULD NOT** try to keep track or interpret transactions as _invalid_ MOM transactions. + +#### Valid MOM transaction's data structure + +| ATTRIBUTE | VALUE | +|:--------|:------------| +| `to` | **MUST** be the same account signing the transaction. | +| `value` | **MUST** be `0` wei. | +| `data` | **MUST** be at least `2` bytes. The first byte **MUST** be operational code and following bytes **MUST** be based on the operational codes listed below. | + +#### List of supported operations and messages + +Each operational code has one or more parameters, and all parameters **MUST** be considered mandatory. + +Optional parameters don't exist: if parameters for the specific operational code are not all present or they don't follow the rules, clients **MUST** ignore the transaction completely. + +Messages **MUST** be always referenced with the multihash of their content. + +Operations are divided into two sets: **CORE** and **EXTENDED** operations. + +- Clients **MUST** support all core operations and they **SHOULD** support as much extended operations as possible. +- Clients **SHOULD** support and implement as much extended operations as possible, but they **MAY** choose to implement only some specific extended operations they are interested in. + +#### Core operations + +| OPERATION | CODE | PARAMETERS | MEANING | EFFECT | +|-----------|:--------:|------------|---------|--------| +| ADD | `0x00` | multihash | Add a message. The parameter **MUST** be the multihash of the message. | Clients **MUST** add the message to the message list of the sender. | +| UPDATE | `0x01` | multihash, multihash | Update a message. The first parameter **MUST** be the multihash of the message to be updated. The second parameter **MUST** be the multihash of the updated message. | Clients **MUST** update the message list to show the updated message. | +| REPLY | `0x02` | multihash, multihash | Reply to a message. The first parameter **MUST** be the multihash of the message to reply to. The second parameter **MUST** the multihash of the message. | Clients **MUST** insert a new message in the message list and they **MUST** preserve the relationship with the referenced message. | +| DELETE | `0x03` | multihash | Delete a message. The parameter **MUST** be the multihash of the message to delete. | Clients **MUST** remove the message from the message list. | +| CLOSE ACCOUNT | `0xFD` | multihash | Close an account. The parameter **MUST** be the multihash of the message with the motivations for closing the account. | Clients **MUST** add the message with motivations to the message list and they **MUST NOT** consider MOM messages sent by that address to be valid anymore, ever. In other words, MOM clients **MUST** ignore any other transaction sent by that address while creating the message list. This is useful when users want to change account, for example because the private key seems compromised. | +| RAW | `0xFF` | any | The parameter **MUST** be at least `1` byte. Content type is not disclosed and it **MUST NOT** be considered as `text/markdown`. | Clients **MUST** add the message to the message list but they **MUST NOT** try to decode the content. Clients **SHOULD** allow users to see this message only if explicitly asked for. This operation can be used for _blind_ notarization that general client can ignore. | + +#### Note about `DELETE` operational code + +Please note that sending a `DELETE` command users are not asking to actually delete anything from the blockchain, they are just asking clients to hide that specific message because it's not valid anymore for some reasons. You can think of it like if users say: _I changed my mind so please ÐApps don't show this anymore_. As already stated in the specifications above, clients **MUST** follow this request by the author, unless expressly asked otherwise by the user. + +Please also note that, because it's usually up to the author of a message to be sure the content is available to everyone, if a `DELETE` message was sent it's very likely the content referenced by the multihash isn't available anymore, simply because probably it's not shared by anyone. + +#### Extended operations + +| OPERATION | CODE | PARAMETERS | MEANING | EFFECT | +|-----------|:--------:|------------|---------|--------| +| ADD & REFER | `0x04` | multihash, address | Add a message and refer an account. The first parameter **MUST** be the multihash of the message. The second parameter **MUST** be an address referenced by the message. | Clients **MUST** add the message to the message list and they **MUST** track the reference to the specified account. This can be useful _to invite_ the owner of the referenced account to read this specific message. | +| UPDATE & REFER | `0x05` | multihash, multihash, address | Update a message. The first parameter **MUST** be the multihash of the message to be updated. The second parameter **MUST** be the multihash of the updated message. The third parameter **MUST** be an address referenced by the message.| Clients **MUST** update the message list to show the updated message and they **MUST** track the reference to the specified account. This can be useful _to invite_ the owner of the referenced account to read this specific message. | +| ENDORSE | `0x06` | multihash | Endorse a message identified by the specified multihash. The parameter **MUST** be the multihash of the message to be endorsed. | Clients **MUST** record and track the endorsement for that specific message. Think it as a _like_, a _retwitt_, etc. | +| REMOVE ENDORSEMENT | `0x07` | multihash | Remove endorsement to the message identified by the specified multihash. The parameter **MUST** be the multihash of the message. | Clients **MUST** remove the endorsement for that specific message. | +| DISAPPROVE | `0x08` | multihash | Disapprove a message identified by the specified multihash. The parameter **MUST** be the multihash of the message to disapprove. | Clients **MUST** record and track the disapproval for that specific message. Think it as a _I don't like it_. | +| REMOVE DISAPPROVAL | `0x09` | multihash | Remove disapproval of a message identified by the specified multihash. The parameter **MUST** be the multihash of the message. | Clients **MUST** remove the disapproval for that specific message. | +| ENDORSE & REPLY | `0x0A` | multihash, multihash | Endorse a message and reply to it. The first parameter **MUST** be the multihash of the message to reply to. The second parameter **MUST** be the multihash of the message. | Clients **MUST** insert a new message in the message list and they **MUST** preserve the relationship with the referenced message. Clients **MUST** also record and track the endorsement for that specific message. | +| DISAPPROVE & REPLY | `0x0B` | multihash, multihash | Disapprove a message and reply to it. The first parameter **MUST** be the multihash of the message to reply to. The second parameter **MUST** be the multihash of the message. | Clients **MUST** insert a new message in the message list and they **MUST** preserve the relationship with the referenced message. Clients **MUST** also record and track the disapproval for that specific message. | + +## Rationale + +Ethereum is _account based_, so it's good to be identified as a single source of information. + +It is also able of doing notarization very well and to impose some restrictions on transaction's structure, so it's good for commands. + +IPFS, Swarm or other CANs (Content Addressable Networks) or storage methods are good to store a lot of information. So, the union of both worlds it's a good solution to achieve the objectives of this message standard. + +The objective is also to avoid in the first place any kind of scam and malicious behaviors, so MOM don't allow to send transactions to other accounts and the value of a MOM transaction is always 0. + +### Why not using a smart contract? + +MOM wants to be useful, easy to implement and read, error proof, fast and cheap, but: + +- using a smart contract for messages can leads more easily to errors and misunderstandings: + - address of the contract can be wrong + - smart contract must be deployed on that specific network to send messages +- executing a smart contract costs much more than sending transactions +- executing a smart contract just to store static data is the best example of an anti-pattern (expensive and almost useless) + +Without a specific smart contract to rely on, the MOM standard can be implemented and used right now in any existing networks, and even in future ones. + +Finally, if you can achieve exactly the same result without a smart contract, you didn't need a smart contract at the first place. + +### Why not storing messages directly on-chain? + +There's no benefit to store _static_ messages on-chain, if they are not related to some smart contract's state or if they don't represent exchange of value. The cost of storing data on-chain is also very high. + +### Why not storing op codes inside the message? + +While cost effectiveness is a very important feature in a blockchain related standard, there's also a compromise to reach with usability and usefulness. + +Storing commands inside the messages forces the client to actually download messages to understand what to do with them. This is very inefficient, bandwidth and time consuming. + +Being able to see the commands before downloading the content, it allows the client to recreate the history of all messages and then, at the end, download only updated messages. + +Creating a structure for the content of the messages leads to many issues and considerations in parsing the content, if it's correct, misspelled, and so on. + +Finally, the **content must remain clean**. You really want to notarize the content and not to refer to a data structure, because this can lead to possible false-negative when checking if a content is the same of another. + +### Why multihash? + +[Multihash](https://github.com/multiformats/multihash) is flexible, future-proof and there are already tons of library supporting it. Ethereum must be easily integrable with many different platforms and architectures, so MOM standard follows that idea. + +## Backwards Compatibility + +You can already find few transactions over the Ethereum network that use a pattern similar to this EIP. Sometimes it's done to invalidate a previous transaction in memory pool, using the same nonce but with more gas price, so that transaction is mined cancelling the previous one still in the memory pool. This kind of transactions can be easily ignored if created before the approval of this EIP or just checking if the payload follows the correct syntax. + +## Test Cases + +A MOM-compliant client can be found and tested on [GitHub](https://github.com/InternetOfPeers/mom-client). + +You can use the latest version of MOM client directly via [GitHub Pages](https://internetofpeers.github.io/mom-client) or via IPFS (see the [client repo](https://github.com/InternetOfPeers/mom-client) for the latest updated address). + +## Implementation + +You can use an already working MOM JavaScript package on [GitHub Packages](https://github.com/InternetOfPeers/mom-js/packages/323930) or [npmjs](https://www.npmjs.com/package/@internetofpeers/mom-js). The package is already used by the MOM client above, and you can use it in your ÐApps too with: + +```bash +npm install @internetofpeers/mom-js +``` + +Transaction [`0x8e49485c56897757a6f2707b92cd5dad06126afed92261b9fe1a19b110bc34e6`](https://etherscan.io/tx/0x8e49485c56897757a6f2707b92cd5dad06126afed92261b9fe1a19b110bc34e6) is an example of a valid MOM transaction already mined on the Main net; it's an `ADD` message. + +## Security Considerations + +MOM is very simple and it has no real security concerns by itself. The standard already considers valid only transactions with `0` value and where `from` and `to` addresses are equals. + +The only concerns can come from the payload, but it is more related to the client and not to the standard itself, so here you can find some security suggestions related to clients implementing the standard. + +### Parsing commands + +MOM standard involves parsing payloads generated by potentially malicious clients, so attention must be made to avoid unwanted code execution. + +- Strictly follow only the standard codes +- Don't execute any commands outside of the standard ones, unless expressly acknowledged by the user +- Ignore malformed transactions (transactions that don't strictly follow the rules) + +### Messages + +Default content-type of a message following the MOM standard is Markdown text in UTF8 without BOM. It is highly recommended to disallow the reading of any not-text content-type, unless expressly acknowledged by the user. + +Because content multihash is always stored into the chain, clients can download that content from Content Addressable Network (like IPFS or Swarm) or from central servers. In the latter case, a client should always check the integrity of the received messages, or it must warn the user if it cannot do that (feature not implemented or in error). + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2876.md b/EIPS/eip-2876.md new file mode 100644 index 0000000..e2b55e4 --- /dev/null +++ b/EIPS/eip-2876.md @@ -0,0 +1,184 @@ +--- +eip: 2876 +title: Deposit contract and address standard +author: Jonathan Underwood (@junderw) +discussions-to: https://github.com/junderw/deposit-contract-poc/issues/1 +status: Stagnant +type: Standards Track +category: ERC +created: 2020-08-13 +--- + +## Simple Summary +This ERC defines a simple contract interface for managing deposits. It also defines a new address format that encodes the extra data passed into the interface's main deposit function. + +## Abstract +An ERC-2876 compatible **deposit system** can accept ETH payments from multiple depositors without the need for managing multiple keys or requiring use of a hot wallet. + +An ERC-2876 compatible **wallet application** can send ETH to ERC-2876 compatible **deposit systems** in a way that the **deposit system** can differentiate their payment using the 8 byte id specified in this standard. + +Adoption of ERC-2876 by all exchanges (as a deposit system and as a wallet for their withdrawal systems), merchants, and all wallet applications/libraries will likely decrease total network gas usage by these systems, since two value transactions cost 42000 gas while a simple ETH forwarding contract will cost closer to 30000 gas depending on the underlying implementation. + +This also has the benefit for deposit system administrators of allowing for all deposits to be forwarded to a cold wallet directly without any manual operations to gather deposits from multiple external accounts. + +## Motivation +Centralized exchanges and merchants (Below: "apps") require an address format for accepting deposits. Currently the address format used refers to an account (external or contract), but this creates a problem. It requires that apps create a new account for every invoice / user. If the account is external, that means the app must have the deposit addresses be hot wallets, or have increased workload for cold wallet operators (as each deposit account will create 1 value tx to sweep). If the account is contract, generating an account costs at least 60k gas for a simple proxy, which is cost-prohibitive. + +Therefore, merchant and centralized exchange apps are forced between taking on one of the following: + +- Large security risk (deposit accounts are hot wallets) +- Large manual labor cost (cold account manager spends time sweeping thousands of cold accounts) +- Large service cost (deploying a contract-per-deposit-address model). + +The timing of this proposal is within the context of increased network gas prices. During times like this, more and more services who enter the space are being forced into hot wallets for deposits, which is a large security risk. + +The motivation for this proposal is to lower the cost of deploying and managing a system that accepts deposits from many users, and by standardizing the methodology for this, services across the world can easily use this interface to send value to and from each other without the need to create multiple accounts. + +## Specification + +### Definitions +- 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. +- `The contract interface` is the contract component of this ERC. +- `The deposit address format` is the newly made format described in "Deposit Address Format" for encoding the 20 byte account address and the 8 byte id. +- `The contract` refers to the contract that implements `the contract interface` of this ERC. +- `The 8 byte "id"` is an 8 byte id used as the input parameter for the contract interface. +- `The 5 byte "nonce"` is the first 5 most significant bytes of the `"id"`. +- `The 3 byte "checksum"` is the last 3 least significant bytes of the `"id"` +- `deposit(bytes8)` refers to the function of that signature, which is defined in `the contract interface`. +- `The parent application` refers to the application that will use the information gained within the `deposit(bytes8)` function. (ie. an exchange backend or a non-custodial merchant application) +- `The depositor` refers to the person that will send value to `the contract` via the `deposit(bytes8)` call. +- `The wallet` refers to any application or library that sends value transactions upon the request of `the depositor`. (ie. MyEtherWallet, Ledger, blockchain.com, various libraries) + +### Deposit Address Format + +In order to add the 8 byte "id" data, we need to encode it along with the 20 byte +account address. The 8 bytes are appended to the 20 byte address. + +A 3 byte checksum is included in the id, which is the first 3 bytes of the keccak256 +hash of the 20 byte address and first 5 byte nonce of the id concatenated (25 bytes). + +The Deposit Address format can be generated with the following JavaScript code: + +```js +/** + * Converts a 20 byte account address and a 5 byte nonce to a deposit address. + * The format of the return value is 28 bytes as follows. The + operator is byte + * concatenation. + * (baseAddress + nonce + keccak256(baseAddress + nonce)[:3]) + * + * @param {String} baseAddress the given HEX address (20 byte hex string with 0x prepended) + * @param {String} nonce the given HEX nonce (5 byte hex string with 0x prepended) + * @return {String} + */ +function generateAddress (baseAddress, nonce) { + if ( + !baseAddress.match(/^0x[0-9a-fA-F]{40}$/) || + !nonce.match(/^0x[0-9a-fA-F]{10}$/) + ) { + throw new Error('Base Address and nonce must be 0x hex strings'); + } + const ret = + baseAddress.toLowerCase() + nonce.toLowerCase().replace(/^0x/, ''); + const myHash = web3.utils.keccak256(ret); + return ret + myHash.slice(2, 8); // first 3 bytes from the 0x hex string +}; +``` + +The checksum can be verified within the deposit contract itself using the following: + +```solidity +function checksumMatch(bytes8 id) internal view returns (bool) { + bytes32 chkhash = keccak256( + abi.encodePacked(address(this), bytes5(id)) + ); + bytes3 chkh = bytes3(chkhash); + bytes3 chki = bytes3(bytes8(uint64(id) << 40)); + return chkh == chki; +} +``` + +### The Contract Interface + +A contract that follows this ERC: + +- `The contract` MUST revert if sent a transaction where `msg.data` is null (A pure value transaction). +- `The contract` MUST have a deposit function as follows: + +```solidity +interface DepositEIP { + function deposit(bytes8 id) external payable returns (bool); +} +``` + +- `deposit(bytes8)` MUST return `false` when the contract needs to keep the value, but signal to the depositor that the deposit (in terms of the parent application) itself has not yet succeeded. (This can be used for partial payment, ie. the invoice is for 5 ETH, sending 3 ETH returns false, but sending a second tx with 2 ETH will return true.) +- `deposit(bytes8)` MUST revert if the deposit somehow failed and the contract does not need to keep the value sent. +- `deposit(bytes8)` MUST return `true` if the value will be kept and the payment is logically considered complete by the parent application (exchange/merchant). +- `deposit(bytes8)` SHOULD check the checksum contained within the 8 byte id. (See "Deposit Address Format" for an example) +- `The parent application` SHOULD return any excess value received if the deposit id is a one-time-use invoice that has a set value and the value received is higher than the set value. However, this SHOULD NOT be done by sending back to `msg.sender` directly, but rather should be noted in the parent application and the depositor should be contacted out-of-band to the best of the application manager's ability. + +### Depositing Value to the Contract from a Wallet + +- `The wallet` MUST accept `the deposit address format` anywhere the 20-byte address format is accepted for transaction destination. +- `The wallet` MUST verify the 3 byte checksum and fail if the checksum doesn't match. +- `The wallet` MUST fail if the destination address is `the deposit address format` and the `data` field is set to anything besides null. +- `The wallet` MUST set the `to` field of the underlying transaction to the first 20 bytes of the deposit address format, and set the `data` field to `0x3ef8e69aNNNNNNNNNNNNNNNN000000000000000000000000000000000000000000000000` where `NNNNNNNNNNNNNNNN` is the last 8 bytes of the deposit address format. (ie. if the deposit address format is set to `0x433e064c42e87325fb6ffa9575a34862e0052f26913fd924f056cd15` then the `to` field is `0x433e064c42e87325fb6ffa9575a34862e0052f26` and the `data` field is `0x3ef8e69a913fd924f056cd15000000000000000000000000000000000000000000000000`) + +## Rationale +The contract interface and address format combination has one notable drawback, which was brought up in discussion. This ERC can only handle deposits for native value (ETH) and not other protocols such as ERC-20. However, this is not considered a problem, because it is best practice to logically AND key-wise separate wallets for separate currencies in any exchange/merchant application for accounting reasons and also for security reasons. Therefore, using this method for the native value currency (ETH) and another method for ERC-20 tokens etc. is acceptable. Any attempt at doing something similar for ERC-20 would require modifying the ERC itself (by adding the id data as a new input argument to the transfer method etc.) which would grow the scope of this ERC too large to manage. However, if this address format catches on, it would be trivial to add the bytes8 id to any updated protocols (though adoption might be tough due to network effects). + +The 8 byte size of the id and the checksum 3 : nonce 5 ratio were decided with the following considerations: + +- 24 bit checksum is better than the average 15 bit checksum of an EIP-55 address. +- 40 bit nonce allows for over 1 trillion nonces. +- 64 bit length of the id was chosen as to be long enough to support a decent checksum and plenty of nonces, but not be too long. (Staying under 256 bits makes hashing cheaper in gas costs as well.) + +## Backwards Compatibility +An address generated with the deposit address format will not be considered a valid address for applications that don't support it. If the user is technical enough, they can get around lack of support by verifying the checksum themselves, creating the needed data field by hand, and manually input the data field. (assuming the wallet app allows for arbitrary data input on transactions) A tool could be hosted on github for users to get the needed 20 byte address and msg.data field from a deposit address. + +Since a contract following this ERC will reject any plain value transactions, there is no risk of extracting the 20 byte address and sending to it without the calldata. + +However, this is a simple format, and easy to implement, so the author of this ERC will first implement in web3.js and encourage adoption with the major wallet applications. + +## Test Cases +``` +[ + { + "address": "0x083d6b05729c58289eb2d6d7c1bb1228d1e3f795", + "nonce": "0xbdd769c69b", + "depositAddress": "0x083d6b05729c58289eb2d6d7c1bb1228d1e3f795bdd769c69b3b97b9" + }, + { + "address": "0x433e064c42e87325fb6ffa9575a34862e0052f26", + "nonce": "0x913fd924f0", + "depositAddress": "0x433e064c42e87325fb6ffa9575a34862e0052f26913fd924f056cd15" + }, + { + "address": "0xbbc6597a834ef72570bfe5bb07030877c130e4be", + "nonce": "0x2c8f5b3348", + "depositAddress": "0xbbc6597a834ef72570bfe5bb07030877c130e4be2c8f5b3348023045" + }, + { + "address": "0x17627b07889cd22e9fae4c6abebb9a9ad0a904ee", + "nonce": "0xe619dbb618", + "depositAddress": "0x17627b07889cd22e9fae4c6abebb9a9ad0a904eee619dbb618732ef0" + }, + { + "address": "0x492cdf7701d3ebeaab63b4c7c0e66947c3d20247", + "nonce": "0x6808043984", + "depositAddress": "0x492cdf7701d3ebeaab63b4c7c0e66947c3d202476808043984183dbe" + } +] +``` + +## Implementation +A sample implementation with an example contract and address generation (in the tests) is located here: + +https://github.com/junderw/deposit-contract-poc + +## Security Considerations +In general, contracts that implement the contract interface should forward funds received to the deposit(bytes8) function to their cold wallet account. This address SHOULD be hard coded as a constant OR take advantage of the `immutable` keyword in solidity versions `>=0.6.5`. + +To prevent problems with deposits being sent after the parent application is shut down, a contract SHOULD have a kill switch that will revert all calls to deposit(bytes8) rather than using `selfdestruct(address)` (since users who deposit will still succeed, since an external account will receive value regardless of the calldata, and essentially the self-destructed contract would become a black hole for any new deposits) + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2917.md b/EIPS/eip-2917.md new file mode 100644 index 0000000..cc88c59 --- /dev/null +++ b/EIPS/eip-2917.md @@ -0,0 +1,165 @@ +--- +eip: 2917 +title: Staking Reward Calculation +author: Tony Carson , Mehmet Sabir Kiraz , Süleyman Kardaş +discussions-to: https://github.com/ethereum/EIPs/issues/2925 +status: Stagnant +type: Standards Track +category: ERC +created: 2020-08-28 +--- + +## Simple Summary +ERC2917 is a new standardization for on-chain calculation of staking reward. + +## Abstract +Based on the product of effective collateral and time, ERC2917 calculates the reward a user can get at any time, and realize the real decentralized DeFi. Here below is the formula for the calculation of reward for a user U: + +![concept image](../assets/eip-2917/erc-reward-formula.png "erc-reward-formula") + +where ∆pi denotes individual productivity of the user U between the consecutive block numbers ti-1 and ti, ∆Pi denotes global productivity between the consecutive block numbers ti-1 and ti, and ∆Gi denotes gross product between the consecutive block numbers ti-1 and ti. The formula ensures that there is no benefit in case of exiting earlier or entering later in the computation. The reward a user can get for a period is based on his total productivity during that specific time. The formula has been simplified through Solidity and generalized design to make it available across all DeFi products. +We note that the smart contract can be triggered for every computation of on the following events: +- whenever the productivity of a user changes (increase/decrease), +- whenever a user withdraws. + +## Motivation + +One of the main drawbacks of many DeFi projects is the reward distribution mechanism within the smart contract. In fact, there are two main mechanisms are adopted so far. +1. Distribution of rewards is only given when all users exit the contract +2. The project collects on-chain data, conducts calculation off-chain, and sends the results +to the chain before starting rewards distribution accordingly + +The first approach conducts all calculation in an on-chain fashion, the cycle of its rewards distribution is too long. Furthermore, users need to remove their collateral before getting the rewards, which can be harmful for their rewards. The second approach is a semi-decentralized model since the main algorithm involves an off-chain computation. Therefore, the fairness and transparency properties cannot be reflected and this can even create the investment barrier for users. + +Since there is more DeFi projects coming out everyday, users could not find a proper way to get to know: +1) amount of interests he/she would get +2) how the interest calculated +3) what is his/her contribution compare to the overall + +By standardizing ERC2917, it abstracts the interface for interests generation process. Making wallet applications easier to collect each DeFi's metrics, user friendlier. + +## Specification + +Every ERC-2917 compliant contract must implement the ERC2917 and ERC20 interfaces (if necessary): + +```solidity +interface IERC2917 is IERC20 { + + /// @dev This emit when interests amount per block is changed by the owner of the contract. + /// It emits with the old interests amount and the new interests amount. + event InterestRatePerBlockChanged (uint oldValue, uint newValue); + + /// @dev This emit when a users' productivity has changed + /// It emits with the user's address and the the value after the change. + event ProductivityIncreased (address indexed user, uint value); + + /// @dev This emit when a users' productivity has changed + /// It emits with the user's address and the the value after the change. + event ProductivityDecreased (address indexed user, uint value); + + + /// @dev Return the current contract's interests rate per block. + /// @return The amount of interests currently producing per each block. + function interestsPerBlock() external view returns (uint); + + /// @notice Change the current contract's interests rate. + /// @dev Note the best practice will be restrict the gross product provider's contract address to call this. + /// @return The true/false to notice that the value has successfully changed or not, when it succeed, it will emite the InterestRatePerBlockChanged event. + function changeInterestRatePerBlock(uint value) external returns (bool); + + /// @notice It will get the productivity of given user. + /// @dev it will return 0 if user has no productivity proved in the contract. + /// @return user's productivity and overall productivity. + function getProductivity(address user) external view returns (uint, uint); + + /// @notice increase a user's productivity. + /// @dev Note the best practice will be restrict the callee to prove of productivity's contract address. + /// @return true to confirm that the productivity added success. + function increaseProductivity(address user, uint value) external returns (bool); + + /// @notice decrease a user's productivity. + /// @dev Note the best practice will be restrict the callee to prove of productivity's contract address. + /// @return true to confirm that the productivity removed success. + function decreaseProductivity(address user, uint value) external returns (bool); + + /// @notice take() will return the interests that callee will get at current block height. + /// @dev it will always calculated by block.number, so it will change when block height changes. + /// @return amount of the interests that user are able to mint() at current block height. + function take() external view returns (uint); + + /// @notice similar to take(), but with the block height joined to calculate return. + /// @dev for instance, it returns (_amount, _block), which means at block height _block, the callee has accumulated _amount of interests. + /// @return amount of interests and the block height. + function takeWithBlock() external view returns (uint, uint); + + /// @notice mint the available interests to callee. + /// @dev once it mint, the amount of interests will transfer to callee's address. + /// @return the amount of interests minted. + function mint() external returns (uint); +} +``` + +### InterestRatePerBlockChanged + +This emit when interests amount per block is changed by the owner of the contract. It emits with the old interests amount and the new interests amount. + + +### ProductivityIncreased + +It emits with the user's address and the the value after the change. + + +### ProductivityDecreased + +It emits with the user's address and the the value after the change. + +### interestsPerBlock + +It returns the amount of interests currently producing per each block. + +### changeInterestRatePerBlock + +Note the best practice will be restrict the gross product provider's contract address to call this. + +The true/false to notice that the value has successfully changed or not, when it succeed, it will emite the InterestRatePerBlockChanged event. + +### getProductivity + +It returns user's productivity and overall productivity. It returns 0 if user has no productivity proved in the contract. + +### increaseProductivity + +It increases a user's productivity. + +### decreaseProductivity + +It decreases a user's productivity. + +### take + +It returns the interests that callee will get at current block height. + +### takeWithBlock + +Similar to take(), but with the block height joined to calculate return. + +For instance, it returns (_amount, _block), which means at block height _block, the callee has accumulated _amount of interests. + +It returns amount of interests and the block height. + +### mint +it mints the amount of interests will transfer to callee's address. It returns the amount of interests minted. + +## Rationale +TBD + +## Implementation +The implementation code is on the github: + +- [ERC2917 Demo](https://github.com/gnufoo/ERC3000-Proposal) + +## Security Considerations +TBD + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2926.md b/EIPS/eip-2926.md new file mode 100644 index 0000000..bb1562d --- /dev/null +++ b/EIPS/eip-2926.md @@ -0,0 +1,161 @@ +--- +eip: 2926 +title: Chunk-Based Code Merkleization +author: Sina Mahmoodi (@s1na), Alex Beregszaszi (@axic) +discussions-to: https://ethereum-magicians.org/t/eip-2926-chunk-based-code-merkleization/4555 +status: Stagnant +type: Standards Track +category: Core +created: 2020-08-25 +requires: 161, 170, 2584 +--- + +## Abstract + +Code merkleization, along with binarification of the trie and gas cost bump of state accessing opcodes, are considered as the main levers for decreasing block witness sizes in stateless or partial-stateless Eth1x roadmaps. Here we specify a fixed-sized chunk approach to code merkleization and outline how the transition of existing contracts to this model would look like. + +## Motivation + +Bytecode is currently the [second contributor](https://github.com/mandrigin/ethereum-mainnet-bin-tries-data) to block witness size, after the proof hashes. Transitioning the trie from hexary to binary reduces the hash section of the witness by 3x, thereby making code the first contributor. By breaking contract code into chunks and committing to those chunks in a merkle tree, stateless clients would only need the chunks that were touched during a given transaction to execute it. + +## Specification + +This specification assumes that [EIP-2584](./eip-2584.md) is deployed, and the merkleization rules and gas costs are proposed accordingly. What follows is structured to have two sections: + +1. How a given contract code is split into chunks and then merkleized +2. How to merkleize all existing contract codes during a hardfork + +### Constants and Definitions + +#### Constants + +- `CHUNK_SIZE`: 32 (bytes) +- `KEY_LENGTH`: 2 (bytes) +- `MAX_CHUNK_COUNT`: `0xfffc` +- `VERSION_KEY`: `0xfffd` +- `CODELEN_KEY`: `0xfffe` +- `CODEHASH_KEY`: `0xffff` +- `VERSION`: 0 +- `EMPTY_CODE_ROOT`: `0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470` (==`keccak256('')`) +- `HF_BLOCK_NUMBER`: to be defined + +#### Definitions + +- `BE(x, N)`: casts `x` to an unsigned integer of `N` bytes and returns its big-endian representation + +### Code merkleization + +For an account record `A` with code `C`, the field `A.codeHash` is replaced with `codeRoot`. `codeRoot` is `EMPTY_CODE_ROOT` if `C` is empty. Otherwise it contains the root of `codeTrie`, a [binary trie](https://hackmd.io/uCWOpSrUQaytBgcO0MVkTQ) with the following leaves: + +- Key: `VERSION_KEY`, value: `BE(VERSION, 1)` +- Key: `CODELEN_KEY`, value: `BE(length(C), 4)` +- Key: `CODEHASH_KEY`, value: `keccak256(C)` + +In addition to the above, `codeTrie` commits to a list of code `chunks = [(FIO_0, code_0), ..., (FIO_n, code_n)]` which are derived from `C` in a way that: + +- `n < MAX_CHUNK_COUNT`. +- `code_0 || ... || code_n == C`. +- `length(code_i) == CHUNK_SIZE` where `0 <= i < n`. +- `length(code_n) <= CHUNK_SIZE`. +- `FIO_i` is the offset of the first instruction within the chunk. It should only be greater than zero if the last instruction in `code_i-1` is a multi-byte instruction (like `PUSHN`) crossing the chunk boundary. It is set to `CHUNK_SIZE` in the case where all bytes of a chunk are data. + +The `i`th element of `chunks` is stored in `codeTrie` with: + +- Key: `BE(i, KEY_LENGTH)` +- Value: `BE(FIO_i, 1) || code_i`, where `||` stands for byte-wise concatenation + +#### Contract creation gas cost + +As of now there is a charge of 200 gas per byte of the code stored in state by contract creation operations, be it initiated via `CREATE`, `CREATE2`, or an external transaction. This per byte cost is to be increased from `200` to `TBD` to account for the chunking and merkleization costs. + +### Updating existing code (transition process) + +The transition process involves reading all contracts in the state and applying the above procedure to them. A benchmark showing how long this process will take is still pending, but intuitively it should take longer than the time between two blocks (in the order of hours). Hence we recommend clients to pre-process the changes before the EIP is activated. + +Code has the nice property that it is (mostly) static. Therefore clients can maintain a mapping of `[accountAddress -> codeRoot]` which stores the results for the contracts they have already merkleized. During this pre-computation phase, whenever a new contract is created its `codeRoot` is computed, and added to the mapping. Whenever a contract self-destructs, its corresponding entry is removed. + +At block `HF_BLOCK_NUMBER` when the EIP gets activated, before executing any transaction the client must update the account record for all contracts with non-empty code in the state to replace the `codeHash` field with the pre-computed `codeRoot`. EoA accounts will keep their `codeHash` value as `codeRoot`. *Accounts with empty code will keep their `codeHash` value as `codeRoot`.* + +## Rationale + +### Hexary vs binary trie + +The Ethereum mainnet state is encoded as of now in a hexary Merkle Patricia Tree. As part of the Eth1x roadmap, a transition to a [binary trie](https://ethresear.ch/t/binary-trie-format/7621) has been [investigated](https://medium.com/@mandrigin/stateless-ethereum-binary-tries-experiment-b2c035497768) with the goal of reducing witness sizes. Because code chunks are also stored in the trie, this EIP would benefit from the witness size reduction offered by the binarification. Therefore we have decided to explicitly state [EIP-2584](./eip-2584.md) to be a requirement of this change. Note that if code merkleization is included in a hard-fork beforehand, then all code must be re-merkleized after the binary transition. + +### Chunk size + +The current recommended chunk size of 32 bytes has been selected based on a few observations. Smaller chunks are more efficient (i.e. have higher [chunk utilization](https://ethresear.ch/t/some-quick-numbers-on-code-merkelization/7260/3)), but incur a larger hash overhead (i.e. number of hashes as part of the proof) due to a higher trie depth. Larger chunks are less efficient, but incur less hash overhead. We plan to run a larger experiment comparing various chunk sizes to arrive at a final recommendation. + +### First instruction offset + +The `firstInstructionOffset` fields allows safe jumpdest analysis when a client doesn't have all the chunks, e.g. a stateless clients receiving block witnesses. + +Note: there could be an edge case when computing FIO for the chunks where the data bytes at the end of a bytecode (last chunk) resemble a multi-byte instruction. This case can be safely ignored. + +### Gas cost of code-accessing opcodes + +How merkleized code is stored in the client database affects the performance of code-accessing opcodes, i.e: CALL, CALLCODE, DELEGATECALL, STATICCALL, EXTCODESIZE, EXTCODEHASH, and EXTCODECOPY. Storing the code trie with all intermediate nodes in the database implies multiple look-ups to fetch the code of the callee, which is more than the current one look-up (excluding the trie traversal to get the account) required. Note CODECOPY and CODESIZE are not affected since the code for the current contract has already been loaded to memory. + +The gas cost analysis in this section assumes a specific way of storing it. In this approach clients only merkleize code once during creation to compute `codeRoot`, but then discard the chunks. They instead store the full bytecode as well as the metadata fields in the database. We believe per-chunk metering for calls would be more easily solvable by witness metering in the stateless model. + +### Different chunking logic + +We have considered an alternative option to package chunks, where each chunk is prepended with its `chunkLength` and would only contain complete opcodes (i.e. any multi-byte opcode not fitting the `CHUNK_SIZE` would be deferred to the next chunk). + +This approach has downsides compared to the one specified: +1) Requires a larger `CHUNK_SIZE` -- at least 33 bytes to accommodate the `PUSH32` instruction. +2) It is more wasteful. For example, `DUP1 PUSH32 <32-byte payload>` would be encoded as two chunks, the first chunk contains only `DUP1`, and the second contains only the `PUSH32` instruction with its payload. +3) Calculating the number of chunks is not trivial and would have to be stored explicitly in the metadata. + +Additionally we have reviewed many other options (basic block based, Solidity subroutines (requires determining the control flow), EIP-2315 subroutines). This EIP however only focuses on the chunk-based option. + +### RLP and SSZ + +To remain consistent with the binary transition proposal we avoid using RLP for serializing the leaf values. We have furthermore considered SSZ for both serializing data and merkleization and remain open to adopting it, but decided to use the binary trie format for simplicity. + +### Metadata fields + +The metadata fields `version`, `codeLen` and `codeHash` are added mostly to facilitate a cheaper implementation of `EXTCODESIZE` and `EXTCODEHASH` in a stateless paradigm. The version field allows for differentiating between bytecode types (e.g. [EVM1.5/EIP-615](./eip-615.md), [EIP-2315](./eip-2315.md), etc.) or code merkleization schemes (or merkleization settings, such as larger `CHUNK_SIZE`) in future. + +Instead of encoding `codeHash` and `codeSize` in the metadata, they could be made part of the account. In our opinion, the metadata is a more concise option, because EoAs do not need these fields, resulting in either additional logic (to omit those fields in the accounts) or calculation (to include them in merkleizing the account). + +An alternative option to the version field would be to add an account-level field: either following [EIP-1702](./eip-1702.md), or by adding an `accountKind` field (with potential options: `eoa`, `merkleized_evm_chunk32`, `merkleized_eip2315_chunk64`, etc.) as the first member of the account. One benefit this could provide is omitting `codeHash` for EoAs. + +### The keys in the code trie (and `KEY_LENGTH`) + +As explained in the specification above, the keys in the code trie are indices of the `chunks` array. Having a key length of 2 bytes means the trie can address 65536 - 3 (minus metadata fields) chunks, which corresponds to ~2Mb code size. That allows for roughly ~85x increase in the code size limit in future without requiring a change in merkleization. + +### Alternate values of codeRoot for EoAs + +This proposal changes the meaning of the fourth field (`codeHash`) of the account. Prior to this change, that field represents the Keccak-256 hash of the bytecode, which is logically hash of an empty input for EoAs. + +Since `codeHash` is replaced with `codeRoot`, the root hash of the code trie, the value would be different for EoAs under the new rules: the root of the `codeTrie(metadata=[codeHash=keccak256(''), codeSize=0])`. An alternative would be simply using the hash of an empty trie. Or to avoid introducing yet another constant (the result of the above), one could also consider using `codeRoot = 0` for EoAs. + +However, we wanted to maintain compatibility with [EIP-1052](./eip-1052.md) and decided to keep matters simple by using the hash of empty input (`c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470`) for EoAs. + +## Backwards Compatibility + +From the perspective of contracts, the design aims to be transparent, with the exception of changes in gas costs. + +Outside of the interface presented for contracts, this proposal introduces major changes to the way contract code is stored, and needs a substantial modification of the Ethereum state. Therefore it can only be introduced via a hard fork. + +## Test Cases + +TBD + +Show the `codeRoot` for the following cases: + +1. `code=''` +2. `code='PUSH1(0) DUP1 REVERT'` +3. `code='PUSH32(-1)'` (data passing through a chunk boundary) + +## Implementation + +The implementation of the chunking and merkleization logic in Typescript can be found [here](https://github.com/ewasm/biturbo/blob/merklize-mainnet-blocks/src/relayer/bytecode.ts#L172), and in Python [here](https://github.com/hugo-dc/code-chunks/). Please note neither of these examples currently use a binary tree. + +## Security Considerations + +TBA + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2929.md b/EIPS/eip-2929.md new file mode 100644 index 0000000..0e11417 --- /dev/null +++ b/EIPS/eip-2929.md @@ -0,0 +1,172 @@ +--- +eip: 2929 +title: Gas cost increases for state access opcodes +author: Vitalik Buterin (@vbuterin), Martin Swende (@holiman) +discussions-to: https://ethereum-magicians.org/t/eip-2929-gas-cost-increases-for-state-access-opcodes/4558 +status: Final +type: Standards Track +category: Core +created: 2020-09-01 +--- + +## Simple Summary + +Increases gas cost for `SLOAD`, `*CALL`, `BALANCE`, `EXT*` and `SELFDESTRUCT` when used for the first time in a transaction. + +## Abstract + +Increase the gas cost of `SLOAD` (`0x54`) to 2100, and the `*CALL` opcode family (`0xf1`, `f2`, `f4`, `fA`), `BALANCE` `0x31` and the `EXT*` opcode family (`0x3b`, `0x3c`, `0x3f`) to 2600. Exempts (i) precompiles, and (ii) addresses and storage slots that have already been accessed in the same transaction, which get a decreased gas cost. Additionally reforms `SSTORE` metering and `SELFDESTRUCT` to ensure "de-facto storage loads" inherent in those opcodes are priced correctly. + +## Motivation + +Generally, the main function of gas costs of opcodes is to be an estimate of the time needed to process that opcode, the goal being for the gas limit to correspond to a limit on the time needed to process a block. However, storage-accessing opcodes (`SLOAD`, as well as the `*CALL`, `BALANCE` and `EXT*` opcodes) have historically been underpriced. In the 2016 Shanghai DoS attacks, once the most serious client bugs were fixed, one of the more durably successful strategies used by the attacker was to simply send transactions that access or call a large number of accounts. + +Gas costs were increased to mitigate this, but recent numbers suggest they were not increased enough. Quoting [https://arxiv.org/pdf/1909.07220.pdf](https://arxiv.org/pdf/1909.07220.pdf): + +> Although by itself, this issue might seem benign, `EXTCODESIZE` forces the client to search the contract ondisk, resulting in IO heavy transactions. While replaying the Ethereum history on our hardware, the malicious transactions took around 20 to 80 seconds to execute, compared to a few milliseconds for the average transactions + +This proposed EIP increases the costs of these opcodes by a factor of ~3, reducing the worst-case processing time to ~7-27 seconds. Improvements in database layout that involve redesigning the client to read storage directly instead of hopping through the Merkle tree would decrease this further, though these technologies may take a long time to fully roll out, and even with such technologies the IO overhead of accessing storage would remain substantial. + +A secondary benefit of this EIP is that it also performs most of the work needed to make [stateless witness sizes](https://ethereum-magicians.org/t/protocol-changes-to-bound-witness-size/3885) in Ethereum acceptable. Assuming [a switch to binary tries](https://ethresear.ch/t/binary-trie-format/7621), the theoretical maximum witness size not including code size (hence "most of the work" and not "all") would decrease from `(12500000 gas limit) / (700 gas per BALANCE) * (800 witness bytes per BALANCE) ~= 14.3M bytes` to `12500000 / 2600 * 800 ~= 3.85M bytes`. Pricing for code access could be changed when code merklization is implemented. + +In the further future, there are similar benefits in the case of SNARK/STARK witnesses. Recent numbers from Starkware suggest that they are able to prove 10000 Rescue hashes per second on a consumer desktop; assuming 25 hashes per Merkle branch, and a block full of state accesses, at present this would imply a witness would take `12500000 / 700 * 25 / 10000 ~= 44.64` seconds to generate, but after this EIP that would reduce to `12500000 / 2500 * 25 / 10000 ~= 12.5` seconds, meaning that a single desktop computer would be able to generate witnesses on time under any conditions. Future gains in STARK proving could be spent on either (i) using a more expensive but robust hash function or (ii) reducing proving times further, reducing the delay and hence improving user experience of stateless clients that rely on such witnesses. + +## Specification + +### Parameters + +| Constant | Value | +| - | - | +| `FORK_BLOCK` | 12244000 | +| `COLD_SLOAD_COST` | 2100 | +| `COLD_ACCOUNT_ACCESS_COST` | 2600 | +| `WARM_STORAGE_READ_COST` | 100 | + +For blocks where `block.number >= FORK_BLOCK`, the following changes apply. + +When executing a transaction, maintain a set `accessed_addresses: Set[Address]` and `accessed_storage_keys: Set[Tuple[Address, Bytes32]]` . + +The sets are transaction-context-wide, implemented identically to other transaction-scoped constructs such as the self-destruct-list and global `refund` counter. In particular, if a scope reverts, the access lists should be in the state they were in before that scope was entered. + +When a transaction execution begins, + - `accessed_storage_keys` is initialized to empty, and + - `accessed_addresses` is initialized to include + - the `tx.sender`, `tx.to` (or the address being created if it is a contract creation transaction) + - and the set of all precompiles. + + +### Storage read changes + +When an address is either the target of a (`EXTCODESIZE` (`0x3B`), `EXTCODECOPY` (`0x3C`), `EXTCODEHASH` (`0x3F`) or `BALANCE` (`0x31`)) opcode or the target of a (`CALL` (`0xF1`), `CALLCODE` (`0xF2`), `DELEGATECALL` (`0xF4`), `STATICCALL` (`0xFA`)) opcode, the gas costs are computed as follows: + +* If the target is not in `accessed_addresses`, charge `COLD_ACCOUNT_ACCESS_COST` gas, and add the address to `accessed_addresses`. +* Otherwise, charge `WARM_STORAGE_READ_COST` gas. + +In all cases, the gas cost is charged and the map is updated at the time that the opcode is being called. +When a `CREATE` or `CREATE2` opcode is called, immediately (ie. before checks are done to determine whether or not the address is unclaimed) add the address being created to `accessed_addresses`, but gas costs of `CREATE` and `CREATE2` are unchanged. +Clarification: If a `CREATE`/`CREATE2` operation fails later on, e.g during the execution of `initcode` or has insufficient gas to store the code in the state, the `address` of the contract itself remains in `access_addresses` (but any additions made within the inner scope are reverted). + +For `SLOAD`, if the `(address, storage_key)` pair (where `address` is the address of the contract whose storage is being read) is not yet in `accessed_storage_keys`, charge `COLD_SLOAD_COST` gas and add the pair to `accessed_storage_keys`. If the pair is already in `accessed_storage_keys`, charge `WARM_STORAGE_READ_COST` gas. + +Note: For call-variants, the `100`/`2600` cost is applied immediately (exactly like how `700` was charged before this EIP), i.e: before calculating the `63/64ths` available for entering the call. + +Note 2: There is currently no way to perform a 'cold sload read/write' on a 'cold account', simply because in order to read/write a `slot`, the execution must already be inside the `account`. Therefore, the behaviour of cold storage reads/writes on cold accounts is undefined as of this EIP. Any future EIP which +proposes to add 'remote read/write' would need to define the pricing behaviour of that change. + +### SSTORE changes + +When calling `SSTORE`, check if the `(address, storage_key)` pair is in `accessed_storage_keys`. If it is not, charge an additional `COLD_SLOAD_COST` gas, and add the pair to `accessed_storage_keys`. Additionally, modify the parameters defined in [EIP-2200](./eip-2200.md) as follows: + +| Parameter | Old value | New value | +| - | - | - | +| `SLOAD_GAS` | 800 | `= WARM_STORAGE_READ_COST` | +| `SSTORE_RESET_GAS` | 5000 | `5000 - COLD_SLOAD_COST` | + +The other parameters defined in EIP 2200 are unchanged. +Note: The constant `SLOAD_GAS` is used in several places in EIP 2200, e.g `SSTORE_SET_GAS - SLOAD_GAS`. Implementations that are using composite definitions have to ensure to update those definitions too. + +### SELFDESTRUCT changes + +If the ETH recipient of a `SELFDESTRUCT` is not in `accessed_addresses` (regardless of whether or not the amount sent is nonzero), charge an additional `COLD_ACCOUNT_ACCESS_COST` on top of the existing gas costs, and add the ETH recipient to the set. + +Note: `SELFDESTRUCT` does not charge a `WARM_STORAGE_READ_COST` in case the recipient is already warm, which differs from how the other call-variants work. The reasoning behind this is to keep the changes small, a `SELFDESTRUCT` already costs `5K` and is a no-op if invoked more than once. + +## Rationale + +### Opcode costs vs charging per byte of witness data + +The natural alternative path to changing gas costs to reflect witness sizes is to charge per byte of witness data. However, that would take a longer time to implement, hampering the goal of providing short-term security relief. Furthermore, following that path faithfully would lead to extremely high gas costs to transactions that touch contract code, as one would need to charge for all 24576 contract code bytes; this would be an unacceptably high burden on developers. It is better to wait for [code merklization](https://medium.com/ewasm/evm-bytecode-merklization-2a8366ab0c90) to start trying to properly account for gas costs of accessing individual chunks of code; from a short-term DoS prevention standpoint, accessing 24 kB from disk is not much more expensive than accessing 32 bytes from disk, so worrying about code size is not necessary. + +### Adding the accessed_addresses / accessed_storage_keys sets + +The sets of already-accessed accounts and storage slots are added to avoid needlessly charging for things that can be cached (and in all performant implementations already are cached). Additionally, it removes the current undesirable status quo where it is needlessly unaffordable to do self-calls or call precompiles, and enables contract breakage mitigations that involve pre-fetching some storage key allowing a future execution to still take the expected amount of gas. + +### SSTORE gas cost change + +The change to SSTORE is needed to avoid the possibility of a DoS attack that "pokes" a randomly chosen zero storage slot, changing it from 0 to 0 at a cost of 800 gas but requiring a de-facto storage load. The `SSTORE_RESET_GAS` reduction ensures that the total cost of SSTORE (which now requires paying the `COLD_SLOAD_COST`) remains unchanged. Additionally, note that applications that do `SLOAD` followed by `SSTORE` (eg. `storage_variable += x`) _would actually get cheaper_! + +### Change SSTORE accounting only minimally + +The SSTORE gas costs continue to use Wei Tang's original/current/new approach, instead of being redesigned to use a dirty map, because Wei Tang's approach correctly accounts for the actual costs of changing storage, which only care about current vs final value and not intermediate values. + +### How would gas consumption of average applications increase under this proposal? + +#### Rough analysis from witness sizes + +We can look at [Alexey Akhunov's earlier work](https://medium.com/@akhounov/data-from-the-ethereum-stateless-prototype-8c69479c8abc) for data on average-case blocks. In summary, average blocks have witness sizes of ~1000 kB, of which ~750 kB is Merkle proofs and not code. Assuming a conservative 2000 bytes per Merkle branch this implies ~375 accesses per block (SLOADs have a similar gas-increase-to-bytes ratio so there's no need to analyze them separately). + +Data on [txs per day](https://etherscan.io/chart/tx) and [blocks per day](https://etherscan.io/chart/blocks) from Etherscan gives ~160 transactions per block (reference date: Jul 1), implying a large portion of those accesses are just the `tx.sender` and `tx.to` which are excluded from gas cost increases, though likely less than 320 due to duplicate addresses. + +Hence, this implies ~50-375 chargeable accesses per block, and each access suffers a gas cost increase of 1900; `50 * 1900 = 95000` and `375 * 1900 = 712500`, implying the gas limit would need to be raised by ~1-6% to compensate. However, this analysis may be complicated further in either direction by (i) accounts / storage keys being accessed in multiple transactions, which would appear once in the witness but twice in gas cost increases, and (ii) accounts / storage keys being accessed multiple times in the same transaction, which lead to gas cost _decreases_. + +#### Goerli analysis + +A more precise analysis can be found by scanning Goerli transactions, as done by Martin Swende here: https://github.com/holiman/gasreprice + +The conclusion is that on average gas costs increase by ~2.36%. One major contributing factor to reducing gas costs is that a large number of contracts inefficiently read the same storage slot multiple times, which leads to this EIP giving a few transactions gas cost _savings_ of over 10%. + +## Backwards Compatibility + +These gas cost increases may potentially break contracts that depend on fixed gas costs; see the security considerations section for details and arguments for why we expect the total risks to be low and how if desired they can be reduced further. + +## Test Cases + +Some test cases can be found here: https://gist.github.com/holiman/174548cad102096858583c6fbbb0649a + +Ideally we would test the following: + +* SLOAD the same storage slot {1, 2, 3} times +* CALL the same address {1, 2, 3} times +* (SLOAD | CALL) in a sub-call, then revert, then (SLOAD | CALL) the same (storage slot | address) again +* Sub-call, SLOAD, sub-call again, revert the inner sub-call, SLOAD the same storage slot +* SSTORE the same storage slot {1, 2, 3} times, using all combinations of zero/nonzero for original value and the value being set +* SSTORE then SLOAD the same storage slot +* `OP_1` then `OP_2` to the same address where `OP_1` and `OP_2` are all combinations of (`*CALL`, `EXT*`, `SELFDESTRUCT`) +* Try to `CALL` an address but with all possible failure modes (not enough gas, not enough ETH...), then (`CALL` | `EXT*`) that address again successfully + +## Implementation + +A WIP early-draft implementation for Geth can be found here: https://github.com/holiman/go-ethereum/tree/access_lists + +## Security Considerations + +As with any gas cost increasing EIP, there are three possible cases where it could cause applications to break: + +1. Fixed gas limits to sub-calls in contracts +2. Applications relying on contract calls that consume close to the full gas limit +3. The 2300 base limit given to the callee by ETH-transferring calls + +These risks have been studied before in the context of an earlier gas cost increase, EIP-1884. See [Martin Swende's earlier report](https://github.com/holiman/eip-1884-security) and [Hubert Ritzdorf's analysis](https://gist.github.com/ritzdorf/1c6bd72955391e831f8a397d3152b4e0/) focusing on (1) and (3). (2) has received less analysis, though one can argue that it is very unlikely both because applications tend to very rarely use close to the entire gas limit in a transaction, and because gas limits were very recently raised from 10 million to 12.5 million. EIP-1884 in practice [did lead to a small number of contracts breaking](https://www.coindesk.com/ethereums-istanbul-upgrade-will-break-680-smart-contracts-on-aragon) for this reason. + +There are two ways to look at these risks. First, we can note that as of today developers have had years of warning; gas cost increases on storage-accessing opcodes have been [discussed for a long time](https://ethereum-magicians.org/t/protocol-changes-to-bound-witness-size/3885), with multiple statements made including to major dapp developers around the likelihood of such changes. EIP-1884 itself provided an important wake-up call. Hence, we can argue that risks this time will be significantly lower than EIP-1884. + +### Contract breakage mitigations + +A second way to look at the risks is to explore mitigations. First of all, the existence of an `accessed_addresses` and `accessed_storage_keys` map (present in this EIP, absent in EIP-1884) already makes some cases recoverable: in any case where a contract A needs to send funds to some address B, where that address accepts funds from any source but leaves a storage-dependent log, one can recover by first sending a separate call to B to pull it into the cache, and then call A, knowing that the execution of B triggered by A will only charge 100 gas per SLOAD. This fact does not fix all situations, but it does reduce risks significantly. + +But there are ways to further expand the usability of this pattern. One possibility is to add a `POKE` precompile, which would take an address and a storage key as input and allow transactions that attempt to "rescue" stuck contracts by pre-poking all of the storage slots that they will access. This works even if the address only accepts transactions from the contract, and works in many other contexts with present gas limits. The only case where this will not work would be the case where a transaction call _must_ go from an EOA straight into a specific contract that then sub-calls another contract. + +Another option is [EIP-2930](./eip-2930.md), which would have a similar effect to `POKE` but is more general: it also works for the EOA -> contract -> contract case, and generally should work for all known cases of breakage due to gas cost increases. This option is more complex, though it is arguably a stepping stone toward access lists being used for other use cases (regenesis, account abstraction, SSA all demand access lists). + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2930.md b/EIPS/eip-2930.md new file mode 100644 index 0000000..c9db53a --- /dev/null +++ b/EIPS/eip-2930.md @@ -0,0 +1,140 @@ +--- +eip: 2930 +title: Optional access lists +author: Vitalik Buterin (@vbuterin), Martin Swende (@holiman) +discussions-to: https://ethereum-magicians.org/t/eip-2930-optional-access-lists/4561 +status: Final +type: Standards Track +category: Core +created: 2020-08-29 +requires: 2718, 2929 +--- + +## Simple Summary + +Adds a transaction type which contains an access list, a list of addresses and storage keys that the transaction plans to access. Accesses outside the list are possible, but become more expensive. + +## Abstract + +We introduce a new [EIP-2718](./eip-2718.md) transaction type, with the format `0x01 || rlp([chainId, nonce, gasPrice, gasLimit, to, value, data, accessList, signatureYParity, signatureR, signatureS])`. + +The `accessList` specifies a list of addresses and storage keys; these addresses and storage keys are added into the `accessed_addresses` and `accessed_storage_keys` global sets (introduced in [EIP-2929](./eip-2929.md)). A gas cost is charged, though at a discount relative to the cost of accessing outside the list. + +## Motivation + +This EIP serves two functions: + +1. Mitigates contract breakage risks introduced by [EIP-2929](./eip-2929.md), as transactions could pre-specify and pre-pay for the accounts and storage slots that the transaction plans to access; as a result, in the actual execution, the SLOAD and EXT* opcodes would only cost 100 gas: low enough that it would not only prevent breakage due to that EIP but also "unstuck" any contracts that became stuck due to EIP 1884. +2. Introduces the access list format and the logic for handling the format. This logic can later be repurposed for many other purposes, including block-wide witnesses, use in ReGenesis, moving toward static state access over time, and more. + +## Specification + +### Definitions + +**`TransactionType`** `1`. See [EIP-2718](./eip-2718.md) + +**`ChainId`** The transaction only valid on networks with this `chainID`. + +**`YParity`** The parity (0 for even, 1 for odd) of the y-value of a secp256k1 signature. + + +### Parameters + +| Constant | Value | +| - | - | +| `FORK_BLOCK` | 12244000 | +| `ACCESS_LIST_STORAGE_KEY_COST` | 1900 | +| `ACCESS_LIST_ADDRESS_COST` | 2400 | + +As of `FORK_BLOCK_NUMBER`, a new [EIP-2718](./eip-2718.md) transaction is introduced with `TransactionType` `1`. + +The [EIP-2718](./eip-2718.md) `TransactionPayload` for this transaction is `rlp([chainId, nonce, gasPrice, gasLimit, to, value, data, accessList, signatureYParity, signatureR, signatureS])`. + +The `signatureYParity, signatureR, signatureS` elements of this transaction represent a secp256k1 signature over `keccak256(0x01 || rlp([chainId, nonce, gasPrice, gasLimit, to, value, data, accessList]))`. + +The [EIP-2718](./eip-2718.md) `ReceiptPayload` for this transaction is `rlp([status, cumulativeGasUsed, logsBloom, logs])`. + +For the transaction to be valid, `accessList` must be of type `[[{20 bytes}, [{32 bytes}...]]...]`, where `...` means "zero or more of the thing to the left". For example, the following is a valid access list (all hex strings would in reality be in byte representation): + +``` +[ + [ + "0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae", + [ + "0x0000000000000000000000000000000000000000000000000000000000000003", + "0x0000000000000000000000000000000000000000000000000000000000000007" + ] + ], + [ + "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + [] + ] +] +``` + +At the beginning of execution (ie. at the same time as the `21000 + 4 * zeroes + 16 * nonzeroes` start gas is charged according to [EIP-2028](./eip-2028.md) rules), we charge additional gas for the access list: `ACCESS_LIST_ADDRESS_COST` gas per address and `ACCESS_LIST_STORAGE_KEY_COST` gas per storage key. For example, the above example would be charged `ACCESS_LIST_ADDRESS_COST * 2 + ACCESS_LIST_STORAGE_KEY_COST * 2` gas. + +Note that non-unique addresses and storage keys are not disallowed, though they will be charged for multiple times, and aside from the higher gas cost there is no other difference in execution flow or outcome from multiple-inclusion of a value as opposed to the recommended single-inclusion. + +The address and storage keys would be immediately loaded into the `accessed_addresses` and `accessed_storage_keys` global sets; this can be done using the following logic (which doubles as a specification-in-code of validation of the RLP-decoded access list) + +```python +def process_access_list(access_list) -> Tuple[List[Set[Address], Set[Pair[Address, Bytes32]]], int]: + accessed_addresses = set() + accessed_storage_keys = set() + gas_cost = 0 + assert isinstance(access_list, list) + for item in access_list: + assert isinstance(item, list) and len(item) == 2 + # Validate and add the address + address = item[0] + assert isinstance(address, bytes) and len(address) == 20 + accessed_addresses.add(address) + gas_cost += ACCESS_LIST_ADDRESS_COST + # Validate and add the storage keys + assert isinstance(item[1], list) + for key in item[1]: + assert isinstance(key, bytes) and len(key) == 32 + accessed_storage_keys.add((address, key)) + gas_cost += ACCESS_LIST_STORAGE_KEY_COST + return ( + accessed_addresses, + accessed_storage_keys, + gas_cost + ) +``` + +The access list is NOT charged per-byte fees like tx data is; the per-item costs described above are meant to cover the bandwidth costs of the access list data in addition to the costs of accessing those accounts and storage keys when evaluating the transaction. + +## Rationale + +### Charging less for accesses in the access list + +This is done to encourage transactions to use the access list as much as possible, and because processing transactions is easier when their storage reads are predictable (because clients can pre-load the data from databases and/or ask for witnesses at the time the transaction is received, or at least load the data in parallel). + +### Allowing duplicates + +This is done because it maximizes simplicity, avoiding questions of what to prevent duplication against: just between two addresses/keys in the access list, between the access list and the tx sender/recipient/newly created contract, other restrictions? Because gas is charged per item, there is no gain and only cost in including a value in the access list twice, so this should not lead to extra chain bloat in practice. + +### Signature signs over the transaction type as well as the transaction data + +This is done to ensure that the transaction cannot be "re-interpreted" as a transaction of a different type. + +## Backwards Compatibility + +This EIP does make it more gas-expensive to perform "unexpected" SLOADs and account accesses. Because gas is prepaid and so does not affect fixed-gas local calls, it does not break contracts in the way that previous gas cost increases would risk. However, it does make applications that heavily rely on storage access much less economically viable. + +## Security Considerations + +### Access list generation + +Access lists are difficult to construct in real-time in many situations, and this is exacerbated in environments where there is a high time lag between transaction generation and signing or simplicity of the transaction generator is highly valued (eg. either or both may apply in hardware wallets). + +However, this EIP proposes only a 10% initial discount to access lists, so there is almost no cost to not bothering with access list generation and only making a simple transaction. The cost of accessing state outside the access list is expected to be ramped up in future hard forks over time as tools are developed and access list generation becomes more mature. + +### Transaction size bloating + +Average block size will increase as a result of access lists being used. However, the per-byte cost of access lists is `1900 / 32 = 59.375` for storage keys and `2400 / 20 = 120` for addresses, making it much more expensive than calldata; hence, worst-case block size will not increase. Additionally, increases in average block size will be partially compensated for by the ability to pre-fetch storage at time of receiving a transaction and/or load storage in parallel upon receiving a block. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2935.md b/EIPS/eip-2935.md new file mode 100644 index 0000000..bbe12f2 --- /dev/null +++ b/EIPS/eip-2935.md @@ -0,0 +1,63 @@ +--- +eip: 2935 +title: Save historical block hashes in state +author: Vitalik Buterin (@vbuterin), Tomasz Stanczak (@tkstanczak) +discussions-to: https://ethereum-magicians.org/t/eip-2935-save-historical-block-hashes-in-state/4565 +status: Stagnant +type: Standards Track +category: Core +created: 2020-09-03 +--- + +## Simple Summary + +Store historical block hashes in a contract, and modify the `BLOCKHASH (0x40)` opcode to read this contract. + +## Motivation + +There is increasingly a desire to remove the need for most clients to store history older than some relatively short duration (often between 1 week and 1 year) to save disk space. This requires some form of layer-2 network to help clients access historical information. These protocols can be made much simpler if blocks contained a quick Merkle path to historical blocks. + +Additional secondary motivations include: + +* The protocol can be used to make more secure efficient light clients with flyclient-like technology (while the "optimal" flyclient protocol is fairly complex, large security gains over the status quo (trusted "canonical hash trees") can be made cheaply) +* Improving cleanness of the protocol, as the BLOCKHASH opcode would then access state and not history. + +## Specification + +| Parameter | Value | +| - | - | +| `FORK_BLKNUM` | TBD | +| `HISTORY_STORAGE_ADDRESS` | `0xfffffffffffffffffffffffffffffffffffffffe`| + +At the start of processing any block where `block.number > FORK_BLKNUM` (ie. before processing any transactions), run `sstore(HISTORY_STORAGE_ADDRESS, block.number - 1, block.prevhash)`. + +When `block.number > FORK_BLKNUM + 256`, change the logic of the `BLOCKHASH` opcode as follows: if `FORK_BLKNUM <= arg < block.number`, return `sload(HISTORY_STORAGE_ADDRESS, arg)`. Otherwise return 0. + +## Rationale + +Very similar ideas were proposed before in EIP-98 and EIP-210. This EIP is a simplification, removing two sources of needless complexity: + +1. Having a tree-like structure with multiple layers as opposed to a single list +2. Writing the EIP in EVM code + +The former was intended to save space. Since then, however, storage usage has increased massively, to the point where even eg. 5 million new storage slots are fairly negligible compared to existing usage. The latter was intended as a first step toward "writing the Ethereum protocol in EVM" as much as possible, but this goal has since been de-facto abandoned. + +## Backwards Compatibility + +The range of `BLOCKHASH` is increased by this opcode, but behavior within the previous 256-block range remains unchanged. + +## Test Cases + +TBD + +## Implementation + +TBD + +## Security Considerations + +Adding ~2.5 million storage slots per year bloats the state somewhat (but not much relative to the hundreds of millions of existing state objects). However, this EIP is not intended to be permanent; when eth1 is merged into eth2, the BLOCKHASH opcode would likely be repurposed to use eth2's built-in history accumulator structure (see [phase 0 spec](https://github.com/ethereum/annotated-spec/blob/master/phase0/beacon-chain.md#slots_per_historical_root)). + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2936.md b/EIPS/eip-2936.md new file mode 100644 index 0000000..daad2d0 --- /dev/null +++ b/EIPS/eip-2936.md @@ -0,0 +1,60 @@ +--- +eip: 2936 +title: EXTCLEAR Opcode For SELFDESTRUCTed contracts +author: William Morriss (@wjmelements) +discussions-to: https://ethereum-magicians.org/t/eip-2936-extclear-for-selfdestruct/4569 +status: Stagnant +type: Standards Track +category: Core +created: 2020-09-03 +--- + +## Simple Summary +Enable new opcode to clear storage for `SELFDESTRUCTED`ed contracts. + +## Abstract +Changes `SELFDESTRUCT` (`0xff`) to not clear any storage and adds a new `EXTCLEAR` (`0x5c`) opcode that will clear a specific storage slot for a contract that has previously been self destructed. + +## Motivation +`SELFDESTRUCT` (`0xFF`) is unnecessarily complex because it clears an unbounded amount of contract storage. +It is computationally expensive for nodes to track all of the storage used in every contract in case the contract `SELFDESTRUCT`s. +Further, contracts can be re-initialized using `CREATE2` (`0xF5`), and then `SLOAD` (`0x54`) prior storage. +Therefore, several ethereum clients do not clear storage at all, and just check if the contract was initiated since `SSTORE` (`0x55`) during `SLOAD`. +`CREATE2` was not intended to complicate `SLOAD`, and this change reverts that complexity. +Also, bugs in this implementation could split the network. + +Instead this defers the time of storage cleanup, and leaves the storage in-place, which reduces the complexity of `SLOAD` and `SELFDESTRUCT`. + +This empowers the `CREATE2` reincarnation proxy pattern by retaining storage during upgrade, which would otherwise have to be reset again. +An atomic reincarnation upgrade could clear a subset of storage during the upgrade, while the contract is destroyed, before reinstating it. + +## Specification + +After `FORK_BLOCK_NUM`, a new opcode, `EXTCLEAR`, is enabled at `0x5C` to clear storage for `SELFDESTRUCT`ed contracts. +`EXTCLEAR`: +* does not push any words onto the stack +* pops two words off the stack: the destroyed contract address and a storage address +* if the contract exists, charge the same gas cost as `EXTCODEHASH` (`0x3F`) +* otherwise, if the storage is zero, charge the same gas as `EXTCODEHASH` plus `SLOAD` +* otherwise, the destroyed contract's slot is reset to 0, charging the same gas as `EXTCODEHASH` and `SSTORE` when resetting storage, while also refunding the amount specified in `SSTORE`. + +`SELFDESTRUCT` is modified to not clear contract storage. +This change also works retroactively: all prior destroyed contracts can be cleaned up. + +## Rationale +`0x5C` is available in the same range as `SSTORE` and `SLOAD`. + +## Backwards Compatibility +A reincarnation upgrade mechanism that expects all internal storage to be cleared might break, but such an upgrade mechanism would allow adaptation to this new behavior. + +## Test Cases +TODO + +## Implementation +Implementation is required on all major clients to add the opcode. + +## Security Considerations +A reincarnated contract that does not expect its state to be cleared by malicious actors SHOULD reinitialize itself to avoid antagonistic `EXTCLEAR`. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2937.md b/EIPS/eip-2937.md new file mode 100644 index 0000000..4122d7c --- /dev/null +++ b/EIPS/eip-2937.md @@ -0,0 +1,48 @@ +--- +eip: 2937 +title: SET_INDESTRUCTIBLE opcode +author: Vitalik Buterin (@vbuterin) +discussions-to: https://ethereum-magicians.org/t/eip-2937-set-indestructible/4571 +status: Stagnant +type: Standards Track +category: Core +created: 2020-09-04 +--- + +## Simple Summary + +Add a `SET_INDESTRUCTIBLE (0xA8)` opcode that prevents the contract from calling `SELFDESTRUCT (0xFF)`. + +## Abstract + +## Motivation + +The intended use case would be for contracts to make their first byte of code be the `SET_INDESTRUCTIBLE` opcode if they wish to serve as libraries that guarantee to users that their code will exist unmodified forever. This is useful in account abstraction as well as other contexts. + +Unlike EIPs that disable the `SELFDESTRUCT` opcode entirely, this EIP does not modify behavior of any existing contracts. + +## Specification + +Add a transaction-wide global variable `globals.indestructible: Set[Address]` (i.e. a variable that operates the same way as the selfdestructs set), initialized to the empty set. + +Add a `SET_INDESTRUCTIBLE` opcode at `0xA8`, with gas cost `G_base`, that adds the current `callee` to the `globals.indestructible` set. If in the current execution context the `callee` is in `globals.indestructible`, the `SELFDESTRUCT` opcode throws an exception. + +## Rationale + +Alternative proposals to this include: + +* Simply banning `SELFDESTRUCT` outright. This would be ideal, but has larger backwards compatibility issues. +* Using a local variable instead of a global variable. This is problematic because it would be broken by `DELEGATECALL`. + +## Backwards Compatibility + +TBD + +## Security Considerations + +This breaks forward compatibility with _some_ forms of state rent, which would simply delete contracts that get too old without paying some maintenance fee. However, this is not the case with all state size control schemes; for example this is not an issue if we use [ReGenesis](https://ledgerwatch.github.io/regenesis_plan.html). + +If `SELFDESTRUCT` is ever removed in the future, this EIP would simply become a no-op. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2938.md b/EIPS/eip-2938.md new file mode 100644 index 0000000..2d208f4 --- /dev/null +++ b/EIPS/eip-2938.md @@ -0,0 +1,287 @@ +--- +eip: 2938 +title: Account Abstraction +author: Vitalik Buterin (@vbuterin), Ansgar Dietrichs (@adietrichs), Matt Garnett (@lightclient), Will Villanueva (@villanuevawill), Sam Wilson (@SamWilsn) +discussions-to: https://ethereum-magicians.org/t/eip-2938-account-abstraction/4630 +status: Stagnant +type: Standards Track +category: Core +created: 2020-09-04 +requires: 2718 +--- + +## Simple Summary + +Account abstraction (AA) allows a contract to be the top-level account that pays fees and starts transaction execution. + +## Abstract + +**See also: [https://ethereum-magicians.org/t/implementing-account-abstraction-as-part-of-eth1-x/4020](https://ethereum-magicians.org/t/implementing-account-abstraction-as-part-of-eth1-x/4020) and the links therein for historical work and motivation.** + +Transaction validity, as of Muir Glacier, is defined rigidly by the protocol: ECDSA signature, a simple nonce, and account balance. Account abstraction extends the validity conditions of transactions with the execution of arbitrary EVM bytecode (with some limits on what state may be accessed.) To signal validity, we propose a new EVM opcode `PAYGAS`, which also sets the gas price and gas limit the contract is willing to pay. + +We split account abstraction into two tiers: **single-tenant AA**, which is intended to support wallets or other use cases with few participants, and **multi-tenant AA**, which is intended to support applications with many participants (eg. tornado.cash, Uniswap). + +## Motivation + +The existing limitations preclude innovation in a number of important areas, particularly: + +1. Smart contract wallets that use signature verification other than ECDSA (eg. Schnorr, BLS, post-quantum...) +2. Smart contract wallets that include features such as multisig verification or social recovery, reducing the highly prevalent risk of funds being lost or stolen +3. Privacy-preserving systems like [tornado.cash](http://tornado.cash) +4. Attempts to improve gas efficiency of DeFi protocols by preventing transactions that don't satisfy high-level conditions (eg. existence of a matching order) from being included on chain +5. Users being able to pay for transaction fees in a token other than ETH (eg. by converting that token into the ETH needed for fees inside the transaction in real-time) + +Most of the above use cases are currently possible using intermediaries, most notably the [Gas Station Network](https://www.opengsn.org/) and application-specific alternatives. These implementations are (i) technically inefficient, due to the extra 21000 gas to pay for the relayer, (ii) economically inefficient, as relayers need to make a profit on top of the gas fees that they pay. Additionally, use of intermediary protocols means that these applications cannot simply rely on base Ethereum infrastructure and need to rely on extra protocols that have smaller userbases and higher risk of no longer being available at some future date. + +Out of the five use cases above, single-tenant AA approximately supports (1) and (2), and multi-tenant AA approximately supports (3) and (4). We discuss the differences between the two tiers in the specification and rationale sections below. + +## Specification + +### Single Tenant + +After `FORK_BLOCK`, the following changes will be recognized by the protocol. + +#### Constants + +| Constant | Value | +| - | - | +| **`AA_ENTRY_POINT`** | `0xffffffffffffffffffffffffffffffffffffffff` | +| **`AA_TX_TYPE`** | `2` | +| **`FORK_BLOCK`** | TBD | +| **`AA_BASE_GAS_COST`** | 15000 | + +#### New Transaction Type + +A new [EIP-2718](./eip-2718.md) transaction with type `AA_TX_TYPE` is introduced. Transactions of this type are referred to as "AA transactions". Their payload should be interpreted as `rlp([nonce, target, data])`. + +The base gas cost of this transaction is set to `AA_BASE_GAS_COST` instead of 21000 to reflect the lack of "intrinsic" ECDSA and signature. + +Nonces are processed analogously to existing transactions (check `tx.nonce == tx.target.nonce`, transaction is invalid if this fails, otherwise proceed and immediately set `tx.nonce += 1`). + +Note that this transaction type has no intrinsic gas limit; when beginning execution, the gas limit is simply set to the remaining gas in the block (ie. `block.gas_limit` minus gas spent on previous transactions), and the `PAYGAS` opcode (see below) can adjust the gas limit downwards. + +#### Transaction-wide global variables + +Introduce some new transaction-wide global variables. These variables work similarly (in particular, have similar reversion logic) to the SSTORE refunds counter. + +| Variable | Type | Initial value | +| - | - | - | +| `globals.transaction_fee_paid` | `bool` | `False if type(tx) == AA_TX_TYPE else True` | +| `globals.gas_price` | `int` | `0 if type(tx) == AA_TX_TYPE else tx.gas_price` | +| `globals.gas_limit` | `int` | `0 if type(tx) == AA_TX_TYPE else tx.gas_limit` | + +#### `NONCE (0x48)` Opcode + +A new opcode `NONCE (0x48)` is introduced, with gas cost `G_base`, which pushes the `nonce` of the callee onto the stack. + +#### `PAYGAS (0x49)` Opcode + +A new opcode `PAYGAS (0x49)` is introduced, with gas cost `G_base`. It takes two arguments off the stack: (top) `version_number`, (second from top) `memory_start`. In the initial implementation, it will `assert version_number == 0` and read: + +* `gas_price = bytes_to_int(vm.memory[memory_start: memory_start + 32])` +* `gas_limit = bytes_to_int(vm.memory[memory_start + 32: memory_start + 64])` + +Both reads use similar mechanics to MLOAD and CALL, so memory expands if needed. + +Future hard forks may add support for different version numbers, in which case the opcode may take different-sized memory slices and interpret them differently. Two particular potential use cases are [EIP 1559](https://notes.ethereum.org/@vbuterin/BkSQmQTS8) and [the escalator mechanism](https://ethresear.ch/t/another-simple-gas-fee-model-the-escalator-algorithm-from-the-agoric-papers/6399). + +The opcode works as follows. If all three of the following conditions (in addition to the version number check above) are satisfied: + +1. The account's balance is `>= gas_price * gas_limit` +2. `globals.transaction_fee_paid == False` +3. We are in a top-level AA execution frame (ie. if the currently running EVM execution exits or reverts, the EVM execution of the entire transaction is finished) + +Then do the following: + +* Subtract `gas_price * gas_limit` from the contract's balance +* Set `globals.transaction_fee_paid` to `True` +* Set `globals.gas_price` to `gas_price`, and `globals.gas_limit` to `gas_limit` +* Set the remaining gas in the current execution context to equal `gas_limit` minus the gas that was already consumed + +If any of the above three conditions are not satisfied, throw an exception. + +At the end of execution of an AA transaction, it is mandatory that `globals.transaction_fee_paid == True`; if it is not, then the transaction is invalid. At the end of execution, the contract is refunded `globals.gas_price * remaining_gas` for any remaining gas, and `(globals.gas_limit - remaining_gas) * globals.gas_price` is transferred to the miner. + +`PAYGAS` also serves as an EVM execution _checkpoint_: if the top-level execution frame reverts after `PAYGAS` has been called, then the execution only reverts up to the point right after `PAYGAS` was called, and exits there. In that case, the contract receives no refund, and `globals.gas_limit * globals.gas_price` is transferred to the miner. + +#### Replay Protection + +One of the two following approaches must be implemented to safeguard against replay attacks. + +##### Require `SET_INDESTRUCTIBLE` + +Require that contracts targeted by AA transactions begin with [EIP-2937]'s `SET_INDESTRUCTIBLE` opcode. AA transactions targeting contracts that do not begin with `SET_INDESTRUCTIBLE` are invalid, and cannot be included in blocks. + +`AA_PREFIX` would need to be modified to include this opcode. + +[EIP-2937]: ./eip-2937.md + +##### Preserve Nonce on `SELFDESTRUCT` + +The other option is to preserve contract nonces across `SELFDESTRUCT` invocations, instead of setting the nonce to zero. + +#### Miscellaneous + +* If `CALLER (0x33)` is invoked in the first frame of + execution of a call initiated by an AA transaction, then it must return `AA_ENTRY_POINT`. +* If `ORIGIN (0x32)` is invoked in any frame of execution of an AA + transaction it must return `AA_ENTRY_POINT`. +* The `GASPRICE (0x3A)` opcode now pushes the value `globals.gas_price` + +Note that the new definition of `GASPRICE` does not lead to any changes in behavior in non-AA transactions, because `globals.gas_price` is initialized to `tx.gas_price` and cannot be changed as `PAYGAS` cannot be called. + +#### Mining and Rebroadcasting Strategies + +Much of the complexity in account abstraction originates from the strategies used by miners and validating nodes to determine whether or not to accept and rebroadcast transactions. Miners need to determine if a transaction will actually pay the fee if they include it after only a small amount of processing to avoid [DoS attacks](https://hackingdistributed.com/2016/06/28/ethereum-soft-fork-dos-vector/). Validating nodes need to perform an essentially identical verification to determine whether or not to rebroadcast the transaction. + +By keeping the consensus changes minimal, this EIP allows for gradual introduction of AA mempool support by miners and validating nodes. Initial support would be focused on enabling simple, single-tenant use cases, while later steps would additionally allow for more complex, multi-tenant use cases. Earlier stages are deliberately more fully fleshed-out than later stages, as there is still more time before later stages need to be implemented. + +##### Transactions with Fixed Nonces + +| Constant | Value | +| - | - | +| `VERIFICATION_GAS_MULTIPLIER` | `6` | +| `VERIFICATION_GAS_CAP` | `= VERIFICATION_GAS_MULTIPLIER * AA_BASE_GAS_COST = 90000` | +| `AA_PREFIX` | `if(msg.sender != shr(-1, 12)) { LOG1(msg.sender, msg.value); return }`; compilation to EVM TBD | + +When a node receives an AA transaction, they process it (i.e. attempt to execute it against the current chain head's post-state) to determine its validity, continuing to execute until one of several events happens: + +* If the code of the `target` is NOT prefixed with `AA_PREFIX`, exit with failure +* If the execution hits any of the following, exit with failure: + * An environment opcode (`BLOCKHASH`, `COINBASE`, `TIMESTAMP`, `NUMBER`, `DIFFICULTY`, `GASLIMIT`) + * `BALANCE` (of any account, including the `target` itself) + * An external call/create that changes the `callee` to anything but the `target` or a precompile (`CALL`, `CALLCODE`, `STATICCALL`, `CREATE`, `CREATE2`). + * An external state access that reads code (`EXTCODESIZE`, `EXTCODEHASH`, `EXTCODECOPY`, but also `CALLCODE` and `DELEGATECALL`), unless the address of the code that is read is the `target`. +* If the execution consumes more gas than `VERIFICATION_GAS_CAP` (specified above), or more gas than is available in the block, exit with failure +* If the execution reaches `PAYGAS`, then exit with success or failure depending on whether or not the balance is sufficient (e.g. `balance >= gas_price * gas_limit`). + +Nodes do not keep transactions with nonces higher than the current valid nonce in the mempool. If the mempool already contains a transaction with a currently valid nonce, another incoming transaction to the same contract and with the same nonce either replaces the existing one (if its gas price is sufficiently higher) or is dropped. Thus, the mempool keeps only at most one pending transaction per account. + +While processing a new block, take note of which accounts were the `target` of an AA transaction (each block currently has `12500000` gas and an AA transaction costs `>= 15000` so there would be at most `12500000 // 15000 = 833` targeted accounts). Drop all pending transactions targeting those accounts. All other transactions remain in the mempool. + +### Single Tenant+ + +If the [indestructible contracts EIP](http://github.com/ethereum/EIPs/pull/2937) is added, Single Tenant AA can be adapted to allow for `DELEGATECALL` during transaction verification: during execution of a new AA transaction, external state access that reads code (`EXTCODESIZE`, `EXTCODEHASH`, `EXTCODECOPY`, `CALLCODE`, `DELEGATECALL`) of any contract whose first byte is the `SET_INDESTRUCTIBLE` opcode is no longer banned. However, calls to anything but the `target` or a precompile that change the `callee` (i.e., calls other than `CALLCODE` and `DELEGATECALL`) are still not permitted. + +If the [IS_STATIC EIP](http://github.com/ethereum/EIPs/pull/2975) is added, the list of allowed prefixes can be extended to allow a prefix that enables incoming static calls but not state-changing calls. + +The list of allowed prefixes can also be extended to enable other benign use cases (eg. logging incoming payments). + +External calls _into_ AA accounts can be allowed as follows. We can add an opcode `RESERVE_GAS`, which takes as argument a value `N` and has simple behavior: immediately burn `N` gas and add `N` gas to the refund. We then add an allowed `AA_PREFIX` that reserves `>= AA_BASE_GAS_COST * 2` gas. This ensures that at least `AA_BASE_GAS_COST` gas must be spent (as refunds can refund max 50% of total consumption) in order to call into an account and invalidate transactions targeting that account in the mempool, preserving that invariant. + +Note that accounts may also opt to set a higher `RESERVE_GAS` value in order to safely have a higher `VERIFICATION_GAS_CAP`; the goal would be to preserve a `VERIFICATION_GAS_MULTIPLIER`-to-1 ratio between the minimum gas cost to edit an account (ie. half its `RESERVE_GAS`) and the `VERIFICATION_GAS_CAP` that is permitted that account. This would also preserve invariants around maximum reverification gas consumption that are implied by the previous section. + +### Multi-Tenant & Beyond + +In a later stage, we can add support for multiple pending transactions per account in the mempool. The main challenge here is that a single transaction can potentially cause state changes that invalidate all other transactions to that same account. Additionally, if we naively prioritize transactions by gasprice, there is an attack vector where the user willing to pay the highest gasprice publishes many (mutually exclusive) versions of their transaction with small alterations, thereby pushing everyone else's transactions out of the mempool. + +Here is a sketch of a strategy for mitigating this problem. We would require incoming transactions to contain an [EIP-2930](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2930.md)-style access list detailing the storage slots that the transaction reads or modifies, and make it binding; that is, accesses outside the access list would be invalid. A transaction would only be included in the mempool if its access list is disjoint from the access lists of other transactions in the mempool (or if its gasprice is higher). An alternative way to think about this is to have per-storage-slot mempools instead of just per-account mempools, except a transaction could be part of multiple per-storage-slot mempools (if desired it could be capped to eg. 5 storage slots). + +Note also that multi-tenant AA will almost certainly require allowing miners to edit the nonces of incoming transactions to put them into sequence, with the result that the final hash of a transaction is unpredictable at publication time. Clients will need to explicitly work around this. + +More research is required to refine these ideas, and this is left for later work. + +## Rationale + +The core problem in an account abstraction setup is always that miners and network nodes need to be able to verify that a transaction that they attempt to include, or rebroadcast, will actually pay a fee. Currently, this is fairly simple, because a transaction is guaranteed to be includable and pay a fee as long as the signature and nonce are valid and the balance and gasprice are sufficient. These checks can be done quickly. + +In an account abstraction setup, the goal is to allow accounts to specify EVM code that can establish more flexible conditions for a transaction's validity, but with the requirement that this EVM code can be quickly verified, with the same safety properties as the existing setup. + +In a normal transaction, the top-level call goes from the `tx.sender` to `tx.to` and carries with it `tx.value`. In an AA transaction, the top-level call goes from the _entry point address_ (`0xFFFF...FF`) to the `tx.target`. + +The top-level code execution is expected to be split into two phases: the shorter **verification phase** (before `PAYGAS`) and the longer **execution phase** (after `PAYGAS`). If execution throws an exception during the verification phase, the transaction is invalid, much like a transaction with an invalid signature in the current system. If execution throws an exception after the verification phase, the transaction pays fees, and so the miner can still include it. + +The transition between different stages of AA is entirely done through changes in miner strategy. The first stage supports **single-tenant AA**, where the only use cases that can be easily implemented are where the `tx.target` is a contract representing a user account (that is, a smart contract wallet, eg. multisig). Later stages improve support for eg. logs and libraries, and also move toward supporting **multi-tenant AA**, where the goal is to try to support cases where the `tx.target` represents an _application_ that processes incoming activity from multiple users. + +### Nonces still enshrined in single-tenant AA + +Nonces are still enforced in single-tenant AA to ensure that single-target AA does not break the invariant that each transaction (and hence each transaction hash) can only be included in the chain once. While there is some limited value in allowing arbitrary-order transaction inclusion in single-tenant AA, there is not enough value to justify breaking that invariant. + +Note that nonces in AA accounts do end up having a dual-purpose: they are both there for replay protection and for contract address generation when using the `CREATE` opcode. This does mean that a single transaction could increment the nonce by more than 1. This is deemed acceptable, as the other mechanics introduced by AA already break the ability to easily verify that a chain longer than one transaction can be processed. However, we strongly recommend that AA contracts use `CREATE2` instead of `CREATE`. + +In multi-tenant AA, as mentioned above, nonces are expected to become malleable and applications that use multi-tenant AA systems would need to manage this. + +### Nonces are exposed to the EVM + +This is done to allow signature checking done in validation code to validate the nonce. + +### Replay Protection + +One of the above two approaches (requiring `SET_INDESTRUCTIBLE` or modifying `SELFDESTRUCT` behavior) must be implemented so that nonces cannot be reused. It must be a consensus change, and not simply part of `AA_PREFIX`, so that transaction hash uniqueness is maintained. + +### Miners refuse transactions that access external data or the target's own balance, before PAYGAS + +An important property of traditional transactions is that activity happening as part of transactions that originate outside of some given account X cannot make transactions whose sender is X invalid. The only state change that an outside transaction can impose on X is increasing its balance, which cannot invalidate a transaction. + +Allowing AA contracts to access external data (both other accounts and environment variables such as GASPRICE, DIFFICULTY, etc.) before they call `PAYGAS` (ie. during the verification phase) breaks this invariant. For example, imagine someone sends many thousands of AA transactions that perform an external call `if FOO.get_number() != 5: throw()`. `FOO.number` might be set to `5` when those transactions are all sent, but a single transaction to `FOO` could set the `number` to something else, invalidating _all of the thousands of AA transactions_ that depend on it. This would be a serious DoS vector. + +The one allowed exception is contracts that are indestructible (that is, whose first byte is the `SET_INDESTRUCTIBLE` opcode defined in [this EIP](https://hackmd.io/@HWeNw8hNRimMm2m2GH56Cw/SyNT3Cdmw)). This is a safe exception, because the data that is being read cannot be changed. + +Disallowing reading `BALANCE` blocks a milder attack vector: an attacker could force a transaction to be reprocessed at a mere cost of 6700 gas (not 15000 or 21000), in the worst case more than doubling the number of transactions that would need to be reprocessed. + +In the long term, AA could be expanded to allow reading external data, though protections such as mandatory access lists would be required. + +### AA transactions must call contracts with prefix + +The prelude is used to ensure that *only* AA transactions can call the contract. This is another measure taken to ensure the invariant described above. If this check did not occur, it would be possible for a transaction originating outside some AA account X to call into X and make a storage change, forcing transactions targeting that account to be reprocessed at the cost of a mere 5000 gas. + +### Multi-tenant AA + +Multi-tenant AA extends single-tenant AA by **better handling cases where distinct and uncoordinated users attempt to send transactions for/to the same account and those transactions may interfere with each other**. + +We can understand the value of multi-tenant AA by examining two example use cases: (i) [tornado.cash](http://tornado.cash) and (ii) [Uniswap](http://uniswap.exchange). In both of these cases, there is a single central contract that represents the application, and not any specific user. Nevertheless, there is important value in using abstraction to do application-specific validation of transactions. + +#### Tornado Cash + +The tornado.cash workflow is as follows: + +1. A user sends a transaction to the TC contract, depositing some standard quantity of coins (eg. 1 ETH). A record of their deposit, containing the hash of a secret known by the user, is added to a Merkle tree whose root is stored in the TC contract. +2. When that user later wants to withdraw, they generate and send a ZK-SNARK proving that they know a secret whose hash is in a leaf somewhere in the deposit tree (without revealing where). The TC contract verifies the ZK-SNARK, and also verifies that a nullifier value (also derivable from the secret) has not yet been spent. The contract sends 1 ETH to the user's desired address, and saves a record that the user's nullifier has been spent. + +The privacy provided by TC arises because when a user makes a withdrawal, they can prove that it came from _some_ unique deposit, but no one other than the user knows which deposit it came from. However, implementing TC naively has a fatal flaw: the user usually does not yet have ETH in their withdrawal address, and if the user uses their deposit address to pay for gas, that creates an on-chain link between their deposit address and their withdrawal address. + +Currently, this is solved via relayers; a third-party relayer verifies the ZK-SNARK and unspent status of the nullifier, publishes the transaction using their own ETH to pay for gas, and collects the fee back from the user from the TC contract. + +AA allows this without relayers: the user could simply send an AA transaction targeting the TC contract, the ZK-SNARK verification and the nullifier checking can be done in the verification step, and PAYGAS can be called directly after that. This allows the withdrawer to pay for gas directly out of the coins going to their withdrawal address, avoiding the need for relayers or for an on-chain link to their deposit address. + +Note that fully implementing this functionality requires AA to be structured in a way that supports multiple users sending withdrawals at the same time (requiring nonces would make this difficult), and that allows a single account to support both AA transactions (the withdrawals) and externally-initiated calls (the deposits). + +#### Uniswap + +A new version of Uniswap could be built that allows transactions to be sent that directly target the Uniswap contract. Users could deposit tokens into Uniswap ahead of time, and Uniswap would store their balances as well as a public key that transactions spending those balances could be verified against. An AA-initiated Uniswap trade would only be able to spend these internal balances. + +This would be useless for normal traders, as normal traders have their coins outside the Uniswap contract, but it would be a powerful boon to arbitrageurs. Arbitrageurs would deposit their coins into Uniswap, and they would be able to send transactions that perform arbitrage every time external market conditions change, and logic such as price limits could be enforced during the verification step. Hence, transactions that do not get in (eg. because some other arbitrageur made the trade first) would not be included on-chain, allowing arbitrageurs to not pay gas, and reducing the number of "junk" transactions that get included on-chain. This could significantly increase both de-facto blockchain scalability as well as market efficiency, as arbitrageurs would be able to much more finely correct for cross-exchange discrepancies between prices. + +Note that here also, Uniswap would need to support both AA transactions and externally-initiated calls. + +## Backwards Compatibility + +This AA implementation preserves the existing transaction type. The use of `assert origin == caller` to verify that an account is an EOA remains sound, but is not extensible to AA accounts; AA transactions will always have `origin == AA_ENTRY_POINT`. + +Badly-designed single-tenant AA contracts will break the transaction non-malleability invariant. That is, it is possible to take an AA transaction in-flight, modify it, and have the modified version still be valid; AA account contracts can be designed in such a way as to make that not possible, but it is their responsibility. Multi-tenant AA will break the transaction non-malleability invariant much more thoroughly, making the transaction hash unpredictable even for legitimate applications that use the multi-tenant AA features (though the invariant will not further break for applications that existed before then). + +AA contracts may not have replay protection unless they build it in explicitly; this can be done with the `CHAINID (0x46)` opcode introduced in [EIP 1344](./eip-1344.md). + +## Test Cases +See: [https://github.com/quilt/tests/tree/account-abstraction](https://github.com/quilt/tests/tree/account-abstraction) + +## Implementation +See: [https://github.com/quilt/go-ethereum/tree/account-abstraction](https://github.com/quilt/go-ethereum/tree/account-abstraction) + +## Security Considerations + +See [https://ethresear.ch/t/dos-vectors-in-account-abstraction-aa-or-validation-generalization-a-case-study-in-geth/7937](https://ethresear.ch/t/dos-vectors-in-account-abstraction-aa-or-validation-generalization-a-case-study-in-geth/7937) for an analysis of DoS issues. + +### Re-validation + +When a transaction enters the mempool, the client is able to quickly ascertain whether the transaction is valid. Once it determines this, it can be confident that the transaction will continue to be valid unless a transaction from the same account invalidates it. + +There are, however, cases where an attacker can publish a transaction that invalidates existing transactions and requires the network to perform more recomputation than the computation in the transaction itself. The EIP maintains the invariant that recomputation is bounded to a theoretical maximum of six times the block gas limit in a single block; this is somewhat more expensive than before, but not that much more expensive. + +#### Peer denial-of-service + +Denial-of-Service attacks are difficult to defend against, due to the difficulty in identifying sybils within a peer list. At any moment, one may decide (or be bribed) to initiate an attack. This is not a problem that Account Abstraction introduces. It can be accomplished against existing clients today by inundating a target with transactions whose signatures are invalid. However, due to the increased allotment of validation work allowed by AA, it's important to bound the amount of computation an adversary can force a client to expend with invalid transactions. For this reason, it's best for the miner to follow the recommended mining strategies. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2942.md b/EIPS/eip-2942.md new file mode 100644 index 0000000..ea2ec7e --- /dev/null +++ b/EIPS/eip-2942.md @@ -0,0 +1,66 @@ +--- +eip: 2942 +title: EthPM URI Specification +author: Nick Gheorghita (@njgheorghita), Piper Merriam (@pipermerriam), g. nicholas d'andrea (@gnidan), Benjamin Hauser (@iamdefinitelyahuman) +discussions-to: https://ethereum-magicians.org/t/ethpm-v3-specification-working-group/4086/7 +status: Stagnant +type: Standards Track +category: ERC +created: 2020-09-04 +requires: 2678 +--- + +## Simple Summary +A custom URI scheme to identify an EthPM registry, package, release, or specific contract asset within a release. + +## Abstract +When interacting with the EthPM ecosystem, users and tooling can benefit from a URI scheme to identify EthPM assets. Being able to specify a package, registry, or release with a single string makes simplifies the steps required to install, publish, or distribute EthPM packages. + +## Specification +`scheme://registry_address[:chain_id][/package_name[@package_version[/json_pointer]]]` + +#### `scheme` +- Required +- Must be one of `ethpm` or `erc1319`. If future versions of the EthPM registry standard are designed and published via the ERC process, those ERCs will also be valid schemes. + +#### `registry_address` +- Required +- This **SHOULD** be either an ENS name or a 0x-prefixed, checksummed address. ENS names are more suitable for cases where mutability of the underlying asset is acceptable and there is implicit trust in the owner of the name. 0x prefixed addresses are more preferable in higher security cases to avoid needing to trust the controller of the name. + +#### `chain_id` +- Optional +- Integer representing the chain id on which the registry is located +- If omitted, defaults to `1` (mainnet). + +#### `package_name` +- Optional +- String of the target package name + +#### `package_version` +- Optional +- String of the target package version +- If the package version contains any [url unsafe characters](https://en.wikipedia.org/wiki/Percent-encoding), they **MUST** be safely escaped +- Since semver is not strictly enforced by the ethpm spec, if the `package_version` is omitted from a uri, tooling **SHOULD** avoid guessing in the face of any ambiguity and present the user with a choice from the available versions. + +#### `json_pointer` +- Optional +- A path that identifies a specific asset within a versioned package release. +- This path **MUST** conform to the [JSON pointer](https://tools.ietf.org/html/rfc6901) spec and resolve to an available asset within the package. + +## Rationale +Most interactions within the EthPM ecosystem benefit from a single-string representation of EthPM assets; from installing a package, to identifying a registry, to distributing a package. A single string that can faithfully represent any kind of EthPM asset, across the mainnet or testnets, reduces the mental overload for new users, minimizes configuration requirements for frameworks, and simplifies distribution of packages for package authors. + +## Test Cases +A JSON file for testing various URIs can be found in the [`ethpm-spec`](https://github.com/ethpm/ethpm-spec/) repository fixtures. + +## Implementation +The EthPM URI scheme has been implemented in the following libraries: +- [Brownie](https://eth-brownie.readthedocs.io/en/stable/) +- [Truffle](https://www.trufflesuite.com/docs/truffle/overview) +- [EthPM CLI](https://ethpm-cli.readthedocs.io/en/latest/) + +## Security Considerations +In most cases, an EthPM URI points to an immutable asset, giving full security that the target asset has not been modified. However, in the case where an EthPM URI uses an ENS name as its registry address, it is possible that the ENS name has been redirected to a new registry, in which case the guarantee of immutability no longer exists. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2970.md b/EIPS/eip-2970.md new file mode 100644 index 0000000..0d58fe4 --- /dev/null +++ b/EIPS/eip-2970.md @@ -0,0 +1,39 @@ +--- +eip: 2970 +title: IS_STATIC opcode +author: Vitalik Buterin (@vbuterin) +discussions-to: https://ethereum-magicians.org/t/is-static-opcode-useful-for-aa/4609 +status: Stagnant +type: Standards Track +category: Core +created: 2020-09-13 +--- + +## Simple Summary + +Add a `IS_STATIC (0x4A)` opcode that pushes `1` if the current context is static (ie. the execution is in a `STATICCALL` or a descendant thereof, so state-changing operations are not possible), and `0` if it is not. + +## Abstract + +## Motivation + +The main intended use case is to allow account abstraction (EIP 2938) to be extended so that accounts can allow static calls from the outside (which are harmless to AA's security model) but not state-changing calls. + +## Specification + +Add a `IS_STATIC (0x4A)` opcode that pushes `1` if the current context is static (ie. the execution is in a `STATICCALL` or a descendant thereof, so state-changing operations are not possible), and `0` if it is not. + +## Rationale + +Determining staticness is already possibly using the following hacky technique: make a `CALL` with limited gas, and inside that `CALL` issue one `LOG` and exit. If the context is static, the `CALL` would fail and leave a 0 on the stack; if the context is non-static, the `CALL` would succeed. However, this technique is fragile against changes to gas costs, and is needlessly wasteful. Hence, the status quo neither allows a reasonably effective way of determining whether or not the context is static, nor provides any kind of invariant that executions that do not fail outright will execute the same way in a static and non-static context. This EIP provides a cleaner way of determining staticness. + +## Backwards Compatibility + +TBD + +## Security Considerations + +TBD + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2972.md b/EIPS/eip-2972.md new file mode 100644 index 0000000..0db0c86 --- /dev/null +++ b/EIPS/eip-2972.md @@ -0,0 +1,127 @@ +--- +eip: 2972 +title: Wrapped Legacy Transactions +author: Micah Zoltu (@MicahZoltu) +discussions-to: https://ethereum-magicians.org/t/eip-2972-wrapped-legacy-transactions/4604 +status: Withdrawn +type: Standards Track +category: Core +created: 2020-09-12 +requires: 155, 2718 +--- + +## Simple Summary +Two new transaction types for wrapping legacy transactions with and without a chain ID. + +## Abstract +Introduces two new [EIP-2718](./eip-2718.md) transactions that are signature compatible with legacy transactions and can be automatically upgraded by any client. + +* `0x00 || ssz.serialize(yParity, r, s, rlp([nonce, gasPrice, gasLimit, to, value, data]))` +* `0x01 || ssz.serialize(yParity, r, s, rlp([nonce, gasPrice, gasLimit, to, value, data, chainId, 0, 0]))` + +## Motivation +We would like to eventually deprecate legacy transactions so we no longer have to retain code in the networking and signer layer that deals with them. +However, we also want to ensure that signatures for transactions that were generated prior to that deprecation are still valid and funds don't end up stuck because of an inability to sign a new style transaction. +This EIP provides a mechanism for transmitting/including transactions in a way that is [EIP-2718](./eip-2718.md) compatible while still being signature compatible with legacy transactions. + +## Specification +### Definitions +* `||` is the byte/byte-array concatenation operator. +* `yParity` is the parity (0 for even, 1 for odd) of the `y` value of the curve point for which `r` is the `x` value in the secp256k1 signing process. + +### Transactions +As of `FORK_BLOCK_NUMBER`, `0x00 || ssz.serialize(yParity, r, s, rlp([nonce, gasPrice, gasLimit, to, value, data]))` will be a valid transaction where: +* the RLP encoded transaction portion is signed/processed/handled exactly the same as legacy transactions were signed/processed/handled, with the exception of the final encoding +* TODO: Hashing or Merkleizing for block transaction root + +As of `FORK_BLOCK_NUMBER`, `0x01 || ssz.serialize(yParity, r, s, rlp([nonce, gasPrice, gasLimit, to, value, data, chainId, 0, 0]))` will be a valid transaction where: +* the RLP encoded transaction portion is signed/processed/handled exactly the same as legacy transactions were signed/processed/handled, with the exception of the final encoding +* TODO: Hashing or Merkleizing for block transaction root + +The SSZ schema for both transaction types is: + ``` + Transaction[ + yParity: boolean, + r: bytes32, + s: bytes32, + signedData: bytes, + ] + ``` + +Note: `sszencode(yParity, r, s, rlp(...))` is the same as `yParity || r || s || 0x45000000 || rlp(...)` + +As of `FORK_BLOCK_NUMBER`, `rlp(nonce, gasPrice, gasLimit, to, value, data, v, r, s)` will no longer be a valid transaction in a block. + +### Receipts +As of `FORK_BLOCK_NUMBER`, `0 || ssz.serialize(status, cumulativeGasUsed, logsBloom, logs)` will be a valid receipt where: +* the `ReceiptPayload` will be generated/processed/handled exactly the same as legacy receipts were processed/handled with the exception of its encoding +* TODO: Hashing or Merkleizing for block receipt root + +As of `FORK_BLOCK_NUMBER`, `1 || ssz.serialize(status, cumulativeGasUsed, logsBloom, logs)` will be a valid receipt where: +* the `ReceiptPayload` will be generated/processed/handled exactly the same as legacy receipts were processed/handled with the exception of its encoding +* TODO: Hashing or Merkleizing for block receipt root + +The SSZ schema for both receipt types is: +``` +Log[ + address: bytes20, + topics: List[bytes32, 4], + data: List[uint8, 0xffffff], +] +Receipt[ + status: uint8, + cumulativeGasUsed: uint64, + logsBloom: BitVector[2048], + logs: List[Log, 0xffffff], +] +``` + +As of `FORK_BLOCK_NUMBER`, `rlp(status, cumulativeGasUsed, logsBloom, logs)` will no longer be a valid receipt in a block. + +## Rationale +### Signature doesn't include transaction type as first signature byte +These transaction types are explicitly designed to be signature compatible with legacy transactions, which means we cannot change the data being signed. +See Security Considerations section for more details. +### Two transaction types instead of one +With the introduction of typed transactions, we no longer need to do bit packing to avoid changing the shape of the signature. +Legacy transactions introduced chain ID in [EIP-155](./eip-155.md) and wanted to avoid changing the transaction array length, so it bitpacked the chainID into the signature's `v` value. +Since we no longer need to guarantee consistent payload lengths between transaction types, we have opted to have two transaction types with clear fields. +### Signature separate from signed data +When validating a signature, one must first separate out the signed data from the signature and then validate the signature against the signed data. +In the case of legacy transactions, this was a bit of a burden since you had to first RLP decode the transaction, then extract out the signature, then RLP encode a subset of the transaction. +EIP-155 made this process even worse by requiring the validator to further decode the `v` signature value to extract the chain ID (if present) and include that in the signed data payload. +By having the signed data encoded exactly as it is signed, we make it so one can verify the transaction's signature without having to do any decoding before hand. +By having the signature SSZ encoded up front, we can easily extract the signature without even having to use a decoder. +### SSZ for serialization +There is a weak consensus that RLP is not a particularly good encoding scheme for hashed data partially due to its inability to be streamed. +SSZ is almost certainly going to be included in Ethereum at some point in the future, so clients likely have access to an SSZ decoder. +For this particular case, manual decoding without a full SSZ decoder isn't too complicated, though it does require doing a bit of "pointer math" since `logs` is an array of variable length items. +### Deprecating legacy transactions +By deprecating legacy transactions, we make it easier for clients as they can always deal with typed transactions in blocks. +### Max length of logs and logs data +[EIP-706](./eip-706.md) limits devp2p messages to 24 bit length, which gives us a pragmatic cap at that for any single transaction at the moment. +This number seems to far exceed what is reasonable anytime in the near future, so feels like as reasonable of a cap as any. + +## Backwards Compatibility +The new transactions are signature compatible with legacy transactions. +Legacy transactions can be decoded and then encoded as type 0 or type 1 transactions. +This EIP does not introduce any deprecation process for legacy encoded transactions, though the authors do encourage client developers to upgrade legacy encoded transactions to typed transactions as soon as it is reasonable. + +The signature compatibility means that a client may see the same transaction encoded both ways. +In such a case the client can choose which to retain, but it is encouraged to retain the typed transaction rather than the legacy encoded transaction. +Since the two transactions would share a nonce, only one will ever be valid in a chain at a time. + +## Test Cases +TBD + +## Implementation +TBD + +## Security Considerations +While [EIP-2718](./eip-2718.md) strongly recommends including the transaction type as the first byte of the signed data, we cannot accomplish that in this case because we need to remain signature compatible with legacy transactions. +Luckily, [EIP-2718](./eip-2718.md) also excludes transaction types `0xc0` to `0xfe` from valid transaction types, and the first byte of the signature in this case is in that range so we can be sure this will not conflict with any future transaction types. + +A signature for these transaction types **does** collide with legacy transactions, but the transactions will be processed the same so it doesn't matter if the transaction ends up included as a legacy transaction or a typed transaction. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2976.md b/EIPS/eip-2976.md new file mode 100644 index 0000000..6f8056f --- /dev/null +++ b/EIPS/eip-2976.md @@ -0,0 +1,97 @@ +--- +eip: 2976 +title: Typed Transactions over Gossip +description: Adds support for transmission of typed transactions over devp2p. +author: Micah Zoltu (@MicahZoltu) +discussions-to: https://ethereum-magicians.org/t/eip-2976-eth-typed-transactions-over-gossip/4610 +status: Final +type: Standards Track +category: Networking +created: 2020-09-13 +requires: 2718 +--- + +## Abstract +[Typed Transactions](./eip-2718.md) can be sent over devp2p as `TransactionType || TransactionPayload`. +The exact contents of the `TransactionPayload` are defined by the `TransactionType` in future EIPs, and clients may start supporting their gossip without incrementing the devp2p version. +If a client receives a `TransactionType` that it doesn't recognize, it **SHOULD** disconnect from the peer who sent it. +Clients **MUST NOT** send new transaction types before they believe the fork block is reached. + +## Motivation +[EIP-2718](./eip-2718.md) introduced new transaction types for blocks (which presents itself in the makeup of a block header's transaction root and receipts root). +However, without a mechanism for gossiping these transactions, no one can actually include them in a block. +By updating devp2p to support the gossip of Typed Transactions, we can benefit from these new transaction types. + +*Note: See [EIP-2718](./eip-2718.md) for additional motivations of Typed Transactions.* + +## Specification +All changes specified below apply to all protocol/versions retroactively. + +### Definitions +* `||` is the byte/byte-array concatenation operator. +* `|` is the type union operator. +* `DEVP2P_VERSION = TBD` +* `Transaction` is either `TypedTransaction` or `LegacyTransaction` +* `TypedTransaction` is a byte array containing `TransactionType || TransactionPayload` +* `TypedTransactionHash` is `keccak256(TypedTransaction)` +* `TransactionType` is a positive unsigned 8-bit number between `0` and `0x7f` that represents the type of the transcation +* `TransactionPayload` is an opaque byte array whose interpretation is dependent on the `TransactionType` and defined in future EIPs +* `LegacyTransaction` is an array of the form `[nonce, gasPrice, gasLimit, to, value, data, v, r, s]` +* `LegacyTransactionHash` is `keccak256(rlp(LegacyTransaction))` +* `TransactionId` is `keccak256(TypedTransactionHash | LegacyTransactionHash)` +* `Receipt` is either `TypedReceipt` or `LegacyReceipt` +* `TypedReceipt` is a byte array containing `TransactionType || ReceiptPayload` +* `ReceiptPayload` is an opaque byte array whose interpretation is dependent on the `TransactionType` and defined in future EIPs +* `LegacyReceipt` is an array of the form `[status, cumulativeGasUsed, logsBloom, logs]` +* `LegacyReceiptHash` is `keccak256(rlp(LegacyReceipt))` + +### Protocol Behavior +If a client receives a `TransactionType` it doesn't recognize via any message, it **SHOULD** disconnect the peer that sent it. + +If a client receives a `TransactionPayload` that isn't valid for the `TransactionType`, it **SHOULD** disconnect the peer that sent it. + +Clients **MUST NOT** send transactions of a new `TransactionType` until that transaction type's introductory fork block. + +Clients **MAY** disconnect peers who send transactions of a new `TransactionType` significantly before that transaction type's introductory fork block. + +### Protocol Messages +`Transactions (0x02)`: `[Transaction_0, Transaction_1, ..., Transaction_n]` + +`BlockBodies (0x06)`: `[BlockBody_0, BlockBody_1, ..., BlockBody_n]` where: +* `BlockBody` is `[TransactionList, UncleList]` +* `TransactionList` is `[Transaction_0, Transaction_1, ..., Transaction_n]` +* `UnclesList` is defined in previous versions of the devp2p specification + +`NewBlock (0x07)`: `[[BlockHeader, TransactionList, UncleList], TotalDifficulty]` where: +* `BlockHeader` is defined in previous versions of the devp2 specification +* `TransactionList` is `[Transaction_0, Transaction_1, ..., Transaction_n]` +* `UnclesList` is defined in previous versions of the devp2p specification +* `TotalDifficulty` is defined in previous versions of the devp2p specification + +`NewPooledTransactionIds (0x08)`: `[TransactionId_0, TransactionId_1, ..., TransactionId_n]` + +`GetPooledTransactions (0x09)`: `[TransactionId_0, TransactionId_1, ..., TransactionId_n]` + +`PooledTransactions (0x0a)`: `[Transaction_0, Transaction_1, ..., Transaction_n]` + +`Receipts (0x10)`: `[ReceiptList_0, ReceiptList_1, ..., ReceiptList_n]` where: +* `ReceiptList` is `[Receipt_0, Receipt_1, ..., Receipt_n]` + +## Rationale +### Why not specify each transaction type at the protocol layer? +We could have chosen to make the protocol aware of the shape of the transaction payloads. +The authors felt that it would be too much maintenance burden long term to have every new transaction type require an update to devp2p, so instead we merely define that typed transactions are supported. +### Why have peers disconnect if they receive an unknown transaction type? +We could encourage peers to remain connected to peers that submit an unknown transaction type, in case the transaction is some new transaction type that the receiver isn't aware of it. +However, doing so may open clients up to DoS attacks where someone would send them transactions of an undefined `TransactionType` in order to avoid being disconnected for spamming. +Also, in most cases we expect that by the time new transaction types are being sent over devp2p, a hard fork that requires all connected clients to be aware of the new transaction type is almost certainly imminent. + +## Backwards Compatibility +Legacy transactions are still supported. + +## Security Considerations +If a client chooses to ignore the **SHOULD** recommendation for disconnecting peers that send unknown transaction types they may be susceptible to DoS attacks. +Ignoring this recommendation should be limited to trusted peers only, or other situations where the risk of DoS is extremely low. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2980.md b/EIPS/eip-2980.md new file mode 100644 index 0000000..ec74e0e --- /dev/null +++ b/EIPS/eip-2980.md @@ -0,0 +1,197 @@ +--- +eip: 2980 +title: Swiss Compliant Asset Token +description: An interface for asset tokens, compliant with Swiss Law and compatible with [ERC-20](./eip-20.md). +author: Gianluca Perletti (@Perlets9), Alan Scarpellini (@alanscarpellini), Roberto Gorini (@robertogorini), Manuel Olivi (@manvel79) +discussions-to: https://github.com/ethereum/EIPs/issues/2983 +status: Stagnant +type: Standards Track +category: ERC +created: 2020-09-08 +requires: 20 +--- + +## Abstract + +This new standard is an [ERC-20](./eip-20.md) compatible token with restrictions that comply with the following Swiss laws: the [Stock Exchange Act](../assets/eip-2980/Swiss-Confederation-SESTA.pdf), the [Banking Act](../assets/eip-2980/Swiss-Confederation-BA.pdf), the [Financial Market Infrastructure Act](../assets/eip-2980/Swiss-Confederation-FMIA.pdf), the [Act on Collective Investment Schemes](../assets/eip-2980/Swiss-Confederation-CISA.pdf) and the [Anti-Money Laundering Act](../assets/eip-2980/Swiss-Confederation-AMLA.pdf). The [Financial Services Act](../assets/eip-2980/Swiss-Confederation-FINSA.pdf) and the [Financial Institutions Act](../assets/eip-2980/Swiss-Confederation-FINIA.pdf) must also be considered. The solution achieved meet also the European jurisdiction. + +This new standard meets the new era of asset tokens (known also as "security tokens"). These new methods manage securities ownership during issuance and trading. The issuer is the only role that can manage a white-listing and the only one that is allowed to execute “freeze” or “revoke” functions. + +## Motivation + +In its ICO guidance dated February 16, 2018, FINMA (Swiss Financial Market Supervisory Authority) defines asset tokens as tokens representing assets and/or relative rights ([FINMA ICO Guidelines](../assets/eip-2980/Finma-ICO-Guidelines.pdf)). It explicitly mentions that asset tokens are analogous to and can economically represent shares, bonds, or derivatives. The long list of relevant financial market laws mentioned above reveal that we need more methods than with Payment and Utility Token. + +## Specification + +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. + +The words "asset tokens" and "security tokens" can be considered synonymous. + +Every ERC-2980 compliant contract MUST implement the ERC-2980 interface. + +### ERC-2980 (Token Contract) + +``` solidity +interface ERC2980 extends ERC20 { + + /// @dev This emits when funds are reassigned + event FundsReassigned(address from, address to, uint256 amount); + + /// @dev This emits when funds are revoked + event FundsRevoked(address from, uint256 amount); + + /// @dev This emits when an address is frozen + event FundsFrozen(address target); + + /** + * @dev getter to determine if address is in frozenlist + */ + function frozenlist(address _operator) external view returns (bool); + + /** + * @dev getter to determine if address is in whitelist + */ + function whitelist(address _operator) external view returns (bool); + +} +``` + +The ERC-2980 extends [ERC-20](./eip-20.md). Due to the indivisible nature of asset tokens, the decimals number MUST be zero. + +### Whitelist and Frozenlist + +The accomplishment of the Swiss Law requirements is achieved by the use of two distinct lists of address: the Whitelist and the Frozenlist. +Addresses can be added to one or the other list at any time by operators with special privileges, called Issuers, and described below. +Although these lists may look similar, they differ for the following reasons: the Whitelist members are the only ones who can receive tokens from other addresses. There is no restriction on the possibility that these addresses can transfer the tokens already in their ownership. +This can occur when an address, present in the Whitelist, is removed from this list, without however being put in the Frozenlist and remaining in possession of its tokens. +On the other hand, the addresses assigned to the Frozenlist, as suggested by the name itself, have to be considered "frozen", so they cannot either receive tokens or send tokens to anyone. + +Below is an example interface for the implementation of a whitelist-compatible and a frozenlist-compratible contract. + +``` solidity +Interface Whitelistable { + + /** + * @dev add an address to the whitelist + * Throws unless `msg.sender` is an Issuer operator + * @param _operator address to add + * @return true if the address was added to the whitelist, false if the address was already in the whitelist + */ + function addAddressToWhitelist(address _operator) external returns (bool); + + /** + * @dev remove an address from the whitelist + * Throws unless `msg.sender` is an Issuer operator + * @param _operator address to remove + * @return true if the address was removed from the whitelist, false if the address wasn't in the whitelist in the first place + */ + function removeAddressFromWhitelist(address _operator) external returns (bool); + +} + +Interface Freezable { + + /** + * @dev add an address to the frozenlist + * Throws unless `msg.sender` is an Issuer operator + * @param _operator address to add + * @return true if the address was added to the frozenlist, false if the address was already in the frozenlist + */ + function addAddressToFrozenlist(address _operator) external returns (bool); + + /** + * @dev remove an address from the frozenlist + * Throws unless `msg.sender` is an Issuer operator + * @param _operator address to remove + * @return true if the address was removed from the frozenlist, false if the address wasn't in the frozenlist in the first place + */ + function removeAddressFromFrozenlist(address _operator) external returns (bool); + +} +``` + +### Issuers + +A key role is played by the Issuer. This figure has the permission to manage Whitelists and Frozenlists, to revoke tokens and reassign them and to transfer the role to another address. No restrictions on the possibility to have more than one Issuer per contract. Issuers are nominated by the Owner of the contract, who also is in charge of remove the role. The possibility of nominating the Owner itself as Issuer at the time of contract creation (or immediately after) is not excluded. + +Below is an example interface for the implementation of the Issuer functionalities. + +``` solidity +Interface Issuable { + + /** + * @dev getter to determine if address has issuer role + */ + function isIssuer(address _addr) external view returns (bool); + + /** + * @dev add a new issuer address + * Throws unless `msg.sender` is the contract owner + * @param _operator address + * @return true if the address was not an issuer, false if the address was already an issuer + */ + function addIssuer(address _operator) external returns (bool); + + /** + * @dev remove an address from issuers + * Throws unless `msg.sender` is the contract owner + * @param _operator address + * @return true if the address has been removed from issuers, false if the address wasn't in the issuer list in the first place + */ + function removeIssuer(address _operator) external returns (bool); + + /** + * @dev Allows the current issuer to transfer its role to a newIssuer + * Throws unless `msg.sender` is an Issuer operator + * @param _newIssuer The address to transfer the issuer role to + */ + function transferIssuer(address _newIssuer) external; + +} +``` + +### Revoke and Reassign + +Revoke and Reassign methods allow Issuers to move tokens from addresses, even if they are in the Frozenlist. The Revoke method transfers the entire balance of the target address to the Issuer who invoked the method. The Reassign method transfers the entire balance of the target address to another address. These rights for these operations MUST be allowed only to Issuers. + +Below is an example interface for the implementation of the Revoke and Reassign functionalities. + +``` solidity +Interface RevokableAndReassignable { + + /** + * @dev Allows the current Issuer to transfer token from an address to itself + * Throws unless `msg.sender` is an Issuer operator + * @param _from The address from which the tokens are withdrawn + */ + function revoke(address _from) external; + + /** + * @dev Allows the current Issuer to transfer token from an address to another + * Throws unless `msg.sender` is an Issuer operator + * @param _from The address from which the tokens are withdrawn + * @param _to The address who receives the tokens + */ + function reassign(address _from, address _to) external; + +} +``` + +## Rationale + +There are currently no token standards that expressly facilitate conformity to securities law and related regulations. EIP-1404 (Simple Restricted Token Standard) it’s not enough to address FINMA requirements around re-issuing securities to Investors. +In Swiss law, an issuer must eventually enforce the restrictions of their token transfer with a “freeze” function. The token must be “revocable”, and we need to apply a white-list method for AML/KYC checks. + +## Backwards Compatibility + +This EIP does not introduce backward incompatibilities and is backward compatible with the older ERC-20 token standard. +This standard allows the implementation of ERC-20 functions transfer, transferFrom, approve and allowance alongside to make a token fully compatible with ERC-20. +The token MAY implement decimals() for backward compatibility with ERC-20. If implemented, it MUST always return 0. + +## Security Considerations + +The security considerations mainly concern the role played by the Issuers. This figure, in fact, is not generally present in common ERC-20 tokens but has very powerful rights that allow him to move tokens without being in possession and freeze other addresses, preventing them from transferring tokens. It must be the responsibility of the owner to ensure that the addresses that receive this charge remain in possession of it only for the time for which they have been designated to do so, thus preventing any abuse. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2981.md b/EIPS/eip-2981.md new file mode 100644 index 0000000..a24821b --- /dev/null +++ b/EIPS/eip-2981.md @@ -0,0 +1,183 @@ +--- +eip: 2981 +title: NFT Royalty Standard +author: Zach Burks (@vexycats), James Morgan (@jamesmorgan), Blaine Malone (@blmalone), James Seibel (@seibelj) +discussions-to: https://github.com/ethereum/EIPs/issues/2907 +status: Final +type: Standards Track +category: ERC +created: 2020-09-15 +requires: 165 +--- + +## Simple Summary + +A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal support for royalty payments across all NFT marketplaces and ecosystem participants. + +## Abstract + +This standard allows contracts, such as NFTs that support [ERC-721](./eip-721.md) and [ERC-1155](./eip-1155.md) interfaces, to signal a royalty amount to be paid to the NFT creator or rights holder every time the NFT is sold or re-sold. This is intended for NFT marketplaces that want to support the ongoing funding of artists and other NFT creators. The royalty payment must be voluntary, as transfer mechanisms such as `transferFrom()` include NFT transfers between wallets, and executing them does not always imply a sale occurred. Marketplaces and individuals implement this standard by retrieving the royalty payment information with `royaltyInfo()`, which specifies how much to pay to which address for a given sale price. The exact mechanism for paying and notifying the recipient will be defined in future EIPs. This ERC should be considered a minimal, gas-efficient building block for further innovation in NFT royalty payments. + +## Motivation +There are many marketplaces for NFTs with multiple unique royalty payment implementations that are not easily compatible or usable by other marketplaces. Just like the early days of ERC-20 tokens, NFT marketplace smart contracts are varied by ecosystem and not standardized. This EIP enables all marketplaces to retrieve royalty payment information for a given NFT. This enables accurate royalty payments regardless of which marketplace the NFT is sold or re-sold at. + +Many of the largest NFT marketplaces have implemented bespoke royalty payment solutions that are incompatible with other marketplaces. This standard implements standardized royalty information retrieval that can be accepted across any type of NFT marketplace. This minimalist proposal only provides a mechanism to fetch the royalty amount and recipient. The actual funds transfer is something which the marketplace should execute. + +This standard allows NFTs that support [ERC-721](./eip-721.md) and [ERC-1155](./eip-1155.md) interfaces, to have a standardized way of signalling royalty information. More specifically, these contracts can now calculate a royalty amount to provide to the rightful recipient. + +Royalty amounts are always a percentage of the sale price. If a marketplace chooses *not* to implement this EIP, then no funds will be paid for secondary sales. It is believed that the NFT marketplace ecosystem will voluntarily implement this royalty payment standard; in a bid to provide ongoing funding for artists/creators. NFT buyers will assess the royalty payment as a factor when making NFT purchasing decisions. + +Without an agreed royalty payment standard, the NFT ecosystem will lack an effective means to collect royalties across all marketplaces and artists and other creators will not receive ongoing funding. This will hamper the growth and adoption of NFTs and demotivate NFT creators from minting new and innovative tokens. + +Enabling all NFT marketplaces to unify on a single royalty payment standard will benefit the entire NFT ecosystem. + +While this standard focuses on NFTs and compatibility with the ERC-721 and ERC-1155 standards, EIP-2981 does not require compatibility with ERC-721 and ERC-1155 standards. Any other contract could integrate with EIP-2981 to return royalty payment information. ERC-2981 is, therefore, a universal royalty standard for many asset types. + +At a glance, here's an example conversation summarizing NFT royalty payments today: + +>**Artist**: "Do you support royalty payments on your platform?" +>**Marketplace**: "Yes we have royalty payments, but if your NFT is sold on another marketplace then we cannot enforce this payment." +>**Artist**: "What about other marketplaces that support royalties, don't you share my royalty information to make this work?" +>**Marketplace**: "No, we do not share royalty information." + +## Specification + +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. + +**ERC-721 and ERC-1155 compliant contracts MAY implement this ERC for royalties to provide a standard method of specifying royalty payment information.** + +Marketplaces that support this standard **SHOULD** implement some method of transferring royalties to the royalty recipient. Standards for the actual transfer and notification of funds will be specified in future EIPs. + +Marketplaces **MUST** pay the royalty in the same unit of exchange as that of the `_salePrice` passed to `royaltyInfo()`. This is equivalent to saying that the `_salePrice` parameter and the `royaltyAmount` return value **MUST** be denominated in the same monetary unit. For example, if the sale price is in ETH, then the royalty payment must also be paid in ETH, and if the sale price is in USDC, then the royalty payment must also be paid in USDC. + +Implementers of this standard **MUST** calculate a percentage of the `_salePrice` when calculating the royalty amount. Subsequent invocations of `royaltyInfo()` **MAY** return a different `royaltyAmount`. Though there are some important considerations for implementers if they choose to perform different percentage calculations between `royaltyInfo()` invocations. + +The `royaltyInfo()` function is not aware of the unit of exchange for the sale and royalty payment. With that in mind, implementers **MUST NOT** return a fixed/constant `royaltyAmount`, wherein they're ignoring the `_salePrice`. For the same reason, implementers **MUST NOT** determine the `royaltyAmount` based on comparing the `_salePrice` with constant numbers. In both cases, the `royaltyInfo()` function makes assumptions on the unit of exchange, which **MUST** be avoided. + +The percentage value used must be independent of the sale price for reasons previously mentioned (i.e. if the percentage value 10%, then 10% **MUST** apply whether `_salePrice` is 10, 10000 or 1234567890). If the royalty fee calculation results in a remainder, implementers **MAY** round up or round down to the nearest integer. For example, if the royalty fee is 10% and `_salePrice` is 999, the implementer can return either 99 or 100 for `royaltyAmount`, both are valid. + +The implementer **MAY** choose to change the percentage value based on other predictable variables that do not make assumptions about the unit of exchange. For example, the percentage value may drop linearly over time. An approach like this **SHOULD NOT** be based on variables that are unpredictable like `block.timestamp`, but instead on other more predictable state changes. One more reasonable approach **MAY** use the number of transfers of an NFT to decide which percentage value is used to calculate the `royaltyAmount`. The idea being that the percentage value could decrease after each transfer of the NFT. Another example could be using a different percentage value for each unique `_tokenId`. + +Marketplaces that support this standard **SHOULD NOT** send a zero-value transaction if the `royaltyAmount` returned is `0`. This would waste gas and serves no useful purpose in this EIP. + +Marketplaces that support this standard **MUST** pay royalties no matter where the sale occurred or in what currency, including on-chain sales, over-the-counter (OTC) sales and off-chain sales such as at auction houses. As royalty payments are voluntary, entities that respect this EIP must pay no matter where the sale occurred - a sale conducted outside of the blockchain is still a sale. The exact mechanism for paying and notifying the recipient will be defined in future EIPs. + +Implementers of this standard **MUST** have all of the following functions: + +```solidity +pragma solidity ^0.6.0; +import "./IERC165.sol"; + +/// +/// @dev Interface for the NFT Royalty Standard +/// +interface IERC2981 is IERC165 { + /// ERC165 bytes to add to interface array - set in parent contract + /// implementing this standard + /// + /// bytes4(keccak256("royaltyInfo(uint256,uint256)")) == 0x2a55205a + /// bytes4 private constant _INTERFACE_ID_ERC2981 = 0x2a55205a; + /// _registerInterface(_INTERFACE_ID_ERC2981); + + /// @notice Called with the sale price to determine how much royalty + // is owed and to whom. + /// @param _tokenId - the NFT asset queried for royalty information + /// @param _salePrice - the sale price of the NFT asset specified by _tokenId + /// @return receiver - address of who should be sent the royalty payment + /// @return royaltyAmount - the royalty payment amount for _salePrice + function royaltyInfo( + uint256 _tokenId, + uint256 _salePrice + ) external view returns ( + address receiver, + uint256 royaltyAmount + ); +} + +interface IERC165 { + /// @notice Query if a contract implements an interface + /// @param interfaceID The interface identifier, as specified in ERC-165 + /// @dev Interface identification is specified in ERC-165. This function + /// uses less than 30,000 gas. + /// @return `true` if the contract implements `interfaceID` and + /// `interfaceID` is not 0xffffffff, `false` otherwise + function supportsInterface(bytes4 interfaceID) external view returns (bool); +} +``` + +### Examples + +This standard being used on an ERC-721 during deployment: + +#### Deploying an ERC-721 and signaling support for ERC-2981 + +```solidity +constructor (string memory name, string memory symbol, string memory baseURI) { + _name = name; + _symbol = symbol; + _setBaseURI(baseURI); + // register the supported interfaces to conform to ERC721 via ERC165 + _registerInterface(_INTERFACE_ID_ERC721); + _registerInterface(_INTERFACE_ID_ERC721_METADATA); + _registerInterface(_INTERFACE_ID_ERC721_ENUMERABLE); + // Royalties interface + _registerInterface(_INTERFACE_ID_ERC2981); + } +``` + +#### Checking if the NFT being sold on your marketplace implemented royalties + +```solidity +bytes4 private constant _INTERFACE_ID_ERC2981 = 0x2a55205a; + +function checkRoyalties(address _contract) internal returns (bool) { + (bool success) = IERC165(_contract).supportsInterface(_INTERFACE_ID_ERC2981); + return success; + } +``` + +## Rationale + +### Optional royalty payments + +It is impossible to know which NFT transfers are the result of sales, and which are merely wallets moving or consolidating their NFTs. Therefore, we cannot force every transfer function, such as `transferFrom()` in ERC-721, to involve a royalty payment as not every transfer is a sale that would require such payment. We believe the NFT marketplace ecosystem will voluntarily implement this royalty payment standard to provide ongoing funding for artists/creators. NFT buyers will assess the royalty payment as a factor when making NFT purchasing decisions. + +### Simple royalty payments to a single address + +This EIP does not specify the manner of payment to the royalty recipient. Furthermore, it is impossible to fully know and efficiently implement all possible types of royalty payments logic. With that said, it is on the royalty payment receiver to implement all additional complexity and logic for fee splitting, multiple receivers, taxes, accounting, etc. in their own receiving contract or off-chain processes. Attempting to do this as part of this standard, it would dramatically increase the implementation complexity, increase gas costs, and could not possibly cover every potential use-case. This ERC should be considered a minimal, gas-efficient building block for further innovation in NFT royalty payments. Future EIPs can specify more details regarding payment transfer and notification. + +### Royalty payment percentage calculation + +This EIP mandates a percentage-based royalty fee model. It is likely that the most common case of percentage calculation will be where the `royaltyAmount` is always calculated from the `_salePrice` using a fixed percent i.e. if the royalty fee is 10%, then a 10% royalty fee must apply whether `_salePrice` is 10, 10000 or 1234567890. + +As previously mentioned, implementers can get creative with this percentage-based calculation but there are some important caveats to consider. Mainly, ensuring that the `royaltyInfo()` function is not aware of the unit of exchange and that unpredictable variables are avoided in the percentage calculation. To follow up on the earlier `block.timestamp` example, there is some nuance which can be highlighted if the following events ensued: + +1. Marketplace sells NFT. +2. Marketplace delays `X` days before invoking `royaltyInfo()` and sending payment. +3. Marketplace receives `Y` for `royaltyAmount` which was significantly different from the `royaltyAmount` amount that would've been calculated `X` days prior if no delay had occurred. +4. Royalty recipient is dissatisfied with the delay from the marketplace and for this reason, they raise a dispute. + +Rather than returning a percentage and letting the marketplace calculate the royalty amount based on the sale price, a `royaltyAmount` value is returned so there is no dispute with a marketplace over how much is owed for a given sale price. The royalty fee payer must pay the `royaltyAmount` that `royaltyInfo()` stipulates. + +### Unit-less royalty payment across all marketplaces, both on-chain and off-chain + +This EIP does not specify a currency or token used for sales and royalty payments. The same percentage-based royalty fee must be paid regardless of what currency, or token was used in the sale, paid in the same currency or token. This applies to sales in any location including on-chain sales, over-the-counter (OTC) sales, and off-chain sales using fiat currency such as at auction houses. As royalty payments are voluntary, entities that respect this EIP must pay no matter where the sale occurred - a sale outside of the blockchain is still a sale. The exact mechanism for paying and notifying the recipient will be defined in future EIPs. + +### Universal Royalty Payments + +Although designed specifically with NFTs in mind, this standard does not require that a contract implementing EIP-2981 is compatible with either ERC-721 or ERC-1155 standards. Any other contract could use this interface to return royalty payment information, provided that it is able to uniquely identify assets within the constraints of the interface. ERC-2981 is, therefore, a universal royalty standard for many other asset types. + +## Backwards Compatibility + +This standard is compatible with current ERC-721 and ERC-1155 standards. + +## Security Considerations + +There are no security considerations related directly to the implementation of this standard. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2982.md b/EIPS/eip-2982.md new file mode 100644 index 0000000..eba2dd8 --- /dev/null +++ b/EIPS/eip-2982.md @@ -0,0 +1,443 @@ +--- +eip: 2982 +title: Serenity Phase 0 +description: Phase 0 of the release schedule of Serenity, a series of updates to Ethereum a scalable, proof-of-stake consensus +author: Danny Ryan (@djrtwo), Vitalik Buterin (@vbuterin) +discussions-to: https://ethereum-magicians.org/t/serenity-phase-0-eip/4621 +status: Final +type: Informational +created: 2020-09-15 +--- + +## Abstract + +This EIP specifies Phase 0 of Serenity (eth2), a multi-phased upgrade to the consensus mechanism for Ethereum mainnet. In Phase 0, the existing PoW chain and mechanics are entirely unaffected, while a PoS chain -- the beacon chain -- is built in parallel to serve as the core of the upgraded consensus. In subsequent phases, the beacon chain is enhanced to support and secure the consensus of a number of parallel shard chains, ultimately incorporating current Ethereum mainnet as one of those shards. + +At the core of the beacon chain is a proof of stake consensus mechanism called Casper the Friendly Finality Gadget (FFG) and a fork-choice rule called Latest Message Driven Greedy Heaviest Observed Sub-Tree (LMD-GHOST). Phase 0 is centered primarily around the mechanics and incentives of validators executing these algorithms. The detailed specifications for eth2 are contained in an independent repository from this EIP, and safety and liveness proofs can be found in the [Combining GHOST and Casper](../assets/eip-2982/arxiv-2003.03052-Combining-GHOST-and-Casper.pdf) paper. To avoid duplication, this EIP just references relevant spec files and releases. + +Early phases of eth2 are executed without any breaking consensus changes on current Ethereum mainnet. This EIP serves to document the bootstrapping of this consensus mechanism and note the path for eth2 to supplant Ethereum's current proof-of-work (PoW) consensus. + +## Motivation + +Eth2 aims to fulfill the original vision of Ethereum to support an efficient, global-scale, general-purpose transactional platform while retaining high cryptoeconomic security and decentralization. + +Today, Ethereum blocks are consistently full due to increasingly high demand for decentralized applications. Ever since the first serious spikes in adoption in 2017 (cryptokitties), the Ethereum community has consistently and vocally demanded scaling solutions. + +Since day 0 of Ethereum, the investigation and expectation in scaling solutions has been two-pronged -- scale from both Layer 1 upgrades and Layer 2 adoption. This EIP represents the start to a multi-phased rollout of the former. + +### Scaling through sharding + +As the Ethereum network and the applications built upon it have seen increasing usage over the past 5 years, blocks have become regularly full and the gas price market continues to climb. Simple increases to the block gas-limit of the current Ethereum chain are unable to account for the increase in demand of the system without inducing an unsustainably high resource burden (in the form of bandwidth, computational, and disk resources) on consumer nodes. To retain decentralization while scaling up the Ethereum network, another path must be taken. + +To provide more scale to Ethereum, while not inducing a restrictively high burden on both consumer and consensus nodes, eth2 introduces a "sharded" solution in which a number of blockchain shards -- each of similar capacity to Ethereum mainnet today -- run in parallel under a unified consensus mechanism. The core consensus (the beacon chain) and a small number of these shards can be processed via a single consumer machine, while the aggregate of the system provides much higher capacity. + +### Decentralization and economic finality through proof-of-stake + +Since the early days of Ethereum, proof-of-stake has been a long-awaited desideratum for the following: +* Increased decentralization of the core consensus by lowering the barrier to entry and technical requirements of participation +* Increased cryptoeconomic security via in-protocol penalties for misbehaviour and the addition of economic finality +* Elimination of the energy hungry mining of the current PoW consensus mechanism + +In addition to the above, PoS has synergies with the sharding scaling solution. Due to the random sampling requirement of sharding, PoS provides a more simple and direct access to the "active validator set" than PoW and thus allows for a more direct sharded protocol construction. + + +## Specification + +Phase 0 is designed to require _no breaking consensus changes_ to existing Ethereum mainnet. Instead, this is the bootstraping a new PoS consensus that can, once stable, supplant the current PoW consensus. + +Phase 0 specifications are maintained in a repository independent of this EIP. `SPEC_RELEASE_VERSION` release of the specs at `SPEC_RELEASE_COMMIT` are considered the canonical Phase 0 specs for this EIP. + +This EIP provides a high level view on the Phase 0 mechanisms, especially those that are relevant to Ethereum mainnet (e.g. the deposit contract) and users (e.g. validator mechanics and eth2 issuance). The extended and low level details remain in the `consensus-specs` repository + +### Parameters + +| Parameter | Value | +| - | - | +| `SPEC_RELEASE_VERSION` | `v1.0.0` | +| `SPEC_RELEASE_COMMIT` | `579da6d2dc734b269dbf67aa1004b54bb9449784` | +| `DEPOSIT_CONTRACT_ADDRESS` | `0x00000000219ab540356cBB839Cbe05303d7705Fa` | +| `MIN_GENESIS_TIME` | `1606824000` | +| `BASE_REWARD_FACTOR` | `2**6` (64) | +| `INACTIVITY_PENALTY_QUOTIENT` | `2**26` (67,108,864) | +| `PROPORTIONAL_SLASHING_MULTIPLIER` | `1` | +| `MIN_SLASHING_PENALTY_QUOTIENT` | `2**7` (128) | + +_Note:_ Eth2 has many more Phase 0 configuration parameters but the majority are left out of this EIP for brevity. + +### Validator deposit contract + +In Phase 0, eth2 uses a contract deployed on Ethereum mainnet -- the Deposit Contract -- at `DEPOSIT_CONTRACT_ADDRESS` to onboard validators into the PoS consensus of the beacon chain. + +To participate in the PoS consensus, users submit validator deposits to the deposit contract. The beacon chain comes to consensus on the state of this contract and processes new validator deposits. This uni-directional deposit mechanism is the only technical link between the two components of the system (Ethereum mainnet and beacon chain) in Phase 0. + +### Beacon chain and validator mechanics + +Users who choose to participate in eth2 consensus deposit ETH collateral into the deposit contract in order to be inducted into the beacon chain validator set. From there, these validators are responsible for constructing the **beacon chain** (note that these consensus participants in PoS are akin to miners in PoW). + +The beacon chain is a pure PoS chain that in Phase 0 is primarily concerned with maintaining its own consensus and managing the registry of validators. The consensus rules define _roles_ (e.g. block proposal, block attesting) that validators are expected to participate in; validators who perform their roles well are rewarded, and validators who perform their roles poorly or are offline are penalized. Phase 0 does not yet include any ETH transfer, sharding or smart contract / VM execution capabilities. + +In subsequent phases, additional mechanisms and validator responsibilities will be added to the beacon chain to manage the consensus of a number of parallel shard chains ("Phase 1"), to integrate the existing Ethereum system ("Phase 1.5") and to add full support for sharded smart contract execution ("Phase 2"). + +### Issuance + +To incentivize validators to deposit ether collateral and participate in the eth2 consensus, we propose that rewards (in the form of Ethereum's native asset, ether) be regularly issued to consensus participants. Due to the beacon chain operating in parallel to the existing PoW chain in early phases of eth2, this issuance is _in addition to_ any PoW rewards until the existing chain is merged into eth2 as a shard. + +The amount of ether issued to validators on the beacon chain is proportional to the square root of the total ether deposited. This issuance curve was chosen as a more stable and sustainable curve to the two obvious alternatives -- fixed total issuance and fixed issuance per ether staked. For a more technical discussion on this choice see [here](../assets/eip-2982/ef-Discouragement-Attacks.pdf). + +In eth2, this curve is parameterized by `BASE_REWARD_FACTOR` in the context of slot time and epoch length. Below is the issuance curve as a function of ether staked, along with a table of examples for illustration. Note, all figures shown are annualized. + +![](../assets/eip-2982/2982-issuance.png) + +| Active Deposits | Max Annual Validator Reward\* | Max Annual ETH Issued\* | +| -------- | -------: | --------: | +| 0.5M ETH | 23.50% | 117,527 | +| 1M ETH | 16.60% | 166,208 | +| 2M ETH | 11.75% | 235,052 | +| 4M ETH | 8.31% | 332,411 | +| 8M ETH | 5.88% | 470,104 | +| 16M ETH | 4.16% | 664,801 | +| 32M ETH | 2.94% | 940,167 | +| 64M ETH | 2.08% | 1,329,603 | +| 128M ETH | 1.47% | 1,880,334 | + +_\*Assuming validators are online 100% of the time and behaving optimally. Suboptimal validator behavior will lead to reduced rewards and/or penalties that reduce total issuance._ + +### Initial punitive parameters + +For PoS protocols to be crypto-economically secure, in-protocol penalties are required. Small offline penalties incentivize validator liveness, whereas (potentially) much larger penalties provide protocol security in the event of tail-risk scenarios. + +Specifically, the following significant penalties exist: +* **Inactivity Leak**: an offline penalty that increases each epoch is applied to validators during extended times of no finality (e.g. if one-third or more are offline or not on the canonical chain). This ensures the chain can eventually regain finality even under catastrophic conditions. +* **Slashing**: a penalty applied to validators that sign _explicitly malicious_ messages that could lead to the construction and finalization of two conflicting chains (e.g. two blocks or attestations in the same slot). This penalty is designed to scale up in proportion to the number of slashable validators in the same time period such that if a critical number (wrt chain safety) of slashings have occurred, validators are _maximally_ punished. + +For the initial launch of Phase 0, the parameters defining the magnitude of these penalties -- `INACTIVITY_PENALTY_QUOTIENT`, `PROPORTIONAL_SLASHING_MULTIPLIER`, and `MIN_SLASHING_PENALTY_QUOTIENT` -- have been tuned to be less punitive than their final intended values. This provides a more forgiving environment for early validators and client software in an effort to encourage validation in this early, higher technical-risk stage. + +_`INACTIVITY_PENALTY_QUOTIENT` is configured initially to four times its final value_. This results in a slower inactivity leak during times of non-finality, which means the chain is less responsive to such an event. If there is an extended time of non-finality during the early months of eth2, it is far more likely to be due to technical issues with client software rather than some sort of global catastrophic event. + +_`PROPORTIONAL_SLASHING_MULTIPLIER` is configured initially to one-third of its final value_. This results in a lower accountable safety margin in the event of an attack. If any validators are slashed in the early months of eth2, it is far more likely to be the result of user mismanagement of keys and/or issues with client software than an organized attack. + +_`MIN_SLASHING_PENALTY_QUOTIENT` configured initially to four times its final value_. This results in a lower guaranteed minimum penalty for a slashable offense and thus reduces the baseline punitive incentive to keep an individual validator's system secure. As with `PROPORTIONAL_SLASHING_MULTIPLIER`, slashings during the early months of eth2 are far more likely to be due to user mismanagement, or issues with client software, than an organized attack. + +## Rationale + +### Principles + +* **Simplicity**: especially since cryptoeconomic proof of stake and quadratic sharding are inherently complex, the protocol should strive for maximum simplicity in its decisions as much as possible. This is important because it (i) minimizes development costs, (ii) reduces risk of unforeseen security issues, and (iii) allows protocol designers to more easily convince users that parameter choices are legitimate. When complexity is unavoidable to achieve a given level of functionality, the preference order for where the complexity goes is: layer 2 protocols > client implementations > protocol spec. +* **Long-term stability**: the low levels of the protocol should ideally be built so that there is no need to change them for a decade or longer, and any needed innovation can happen on higher levels (client implementations or layer 2 protocols). +* **Sufficiency**: it should be fundamentally possible to build as many classes of applications as possible on top of the protocol. +* **Defense in depth**: the protocol should continue to work as well as possible under a variety of possible security assumptions (eg. regarding network latency, fault count, the motivations of users) +* **Full light-client verifiability**: given some assumptions (eg. network latency, bounds on attacker budget, 1-of-N or few-of-N honest minority), a client verifying a small fixed amount of data (ideally just the beacon chain) should be able to gain indirect assurance that all of the data in the full system is available and valid, even under a 51% attack (note: this is a form of defense-in-depth but it's important enough to be separate) + +### The Layer 1 vs Layer 2 Tradeoff + +The Ethereum roadmap uses a mixed layer 1 / layer 2 approach. We focus on serving a particular type of layer 2 (rollups) because it's the only kind of layer 2 that both inherits the security of layer 1 and provides scaling of general-purpose applications. However, rollups come at a cost: they require some on-chain data _per transaction_, and so a blockchain with really high capacity rollups must be able to handle a still quite high amount of data bandwidth. So make this more feasible, we are implementing on scalable data layer technologies, particularly data availability sampling. + +The reason to not take a pure layer 2 approach is that pure layer 2 scaling can only be done either with trust-based solutions (not desirable), or with channels or plasma (which have inherent limitations and cannot support the full EVM. + +The reason to not take a pure layer 1 approach is to enable more room for experimentation in execution layers, and allow the base protocol to be simpler and have less intensive governance. + +### Why proof of stake + +In short: + +* **No need to consume large quantities of electricity** in order to secure a blockchain (e.g. it's estimated that both Bitcoin and Ethereum burn over $1 million worth of electricity and hardware costs per day as part of their consensus mechanism). +* Because of the lack of high electricity consumption, there is **not as much need to issue as many new coins** in order to motivate participants to keep participating in the network. It may theoretically even be possible to have negative net issuance, where a portion of transaction fees is "burned" and so the supply goes down over time. +* Proof of stake opens the door to a wider array of techniques that use game-theoretic mechanism design in order to better **discourage centralized cartels** from forming and, if they do form, from acting in ways that are harmful to the network (e.g. like selfish mining in proof of work). +* **Reduced centralization risks**, as economies of scale are much less of an issue. $10 million of coins will get you exactly 10 times higher returns than $1 million of coins, without any additional disproportionate gains because at the higher level you can afford better mass-production equipment, which is an advantage for Proof-of-Work. +* Ability to use economic penalties to **make various forms of 51% attacks vastly more expensive** to carry out than proof of work - to paraphrase Vlad Zamfir, "it's as though your ASIC farm burned down if you participated in a 51% attack". + +### Why Casper + +There are currently three major schools of proof of stake consensus algorithm: + +* **Nakamoto-inspired** (Peercoin, NXT, Ouroboros...) +* **PBFT-inspired** (Tendermint, Casper FFG, Hotstuff...) +* **CBC Casper** + +Within the latter two camps, there is also the question of whether and how to use security deposits and slashing (Nakamoto-inspired algorithms are incompatible with non-trivial slashing). All three are superior to proof of work, but we want to defend our own approach. + +#### Slashing + +Ethereum 2.0 uses a **slashing** mechanism where a validator that is detected to have misbehaved can be penalized, in the best case ~1% but in the worst case up to its entire deposit. + +We defend our use of slashing as follows: + +1. **Raising the cost of attack**: We want to be able to make a hard claim that a 51% attack on a proof of stake blockchain forces the attacker to incur a very large amount of expense (think: hundreds of millions of dollars worth of coins) that get burned, and any attack can be recovered from quickly. This makes the attack/defense calculus very unfavorable for attackers, and in fact makes attacks potentially _counterproductive_, because the disruption to service is outweighed by price gains to legitimate coin holders. +2. **Overcoming the validator's dilemma**: the most realistic immediate way for nodes to start to deviate from "honest" behavior is _laziness_ (ie. not validating things that one should be validating, signing everything just in case, etc). See [the validator's dilemma paper](../assets/eip-2982/iacr-2015-702-Demystifying-Incentives-in-the-Consensus-Computer.pdf) (Luu et al., CC BY) for theoretical reasoning and the Bitcoin SPV mining fork for examples of this happening and leading to very harmful consequences in the wild. Having very large penalties for self-contradicting or for signing incorrect things helps to alleviate this. + +A more subtle instance of (2) can be seen as follows. In July 2019 a validator on Cosmos was slashed for signing two conflicting blocks. An investigation revealed that this happened because that validator was running a primary and a backup node (to ensure that one of the two going offline would not prevent them from getting rewards) and the two were accidentally turned on at the same time, leading to them contradicting each other. + +If it became standard practice to have a primary and backup node, then an attacker could partition the network and get the primaries and the backups of all the validators to commit to different blocks, and thereby lead to two conflicting blocks being finalized. Slashing penalties help to heavily disincentivize this practice, reducing the risk of such a scenario taking place. + +#### Choice of consensus algorithm + +Only the BFT-inspired and CBC schools of consensus algorithm have a notion of finality, where a block is confirmed in such a way that a large portion (1/3 in BFT-inspired, 1/4 in CBC) of validators would need to misbehave and get slashed for that block to get reverted in favor of some conflicting block; Nakamoto-inspired (ie. longest-chain-rule) consensus algorithms have no way of achieving finality in this sense. + +Note that finality requires a (super)majority of validators being online, but this is a requirement of the sharding mechanism already, as it requires 2/3 of a randomly sampled committee of validators to sign off on a crosslink for that crosslink to be accepted. + +Our choice of [Casper FFG](../assets/eip-2982/arxiv-1710.09437-Casper-the-Friendly-Finality-Gadget.pdf) was simply a matter of it being the simplest algorithm available at the time that part of the protocol was being finalized. Details are still subject to long-term change; in particular, we are actively exploring solutions to achieve single slot finality. + +### Sharding - or, why do we hate supernodes? + +The main alternative to sharding for layer-1 scaling is the use of supernodes - simply requiring every consensus node to have a powerful server so that it can individually process every transaction. Supernode-based scaling is convenient because it is simple to implement: it works just the same way blockchains do now, except that more software-engineering work is required to build things in a way that is more parallelizable. + +Our main objections to this approach are as follows: + +* **Pool centralization risk**: in a supernode-based system, running a node has a high fixed cost, so far fewer users can participate. This is usually rebutted with "well consensus in most PoW and PoS coins is dominated by 5-20 pools anyway, and the pools will be able to run nodes just fine". However, this response ignores the risk of centralization pressure even between pools that can afford it. If the fixed cost of running a validator is significant relative to the returns, then larger pools will be able to offer smaller fees than smaller ones and this could lead to smaller pools being pushed out or feeling pressure to merge. In a sharded system, on the other hand, validators with more ETH need to verify more transactions, so costs are not fixed. +* **AWS centralization risk**: in a supernode-based system, home staking is infeasible and so it's more likely that most staking will happen inside cloud computing environments, of which there are only a few options to choose from. This creates a single point of failure. +* **Reduced censorship resistance**: making it impossible to participate in consensus without high computation+bandwidth requirements makes detection and censorship of validators easier. +* **Scalability**: as transaction throughput increases, in a supernode-based system the above risks increase, whereas sharded systems can more easily handle the increased load. + +These centralization risks are also why we are NOT attempting to achieve super-low-latency (<1s) of the blockchain, instead opting for (relatively!) conservative numbers. + +Instead, Ethereum is taking an approach where each validator is only assigned to process a small portion of all data. Only validators staking large amounts of ETH (think: tens of thousands or more) are required to process the entire data in the chain. + +Note that there is a possible middle-ground in sharding design where block _production_ is centralized but (i) block _verification_ is decentralized and (ii) there exist "bypass channels" where users can send transactions and block producers are forced to include them, so even a monopoly producer cannot censor. We are actively considering sharding designs that lean somewhat in this direction to increase simplicity so that scaling can be deployed faster, though if desired even within this spec it's possible to run distributed builders and avoid centralization even there. + + +### Security models + +It's commonly assumed that blockchains depend on an "honest majority" assumption for their security: that >=50% of participants will faithfully follow a prescribed protocol, even forgoing opportunities to defect for their own personal interest. In reality, (i) an honest majority model is unrealistic, with participants being "lazy" and signing off on blocks without validating them (see [the validator's dilemma paper](../assets/eip-2982/iacr-2015-702-Demystifying-Incentives-in-the-Consensus-Computer.pdf) and the Bitcoin SPV mining fork) being a very common form of defection, but fortunately (ii) blockchains maintain many or all of their security properties under much harsher models, and it's really important to preserve those extra guarantees. + +A common harsher model is the _uncoordinated rational majority_ model, which states that participants act in their own self-interest, but no more than some percentage (eg. 23.21% in simple PoW chains) are cooperating with each other. An even harsher model is the worst-case model where there is a single actor that controls more than 50% of hashpower or stake, and the question becomes: + +* Can we, even under that scenario, force the attacker to have to pay a very high cost to break the chain's guarantees? +* What guarantees can we unconditionally preserve? + +Slashing in proof of stake chains accomplishes the first objective. In non-sharded chains, every node verifying every block accomplishes the second objective for two specific guarantees: (i) that the longest chain is valid, and (ii) that the longest chain is _available_. + +A major challenge in sharding is getting the same two properties without requiring each node to verify the full chain. Our defense-in-depth approach with sharding accomplishes just that. The core idea is to combine together random committee sampling, proof of custody, fraud proofs, [data availability sampling (DAS)](../assets/eip-2982/arxiv-1809.09044-Fraud-and-Data-Availability-Proofs--Maximising-Light-Client-Security-and-Scaling-Blockchains-with-Dishonest-Majorities.pdf) and eventually ZK-SNARKs, to allow clients to detect and reject invalid or unavailable chains without downloading and verifying all of the data, even if the invalid chains are supported by a majority of all proof of stake validators. + +Censorship of transactions can potentially be detected by clients in a consensus-preserving way, but this research has not yet been incorporated into the ethereum roadmap. + +Here is the current expected security properties expressed in a table: + +|| Network delay <3s |Network delay 3s - 6 min|Network delay > 6 min| +|---|---|---|---|---| +|>2/3 validators honest|Perfect operation|Imperfect but acceptable operation. No rigorous proof of liveness, but liveness expected in practice.|Likely intermittent liveness failures, no safety failures| +|>2/3 validators rational, <1/3 coordinated|Perfect operation|Imperfect but acceptable operation, heightened centralization risk|Likely intermittent liveness failures, no safety failures, very high centralization risk| +|51% attacker|Can revert finality or censor, but at high cost; cannot force through invalid or unavailable chains|Can revert finality or censor, but at high cost; cannot force through invalid or unavailable chains|Can revert finality or censor; cannot force through invalid or unavailable chains| + +### Why are the Casper incentives set the way they are? + +#### Base rewards + +During each epoch, every validator is expected to make an "attestation", a signature that expresses that validator's opinion on what the head of the chain is. There is a reward for having one's attestation included, with four components (called "duties"): + +1. Reward for the attestation getting included at all +2. Reward for the attestation specifying the correct epoch checkpoint +3. Reward for the attestation specifying the correct chain head +4. Reward for correctly participating in sync committee signatures + +Note also that mixed into these duties is a timeliness requirement: your attestation has to be included within a certain time to be eligible for the rewards, and for the "correct head" duty that time limit is 1 slot. + +For each duty, the actual reward is computed as follows. If: + +* $R = B * \frac{nom}{den}$ equals the base reward multiplied by the fraction $\frac{nom}{den}$ that corresponds to that particular duty +* $P$ is the portion of validators that did the desired action + +Then: + +* Any validator that fulfilled the dury gets a reward of $R * P$ +* Any validator that did not fulfill the duty gets a penalty $-R$ + +The purpose of this "collective reward" scheme where "if anyone performs better, everyone performs better" is to bound griefing factors (see [this paper](../assets/eip-2982/ef-Discouragement-Attacks.pdf) for one description of griefing factors and why they are important). + +The base reward $B$ is itself calculated as $k * \frac{D_i}{\sqrt{\sum_{j=1}^{n} D_j}}$ where $D_1 ... D_n$ are deposit sizes and $k$ is a constant; this is a halfway compromise between two common models, (i) fixed reward rate, ie. $k * D_i$, and (ii) fixed total reward, ie. $k * \frac{D_i}{\sum_{j=1}^{n} D_j}$. + +The main argument against (i) is that it imposes too much uncertainty on the network of two kinds: uncertainty of the total level of issuance, and uncertainty of the total level of deposits (as if a fixed reward rate is set too low then almost no one will participate, threatening the network, and if a rate is set too high then very many validators will participate, leading to unexpectedly high issuance). The main argument against (ii) is greater vulnerability to discouragement attacks (again see the [discouragement attacks paper](../assets/eip-2982/ef-Discouragement-Attacks.pdf)). The inverse-square-root approach compromises between the two and avoids the worst consequences of each one. + +When an attestation gets a reward, the proposer gets a fraction of that reward. This is to encourage proposers to listen well for messages and accept as many as possible. + +Note also that the rewards are designed to be forgiving to validators who are offline often: being offline 1% of the time only sacrifices about 1.6% of your reward. This is also to promote decentralization: the goal of a decentralized system is to create a reliable whole out of unreliable parts, so we should not be trying to force each individual node to be extremely reliable. + +#### Inactivity leak + +If the chain fails to finalize for $tsf > 4$ epochs ($tsf$ = "time since finality"), then a penalty is added so that the maximum possible reward is zero (validators performing imperfectly get penalized), and a second penalty component is added, proportional to $tsf$ (that is, the longer the chain has not finalized, the higher the _per-epoch_ penalty for being offline). This ensures that if more than 1/3 of validators drop off, validators that are not online get penalized much more heavily, and the total penalty goes up quadratically over time. + +This has three consequences: + +* Penalizes being offline much more heavily in the case where you being offline is actually preventing blocks from being finalized +* Serves the goals of being an anti-correlation penalty (see section below) +* Ensures that if more than 1/3 do go offline, eventually the portion online goes back up to 2/3 because of the declining deposits of the offline validators + +With the current parametrization, if blocks stop being finalized, validators lose 1% of their deposits after 2.6 days, 10% after 8.4 days, and 50% after 21 days. This means for example that if 50% of validators drop offline, blocks will start finalizing again after 21 days. + +#### Slashing and anti-correlation penalties + +If a validator is caught violating the Casper FFG slashing condition, they get penalized a portion of their deposit equal to three times the portion of validators that were penalized around the same time as them (specifically, between 18 days before they were penalized and roughly the time they withdrew). This is motivated by several goals: + +* A validator misbehaving is only really bad for the network if they misbehave at the same time as many other validators, so it makes sense to punish them more in that case +* It heavily penalizes actual attacks, but applies very light penalties to single isolated failures that are likely to be honest mistakes +* It ensures that smaller validators take on less risk than larger validators (as in the normal case, a large validator would be the only one failing at the same time as themselves) +* It creates a disincentive against everyone joining the largest pool + +### BLS Signatures + +BLS signatures are used because of their aggregation-friendliness: any two signatures $S_1$ and $S_2$ of a message $M$ signed by keys $k_1$ and $k_2$ (corresponding pubkeys $K_1 = G * k_1$ and $K_2 = G * k_2$ where $G$ is the generator of the elliptic curve) can simply be aggregated by elliptic curve point addition: $S_1 + S_2$, which verifies against the public key $K_1 + K_2$. This allows many thousands of signatures to be aggregated, with the marginal cost of one signature being one bit of data (to express that a particular public key is present in the aggregate signature) and one elliptic curve addition for computation. + +Note that BLS signatures of this form are vulnerable to _rogue key attacks_: if you see that other validators have already published public keys $K_1 ... K_n$, then you can generate a private key $r$ and publish a public key $G * r - K_1 - ... - K_n$. The aggregate public key would simply be $G * r$, so you would be able to make a signature that verifies against the combined public key by yourself. The standard way to get around this is to require a _proof of possession_: basically, a signature of a message (that depends on the public key, and that would normally not be signed) that verifies against the public key by itself (ie. $sign(message=H'(K), key=k)$ for privkey $k$ and pubkey $K$, where $H'$ is a hash function). This ensures that you personally control the private key connected to the public key that you publish. + +We use the signature of a deposit message (which specifies the signing key but also other important data such as the withdrawal key) as a proof of possession. + +### Why 32 ETH validator sizes? + +Any BFT consensus algorithm with accountable fault tolerance (ie. if two conflicting blocks get finalized you can identify which 1/3 of nodes were responsible) must have all validators participate, and furthermore for technical reasons you need two rounds of every validator participating to finalize a message. + +This leads to the decentralization / finality time / overhead tradeoff: if $n$ is the number of validators in a network, $f$ is the time to finalize a block, and $\omega$ is the overhead in messages per second, then we have: + +$$\omega \ge \frac{2 * n}{f}$$ + +For example, if we are ok with an overhead of 10 messages per second, then a 10000-node network could only have a finality time of at least 2000 seconds (~33 minutes). + +In Ethereum's case, if we assume a total ETH supply of $\approx 2^{27}$ ETH, then with 32 ETH deposit sizes, there are at most $2^{22}$ validators (and that's if everyone validates; in general we expect ~10x less ETH validating). With a finality time of 2 epochs (2 * 32 * 12 = 768 seconds), that implies a max overhead of $\frac{2^{22}}{768} \approx 5461$ messages per second. We can tolerate such high overhead due to BLS aggregation reducing the marginal size of each signature to 1 bit and the marginal verification complexity to one elliptic curve addition. + +32 slots is a safe minimum for another reason: if an attacker manipulates the randomness used for proposer selection, this number still provides enough space to ensure that there will be at least one honest proposer in each epoch, which is sufficient to ensure blocks keep finalizing. Our calculations suggest that current levels of overhead are acceptable, but higher levels would make running a node too difficult. Finally, the validator deposit size is ideal for shard crosslinking (see below). + +### Random sampling + +#### Seed selection + +The seed used for randomness is updated every block by "mixing in" (ie. `seed <- xor(seed, new_data)`) a value that must be revealed by the proposer of the block. Just like proof of custody subkeys, a validator's values are all pre-determined once the validator has deposited, third parties cannot compute subkeys but can verify subkeys that are revealed voluntarily (this mechanism is sometimes called RANDAO). + +This ensures that each proposer has one "bit of manipulation" over the seed: they can either make a block or not make a block. Not making a block is expensive in that the proposer misses out on many rewards. Furthermore, because the persistent and beacon committee sizes (see below) are large, manipulation of the randomness almost certainly cannot allow minority attackers to get 2/3 of any committee. + +In the future we plan to use verifiable delay functions (VDFs) to further increase the random seeds' robustness against manipulation. + +#### Shuffling + +We use the swap-or-not shuffle described by Viet Tung Hoang, Ben Morris, and Phillip Rogaway to shuffle the validator set and assign responsibilities every epoch. This algorithm ensures that: + +* As the shuffle is a permutation, each validator is assigned to be a member of exactly one committee during each epoch (keeping their load stable and reducing the chance manipulation of randomness can be profitable) +* As the shuffle is pointwise-evaluable in the forward direction, a validator can determine their own responsibilities in O(1) time +* As the shuffle is pointwise-evaluable in the reverse direction, the members of any specific committee or the proposer of any specific block can be computed in O(1) time + +#### Shuffling by slot + +There are 32 slots in an epoch, and the validators responsible for attesting to each slot are chosen by shuffling. This ensures that attackers with a significant but still small portion of total stake cannot take over specific slots and cause short-range reorgs. + +#### Beacon committees + +The committee for each slot is in turn split up into some number of _beacon committees_. Today (2022 Jan), this design does serve one useful function, allowing different subsets of attestations to get aggregated in separate subnets and thus making the p2p network more efficient. However, it does not serve any other useful consensus-related role. + +In the original sharding design, the intention was that each beacon committee would be responsible for verifying a specific shard. However, this approach is likely deprecated if we switch to a single-proposer model. Hence, the more fine-grained beacon committees may well only be there vestigially and could eventually be removed or re-structured in a different way to focus on facilitating attestation aggregation. + +#### Sync committee + +A sync committee of 512 validators is selected once every ~27 hours to sign a block; this committee's pubkeys are saved in an easily accessible list, allowing signatures to be easily verified by ultra-light clients. + +### LMD GHOST fork choice rule + +The beacon chain uses an LMD GHOST fork choice rule. + +The LMD GHOST fork choice rule incorporates information from all validators, hundreds in each slot, making it extremely unlikely in the normal case that even a single block will be reverted. Because the fork choice is dependent on all validators, this also ensures that blocks cannot be reverted unless an attacker really does control close to 50% of the entire validator set; one cannot achieve a large advantage by manipulating the randomness. + +### The proof of custody game + +For each 9-day period, each validator has the ability to privately generate a "period subkey". The validator's public key uniquely determines their period subkey for every period, so once a validator has deposited they have no further freedom to choose what their subkeys are. No one can compute any given validator's subkey except the validator themselves, but once a validator reveals a subkey voluntarily, anyone can verify its correctness (this is all done via BLS magic, and if quantum-safety is required in the future it can be done with hash-based commitments). + +When signing a block with data $D$ with root $R$ during period $j$, letting $s$ being the period-$j$ secret of that validator, a validator is required to compute a bitfield $M$ as follows: + +* Split $D$ into 512-byte chunks $D[0] ... D[n-1]$ +* Set $M$ to be the bitfield where the i'th bit is $M[i] = mix(s, D[i])$ + +They include `get_chunk_bits_root(M)` (this is called the **custody bit**) as part of what they are signing. `mix` and `get_chunk_bits_root` can both be viewed as hash-like functions that output a single bit (but are designed for multi-party-computation friendliness). + +If a crosslink includes multiple blocks $B_1 ... B_n$ and the validator computes custody bits $c_1 ... c_n$ for these blocks, then the validator signs all $(B_i, c_i, i)$ tuples and aggregates them. This unconventional self-aggregation is used to ensure that there are only $2n$ different messages (n different block/index pairs * 2 different bit values) being signed by different validators, reducing the number of pairings needed to verify the signatures. + +If a validator publishes their period $j$ subkey during or before period $j$, any other validator can publish a proof-of-knowledge of the subkey to the chain, which then penalizes the validator whose subkey was revealed. The proof of knowledge also proves which validator created it; this prevents block proposers from "stealing" the whistleblowing reward (once again all done via BLS magic, if quantum-safety is required in the future this can be done with STARKs). The goal of this is to make it dangerous to outsource computation of $M$. + +After a validator publishes their period $j$ subkey, anyone else can check their work for any block that they signed. If they discover that a validator provided an incorrect custody bit, they can challenge this on-chain, and slash that validator. In general, random guessing (or any procedure that does not involve all of $D$) will only give a correct answer half the time, leading to a 50% risk of slashing per block signed. + +The purpose of this mechanism is to mitigate the [validator's dilemma](../assets/eip-2982/iacr-2015-702-Demystifying-Incentives-in-the-Consensus-Computer.pdf) problem, where validators have an incentive to avoid verifying data out of laziness and piggyback on the assumption that all _other_ validators are honest; if too many validators think in this way, it could lead to a tragedy of the commons leading to the chain accepting invalid blocks. With this scheme, if a validator attempts to commit to data that they did not personally process, then they would not be able to compute $M$, and so would lose the interactive challenge game. + +### SSZ + +The SimpleSerialize suite contains the following algorithms: + +* A serialization algorithm +* A hashing algorithm (called `SSZTreeHash` or `hash_tree_root`) +* A generalized Merkle proof algorithm (called "SSZ partials") that can handle proofs for multiple values and optimally deduplicates Merkle tree sister nodes in such cases. + +The serialization algorithm has the following design goals: + +* Simplicity (eg. no three clauses like RLP has for long / list encoding depending on item length) +* Being equivalent to simple concatenation in the case of entirely fixed-size objects +* Being usable as both a consensus-layer serialization algorithm and an application-layer ABI + +Note that the resulting SSZ serialization spec is very similar to the ethereum 1.0 ABI, with the major differences being (i) different basic data types and (ii) replacing the 32 byte length/position record size with a 4 byte size. + +The hashing algorithm has the following design goals: + +* Efficiency of computing the hash of an updated object, especially in the case where the object is very large and/or complex and the change is relatively small +* Efficiency (in terms of verification complexity _and_ witness size) of proving the value of a specific field even in a large or complex object with a given hash + +These requirements together make a Merkle tree structure the obvious choice. + +#### Generalized indices + +See https://github.com/ethereum/eth2.0-specs/blob/dev/ssz/merkle-proofs.md for a description of generalized indices, and how they allow for very easy verification of Merkle proofs "poking into" arbitrary positions in an object by representing paths as an integer or bitfield. + +### The validator lifecycle + +#### Depositing + +A validator deposits by sending a transaction that calls a function on the deposit contract on the eth1 chain (eventually, a way to deposit from inside eth2 will also be added). A deposit specifies: + +* The public key corresponding to the private key that will be used to sign messages +* The withdrawal credentials (hash of the public key that will be used to withdraw funds once the validator is done validating) +* The deposit amount + +These values are all signed by the signing key. The goal of having separate signing and withdrawal keys is to allow the more dangerous withdrawal key to be kept more securely (offline, not shared with any staking pools, etc) while the signing key is used to actively sign a message every epoch. + +The deposit contract maintains a Merkle root of all deposits made so far. Once a merkle root containing the deposit gets included into the eth2 chains (via the Eth1Data voting mechanism), an eth2 block proposer can submit a Merkle proof of the deposit and start the deposit process. + +#### Activation + +The validator immediately joins the validator registry, but is at first inactive. The validator becomes active after $N \ge 4$ epochs; the minimum of 4 is there to ensure the RANDAO is not manipulable, and $N$ may exceed 4 if too many validators try to join at the same time. If the active validator set has size $|V|$, a maximum of $max(4, \frac{|V|}{65536})$ valdators can join per epoch; if more validators try to join, they are put in a queue and processed as quickly as possible. + +#### Exiting + +When a validator exits (whether by publishing a voluntary exit message or being slashed), they are similarly put into an exit queue with the same maximum throughput. + +The reason for the entrance/exit queue limits is to ensure that the validator set cannot change too quickly between any two points in time, which ensures that finality guarantees still remain between two chains as long as a validator logs on often enough (once every ~1-2 months if $|V| \ge 262144$). See here https://ethresear.ch/t/rate-limiting-entry-exits-not-withdrawals/4942 for rationale (and specifically why to use a rate limit instead of a fixed waiting time) and https://ethresear.ch/t/weak-subjectivity-under-the-exit-queue-model/5187 for how to calculate safety margin for a client that has been offline for some amount of time. + +#### Withdrawal + +Once a validator leaves the exit queue, there is a ~27 hour period until they can withdraw. This period has several functions: + +* It ensures that if a validator misbehaved there is a period of time within which the error can be caught and the validator can be slashed even if the exit queue is nearly empty. +* It provides time for the last period of shard rewards to be included. +* It provides time for proof of custody challenges to be made. + +If a validator is slashed, a further delay of ~36 days is imposed. This further penalizes them (and forces them to hold the ETH; this makes the penalty somewhat larger for malicious validators that are trying to destroy the ethereum blockchain than those validators that want to support the platform but just made a mistake, as the former category would have to risk the exposure or pay for derivatives to cancel it out), but also allows for a period during which the number of other validators that got slashed can be calculated. + +In phase 0, a "withdrawn" validator cannot actually withdraw to anywhere yet; the only distinction is that it is protected from validator penalties and does not have any responsibilities. In later phases, the ability to move funds from a withdrawn validator slot to an execution environment will be made available. + +#### Effective balances + +Most calculations based on a validator's balance use the validator's "effective balance"; the only exception is calculations that increase or decrease the validator's balance. Only when the validators's balance $B$ falls below $EB$ or goes above $EB + 1.5$ is $EB$ adjusted to equal $floor(B)$. This is done to ensure that effective balance changes infrequently, reducing the amount of hashing needed to do to recompute the state every epoch; on average, only the balances need to all be updated, and the effective balances only get updated relatively rarely per validator. Balances are all stored in a dedicated vector, so $2 * \frac{N * 8}{32} = \frac{N}{2}$ hashes are needed to recompute a balance array, whereas effective balances are stored in validator record objects, where at least $5N$ hashes would be needed per validator to adjust the SSZ hash roots. Additionally, $EB$ can be compressed more easily into a CompactValidator object, as it's only one byte. + +### Fork mechanism + +The `Fork` data structure contains (i) the current "fork ID", (ii) the previous fork ID and (iii) the switchover slot. The fork ID at the current height influences the valid signatures of all messages; hence, a message signed with one fork ID is invalid to a verification function using any other fork ID. + +A fork is done by adding a state transition at some "fork slot" $n$ which sets the previous fork ID to the current fork ID, the current fork ID to a new value, and the switchover slot to $n$. Signature verification functions will verify messages using the fork ID at the slot the message is for, which could be the previous fork ID or the current one (eg. consider the case of attestations included after a delay; an attestation from before the fork slot could be included after the fork slot). + +If any users do not want to join the fork, they can simply continue the chain that does not change the fork ID at the fork slot. The two chains would be able to proceed and validators would be free to validate on both without getting slashed. + +## Backwards Compatibility + +Although this EIP does not introduce any immediate changes to the current Ethereum mainnet, this EIP lays the groundwork for future backwards incompatibilities through the introduction of the new eth2 consensus mechanism in which Ethereum will be integrated in subsequent phases. To secure this mechanism, users move ether into the beacon chain and additional ether is issued. This EIP is a commitment to this path being canonical, as well as directly informing the future and roadmap of Ethereum mainnet. + +## Security Considerations + +Eth2 is a major overhaul of the Ethereum's core consensus from PoW to a sharded PoS. There are inherent risks in this migration but there is extensive research literature analyzing security and trade-offs. _The following only represents a high level selection of the resources available:_ + +* [Casper FFG](../assets/eip-2982/arxiv-1710.09437-Casper-the-Friendly-Finality-Gadget.pdf) +* [Combining GHOST and Casper](../assets/eip-2982/arxiv-2003.03052-Combining-GHOST-and-Casper.pdf) + +In addition to the research supporting this path, a number of audits and formal verification of specs, cryptography, and client implementations have been performed. _Many client and utility library audits are currently in progress and will be appended here upon completion._ + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-2997.md b/EIPS/eip-2997.md new file mode 100644 index 0000000..ee1152b --- /dev/null +++ b/EIPS/eip-2997.md @@ -0,0 +1,103 @@ +--- +eip: 2997 +title: IMPERSONATECALL Opcode +author: Sergio Demian Lerner (@SergioDemianLerner) +discussions-to: https://ethresear.ch/t/impersonatecall-opcode/8020 +category: Core +type: Standards Track +status: Stagnant +created: 2020-09-24 +--- + +## Abstract + +Add a new opcode, `IMPERSONATECALL` at `0xf6`, which is similar in idea to `CALL (0xF1)`, except that it impersonates a sender, i.e. the callee sees a sender different from the real caller. The impersonated sender address is derived from the real caller address and a salt. + +## Motivation + +This proposal enables native multi-user wallets (wallets that serve multiple users) that can be commanded by EIP-712 based messages and therefore enable meta-transactions. Multi-user wallets also enable the aggregation of transfer operations in batches similar to rollups, but maintaining the same address space as normal onchain transactions, so the sender's wallet does not need to be upgraded to support sinding ether or tokens to a user of a multi-user wallet. +Additionally, many times a sponsor company wants to deploy non-custodial smart wallets for all its users. The sponsor does not want to pay the deployment cost of each user contract in advance. Counterfactual contract creation enables this, yet it forces the sponsor to create the smart wallet (or a proxy contract to it) when the user wants to transfer ether or tokens out of his/her account for the first time. This proposal avoids this extra cost, which is at least 42000 gas per user. + + +## Specification + +`IMPERSONATECALL`: `0xf6`, takes 7 operands: + +- `gas`: the amount of gas the code may use in order to execute; +- `to`: the destination address whose code is to be executed; +- `in_offset`: the offset into memory of the input; +- `in_size`: the size of the input in bytes; +- `ret_offset`: the offset into memory of the output; +- `ret_size`: the size of the scratch pad for the output. +- `salt` is a `32` bytes value (a stack item). + +### Computation of impersonated sender + +The impersonated sender address is computed as `keccak256( 0xff ++ address ++ salt ++ zeros32)[12:]`. + +- `0xff` is a single byte, +- `address` is always `20` bytes, and represents the address of the real caller contract. +- `salt` is always `32` bytes. + +- The field zeros32 corresponds to 32 zero bytes. + +This scheme emulates `CREATE2` address derivation, but it cannot practically collude with the `CREATE2` address space. + +### Notes +- The opcode behaves exactly as `CALL` in terms of gas consumption. +- In the called context `CALLER (0x33)` returns the impersonated address. +- If value transfer is non-zero in the call, the value is transferred from the impersonated account, and not from the real caller. This can be used to transfer ether out of an impersonated account. + +## Rationale + +Even if `IMPERSONATECALL` requires hashing 3 words, implying an additional cost of 180 gas, we think the benefit of accounting for hashing doesn't not compensate increasing the complexity of the implementation. + +We use the zeros32 field to base address derivation in a pre-image of similar size than CREATE2 and reuse the existing address derivation functions. We also avoid worrying about address collisions between EOA derivation (65 bytes pre-image), CREATE derivation (from 23 to 27 bytes pre-image, for a 32bit nonce) and CREATE2 derivation (85 bytes pre-image). + +An option is to omit the zeros32 field: the resulting length of the Keccak pre-image for IMPERSONATECALL address is 53 bytes , which does not generate address collision. + +While the same functionality could be provided in a pre-compiled contract, we believe using a new opcode is a cleaner solution. + + +## Clarifications + +- This EIP makes address collisions possible, yet practically impossible. + +- If a contract already exists with an impersonated address, the `IMPERSONATECALL` is executed in the same way, and the existing code will not be executed. It should be noted that `SELFDESTRUCT` (`0xff`) cannot be executed directly with `IMPERSONATECALL` as no opcode is executed in the context of the impersonated account. + +## Backward Compatibility + +The opcode number `0xf6` is currently unused and results in an out-of-gas (OOG) exception. Solidity uses the `INVALID (0xfe)` opcode (called `ABORT` by EIP-1803) to raise OOG exceptions, so the `0xf6` opcode does not appear in normal Solidity programs. Programmers are already advised not to include this opcode in contracts written in EVM assembly. Therefore is does not pose any backward compatibility risk. + +## Test Cases + +We present 4 examples of impersonated address derivation: + +Example 0 + +* address `0x0000000000000000000000000000000000000000` +* salt `0x0000000000000000000000000000000000000000000000000000000000000000` +* result: `0xFFC4F52F884A02BCD5716744CD622127366F2EDF` + +Example 1 +* address `0xdeadbeef00000000000000000000000000000000` +* salt `0x0000000000000000000000000000000000000000000000000000000000000000` +* result: `0x85F15E045E1244AC03289B48448249DC0A34AA30` + +Example 2 +* address `0xdeadbeef00000000000000000000000000000000` +* salt `0x000000000000000000000000feed000000000000000000000000000000000000` +* result: `0x2DB27D1D6BE32C9ABFA484BA3D591101881D4B9F` + +Example 3 +* address `0x00000000000000000000000000000000deadbeef` +* salt `0x00000000000000000000000000000000000000000000000000000000cafebabe` +* result: `0x5004E448F43EFE3C7BF32F94B83B843D03901457` + +## Security Considerations + +The address derivation scheme prevents address collision with another deployed contract or an externally owned account, as the impersonated sender address is derived from the real caller address and a salt. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3.md b/EIPS/eip-3.md new file mode 100644 index 0000000..3d7c969 --- /dev/null +++ b/EIPS/eip-3.md @@ -0,0 +1,47 @@ +--- +eip: 3 +title: Addition of CALLDEPTH opcode +author: Martin Holst Swende +status: Withdrawn +type: Standards Track +category: Core +created: 2015-11-19 +--- + +# Abstract + +This is a proposal to add a new opcode, `CALLDEPTH`. The `CALLDEPTH` opcode would return the remaining available call stack depth. + +# Motivation + +There is a limit specifying how deep contracts can call other contracts; the call stack. The limit is currently `256`. If a contract invokes another contract (either via `CALL` or `CALLCODE`), the operation will fail if the call stack depth limit has been reached. + +This behaviour makes it possible to subject a contract to a "call stack attack" [1]. In such an attack, an attacker first creates a suitable depth of the stack, e.g. by recursive calls. After this step, the attacker invokes the targeted contract. If the targeted calls another contract, that call will fail. If the return value is not properly checked to see if the call was successful, the consequences could be damaging. + +Example: + +1. Contract `A` wants to be invoked regularly, and pays Ether to the invoker in every block. +2. When contract `A` is invoked, it calls contracts `B` and `C`, which consumes a lot of gas. After invocation, contract `A` pays Ether to the caller. +3. Malicious user `X` ensures that the stack depth is shallow before invoking A. Both calls to `B` and `C` fail, but `X` can still collect the reward. + +It is possible to defend against this in two ways: + +1. Check return value after invocation. +2. Check call stack depth experimentally. A library [2] by Piper Merriam exists for this purpose. This method is quite costly in gas. + + +[1] a.k.a "shallow stack attack" and "stack attack". However, to be precise, the word ''stack'' has a different meaning within the EVM, and is not to be confused with the ''call stack''. + +[2] https://github.com/pipermerriam/ethereum-stack-depth-lib + +# Specification + +The opcode `CALLDEPTH` should return the remaining call stack depth. A value of `0` means that the call stack is exhausted, and no further calls can be made. + +# Rationale + +The actual call stack depth, as well as the call stack depth limit, are present in the EVM during execution, but just not available within the EVM. The implementation should be fairly simple and would provide a cheap and way to protect against call stack attacks. + +# Implementation + +Not implemented. diff --git a/EIPS/eip-3000.md b/EIPS/eip-3000.md new file mode 100644 index 0000000..7b36755 --- /dev/null +++ b/EIPS/eip-3000.md @@ -0,0 +1,154 @@ +--- +eip: 3000 +title: Optimistic enactment governance standard +author: Jorge Izquierdo (@izqui), Fabien Marino (@bonustrack) +discussions-to: https://github.com/ethereum/EIPs/issues/3042 +status: Stagnant +type: Standards Track +category: ERC +created: 2020-09-24 +--- + +## Simple Summary + +Interface for scheduling, executing and challenging contract executions based on off-chain approval + +## Abstract + +ERC-3000 presents a basic on-chain spec for contracts to optimistically enact governance decisions made off-chain. + +The standard is opinionated in defining the 6 entrypoint functions to contracts supporting the standard. But it allows for any sort of resolver mechanism for the challenge/response games characteristic of optimistic contracts. + +While the authors currently believe resolving challenges [using a subjective oracle](https://aragon.org/blog/snapshot) is the right tradeoff, the standard has been designed such that changing to another mechanism is possible (a deterministic resolver like [Optimism's OVM](https://optimism.io) uses), even allowing to hot-swap it in the same live instance. + +## Specification + +### Data structures + +Some data structures are defined which are later used in the standard interfaces: + +```solidity +library ERC3000Data { + struct Container { + Payload payload; + Config config; + } + + struct Payload { + uint256 nonce; + uint256 executionTime; + address submitter; + IERC3000Executor executor; + Action[] actions; + bytes proof; + } + + struct Action { + address to; + uint256 value; + bytes data; + } + + struct Config { + uint256 executionDelay; + Collateral scheduleDeposit; + Collateral challengeDeposit; + Collateral vetoDeposit; + address resolver; + bytes rules; + } + + struct Collateral { + address token; + uint256 amount; + } +} +``` + +### Interface and events + +Given the data structures above, by taking advantage of the Solidity ABI encoder v2, we define four required functions and two optional functions as the interface for contracts to comply with ERC-3000. + +All standard functions are expected to revert (whether to include error messages/revert reasons as part of the standard is yet to be determined) when pre-conditions are not met or an unexpected error occurs. On success, each function must emit its associated event once and only once. + +```solidity +abstract contract IERC3000 { + /** + * @notice Schedules an action for execution, allowing for challenges and vetos on a defined time window + * @param container A Container struct holding both the paylaod being scheduled for execution and + the current configuration of the system + */ + function schedule(ERC3000Data.Container memory container) virtual public returns (bytes32 containerHash); + event Scheduled(bytes32 indexed containerHash, ERC3000Data.Payload payload, ERC3000Data.Collateral collateral); + + /** + * @notice Executes an action after its execution delayed has passed and its state hasn't been altered by a challenge or veto + * @param container A ERC3000Data.Container struct holding both the paylaod being scheduled for execution and + the current configuration of the system + * should be a MUST payload.executor.exec(payload.actions) + */ + function execute(ERC3000Data.Container memory container) virtual public returns (bytes[] memory execResults); + event Executed(bytes32 indexed containerHash, address indexed actor, bytes[] execResults); + + /** + * @notice Challenge a container in case its scheduling is illegal as per Config.rules. Pulls collateral and dispute fees from sender into contract + * @param container A ERC3000Data.Container struct holding both the paylaod being scheduled for execution and + the current configuration of the system + * @param reason Hint for case reviewers as to why the scheduled container is illegal + */ + function challenge(ERC3000Data.Container memory container, bytes memory reason) virtual public returns (uint256 resolverId); + event Challenged(bytes32 indexed containerHash, address indexed actor, bytes reason, uint256 resolverId, ERC3000Data.Collateral collateral); + + /** + * @notice Apply arbitrator's ruling over a challenge once it has come to a final ruling + * @param container A ERC3000Data.Container struct holding both the paylaod being scheduled for execution and + the current configuration of the system + * @param resolverId disputeId in the arbitrator in which the dispute over the container was created + */ + function resolve(ERC3000Data.Container memory container, uint256 resolverId) virtual public returns (bytes[] memory execResults); + event Resolved(bytes32 indexed containerHash, address indexed actor, bool approved); + + /** + * @dev OPTIONAL + * @notice Apply arbitrator's ruling over a challenge once it has come to a final ruling + * @param payloadHash Hash of the payload being vetoed + * @param config A ERC3000Data.Config struct holding the config attached to the payload being vetoed + */ + function veto(bytes32 payloadHash, ERC3000Data.Config memory config, bytes memory reason) virtual public; + event Vetoed(bytes32 indexed containerHash, address indexed actor, bytes reason, ERC3000Data.Collateral collateral); + + /** + * @dev OPTIONAL: implementer might choose not to implement (initial Configured event MUST be emitted) + * @notice Apply a new configuration for all *new* containers to be scheduled + * @param config A ERC3000Data.Config struct holding all the new params that will control the queue + */ + function configure(ERC3000Data.Config memory config) virtual public returns (bytes32 configHash); + event Configured(bytes32 indexed containerHash, address indexed actor, ERC3000Data.Config config); +} +``` + +## Rationale + +The authors believe that it is very important that this standard leaves the other open to any resolver mechanism to be implemented and adopted. + +That's why a lot of the function and variable names were left intentionally bogus to be compatible with future resolvers without changing the standard. + +ERC-3000 should be seen as a public good of top of which public infrastrastructure will be built, being way more important than any particular implementation or the interests of specific companies or projects. + +## Security Considerations + +The standard allows for the resolver for challenges to be configured, and even have different resolvers for coexisting scheduled payloads. Choosing the right resolver requires making the right tradeoff between security, time to finality, implementation complexity, and external dependencies. + +Using a subjective oracle as resolver has its risks, since security depends on the crypto-economic properties of the system. For an analysis of crypto-economic considerations of Aragon Court, you can check [the following doc](https://github.com/aragon/aragon-court/tree/master/docs/3-cryptoeconomic-considerations). + +On the other hand, implementing a deterministic resolver is prone to dangerous bugs given its complexity, and will rely on a specific version of the off-chain protocol, which could rapidly evolve while the standard matures and gets adopted. + +## Implementations + +### 1. Aragon Govern + +- [ERC-3000 interface (MIT license)](https://github.com/aragon/govern/blob/master/packages/erc3k) +- [Implementation (GPL-3.0 license)](https://github.com/aragon/govern/blob/master/packages/govern-core) + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3005.md b/EIPS/eip-3005.md new file mode 100644 index 0000000..c3f1219 --- /dev/null +++ b/EIPS/eip-3005.md @@ -0,0 +1,416 @@ +--- +eip: 3005 +title: Batched meta transactions +author: Matt (@defifuture) +discussions-to: https://ethereum-magicians.org/t/eip-3005-the-economic-viability-of-batched-meta-transactions/4673 +status: Stagnant +type: Standards Track +category: ERC +created: 2020-09-25 +--- + +## Simple Summary + +Defines an extension function for ERC-20 (and other fungible token standards), which allows receiving and processing a batch of meta transactions. + +## Abstract + +This EIP defines a new function called `processMetaBatch()` that extends any fungible token standard, and enables batched meta transactions coming from many senders in one on-chain transaction. + +The function must be able to receive multiple meta transactions data and process it. This means validating the data and the signature, before proceeding with token transfers based on the data. + +The function enables senders to make gasless transactions, while reducing the relayer's gas cost due to batching. + +## Motivation + +Meta transactions have proven useful as a solution for Ethereum accounts that don't have any ether, but hold ERC-20 tokens and would like to transfer them (gasless transactions). + +The current meta transaction relayer implementations only allow relaying one meta transaction at a time. Some also allow batched meta transactions from the same sender. But none offers batched meta transactions from **multiple** senders. + +The motivation behind this EIP is to find a way to allow relaying batched meta transactions from **many senders** in **one on-chain transaction**, which also **reduces the total gas cost** that a relayer needs to cover. + +![](../assets/eip-3005/meta-txs-directly-to-token-smart-contract.png) + +## Specification + +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. + +The key words "MUST (BUT WE KNOW YOU WON'T)", "SHOULD CONSIDER", "REALLY SHOULD NOT", "OUGHT TO", "WOULD PROBABLY", "MAY WISH TO", "COULD", "POSSIBLE", and "MIGHT" in this document are to be interpreted as described in RFC 6919. + +### Meta transaction data + +In order to successfully validate and transfer tokens, the `processMetaBatch()` function MUST process the following data about a meta transaction: + +- sender address +- receiver address +- token amount +- relayer fee +- a (meta tx) nonce +- an expiration date (this COULD be a block number, or it COULD be a block timestamp) +- a token address +- a relayer address +- a signature + +Not all of the data needs to be sent to the function by the relayer (see the function interface specification). Some of the data can be deduced or extracted from other sources (from transaction data and contract state). + +### `processMetaBatch()` function input data + +The `processMetaBatch()` function MUST receive the following data: + +- sender address +- receiver address +- token amount +- relayer fee +- an expiration date (this COULD be a block number, or it COULD be a block timestamp) +- a signature + +The following data is OPTIONAL to be sent to the function, because it can be extracted or derived from other sources: + +- a (meta tx) nonce +- a token address +- a relayer address + +### Meta transaction data hash + +The pseudocode for creating a hash of meta transaction data is the following: + +``` +keccak256(address(sender) + ++ address(recipient) + ++ uint256(amount) + ++ uint256(relayerFee) + ++ uint256(nonce) + ++ uint256(expirationDate) + ++ address(tokenContract) + ++ address(relayer) +) +``` + +The created hash MUST then be signed with the sender's private key. + +### Validation rules + +- Nonce of a new transaction MUST always be bigger by exactly 1 from the nonce of the last successfully processed meta transaction of the same sender to the same token contract. +- Sending to and from a 0x0 address MUST be prohibited. +- A meta transaction MUST be processed before the expiration date. +- Each sender's token balance MUST be equal or greater than the sum of their respective meta transaction token amount and relayer fee. +- A transaction where at least one meta transaction in the batch does not satisfy the above requirements MUST not be reverted. Instead, a failed meta transaction MUST be skipped or ignored. + +### `processMetaBatch()` function interface + +The `processMetaBatch()` function MUST have the following interface: + +```solidity +function processMetaBatch(address[] memory senders, + address[] memory recipients, + uint256[] memory amounts, + uint256[] memory relayerFees, + uint256[] memory blocks, + uint8[] memory sigV, + bytes32[] memory sigR, + bytes32[] memory sigS) public returns (bool); +``` + +The overview of parameters that are passed: + +- `senders`: an array of meta transaction sender addresses (token senders) +- `recipients `: an array of token recipients addresses +- `amounts`: an array of token amounts that are sent from each sender to each recipient, respectively +- `relayerFees`: an array of the relayer fees paid in tokens by senders. The fee receiver is a relayer (`msg.address`) +- `blocks`: an array of block numbers that represent an expiration date by which the meta transaction must be processed (alternatively, a timestamp could be used instead of a block number) +- `sigV`, `sigR`, `sigS`: three arrays that represent parts of meta transaction signatures + +Each entry in each of the arrays MUST represent data from one meta transaction. The order of the data is very important. Data from a single meta transaction MUST have the same index in every array. + +### Meta transaction nonce + +The token smart contract must keep track of a meta transaction nonce for each token holder. + +```solidity +mapping (address => uint256) private _metaNonces; +``` + +The interface for the `nonceOf()` function is the following: + +```solidity +function nonceOf(address account) public view returns (uint256); +``` + +### Token transfers + +After a meta transaction is successfully validated, the meta nonce of the meta transaction sender MUST be increased by 1. + +Then two token transfers MUST occur: + +- The specified token amount MUST go to the recipient. +- The relayer fee MUST go to the relayer (`msg.sender`). + +## Implementation + +The **reference implementation** adds a couple of functions to the existing ERC-20 token standard: + +- `processMetaBatch()` +- `nonceOf()` + +You can see the implementation of both functions in this file: [ERC20MetaBatch.sol](https://github.com/defifuture/erc20-batched-meta-transactions/blob/master/contracts/ERC20MetaBatch.sol). This is an extended ERC-20 contract with added meta transaction batch transfer capabilities. + +### `processMetaBatch()` + +The `processMetaBatch()` function is responsible for receiving and processing a batch of meta transactions that change token balances. + +```solidity +function processMetaBatch(address[] memory senders, + address[] memory recipients, + uint256[] memory amounts, + uint256[] memory relayerFees, + uint256[] memory blocks, + uint8[] memory sigV, + bytes32[] memory sigR, + bytes32[] memory sigS) public returns (bool) { + + address sender; + uint256 newNonce; + uint256 relayerFeesSum = 0; + bytes32 msgHash; + uint256 i; + + // loop through all meta txs + for (i = 0; i < senders.length; i++) { + sender = senders[i]; + newNonce = _metaNonces[sender] + 1; + + if(sender == address(0) || recipients[i] == address(0)) { + continue; // sender or recipient is 0x0 address, skip this meta tx + } + + // the meta tx should be processed until (including) the specified block number, otherwise it is invalid + if(block.number > blocks[i]) { + continue; // if current block number is bigger than the requested number, skip this meta tx + } + + // check if meta tx sender's balance is big enough + if(_balances[sender] < (amounts[i] + relayerFees[i])) { + continue; // if sender's balance is less than the amount and the relayer fee, skip this meta tx + } + + // check if the signature is valid + msgHash = keccak256(abi.encode(sender, recipients[i], amounts[i], relayerFees[i], newNonce, blocks[i], address(this), msg.sender)); + if(sender != ecrecover(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", msgHash)), sigV[i], sigR[i], sigS[i])) { + continue; // if sig is not valid, skip to the next meta tx + } + + // set a new nonce for the sender + _metaNonces[sender] = newNonce; + + // transfer tokens + _balances[sender] -= (amounts[i] + relayerFees[i]); + _balances[recipients[i]] += amounts[i]; + relayerFeesSum += relayerFees[i]; + } + + // give the relayer the sum of all relayer fees + _balances[msg.sender] += relayerFeesSum; + + return true; +} +``` + +### `nonceOf()` + +Nonces are needed due to the replay protection (see *Replay attacks* under *Security Considerations*). + +```solidity +mapping (address => uint256) private _metaNonces; + +// ... + +function nonceOf(address account) public view returns (uint256) { + return _metaNonces[account]; +} +``` + +The link to the complete implementation (along with gas usage results) is here: [https://github.com/defifuture/erc20-batched-meta-transactions](https://github.com/defifuture/erc20-batched-meta-transactions). + +> Note that the OpenZeppelin ERC-20 implementation was used here. Some other implementation may have named the `_balances` mapping differently, which would require minor changes in the `processMetaBatch()` function. + +## Rationale + +### All-in-one + +Alternative implementations (like GSN) use multiple smart contracts to enable meta transactions, although this increases gas usage. This implementation (EIP-3005) intentionally keeps everything within one function which reduces complexity and gas cost. + +The `processMetaBatch()` function thus does the job of receiving a batch of meta transactions, validating them, and then transferring tokens from one address to another. + +### Function parameters + +As you can see, the `processMetaBatch()` function in the reference implementation takes the following parameters: + +- an array of **sender addresses** (meta txs senders, not relayers) +- an array of **receiver addresses** +- an array of **amounts** +- an array of **relayer fees** (relayer is `msg.sender`) +- an array of **block numbers** (a due "date" for meta tx to be processed) +- Three arrays that represent parts of a **signature** (v, r, s) + +**Each item** in these arrays represents **data of one meta transaction**. That's why the **correct order** in the arrays is very important. + +If a relayer gets the order wrong, the `processMetaBatch()` function would notice that (when validating a signature), because the hash of the meta transaction values would not match the signed hash. A meta transaction with an invalid signature is **skipped**. + +### The alternative way of passing meta transaction data into the function + +The reference implementation takes parameters as arrays. There's a separate array for each meta transaction data category (the ones that cannot be deduced or extracted from other sources). + +A different approach would be to bitpack all data of a meta transaction into one value and then unpack it within the smart contract. The data for a batch of meta transactions would be sent in an array, but there would need to be only one array (of packed data), instead of multiple arrays. + +### Why is nonce not one of the parameters in the reference implementation? + +Meta nonce is used for constructing a signed hash (see the `msgHash` line where a `keccak256` hash is constructed - you'll find a nonce there). + +Since a new nonce has to always be bigger than the previous one by exactly 1, there's no need to include it as a parameter array in the `processMetaBatch()` function, because its value can be deduced. + +This also helps avoid the "Stack too deep" error. + +### Can EIP-2612 nonces mapping be re-used? + +The EIP-2612 (`permit()` function) also requires a nonce mapping. At this point, I'm not sure yet if this mapping should be **re-used** in case a smart contract implements both EIP-3005 and EIP-2612. + +At the first glance, it seems the `nonces` mapping from EIP-2612 could be re-used, but this should be thought through (and tested) for possible security implications. + +### Token transfers + +Token transfers in the reference implementation could alternatively be done by calling the `_transfer()` function (part of the OpenZeppelin ERC-20 implementation), but it would increase the gas usage and it would also revert the whole batch if some meta transaction was invalid (the current implementation just skips it). + +Another gas usage optimization is to assign total relayer fees to the relayer at the end of the function, and not with every token transfer inside the for loop (thus avoiding multiple SSTORE calls that cost 5'000 gas). + +## Backwards Compatibility + +The code implementation of batched meta transactions is backwards compatible with any fungible token standard, for example, ERC-20 (it only extends it with one function). + +## Test Cases + +Link to tests: [https://github.com/defifuture/erc20-batched-meta-transactions/tree/master/test](https://github.com/defifuture/erc20-batched-meta-transactions/tree/master/test). + +## Security Considerations + +Here is a list of potential security issues and how are they addressed in this implementation. + +### Forging a meta transaction + +The solution against a relayer forging a meta transaction is for a user to sign the meta transaction with their private key. + +The `processMetaBatch()` function then verifies the signature using `ecrecover()`. + +### Replay attacks + +The `processMetaBatch()` function is secure against two types of a replay attack: + +**Using the same meta transaction twice in the same token smart contract** + +A nonce prevents a replay attack where a relayer would send the same meta transaction more than once. + +**Using the same meta transaction twice in different token smart contracts** + +A token smart contract address must be added into the signed hash (of a meta transaction). + +This address does not need to be sent as a parameter into the `processMetaBatch()` function. Instead, the function uses `address(this)` when constructing a hash in order to verify the signature. This way a meta transaction not intended for the token smart contract would be rejected (skipped). + +### Signature validation + +Signing a meta transaction and validating the signature is crucial for this whole scheme to work. + +The `processMetaBatch()` function validates a meta transaction signature, and if it's **invalid**, the meta transaction is **skipped** (but the whole on-chain transaction is **not reverted**). + +```solidity +msgHash = keccak256(abi.encode(sender, recipients[i], amounts[i], relayerFees[i], newNonce, blocks[i], address(this), msg.sender)); + +if(sender != ecrecover(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", msgHash)), sigV[i], sigR[i], sigS[i])) { + continue; // if sig is not valid, skip to the next meta tx +} +``` + +Why not reverting the whole on-chain transaction? Because there could be only one problematic meta transaction, and the others should not be dropped just because of one rotten apple. + +That said, it is expected of relayers to validate meta transactions in advance before relaying them. That's why relayers are not entitled to a relayer fee for an invalid meta transaction. + +### Malicious relayer forcing a user into over-spending + +A malicious relayer could delay sending some user's meta transaction until the user would decide to make the token transaction on-chain. + +After that, the relayer would relay the delayed meta transaction which would mean that the user would have made two token transactions (over-spending). + +**Solution:** Each meta transaction should have an "expiry date". This is defined in a form of a block number by which the meta transaction must be relayed on-chain. + +```solidity +function processMetaBatch(... + uint256[] memory blocks, + ...) public returns (bool) { + + //... + + // loop through all meta txs + for (i = 0; i < senders.length; i++) { + + // the meta tx should be processed until (including) the specified block number, otherwise it is invalid + if(block.number > blocks[i]) { + continue; // if current block number is bigger than the requested number, skip this meta tx + } + + //... +``` + +### Front-running attack + +A malicious relayer could scout the Ethereum mempool to steal meta transactions and front-run the original relayer. + +**Solution:** The protection that `processMetaBatch()` function uses is that it requires the meta transaction sender to add the relayer's Ethereum address as one of the values in the hash (which is then signed). + +When the `processMetaBatch()` function generates a hash it includes the `msg.sender` address in it: + +```solidity +msgHash = keccak256(abi.encode(sender, recipients[i], amounts[i], relayerFees[i], newNonce, blocks[i], address(this), msg.sender)); + +if(sender != ecrecover(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", msgHash)), sigV[i], sigR[i], sigS[i])) { + continue; // if sig is not valid, skip to the next meta tx +} +``` + +If the meta transaction was "stolen", the signature check would fail because the `msg.sender` address would not be the same as the intended relayer's address. + +### A malicious (or too impatient) user sending a meta transaction with the same nonce through multiple relayers at once + +A user that is either malicious or just impatient could submit a meta transaction with the same nonce (for the same token contract) to various relayers. Only one of them would get the relayer fee (the first one on-chain), while the others would get an invalid meta transaction. + +**Solution:** Relayers could **share a list of their pending meta transactions** between each other (sort of an info mempool). + +The relayers don't have to fear that someone would steal their respective pending transactions, due to the front-running protection (see above). + +If relayers see meta transactions from a certain sender address that have the same nonce and are supposed to be relayed to the same token smart contract, they can decide that only the first registered meta transaction goes through and others are dropped (or in case meta transactions were registered at the same time, the remaining meta transaction could be randomly picked). + +At a minimum, relayers need to share this meta transaction data (in order to detect meta transaction collision): + +- sender address +- token address +- nonce + +### Too big due block number + +The relayer could trick the meta transaction sender into adding too big due block number - this means a block by which the meta transaction must be processed. The block number could be far in the future, for example, 10 years in the future. This means that the relayer would have 10 years to submit the meta transaction. + +**One way** to solve this problem is by adding an upper bound constraint for a block number within the smart contract. For example, we could say that the specified due block number must not be bigger than 100'000 blocks from the current one (this is around 17 days in the future if we assume 15 seconds block time). + +```solidity +// the meta tx should be processed until (including) the specified block number, otherwise it is invalid +if(block.number > blocks[i] || blocks[i] > (block.number + 100000)) { + // If current block number is bigger than the requested due block number, skip this meta tx. + // Also skip if the due block number is too big (bigger than 100'000 blocks in the future). + continue; +} +``` + +This addition could open new security implications, that's why it is left out of this proof-of-concept. But anyone who wishes to implement it should know about this potential constraint, too. + +**The other way** is to keep the `processMetaBatch()` function as it is and rather check for the too big due block number **on the relayer level**. In this case, the user could be notified about the problem and could issue a new meta transaction with another relayer that would have a much lower block parameter (and the same nonce). + +## Copyright + +Copyright and related rights are waived via [CC0](../LICENSE.md). \ No newline at end of file diff --git a/EIPS/eip-3009.md b/EIPS/eip-3009.md new file mode 100644 index 0000000..1919a5d --- /dev/null +++ b/EIPS/eip-3009.md @@ -0,0 +1,536 @@ +--- +eip: 3009 +title: Transfer With Authorization +author: Peter Jihoon Kim (@petejkim), Kevin Britz (@kbrizzle), David Knott (@DavidLKnott) +discussions-to: https://github.com/ethereum/EIPs/issues/3010 +status: Stagnant +type: Standards Track +category: ERC +created: 2020-09-28 +requires: 20, 712 +--- + +## Simple Summary + +A contract interface that enables transferring of fungible assets via a signed authorization. + +## Abstract + +A set of functions to enable meta-transactions and atomic interactions with [ERC-20](./eip-20.md) token contracts via signatures conforming to the [EIP-712](./eip-712.md) typed message signing specification. + +This enables the user to: + +- delegate the gas payment to someone else, +- pay for gas in the token itself rather than in ETH, +- perform one or more token transfers and other operations in a single atomic transaction, +- transfer ERC-20 tokens to another address, and have the recipient submit the transaction, +- batch multiple transactions with minimal overhead, and +- create and perform multiple transactions without having to worry about them failing due to accidental nonce-reuse or improper ordering by the miner. + +## Motivation + +There is an existing spec, [EIP-2612](./eip-2612), that also allows meta-transactions, and it is encouraged that a contract implements both for maximum compatibility. The two primary differences between this spec and EIP-2612 are that: + +- EIP-2612 uses sequential nonces, but this uses random 32-byte nonces, and that +- EIP-2612 relies on the ERC-20 `approve`/`transferFrom` ("ERC-20 allowance") pattern. + +The biggest issue with the use of sequential nonces is that it does not allow users to perform more than one transaction at time without risking their transactions failing, because: + +- DApps may unintentionally reuse nonces that have not yet been processed in the blockchain. +- Miners may process the transactions in the incorrect order. + +This can be especially problematic if the gas prices are very high and transactions often get queued up and remain unconfirmed for a long time. Non-sequential nonces allow users to create as many transactions as they want at the same time. + +The ERC-20 allowance mechanism is susceptible to the [multiple withdrawal attack](https://blockchain-projects.readthedocs.io/multiple_withdrawal.html)/[SWC-114](https://swcregistry.io/docs/SWC-114), and encourages antipatterns such as the use of the "infinite" allowance. The wide-prevalence of upgradeable contracts have made the conditions favorable for these attacks to happen in the wild. + +The deficiencies of the ERC-20 allowance pattern brought about the development of alternative token standards such as the [ERC-777](./eip-777) and [ERC-677](https://github.com/ethereum/EIPs/issues/677). However, they haven't been able to gain much adoption due to compatibility and potential security issues. + +## Specification + +### Event + +```solidity +event AuthorizationUsed( + address indexed authorizer, + bytes32 indexed nonce +); + +// keccak256("TransferWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)") +bytes32 public constant TRANSFER_WITH_AUTHORIZATION_TYPEHASH = 0x7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a2267; + +// keccak256("ReceiveWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)") +bytes32 public constant RECEIVE_WITH_AUTHORIZATION_TYPEHASH = 0xd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de8; + +/** + * @notice Returns the state of an authorization + * @dev Nonces are randomly generated 32-byte data unique to the authorizer's + * address + * @param authorizer Authorizer's address + * @param nonce Nonce of the authorization + * @return True if the nonce is used + */ +function authorizationState( + address authorizer, + bytes32 nonce +) external view returns (bool); + +/** + * @notice Execute a transfer with a signed authorization + * @param from Payer's address (Authorizer) + * @param to Payee's address + * @param value Amount to be transferred + * @param validAfter The time after which this is valid (unix time) + * @param validBefore The time before which this is valid (unix time) + * @param nonce Unique nonce + * @param v v of the signature + * @param r r of the signature + * @param s s of the signature + */ +function transferWithAuthorization( + address from, + address to, + uint256 value, + uint256 validAfter, + uint256 validBefore, + bytes32 nonce, + uint8 v, + bytes32 r, + bytes32 s +) external; + +/** + * @notice Receive a transfer with a signed authorization from the payer + * @dev This has an additional check to ensure that the payee's address matches + * the caller of this function to prevent front-running attacks. (See security + * considerations) + * @param from Payer's address (Authorizer) + * @param to Payee's address + * @param value Amount to be transferred + * @param validAfter The time after which this is valid (unix time) + * @param validBefore The time before which this is valid (unix time) + * @param nonce Unique nonce + * @param v v of the signature + * @param r r of the signature + * @param s s of the signature + */ +function receiveWithAuthorization( + address from, + address to, + uint256 value, + uint256 validAfter, + uint256 validBefore, + bytes32 nonce, + uint8 v, + bytes32 r, + bytes32 s +) external; +``` + +**Optional:** + +``` +event AuthorizationCanceled( + address indexed authorizer, + bytes32 indexed nonce +); + +// keccak256("CancelAuthorization(address authorizer,bytes32 nonce)") +bytes32 public constant CANCEL_AUTHORIZATION_TYPEHASH = 0x158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a1597429; + +/** + * @notice Attempt to cancel an authorization + * @param authorizer Authorizer's address + * @param nonce Nonce of the authorization + * @param v v of the signature + * @param r r of the signature + * @param s s of the signature + */ +function cancelAuthorization( + address authorizer, + bytes32 nonce, + uint8 v, + bytes32 r, + bytes32 s +) external; +``` + + +The arguments `v`, `r`, and `s` must be obtained using the [EIP-712](./eip-712.md) typed message signing spec. + +**Example:** + +``` +DomainSeparator := Keccak256(ABIEncode( + Keccak256( + "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" + ), + Keccak256("USD Coin"), // name + Keccak256("2"), // version + 1, // chainId + 0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48 // verifyingContract +)) +``` + +With the domain separator, the typehash, which is used to identify the type of the EIP-712 message being used, and the values of the parameters, you are able to derive a Keccak-256 hash digest which can then be signed using the token holder's private key. + +**Example:** + +``` +// Transfer With Authorization +TypeHash := Keccak256( + "TransferWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)" +) +Params := { From, To, Value, ValidAfter, ValidBefore, Nonce } + +// ReceiveWithAuthorization +TypeHash := Keccak256( + "ReceiveWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)" +) +Params := { From, To, Value, ValidAfter, ValidBefore, Nonce } + +// CancelAuthorization +TypeHash := Keccak256( + "CancelAuthorization(address authorizer,bytes32 nonce)" +) +Params := { Authorizer, Nonce } +``` + +``` +// "‖" denotes concatenation. +Digest := Keecak256( + 0x1901 ‖ DomainSeparator ‖ Keccak256(ABIEncode(TypeHash, Params...)) +) + +{ v, r, s } := Sign(Digest, PrivateKey) +``` + +Smart contract functions that wrap `receiveWithAuthorization` call may choose to reduce the number of arguments by accepting the full ABI-encoded set of arguments for the `receiveWithAuthorization` call as a single argument of the type `bytes`. + +**Example:** + +```solidity +// keccak256("receiveWithAuthorization(address,address,uint256,uint256,uint256,bytes32,uint8,bytes32,bytes32)")[0:4] +bytes4 private constant _RECEIVE_WITH_AUTHORIZATION_SELECTOR = 0xef55bec6; + +function deposit(address token, bytes calldata receiveAuthorization) + external + nonReentrant +{ + (address from, address to, uint256 amount) = abi.decode( + receiveAuthorization[0:96], + (address, address, uint256) + ); + require(to == address(this), "Recipient is not this contract"); + + (bool success, ) = token.call( + abi.encodePacked( + _RECEIVE_WITH_AUTHORIZATION_SELECTOR, + receiveAuthorization + ) + ); + require(success, "Failed to transfer tokens"); + + ... +} +``` + +### Use with web3 providers + +The signature for an authorization can be obtained using a web3 provider with the `eth_signTypedData{_v4}` method. + +**Example:** + +```javascript +const data = { + types: { + EIP712Domain: [ + { name: "name", type: "string" }, + { name: "version", type: "string" }, + { name: "chainId", type: "uint256" }, + { name: "verifyingContract", type: "address" }, + ], + TransferWithAuthorization: [ + { name: "from", type: "address" }, + { name: "to", type: "address" }, + { name: "value", type: "uint256" }, + { name: "validAfter", type: "uint256" }, + { name: "validBefore", type: "uint256" }, + { name: "nonce", type: "bytes32" }, + ], + }, + domain: { + name: tokenName, + version: tokenVersion, + chainId: selectedChainId, + verifyingContract: tokenAddress, + }, + primaryType: "TransferWithAuthorization", + message: { + from: userAddress, + to: recipientAddress, + value: amountBN.toString(10), + validAfter: 0, + validBefore: Math.floor(Date.now() / 1000) + 3600, // Valid for an hour + nonce: Web3.utils.randomHex(32), + }, +}; + +const signature = await ethereum.request({ + method: "eth_signTypedData_v4", + params: [userAddress, JSON.stringify(data)], +}); + +const v = "0x" + signature.slice(130, 132); +const r = signature.slice(0, 66); +const s = "0x" + signature.slice(66, 130); +``` + +## Rationale + +### Unique Random Nonce, Instead of Sequential Nonce + +One might say transaction ordering is one reason why sequential nonces are preferred. However, sequential nonces do not actually help achieve transaction ordering for meta transactions in practice: + +- For native Ethereum transactions, when a transaction with a nonce value that is too-high is submitted to the network, it will stay pending until the transactions consuming the lower unused nonces are confirmed. +- However, for meta-transactions, when a transaction containing a sequential nonce value that is too high is submitted, instead of staying pending, it will revert and fail immediately, resulting in wasted gas. +- The fact that miners can also reorder transactions and include them in the block in the order they want (assuming each transaction was submitted to the network by different meta-transaction relayers) also makes it possible for the meta-transactions to fail even if the nonces used were correct. (e.g. User submits nonces 3, 4 and 5, but miner ends up including them in the block as 4,5,3, resulting in only 3 succeeding) +- Lastly, when using different applications simultaneously, in absence of some sort of an off-chain nonce-tracker, it is not possible to determine what the correct next nonce value is if there exists nonces that are used but haven't been submitted and confirmed by the network. +- Under high gas price conditions, transactions can often "get stuck" in the pool for a long time. Under such a situation, it is much more likely for the same nonce to be unintentionally reused twice. For example, if you make a meta-transaction that uses a sequential nonce from one app, and switch to another app to make another meta-transaction before the previous one confirms, the same nonce will be used if the app relies purely on the data available on-chain, resulting in one of the transactions failing. +- In conclusion, the only way to guarantee transaction ordering is for relayers to submit transactions one at a time, waiting for confirmation between each submission (and the order in which they should be submitted can be part of some off-chain metadata), rendering sequential nonce irrelevant. + +### Valid After and Valid Before + +- Relying on relayers to submit transactions for you means you may not have exact control over the timing of transaction submission. +- These parameters allow the user to schedule a transaction to be only valid in the future or before a specific deadline, protecting the user from potential undesirable effects that may be caused by the submission being made either too late or too early. + +### EIP-712 + +- EIP-712 ensures that the signatures generated are valid only for this specific instance of the token contract and cannot be replayed on a different network with a different chain ID. +- This is achieved by incorporating the contract address and the chain ID in a Keccak-256 hash digest called the domain separator. The actual set of parameters used to derive the domain separator is up to the implementing contract, but it is highly recommended that the fields `verifyingContract` and `chainId` are included. + +## Backwards Compatibility + +New contracts benefit from being able to directly utilize EIP-3009 in order to create atomic transactions, but existing contracts may still rely on the conventional ERC-20 allowance pattern (`approve`/`transferFrom`). + +In order to add support for EIP-3009 to existing contracts ("parent contract") that use the ERC-20 allowance pattern, a forwarding contract ("forwarder") can be constructed that takes an authorization and does the following: + +1. Extract the user and deposit amount from the authorization +2. Call `receiveWithAuthorization` to transfer specified funds from the user to the forwarder +3. Approve the parent contract to spend funds from the forwarder +4. Call the method on the parent contract that spends the allowance set from the forwarder +5. Transfer the ownership of any resulting tokens back to the user + +**Example:** + +```solidity +interface IDeFiToken { + function deposit(uint256 amount) external returns (uint256); + + function transfer(address account, uint256 amount) + external + returns (bool); +} + +contract DepositForwarder { + bytes4 private constant _RECEIVE_WITH_AUTHORIZATION_SELECTOR = 0xef55bec6; + + IDeFiToken private _parent; + IERC20 private _token; + + constructor(IDeFiToken parent, IERC20 token) public { + _parent = parent; + _token = token; + } + + function deposit(bytes calldata receiveAuthorization) + external + nonReentrant + returns (uint256) + { + (address from, address to, uint256 amount) = abi.decode( + receiveAuthorization[0:96], + (address, address, uint256) + ); + require(to == address(this), "Recipient is not this contract"); + + (bool success, ) = address(_token).call( + abi.encodePacked( + _RECEIVE_WITH_AUTHORIZATION_SELECTOR, + receiveAuthorization + ) + ); + require(success, "Failed to transfer to the forwarder"); + + require( + _token.approve(address(_parent), amount), + "Failed to set the allowance" + ); + + uint256 tokensMinted = _parent.deposit(amount); + require( + _parent.transfer(from, tokensMinted), + "Failed to transfer the minted tokens" + ); + + uint256 remainder = _token.balanceOf(address(this); + if (remainder > 0) { + require( + _token.transfer(from, remainder), + "Failed to refund the remainder" + ); + } + + return tokensMinted; + } +} +``` + +## Test Cases + +See [EIP3009.test.ts](https://github.com/CoinbaseStablecoin/eip-3009/blob/master/test/EIP3009.test.ts). + +## Implementation + +**EIP3009.sol** +```solidity +abstract contract EIP3009 is IERC20Transfer, EIP712Domain { + // keccak256("TransferWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)") + bytes32 public constant TRANSFER_WITH_AUTHORIZATION_TYPEHASH = 0x7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a2267; + + // keccak256("ReceiveWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)") + bytes32 public constant RECEIVE_WITH_AUTHORIZATION_TYPEHASH = 0xd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de8; + + mapping(address => mapping(bytes32 => bool)) internal _authorizationStates; + + event AuthorizationUsed(address indexed authorizer, bytes32 indexed nonce); + + string internal constant _INVALID_SIGNATURE_ERROR = "EIP3009: invalid signature"; + + function authorizationState(address authorizer, bytes32 nonce) + external + view + returns (bool) + { + return _authorizationStates[authorizer][nonce]; + } + + function transferWithAuthorization( + address from, + address to, + uint256 value, + uint256 validAfter, + uint256 validBefore, + bytes32 nonce, + uint8 v, + bytes32 r, + bytes32 s + ) external { + require(now > validAfter, "EIP3009: authorization is not yet valid"); + require(now < validBefore, "EIP3009: authorization is expired"); + require( + !_authorizationStates[from][nonce], + "EIP3009: authorization is used" + ); + + bytes memory data = abi.encode( + TRANSFER_WITH_AUTHORIZATION_TYPEHASH, + from, + to, + value, + validAfter, + validBefore, + nonce + ); + require( + EIP712.recover(DOMAIN_SEPARATOR, v, r, s, data) == from, + "EIP3009: invalid signature" + ); + + _authorizationStates[from][nonce] = true; + emit AuthorizationUsed(from, nonce); + + _transfer(from, to, value); + } +} +``` + +**IERC20Transfer.sol** +```solidity +abstract contract IERC20Transfer { + function _transfer( + address sender, + address recipient, + uint256 amount + ) internal virtual; +} +``` + +**EIP712Domain.sol** +```solidity +abstract contract EIP712Domain { + bytes32 public DOMAIN_SEPARATOR; +} +``` + +**EIP712.sol** +```solidity +library EIP712 { + // keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)") + bytes32 public constant EIP712_DOMAIN_TYPEHASH = 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f; + + function makeDomainSeparator(string memory name, string memory version) + internal + view + returns (bytes32) + { + uint256 chainId; + assembly { + chainId := chainid() + } + + return + keccak256( + abi.encode( + EIP712_DOMAIN_TYPEHASH, + keccak256(bytes(name)), + keccak256(bytes(version)), + address(this), + bytes32(chainId) + ) + ); + } + + function recover( + bytes32 domainSeparator, + uint8 v, + bytes32 r, + bytes32 s, + bytes memory typeHashAndData + ) internal pure returns (address) { + bytes32 digest = keccak256( + abi.encodePacked( + "\x19\x01", + domainSeparator, + keccak256(typeHashAndData) + ) + ); + address recovered = ecrecover(digest, v, r, s); + require(recovered != address(0), "EIP712: invalid signature"); + return recovered; + } +} +``` + +A fully working implementation of EIP-3009 can be found in [this repository](https://github.com/CoinbaseStablecoin/eip-3009/blob/master/contracts/lib/EIP3009.sol). The repository also includes [an implementation of EIP-2612](https://github.com/CoinbaseStablecoin/eip-3009/blob/master/contracts/lib/EI32612.sol) that uses the EIP-712 library code presented above. + +## Security Considerations + +Use `receiveWithAuthorization` instead of `transferWithAuthorization` when calling from other smart contracts. It is possible for an attacker watching the transaction pool to extract the transfer authorization and front-run the `transferWithAuthorization` call to execute the transfer without invoking the wrapper function. This could potentially result in unprocessed, locked up deposits. `receiveWithAuthorization` prevents this by performing an additional check that ensures that the caller is the payee. Additionally, if there are multiple contract functions accepting receive authorizations, the app developer could dedicate some leading bytes of the nonce could as the identifier to prevent cross-use. + +When submitting multiple transfers simultaneously, be mindful of the fact that relayers and miners will decide the order in which they are processed. This is generally not a problem if the transactions are not dependent on each other, but for transactions that are highly dependent on each other, it is recommended that the signed authorizations are submitted one at a time. + +The zero address must be rejected when using `ecrecover` to prevent unauthorized transfers and approvals of funds from the zero address. The built-in `ecrecover` returns the zero address when a malformed signature is provided. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3014.md b/EIPS/eip-3014.md new file mode 100644 index 0000000..408d992 --- /dev/null +++ b/EIPS/eip-3014.md @@ -0,0 +1,48 @@ +--- +eip: 3014 +title: eth_symbol JSON-RPC method +author: Peter Grassberger (@PeterTheOne) +discussions-to: https://github.com/ethereum/EIPs/issues/3012 +status: Stagnant +type: Standards Track +category: Interface +created: 2020-09-30 +--- + +## Simple Summary +Add `eth_symbol` method to the JSON-RPC that returns the symbol of the native coin of the network. + +## Abstract +The new method `eth_symbol` (`eth_`-namespaced) has no parameters and returns a string of the native coin of the network. For the Ethereum mainnet this will be `ETH`, other networks will have other symbols. + +## Motivation +Wallets that deal with multiple networks need some basic information for every blockchain that they connect to. One of those things is the symbol of the native coin of the network. Instead of requiring the user to research and manually add the symbol it could be provided to the wallet via this proposed JSON-RPC endpoint and used automatically. There are lists of networks with symbols like https://github.com/ethereum-lists/chains where a user can manually look up the correct values. But this information could easily come from the network itself. + +## Specification +Method: `eth_symbol`. + +Params: none. + +Returns: `result` - the native coin symbol, string + +Example: + +```js +curl -X POST --data '{"jsonrpc":"2.0","method":"eth_symbol","params":[],"id":1}' + +// Result +{ + "id": 1, + "jsonrpc": "2.0", + "result": "ETH" +} +``` + +## Rationale +This endpoint is similar to [EIP-695](./eip-695.md) but it provides the symbol instead of `chainId`. It provides functionality that is already there for [ERC-20](./eip-20.md) tokens, but not yet for the native coin of the network. Alternative naming of `eth_nativeCurrencySymbol` was considered, but the context and the fact that it just returns one value makes it clear that that it returns the symbol for the native coin of the network. + +## Security Considerations +It is a read only endpoint. The information is only as trusted as the JSON-RPC node itself, it could supply wrong information and thereby trick the user in believing he/she is dealing with another native coin. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3026.md b/EIPS/eip-3026.md new file mode 100644 index 0000000..eee3995 --- /dev/null +++ b/EIPS/eip-3026.md @@ -0,0 +1,277 @@ +--- +eip: 3026 +title: BW6-761 curve operations +author: Youssef El Housni (@yelhousni), Michael Connor (@iAmMichaelConnor), Aurore Guillevic +discussions-to: https://ethereum-magicians.org/t/eip-3026-bw6-761-curve-operations/4790 +status: Stagnant +type: Standards Track +category: Core +requires: 2539 +created: 2020-10-05 +--- + +## Simple Summary +This precompile adds operations for the BW6-761 curve (from the EY/Inria [research paper](https://eprint.iacr.org/2020/351.pdf)) as a precompile in a set necessary to *efficiently* perform verification of one-layer composed zkSNARKs proofs. + +## Abstract + +If `block.number >= X` we introduce *seven* separate precompiles to perform the following operations (addresses to be determined): + +- BW6_G1_ADD - to perform point addition on a curve defined over a prime field +- BW6_G1_MUL - to perform point multiplication on a curve defined over a prime field +- BW6_G1_MULTIEXP - to perform multiexponentiation on a curve defined over a prime field +- BW6_G2_ADD - to perform point addition on a curve twist defined the base a prime field +- BW6_G2_MUL - to perform point multiplication on a curve twist defined over a prime field +- BW6_G2_MULTIEXP - to perform multiexponentiation on a curve twist defined over a prime field +- BW6_PAIRING - to perform a pairing operations between a set of *pairs* of (G1, G2) points + +The multiexponentiation operations are a generalization of point multiplication, but separate precompiles are prosposed because running a single MUL through MULTIEXP seems to be 20% more expensive. + +## Motivation + +This EIP is based on and tends to replace [EIP-2541](https://github.com/matter-labs/EIPs/blob/sw6_wrapping/EIPS/eip-2541.md) for significant performance reasons. In most applications, BW6-761 is used as an outer curve to BLS12-377 considered in [EIP-2539](https://github.com/ethereum/EIPs/pull/2539). +The motivation of this precompile is to allow efficient one-layer composition of SNARK proofs. Currently this is done by Zexe using the BLS12-377/CP6-782 pair of curves. This precompile proposes a replacement of CP6-782 by BW6-761, which allows much faster operations. For example, it was shown that verifying a Groth16 proof with BW6-761 is 30 times faster than with CP6-782. + +### Proposed addresses table + +|Precompile |Address | +|---|---| +|BW6_G1_ADD | 0x13 | +|BW6_G1_MUL | 0x14 | +|BW6_G1_MULTIEXP | 0x15 | +|BW6_G2_ADD | 0x16 | +|BW6_G2_MUL | 0x17 | +|BW6_G2_MULTIEXP | 0x18 | +|BW6_PAIRING | 0x19 | + +## Specification + +Curve parameters: + +The BW6-761 `y^2=x^3-1` curve is fully defined by the following set of parameters: + +``` +Base field modulus = 0x122e824fb83ce0ad187c94004faff3eb926186a81d14688528275ef8087be41707ba638e584e91903cebaff25b423048689c8ed12f9fd9071dcd3dc73ebff2e98a116c25667a8f8160cf8aeeaf0a437e6913e6870000082f49d00000000008b +A coefficient = 0x0 +B coefficient = 0x122e824fb83ce0ad187c94004faff3eb926186a81d14688528275ef8087be41707ba638e584e91903cebaff25b423048689c8ed12f9fd9071dcd3dc73ebff2e98a116c25667a8f8160cf8aeeaf0a437e6913e6870000082f49d00000000008a +Main subgroup order = 0x1ae3a4617c510eac63b05c06ca1493b1a22d9f300f5138f1ef3622fba094800170b5d44300000008508c00000000001 +Extension tower: +Fp3 construction: (Fp3 = Fp[u]/u^3+4) +Fp cubic non-residue = 0x122e824fb83ce0ad187c94004faff3eb926186a81d14688528275ef8087be41707ba638e584e91903cebaff25b423048689c8ed12f9fd9071dcd3dc73ebff2e98a116c25667a8f8160cf8aeeaf0a437e6913e6870000082f49d000000000087 +Twist parameters: +Twist type: M +twist curve A coefficient c0 = 0x0 + c1 = 0x0 +twist curve B coefficient c0 = 0x4 + c1 = 0x0 +Generators: +G1: +X = 0x1075b020ea190c8b277ce98a477beaee6a0cfb7551b27f0ee05c54b85f56fc779017ffac15520ac11dbfcd294c2e746a17a54ce47729b905bd71fa0c9ea097103758f9a280ca27f6750dd0356133e82055928aca6af603f4088f3af66e5b43d +Y = 0x58b84e0a6fc574e6fd637b45cc2a420f952589884c9ec61a7348d2a2e573a3265909f1af7e0dbac5b8fa1771b5b806cc685d31717a4c55be3fb90b6fc2cdd49f9df141b3053253b2b08119cad0fb93ad1cb2be0b20d2a1bafc8f2db4e95363 +G2: +X = 0x110133241d9b816c852a82e69d660f9d61053aac5a7115f4c06201013890f6d26b41c5dab3da268734ec3f1f09feb58c5bbcae9ac70e7c7963317a300e1b6bace6948cb3cd208d700e96efbc2ad54b06410cf4fe1bf995ba830c194cd025f1c +Y = 0x17c3357761369f8179eb10e4b6d2dc26b7cf9acec2181c81a78e2753ffe3160a1d86c80b95a59c94c97eb733293fef64f293dbd2c712b88906c170ffa823003ea96fcd504affc758aa2d3a3c5a02a591ec0594f9eac689eb70a16728c73b61 +Pairing parameters: +e(P,Q)=(ML1(P,Q)*ML2(P,Q)^q)^FE +|loop_count_1| (first miller loop ML1 count) = 0x8508c00000000002 +|loop_count_2| (second miller loop ML2 count) = 0x23ed1347970dec008a442f991fffffffffffffffffffffff +loop_count_1 is negative = false +loop_count_2 is negative = false +``` + +#### Encoding + +##### Field elements encoding: + +To encode points involved in the operation one has to encode elements of only the base field. + +The base field element (Fp) is encoded as `96` bytes by performing BigEndian encoding of the corresponding (unsigned) integer. The corresponding integer **MUST** be less than the base field modulus. + +If encodings do not follow this spec anywhere during parsing in the precompile, the precompile **MUST** revert with "endoding error". + +##### Encoding of uncompressed points: + +Points in both G1 and G2 can be expressed as `(x, y)` affine coordinates, where `x` and `y` are elements of the base field. +Therefore, points in both G1 and G2 are encoded as the byte concatenation of the field element encodings of the `x` and `y` affine coordinates. The total encoding length for a G1/G2 point is thus `192` bytes. + +##### Point at infinity encoding: + +Also referred as the "zero point". For BW6-761 (`y^2=x^3-1`) and its M-twisted curves (`y^3=x^3+4`), the point with coordinates `(0, 0)` (formal zeros in Fp) is *not* on the curve, and so the encoding of `(0, 0)` is used as a convention to encode the point at infinity. + +##### Encoding of scalars for multiplication and multiexponentiation operations: + +For multiplication and multiexponentiation operations, a scalar is encoded as `64` bytes by performing BigEndian encoding of the corresponding (unsigned) integer. + +Note that the main subgroup order for BW6-761 is actually only `377` bits (`48` bytes), but an encoding of `64` bytes has been chosen to have a `32`-byte-aligned ABI (representable as e.g. `bytes32[2]` or `uint256[2]`). + +The corresponding integer **MAY** be greater than the main subgroup order. + +#### ABI for operations + +##### ABI for G1 addition + +G1 addition call expects `384` bytes as an input that is interpreted as the byte concatenation of two G1 points (point-encoded as `192` bytes each). Output is a point-encoding of the addition operation result. + +Error cases: +- Either of the points being not on the curve +- Input has invalid length +- Field element encoding rules apply (obviously) + +##### ABI for G1 multiplication +G1 multiplication call expects `256` bytes as an input that is interpreted as the byte concatenation of the point-encoding of a G1 point (`192` bytes) and the encoding of a scalar value (`64` bytes). Output is a point-encoding of the multiplication operation result. + +Error cases: +- Point being not on the curve +- Input has invalid length +- Field element encoding rules apply (obviously) +- Scalar encoding rules apply (obviously) + +##### ABI for G1 multiexponentiation + +G1 multiplication call expects `256*k` bytes as an input that is interpreted as the byte concatenation of `k` slices, each of them being a byte concatenation of the point-encoding of a G1 point (`192` bytes) and the encoding of a scalar value (`64` bytes). Output is an encoding of the multiexponentiation operation result. + +Error cases: +- Any of the G1 points being not on the curve +- Input has invalid length +- Field element encoding rules apply (obviously) +- Scalar encoding rules apply (obviously) + +##### ABI for G2 addition + +G2 addition call expects `384` bytes as an input that is interpreted as the byte concatenation of two G2 points (point-encoded as `192` bytes each). Output is a point-encoding of the addition operation result. + +Error cases: +- Either of points being not on the curve +- Input has invalid length +- Field elements encoding rules apply (obviously) + +##### ABI for G2 multiplication +G2 multiplication call expects `256` bytes as an input that is interpreted as the byte concatenation of the point-encoding of a G2 point (`192` bytes) and the encoding of a scalar value (`64` bytes). Output is an encoding of multiplication operation result. + +Error cases: +- Point being not on the curve must result in error +- Field elements encoding rules apply (obviously) +- Input has invalid length + +##### ABI for G2 multiexponentiation + +G2 multiplication call expects `240*k` bytes as an input that is interpreted as byte concatenation of `k` slices each of them being a byte concatenation of encoding of G2 point (`192` bytes) and encoding of a scalar value (`48` bytes). Output is an encoding of multiexponentiation operation result. + +Error cases: +- Any of G2 points being not on the curve must result in error +- Field elements encoding rules apply (obviously) +- Input has invalid length + +##### ABI for pairing + +Pairing call expects `384*k` bytes as an input, that is interpreted as the byte concatenation of `k` slices. Each slice has the following structure: +- `192` bytes G1 point encoding +- `192` bytes G2 point encoding + +Output is `32` bytes representing a boolean: + +- `0x0000000000000000000000000000000000000000000000000000000000000001` if the pairing result is equal the to multiplicative identity in the pairing target field; and +- `0x0000000000000000000000000000000000000000000000000000000000000000` otherwise. + +Error cases: +- Any of the G1 or G2 points being not on the curve +- Any of the G1 or G2 points being not in the correct subgroup +- Input has invalid length +- Field elements encoding rules apply (obviously) + +#### Prevention of DDoS on error handling + +This precompile performs extensive computations and in case of any errors during execution it **MUST** consume all gas from the the gas schedule for the corresponding operation. + +#### Gas schedule + +##### G1 addition +`` gas + +##### G1 multiplication +`` gas + +##### G2 addition +`` gas + +##### G2 multiplication +`` gas + +##### G1/G2 Multiexponentiation +Discounts table as a vector of pairs `[k, discount]`: + +``` + +``` + +`max_discount = ` + +##### Pairing operation +Base cost of the pairing operation is `*k + ` where `k` is a number of pairs. + +## Rationale +Gas costs are based on EIP1962 estimation strategy (but do not fully include yet parsing of ABI, decoding and encoding of the result as a byte array). + +#### Gas estimation strategy +Gas cost is derived by taking the average timing of the same operations over different implementations and assuming a constant `30 MGas/second`. Since the execution time is machine-specific, this constant is determined based on execution times of [ECRECOVER](https://github.com/matter-labs/eip1962/blob/master/run_bn_pairing_estimate.sh) and [BNPAIR](https://github.com/matter-labs/eip1962/blob/master/run_bn_pairing_estimate.sh) precompiles on my machine and their proposed gas price (`43.5 MGas/s` for ECRECOVER and `16.5 MGas/s` for BNPAIR). Following are the proposed methods to time the precompile operations: + +- G1 addition: Average timing of 1000 random samples. +- G1 multiplication: Average timing of 1000 samples of random worst-case of double-and-add algorithm (scalar of max bit length and max hamming weight and random base points in G1) +- G2 addition: Average timing of 1000 random samples +- G2 multiplication: Average timing of 1000 samples of radnom worst-case of double-and-add algorithm (scalar of max bit length and max hamming weight and random base points in G2) +- G1 and G2 multiexponentiations: Expected to be performed by the Peppinger algorithm, with a table prepared for discount in case of `k <= 128` points in the multiexponentiation with a discount cup `max_discount` for `k > 128`. To avoid non-integer arithmetic call cost is calculated as `k * multiplication_cost * discount / multiplier` where `multiplier = 1000`, `k` is a number of (scalar, point) pairs for the call, `multiplication_cost` is a corresponding single multiplication call cost for G1/G2. +- Pairing: Average timing of 1000 random samples (random points in G1 and G2) for different number of pairs with linear lifting. + +#### Multiexponentiation as a separate call +Explicit separate multiexponentiation operation that allows one to save execution time (so gas) by both the algorithm used (namely Peppinger algorithm) and (usually forgotten) by the fact that `CALL` operation in Ethereum is expensive (at the time of writing), so one would have to pay non-negigible overhead if e.g. for multiexponentiation of `100` points would have to call the multipication precompile `100` times and addition for `99` times (roughly `138600` would be saved). + +#### Explicit subgroup checks +G2 subgroup check has the same cost as G1 subgroup check. Endomorphisms can be leverages to optimize this operation. + +## Backwards Compatibility +There are no backward compatibility questions. + +## Test Cases + +Due to the large test parameters space we first provide properties that various operations must satisfy. We use additive notation for point operations, capital letters (`P`, `Q`) for points, small letters (`a`, `b`) for scalars. Generator for G1 is labeled as `G`, generator for G2 is labeled as `H`, otherwise we assume random point on a curve in a correct subgroup. `0` means either scalar zero or point of infinity. `1` means either scalar one or multiplicative identity. `group_order` is a main subgroup order. `e(P, Q)` means pairing operation where `P` is in G1, `Q` is in G2. + +Requeired properties for basic ops (add/multiply): + +- Commutativity: `P + Q = Q + P` +- Additive negation: `P + (-P) = 0` +- Doubling `P + P = 2*P` +- Subgroup check: `group_order * P = 0` +- Trivial multiplication check: `1 * P = P` +- Multiplication by zero: `0 * P = 0` +- Multiplication by the unnormalized scalar `(scalar + group_order) * P = scalar * P` + +Required properties for pairing operation: +- Degeneracy `e(P, 0*Q) = e(0*P, Q) = 1` +- Bilinearity `e(a*P, b*Q) = e(a*b*P, Q) = e(P, a*b*Q)` (internal test, not visible through ABI) + +Test vector for all operations are expanded in this [gist](https://gist.github.com/shamatar/506ab3193a7932fe9302a2f3a31a23e8) until it's final. + +## Implementation +There is a various choice of existing implementations: + +**Libraries:** +- Rust implementation (EY/Zexe): https://github.com/yelhousni/zexe/tree/youssef/BW6-761-Fq-ABLR-2ML-M +- C++ implementation (EY/libff): https://github.com/EYBlockchain/zk-swap-libff +- Golang implementation (Consensys/gurvy): https://github.com/ConsenSys/gurvy + +**Stand-alone implementation:** +- Golang implementation with Intel assembly (Onur Kilic): https://github.com/kilic/bw6 + +**Precompiles:** +- OpenEthereum (EY/Parity): https://github.com/EYBlockchain/solidity-elliptic-curves + +**Scripts:** +- SageMath and Magma scripts: https://gitlab.inria.fr/zk-curves/bw6-761/ + +## Security Considerations +Strictly following the spec will eliminate security implications or consensus implications in a contrast to the previous BN254 precompile. + +Important topic is a "constant time" property for performed operations. We explicitly state that this precompile **IS NOT REQUIRED** to perform all the operations using constant time algorithms. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3030.md b/EIPS/eip-3030.md new file mode 100644 index 0000000..7d3ecba --- /dev/null +++ b/EIPS/eip-3030.md @@ -0,0 +1,361 @@ +--- +eip: 3030 +title: BLS Remote Signer HTTP API +author: Herman Junge (@hermanjunge) +discussions-to: https://ethereum-magicians.org/t/eip-3030-bls-remote-signer-http-api-standard/4810 +status: Stagnant +type: Standards Track +category: Interface +created: 2020-09-30 +--- + +## Simple Summary +This EIP defines a HTTP API standard for a BLS remote signer, consumed by validator clients to sign block proposals and attestations in the context of Ethereum 2.0 (eth2). + +## Abstract +A [validator](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/validator.md) client contributes to the consensus of the Eth2 blockchain by signing proposals and attestations of blocks, using a BLS private key which must be available to this client at all times. + +The BLS remote signer API is designed to be consumed by validator clients, looking for a more secure avenue to store their BLS12-381 private key(s), enabling them to run in more permissive and scalable environments. + +## Motivation +Eth2 utilizes [BLS12-381](https://github.com/cfrg/draft-irtf-cfrg-bls-signature/) signatures. + +Consensus on the eth2 Blockchain is achieved via the proposal and attestation of blocks from validator clients, using a BLS private key (_signing_ key) which must be available each time a message is signed: that is, at least once every epoch (6.4 minutes), during a small window of time within this epoch (a _slot_, i.e. 12 seconds), as each validator is expected to attest exactly once per epoch. + +The [eth2 specification](https://github.com/ethereum/eth2.0-specs) does not explicitly provide a directive on where this BLS private key must/should be stored, leaving this implementation detail to the client teams, who assume that this cryptographic secret is stored on the same host as the validator client. + +This assumption is sufficient in the use case where the validator client is running in a physically secure network (i.e. nobody, but the operator, has a chance to log-in into the machine hosting the validator client), as such configuration would only allow _outbound_ calls from the validator client. In this situation, only a physical security breach, or a Remote Code Execution (RCE) vulnerability can allow an attacker to either have arbitrary access to the storage or to the memory of the device. + +There are, however, use cases where it is required by the operator to run a validator client node in less constrained security environments, as the ones given by a cloud provider. Notwithstanding any security expectation, nothing prevents a rogue operator from gaining arbitrary access to the assets running inside a node. + +The situation is not better when the requirement is to execute the validators by leveraging a container orchestration solution (e.g. Kubernetes). The handling of secret keys across nodes can become a burden both from an operational as well as a security perspective. + +The proposed solution comprises running a specialized node with exclusive access to the secret keys, listening to a simple API (defined in the [Specification](#specification) section), and returning the requested signatures. Operators working under this schema must utilize clients with the adequate feature supporting the consumption of this API. + +The focus of this specification is the supply of BLS signatures _on demand_. The aspects of authentication, key management (creation, update, and deletion), and transport encryption are discussed in the [Rationale](#rationale) section of this document. Moreover, the [Threat Model](#threat-model) section of this document provides a (non-exhaustive) list of threats and attack vectors, along with the suggested related mitigation strategy. + +## Specification + +### `GET /upcheck` + +_**Responses**_ + +Success |
+--- | --- +Code | `200` +Content | `{"status": "OK"}` + +--- + +### `GET /keys` + +Returns the identifiers of the keys available to the signer. + +_**Responses**_ + +Success |
+--- | --- +Code | `200` +Content | `{"keys": "[identifier]"}` + +--- + +### `POST /sign/:identifier` + +URL Parameter |
+--- | --- +`:identifier` | `public_key_hex_string_without_0x` + +_**Request**_ + +JSON Body |
|
+--- | --- | --- +`bls_domain` | **Required** | The BLS Signature domain.
As defined in the [specification](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#domain-types), in lowercase, omitting the `domain` prefix.
Supporting `beacon_proposer`, `beacon_attester`, and `randao`. +`data` | **Required** | The data to be signed.
As defined in the specifications for [block](https://github.com/ethereum/eth2.0-APIs/blob/master/types/block.yaml), [attestation](https://github.com/ethereum/eth2.0-APIs/blob/master/types/attestation.yaml), and [epoch](https://github.com/ethereum/eth2.0-APIs/blob/master/types/misc.yaml). +`fork` | **Required** | A `Fork` object containing previous and current versions.
As defined in the [specification](https://github.com/ethereum/eth2.0-APIs/blob/master/types/misc.yaml) +`genesis_validators_root` | **Required** | A `Hash256` for domain separation and chain versioning. +
| Optional | Any other field will be ignored by the signer + +_**Responses**_ + +Success |
+--- | --- +Code | `200` +Content | `{"signature": ""}` + +Where signature is a [BLS signature](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#bls-signatures) byte array encoded as a hexadecimal string. + +_or_ + +Error |
+--- | --- +Code | `400` +Content | `{"error": ""}` + +_or_ + +Error |
+--- | --- +Code | `404` +Content | `{"error": "Key not found: "}` + +--- + +## Rationale + +### UNIX philosophy: Simple API + +This API specification contains only three methods: one for **status**, one for **listing the available keys**, and one to **produce a signature**. There are no methods for authentication, key management, nor transport encryption. + +The following subsections discuss aspects to be considered by the client implementers relative to these subjects. + +#### Implementation of additional features + +From an API pipeline view, we have two nodes: The validator client (1) that makes requests to the remote signer (2). A more sophisticated chain can be built by introducing elements between these two nodes. Either by setting up reverse proxy services, or by adding plugins to the remote signer implementation. + +#### Authentication + +Can be accomplished through the use of an HTTP Request Header. There are several ways to negotiate and issue a valid token to authenticate the communication between the validator client and the remote signer, each of them with potential drawbacks (e.g replay attacks, challenges in distributing the token to the validator client, etc.). In general, any method of authentication must be combined with transport encryption to be effective. + +The operator can also implement network Access Control Lists (ACLs) between the validator client's network and the remote signer's network, reducing the attack surface by requiring a potential attacker to be positioned in the same network as the validator client. + +#### Key management + +There are several ways to store secret keys, namely Hardware Security Modules (HSM), Secrets management applications (e.g. Hashicorp Vault), cloud storage with tight private network ACL rules, or even raw files in a directory. In general the remote signer implementers will abstract the storage medium from the HTTP API. + +It is in this perspective, that any procedure to create, update, or delete keys should be built separate from the client implementation. + +#### Transport Encryption + +If the operator is working with self-signed certificates, it is required that the client enhancement consuming the remote signer allows this option. + +## Test Cases + +### Test Data + +* BLS Pair + * Public key: `0xb7354252aa5bce27ab9537fd0158515935f3c3861419e1b4b6c8219b5dbd15fcf907bddf275442f3e32f904f79807a2a`. + * Secret key: `0x68081afeb7ad3e8d469f87010804c3e8d53ef77d393059a55132637206cc59ec`. +* Signing root: `0xb6bb8f3765f93f4f1e7c7348479289c9261399a3c6906685e320071a1a13955c`. +* Expected signature: `0xb5d0c01cef3b028e2c5f357c2d4b886f8e374d09dd660cd7dd14680d4f956778808b4d3b2ab743e890fc1a77ae62c3c90d613561b23c6adaeb5b0e288832304fddc08c7415080be73e556e8862a1b4d0f6aa8084e34a901544d5bb6aeed3a612`. + +### `GET /upcheck` + +```bash +# Success + +## Request +curl -v localhost:9000/upcheck + +## Response +* Trying 127.0.0.1:9000... +* TCP_NODELAY set +* Connected to localhost (127.0.0.1) port 9000 (#0) +> GET /upcheck HTTP/1.1 +> Host: localhost:9000 +> User-Agent: curl/7.68.0 +> Accept: */* +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< content-length: 15 +< date: Wed, 30 Sep 2020 02:25:08 GMT +< +* Connection #0 to host localhost left intact +{"status":"OK"} +``` + +### `GET /keys` + +```bash +# Success + +## Request +curl -v localhost:9000/keys + +## Response +* Trying 127.0.0.1:9000... +* TCP_NODELAY set +* Connected to localhost (127.0.0.1) port 9000 (#0) +> GET /publicKeys HTTP/1.1 +> Host: localhost:9000 +> User-Agent: curl/7.68.0 +> Accept: */* +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< content-length: 116 +< date: Wed, 30 Sep 2020 02:25:36 GMT +< +* Connection #0 to host localhost left intact +{"keys":["b7354252aa5bce27ab9537fd0158515935f3c3861419e1b4b6c8219b5dbd15fcf907bddf275442f3e32f904f79807a2a"]} + +# Server Error + +## Preparation +## `chmod` keys directory to the octal 311 (-wx--x--x). + +## Request +curl -v localhost:9000/keys + +## Response +* Trying 127.0.0.1:9000... +* TCP_NODELAY set +* Connected to localhost (127.0.0.1) port 9000 (#0) +> GET /publicKeys HTTP/1.1 +> Host: localhost:9000 +> User-Agent: curl/7.68.0 +> Accept: */* +> +* Mark bundle as not supporting multiuse +< HTTP/1.1 500 Internal Server Error +< content-length: 43 +< date: Wed, 30 Sep 2020 02:26:09 GMT +< +* Connection #0 to host localhost left intact +{"error":"Storage error: PermissionDenied"} +``` + +### `POST /sign/:identifier` + +```bash +# Success + +## Request +curl -v -X POST -d @payload.json -H 'Content-Type: application/json' localhost:9000/sign/b7354252aa5bce27ab9537fd0158515935f3c3861419e1b4b6c8219b5dbd15fcf907bddf275442f3e32f904f79807a2a + +## Response +Note: Unnecessary use of -X or --request, POST is already inferred. +* Trying 127.0.0.1:9000... +* TCP_NODELAY set +* Connected to localhost (127.0.0.1) port 9000 (#0) +> POST /sign/b7354252aa5bce27ab9537fd0158515935f3c3861419e1b4b6c8219b5dbd15fcf907bddf275442f3e32f904f79807a2a HTTP/1.1 +> Host: localhost:9000 +> User-Agent: curl/7.68.0 +> Accept: */* +> Content-Type: application/json +> Content-Length: 84 +> +* upload completely sent off: 84 out of 84 bytes +* Mark bundle as not supporting multiuse +< HTTP/1.1 200 OK +< content-type: application/json +< content-length: 210 +< date: Wed, 30 Sep 2020 02:16:02 GMT +< +* Connection #0 to host localhost left intact +{"signature":"0xb5d0c01cef3b028e2c5f357c2d4b886f8e374d09dd660cd7dd14680d4f956778808b4d3b2ab743e890fc1a77ae62c3c90d613561b23c6adaeb5b0e288832304fddc08c7415080be73e556e8862a1b4d0f6aa8084e34a901544d5bb6aeed3a612"} + +# Bad Request Error + +## Request +curl -v -X POST -d 'foobar' -H 'Content-Type: application/json' localhost:9000/sign/b7354252aa5bce27ab9537fd0158515935f3c3861419e1b4b6c8219b5dbd15fcf907bddf275442f3e32f904f79807a2a + +## Response +Note: Unnecessary use of -X or --request, POST is already inferred. +* Trying 127.0.0.1:9000... +* TCP_NODELAY set +* Connected to localhost (127.0.0.1) port 9000 (#0) +> POST /sign/b7354252aa5bce27ab9537fd0158515935f3c3861419e1b4b6c8219b5dbd15fcf907bddf275442f3e32f904f79807a2a HTTP/1.1 +> Host: localhost:9000 +> User-Agent: curl/7.68.0 +> Accept: */* +> Content-Type: application/json +> Content-Length: 23 +> +* upload completely sent off: 23 out of 23 bytes +* Mark bundle as not supporting multiuse +< HTTP/1.1 400 Bad Request +< content-length: 38 +< date: Wed, 30 Sep 2020 02:15:05 GMT +< +* Connection #0 to host localhost left intact +{"error":"Unable to parse body message from JSON: Error(\"expected ident\", line: 1, column: 2)"} + +# No Keys Available + +## Request +curl -v -X POST -d @payload.json -H 'Content-Type: application/json' localhost:9000/sign/000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + +## Response +Note: Unnecessary use of -X or --request, POST is already inferred. +* Trying 127.0.0.1:9000... +* TCP_NODELAY set +* Connected to localhost (127.0.0.1) port 9000 (#0) +> POST /sign/000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 HTTP/1.1 +> Host: localhost:9000 +> User-Agent: curl/7.68.0 +> Accept: */* +> Content-Type: application/json +> Content-Length: 84 +> +* upload completely sent off: 84 out of 84 bytes +* Mark bundle as not supporting multiuse +< HTTP/1.1 404 Not Found +< content-length: 123 +< date: Wed, 30 Sep 2020 02:18:53 GMT +< +* Connection #0 to host localhost left intact +{"error":"Key not found: 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"} + +# Server Error + +## Preparation +## `chmod` both keys directory and file to the octal 311 (-wx--x--x). +## `chmod` back to 755 to delete them afterwards. + +## Request +curl -v -X POST -d @payload.json -H 'Content-Type: application/json' localhost:9000/sign/b7354252aa5bce27ab9537fd0158515935f3c3861419e1b4b6c8219b5dbd15fcf907bddf275442f3e32f904f79807a2a + +## Response +Note: Unnecessary use of -X or --request, POST is already inferred. +* Trying 127.0.0.1:9000... +* TCP_NODELAY set +* Connected to localhost (127.0.0.1) port 9000 (#0) +> POST /sign/b7354252aa5bce27ab9537fd0158515935f3c3861419e1b4b6c8219b5dbd15fcf907bddf275442f3e32f904f79807a2a HTTP/1.1 +> Host: localhost:9000 +> User-Agent: curl/7.68.0 +> Accept: */* +> Content-Type: application/json +> Content-Length: 84 +> +* upload completely sent off: 84 out of 84 bytes +* Mark bundle as not supporting multiuse +< HTTP/1.1 500 Internal Server Error +< content-length: 43 +< date: Wed, 30 Sep 2020 02:21:08 GMT +< +* Connection #0 to host localhost left intact +{"error":"Storage error: PermissionDenied"} +``` + +## Implementation + +Repository Url | Language | Organization | Commentary +--- | --- | --- | --- +[BLS Remote Signer](https://github.com/sigp/rust-bls-remote-signer) | Rust | Sigma Prime | Supports proposed specification. +[Web3signer](https://github.com/PegaSysEng/web3signer) | Java | PegaSys | Supports proposed specification, although with [slightly different methods](https://pegasyseng.github.io/web3signer/web3signer-eth2.html):
{`/sign` => `/api/v1/eth2/sign`, `/publicKeys` => `/api/v1/eth2/publicKeys`}. +[Remote Signing Wallet](https://docs.prylabs.network/docs/wallet/remote/) | Golang | Prysmatics Labs | Supports both gRPC and JSON over HTTP. + +## Security Considerations + +### Threat model + +Let's consider the following threats and their mitigations: + +Threat | Mitigation(s) +--- | --- +An attacker can spoof the validator client. | See the discussion at [Authentication](#authentication). +An attacker can send a crafted message to the signer, leading to a slashing offense. | It is the responsibility of the operator of the remote signer to add a validation module, as discussed at [Implementation of additional features](#implementation-of-additional-features). +An attacker can create, update, or delete secret keys. | Keys are not to be writable via any interface of the remote signer. +An attacker can repudiate a sent message. | Implement logging in the signer. Enhance it by sending logs to a syslog box. +An attacker can disclose the contents of a private key by retrieving the key from storage. | Storage in Hardware security module (HSM).
_or_
Storage in Secrets management applications (e.g. Hashicorp Vault). +An attacker can eavesdrop on the uploading of a secret key. | Upload the keys using a secure channel, based on each storage specification. +An attacker can eavesdrop on the retrieval of a key from the remote signer. | Always pass the data between storage and remote signer node using a secure channel. +An attacker can dump the memory in the remote signer to disclose a secret key. | Prevent physical access to the node running the remote signer.
_or_
Prevent access to the terminal of the node running the remote signer: Logs being sent to a syslog box. Deployments triggered by a simple, non-parameterized API.
_or_
Implement zeroization of the secret key at memory.
_or_
Explore the compilation and running of the remote signer in a Trusted execution environment (TEE). +An attacker can DoS the remote signer. | Implement IP filtering.
_or_
Implement Rate limiting. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3041.md b/EIPS/eip-3041.md new file mode 100644 index 0000000..3a21c44 --- /dev/null +++ b/EIPS/eip-3041.md @@ -0,0 +1,94 @@ +--- +eip: 3041 +title: Adds `baseFee` to `eth_getBlockByHash` +author: Abdelhamid Bakhta (@abdelhamidbakhta) +discussions-to: https://ethereum-magicians.org/t/eip-3041-add-basefee-in-eth-getblockbyhash-response/4825 +status: Stagnant +type: Standards Track +category: Interface +created: 2020-10-13 +requires: 1474, 1559 +--- + +## Simple Summary +Add basefee field to `eth_getBlockByHash` RPC endpoint response. + +## Abstract +Adds `baseFee` property to the `eth_getBlockByHash` JSON-RPC request `result` object. This property will contain the value of the base fee for any block after the EIP-1559 fork. + +## Motivation +[EIP-1559](./eip-1559.md) introduces a base fee per gas in protocol. +This value is maintained under consensus as a new field in the block header structure. +Users may need value of the base fee at a given block. Base fee value is important to make gas price predictions more accurate. + +## Specification + +### `eth_getBlockByHash` + +#### Description + +Returns information about a block specified by hash. +Every block returned by this endpoint whose block number is before the [EIP-1559](./eip-1559.md) fork block **MUST NOT** include a `baseFee` field. +Every block returned by this endpoint whose block number is on or after the [EIP-1559](./eip-1559.md) fork block **MUST** include a `baseFee` field. + +#### Parameters + +Parameters remain unchanged. + +#### Returns +For the full specification of `eth_getBlockByHash` see [EIP-1474](./eip-1474.md). +Add a new JSON field to the `result` object for block headers containing a base fee (post [EIP-1559](./eip-1559.md) fork block). + +- {[`Quantity`](./eip-1474.md#quantity)} `baseFee` - base fee for this block + +#### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1559, + "jsonrpc": "2.0", + "method": "eth_getBlockByHash", + "params":["0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331", true] +}' + +# Response +{ + "id": 1559, + "jsonrpc": "2.0", + "result": { + "difficulty": "0x027f07", + "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000", + "baseFee": "0x7" + "gasLimit": "0x9f759", + "gasUsed": "0x9f759", + "hash": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331", + "logsBloom": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331", + "miner": "0x4e65fda2159562a496f9f3522f89122a3088497a", + "nonce": "0xe04d296d2460cfb8472af2c5fd05b5a214109c25688d3704aed5484f9a7792f2", + "number": "0x1b4", + "parentHash": "0x9646252be9520f6e71339a8df9c55e4d7619deeb018d2a3f2d21fc165dde5eb5", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "size": "0x027f07", + "stateRoot": "0xd5855eb08b3387c0af375e9cdb6acfc05eb8f519e419b874b6ff2ffda7ed1dff", + "timestamp": "0x54e34e8e" + "totalDifficulty": "0x027f07", + "transactions": [] + "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncles": [] + } +} +``` + +## Rationale +The addition of a single parameter instead of introducing a whole new endpoint was the simplest change that would be easiest to get integrated. +For backward compatibility we decided to not include the base fee in the response for pre-1559 blocks. + +## Backwards Compatibility +Backwards compatible. Calls related to block prior to [EIP-1559](./eip-1559.md) fork block will omit the base fee field in the response. + +## Security Considerations +The added field (`baseFee`) is informational and does not introduce technical security issues. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3044.md b/EIPS/eip-3044.md new file mode 100644 index 0000000..73af4b9 --- /dev/null +++ b/EIPS/eip-3044.md @@ -0,0 +1,94 @@ +--- +eip: 3044 +title: Adds `baseFee` to `eth_getBlockByNumber` +author: Abdelhamid Bakhta (@abdelhamidbakhta) +discussions-to: https://ethereum-magicians.org/t/eip-3044-add-basefee-to-eth-getblockbynumber/4828 +status: Stagnant +type: Standards Track +category: Interface +created: 2020-10-14 +requires: 1474, 1559 +--- + +## Simple Summary +Add basefee field to `eth_getBlockByNumber` RPC endpoint response. + +## Abstract +Adds `baseFee` property to the `eth_getBlockByNumber` JSON-RPC request `result` object. This property will contain the value of the base fee for any block after the EIP-1559 fork. + +## Motivation +[EIP-1559](./eip-1559.md) introduces a base fee per gas in protocol. +This value is maintained under consensus as a new field in the block header structure. +Users may need value of the base fee at a given block. Base fee value is important to make gas price predictions more accurate. + +## Specification + +### `eth_getBlockByNumber` + +#### Description + +Returns information about a block specified by number. +Every block returned by this endpoint whose block number is before the [EIP-1559](./eip-1559.md) fork block **MUST NOT** include a `baseFee` field. +Every block returned by this endpoint whose block number is on or after the [EIP-1559](./eip-1559.md) fork block **MUST** include a `baseFee` field. + +#### Parameters + +Parameters remain unchanged. + +#### Returns +For the full specification of `eth_getBlockByNumber` see [EIP-1474](./eip-1474.md). +Add a new JSON field to the `result` object for block headers containing a base fee (post [EIP-1559](./eip-1559.md) fork block). + +- {[`Quantity`](./eip-1474.md#quantity)} `baseFee` - base fee for this block + +#### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1559, + "jsonrpc": "2.0", + "method": "eth_getBlockByNumber", + "params":["latest", true] +}' + +# Response +{ + "id": 1559, + "jsonrpc": "2.0", + "result": { + "difficulty": "0x027f07", + "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000", + "baseFee": "0x7" + "gasLimit": "0x9f759", + "gasUsed": "0x9f759", + "hash": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331", + "logsBloom": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331", + "miner": "0x4e65fda2159562a496f9f3522f89122a3088497a", + "nonce": "0xe04d296d2460cfb8472af2c5fd05b5a214109c25688d3704aed5484f9a7792f2", + "number": "0x1b4", + "parentHash": "0x9646252be9520f6e71339a8df9c55e4d7619deeb018d2a3f2d21fc165dde5eb5", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "size": "0x027f07", + "stateRoot": "0xd5855eb08b3387c0af375e9cdb6acfc05eb8f519e419b874b6ff2ffda7ed1dff", + "timestamp": "0x54e34e8e" + "totalDifficulty": "0x027f07", + "transactions": [] + "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncles": [] + } +} +``` + +## Rationale +The addition of a single parameter instead of introducing a whole new endpoint was the simplest change that would be easiest to get integrated. +For backward compatibility we decided to not include the base fee in the response for pre-1559 blocks. + +## Backwards Compatibility +Backwards compatible. Calls related to block prior to [EIP-1559](./eip-1559.md) fork block will omit the base fee field in the response. + +## Security Considerations +The added field (`baseFee`) is informational and does not introduce technical security issues. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3045.md b/EIPS/eip-3045.md new file mode 100644 index 0000000..f3a2f2c --- /dev/null +++ b/EIPS/eip-3045.md @@ -0,0 +1,94 @@ +--- +eip: 3045 +title: Adds `baseFee` to `eth_getUncleByBlockHashAndIndex` +author: Abdelhamid Bakhta (@abdelhamidbakhta) +discussions-to: https://ethereum-magicians.org/t/add-basefee-to-eth-getunclebyblockhashandindex/4829 +status: Stagnant +type: Standards Track +category: Interface +created: 2020-10-14 +requires: 1474, 1559 +--- + +## Simple Summary +Add basefee field to `eth_getUncleByBlockHashAndIndex` RPC endpoint response. + +## Abstract +Adds `baseFee` property to the `eth_getUncleByBlockHashAndIndex` JSON-RPC request `result` object. This property will contain the value of the base fee for any block after the EIP-1559 fork. + +## Motivation +[EIP-1559](./eip-1559.md) introduces a base fee per gas in protocol. +This value is maintained under consensus as a new field in the block header structure. +Users may need value of the base fee at a given block. Base fee value is important to make gas price predictions more accurate. + +## Specification + +### `eth_getUncleByBlockHashAndIndex` + +#### Description + +Returns information about an uncle specified by block hash and uncle index position +Every block returned by this endpoint whose block number is before the [EIP-1559](./eip-1559.md) fork block **MUST NOT** include a `baseFee` field. +Every block returned by this endpoint whose block number is on or after the [EIP-1559](./eip-1559.md) fork block **MUST** include a `baseFee` field. + +#### Parameters + +Parameters remain unchanged. + +#### Returns +For the full specification of `eth_getUncleByBlockHashAndIndex` see [EIP-1474](./eip-1474.md). +Add a new JSON field to the `result` object for block headers containing a base fee (post [EIP-1559](./eip-1559.md) fork block). + +- {[`Quantity`](./eip-1474.md#quantity)} `baseFee` - base fee for this block + +#### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1559, + "jsonrpc": "2.0", + "method": "eth_getUncleByBlockHashAndIndex", + "params":["0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b", "0x0"] +}' + +# Response +{ + "id": 1559, + "jsonrpc": "2.0", + "result": { + "difficulty": "0x027f07", + "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000", + "baseFee": "0x7" + "gasLimit": "0x9f759", + "gasUsed": "0x9f759", + "hash": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331", + "logsBloom": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331", + "miner": "0x4e65fda2159562a496f9f3522f89122a3088497a", + "nonce": "0xe04d296d2460cfb8472af2c5fd05b5a214109c25688d3704aed5484f9a7792f2", + "number": "0x1b4", + "parentHash": "0x9646252be9520f6e71339a8df9c55e4d7619deeb018d2a3f2d21fc165dde5eb5", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "size": "0x027f07", + "stateRoot": "0xd5855eb08b3387c0af375e9cdb6acfc05eb8f519e419b874b6ff2ffda7ed1dff", + "timestamp": "0x54e34e8e" + "totalDifficulty": "0x027f07", + "transactions": [] + "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncles": [] + } +} +``` + +## Rationale +The addition of a single parameter instead of introducing a whole new endpoint was the simplest change that would be easiest to get integrated. +For backward compatibility we decided to not include the base fee in the response for pre-1559 blocks. + +## Backwards Compatibility +Backwards compatible. Calls related to block prior to [EIP-1559](./eip-1559.md) fork block will omit the base fee field in the response. + +## Security Considerations +The added field (`baseFee`) is informational and does not introduce technical security issues. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3046.md b/EIPS/eip-3046.md new file mode 100644 index 0000000..d5ca817 --- /dev/null +++ b/EIPS/eip-3046.md @@ -0,0 +1,94 @@ +--- +eip: 3046 +title: Adds `baseFee` to `eth_getUncleByBlockNumberAndIndex` +author: Abdelhamid Bakhta (@abdelhamidbakhta) +discussions-to: https://ethereum-magicians.org/t/add-basefee-to-eth-getunclebyblocknumberandindex/4830 +status: Stagnant +type: Standards Track +category: Interface +created: 2020-10-14 +requires: 1474, 1559 +--- + +## Simple Summary +Add basefee field to `eth_getUncleByBlockNumberAndIndex` RPC endpoint response. + +## Abstract +Adds `baseFee` property to the `eth_getUncleByBlockNumberAndIndex` JSON-RPC request `result` object. This property will contain the value of the base fee for any block after the EIP-1559 fork. + +## Motivation +[EIP-1559](./eip-1559.md) introduces a base fee per gas in protocol. +This value is maintained under consensus as a new field in the block header structure. +Users may need value of the base fee at a given block. Base fee value is important to make gas price predictions more accurate. + +## Specification + +### `eth_getUncleByBlockNumberAndIndex` + +#### Description + +Returns information about an uncle specified by block number and uncle index position +Every block returned by this endpoint whose block number is before the [EIP-1559](./eip-1559.md) fork block **MUST NOT** include a `baseFee` field. +Every block returned by this endpoint whose block number is on or after the [EIP-1559](./eip-1559.md) fork block **MUST** include a `baseFee` field. + +#### Parameters + +Parameters remain unchanged. + +#### Returns +For the full specification of `eth_getUncleByBlockNumberAndIndex` see [EIP-1474](./eip-1474.md). +Add a new JSON field to the `result` object for block headers containing a base fee (post [EIP-1559](./eip-1559.md) fork block). + +- {[`Quantity`](./eip-1474.md#quantity)} `baseFee` - base fee for this block + +#### Example + +```sh +# Request +curl -X POST --data '{ + "id": 1559, + "jsonrpc": "2.0", + "method": "eth_getUncleByBlockNumberAndIndex", + "params":["latest", "0x0"] +}' + +# Response +{ + "id": 1559, + "jsonrpc": "2.0", + "result": { + "difficulty": "0x027f07", + "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000", + "baseFee": "0x7" + "gasLimit": "0x9f759", + "gasUsed": "0x9f759", + "hash": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331", + "logsBloom": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331", + "miner": "0x4e65fda2159562a496f9f3522f89122a3088497a", + "nonce": "0xe04d296d2460cfb8472af2c5fd05b5a214109c25688d3704aed5484f9a7792f2", + "number": "0x1b4", + "parentHash": "0x9646252be9520f6e71339a8df9c55e4d7619deeb018d2a3f2d21fc165dde5eb5", + "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "size": "0x027f07", + "stateRoot": "0xd5855eb08b3387c0af375e9cdb6acfc05eb8f519e419b874b6ff2ffda7ed1dff", + "timestamp": "0x54e34e8e" + "totalDifficulty": "0x027f07", + "transactions": [] + "transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "uncles": [] + } +} +``` + +## Rationale +The addition of a single parameter instead of introducing a whole new endpoint was the simplest change that would be easiest to get integrated. +For backward compatibility we decided to not include the base fee in the response for pre-1559 blocks. + +## Backwards Compatibility +Backwards compatible. Calls related to block prior to [EIP-1559](./eip-1559.md) fork block will omit the base fee field in the response. + +## Security Considerations +The added field (`baseFee`) is informational and does not introduce technical security issues. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3068.md b/EIPS/eip-3068.md new file mode 100644 index 0000000..8283305 --- /dev/null +++ b/EIPS/eip-3068.md @@ -0,0 +1,296 @@ +--- +eip: 3068 +title: Precompile for BN256 HashToCurve Algorithms +author: Dr. Christopher Gorman (@chgormanMH) +discussions-to: https://ethereum-magicians.org/t/pre-compile-for-bls/3973 +status: Stagnant +type: Standards Track +category: Core +created: 2020-10-23 +requires: 198, 1108 +--- + +## Simple Summary +This EIP defines a hash-to-curve precompile for use in BN256 +and would allow for cheaper BLS signature verification. + +## Abstract +There is currently no inexpensive way to perform BLS signature +verification for arbitrary messages. +This stems from the fact that there is no precompiled contract +in the EVM for a hash-to-curve algorithm for the BN256 elliptic curve. +The gas cost of calling a deterministic hash-to-curve algorithm +written in Solidity is approximately that of one pairing check, +although the latter requires an order of magnitude +more computation. +This EIP remedies this by implementing a hash-to-curve algorithm +for the BN256 G1 curve, which would reduce the cost of +signature verification to essentially that of the pairing check +precompiled contract. +We also include a hash-to-curve algorithm for the BN256 G2 group. + +## Motivation +The precompiled contracts in +[EIP-198](./eip-198.md) and +[EIP-1108](./eip-1108.md) +increased usage of cryptographic operations in the EVM +by reducing the gas costs. +In particular, the cost reduction from +[EIP-1108](./eip-1108.md) +helps increase the use of SNARKs in Ethereum +via an elliptic curve pairing check; +however, a hash-to-curve algorithm enabling arbitrary +BLS signature verification on BN256 in the EVM was noticeably missing. +There is interest in having a precompiled contract which would allow +for signature verification, as noted +[here](https://ethereum-magicians.org/t/pre-compile-for-bls/3973). + +At this time, we are able to perform addition, scalar multiplication, +and pairing checks in BN256. +Reducing these costs in +[EIP-1108](./eip-1108.md) +made [ETHDKG](https://github.com/PhilippSchindler/ethdkg), +a distributed key generation protocol in Ethereum, +less expensive. +ETHDKG by itself is useful; however, what it is lacking is +the ability to verify arbitrary BLS signatures. +Creating group signatures by aggregating partial signatures +is one goal of a DKG protocol. +The DKG enables the computation of partial signatures to be +combined into a group signature offline, but there is no +easy way to verify partial signatures or group signatures +in the EVM. + +In order to perform BLS signature validation, a hash-to-curve +algorithm is required. +While the MapToGroup method initially discussed in the original BLS +[paper](../assets/eip-3068/weilsigs.pdf) +works in practice, the nondeterministic nature of the algorithm +leaves something to be desired as we would like to bound +the overall computational cost in the EVM. +A deterministic method for mapping to BN curves is given +[here](../assets/eip-3068/latincrypt12.pdf); +in the paper, Fouque and Tibouchi proved their mapping +was indifferentiable from a random oracle. +This gives us the desired algorithm. + +## Specification +Here is the pseudocode for the `HashToG1` function: + +``` +function HashToG1(msg) + fieldElement0 = HashToBase(msg, 0x00, 0x01) + fieldElement1 = HashToBase(msg, 0x02, 0x03) + curveElement0 = BaseToG1(fieldElement0) + curveElement1 = BaseToG1(fieldElement1) + g1Element = ECAdd(curveElement0, curveElement1) + return g1Element +end function +``` + +Here is the pseudocode for `HashToBase`; +`msg` is the byte slice to be hashed while `dsp1` and `dsp2` +are domain separation parameters. +`fieldPrime` is the prime of the underlying field. + +``` +function HashToBase(msg, dsp1, dsp2) + hashResult0 = uint256(Keccak256(dsp1||msg)) + hashResult1 = uint256(Keccak256(dsp2||msg)) + constant = 2^256 mod fieldPrime + fieldElement0 = hashResult0*constant mod fieldPrime + fieldElement1 = hashResult1 mod fieldPrime + fieldElement = fieldElement0 + fieldElement1 mod fieldPrime + return fieldElement +end function +``` + +Here is the pseudocode for `BaseToG1`. +All of these operations are performed in the finite field. +`inverse` computes the multiplicative inverse in the underlying +finite field; we have the convention `inverse(0) == 0`. +`is_square(a)` computes the Legendre symbol of the element, +returning 1 if `a` is a square, -1 if `a` is not a square, +and 0 if `a` is 0. +`sqrt` computes the square root of the element in the finite +field; a square root is assumed to exist. +`sign0` returns the sign of the finite field element. + +``` +function BaseToG1(t) + # All operations are done in the finite field GF(fieldPrime) + # Here, the elliptic curve satisfies the equation + # y^2 == g(x) == x^3 + curveB + constant1 = (-1 + sqrt(-3))/2 + constant2 = -3 + constant3 = 1/3 + constant4 = g(1) + s = (constant4 + t^2)^3 + alpha = inverse(t^2*(constant4 + t^2)) + x1 = constant1 - constant2*t^4*alpha + x2 = -1 - x1 + x3 = 1 - constant3*s*alpha + a1 = x1^3 + curveB + a2 = x2^3 + curveB + residue1 = is_square(a1) + residue2 = is_square(a2) + index = (residue1 - 1)*(residue2 - 3)/4 + 1 + coef1 = ConstantTimeEquality(1, index) + coef2 = ConstantTimeEquality(2, index) + coef3 = ConstantTimeEquality(3, index) + x = coef1*x1 + coef2*x2 + coef3*x3 + y = sign0(t)*sqrt(x^3 + curveB) + return (x, y) +end function + +function sign0(t) + if t <= (fieldPrime-1)/2 + return 1 + else + return fieldPrime-1 + end if +end function + +function ConstantTimeEquality(a, b) + # This function operates in constant time + if a == b + return 1 + else + return 0 + end if +end function +``` + +In `HashToG2`, we first map to the underlying twist curve +and then clear the cofactor to map to G2. +Here is the pseudocode for `HashToG2`: + +``` +function HashToG2(msg) + fieldElement00 = HashToBase(msg, 0x04, 0x05) + fieldElement01 = HashToBase(msg, 0x06, 0x07) + fieldElement10 = HashToBase(msg, 0x08, 0x09) + fieldElement11 = HashToBase(msg, 0x0a, 0x0b) + fieldElement0 = (fieldElement00, fieldElement01) + fieldElement1 = (fieldElement10, fieldElement11) + twistElement0 = BaseToTwist(fieldElement0) + twistElement1 = BaseToTwist(fieldElement1) + twistElement = ECAdd(twistElement0, twistElement1) + g2Element = ClearCofactor(twistElement) + return g2Element +end function + +function ClearCofactor(twistElement) + return ECMul(twistElement, cofactor) +end function +``` + +Here is the pseudocode for `BaseToTwist`. + +``` +function BaseToTwist(t) + # All operations are done in the finite field GF(fieldPrime^2) + # Here, the twist curve satisfies the equation + # y^2 == g'(x) == x^3 + curveBPrime + constant1 = (-1 + sqrt(-3))/2 + constant2 = -3 + constant3 = 1/3 + constant4 = g'(1) + s = (constant4 + t^2)^3 + alpha = inverse(t^2*(constant4 + t^2)) + x1 = constant1 - constant2*t^4*alpha + x2 = -1 - x1 + x3 = 1 - constant3*s*alpha + a1 = x1^3 + curveBPrime + a2 = x2^3 + curveBPrime + residue1 = is_square(a1) + residue2 = is_square(a2) + index = (residue1 - 1)*(residue2 - 3)/4 + 1 + coef1 = ConstantTimeEquality(1, index) + coef2 = ConstantTimeEquality(2, index) + coef3 = ConstantTimeEquality(3, index) + x = coef1*x1 + coef2*x2 + coef3*x3 + y = sign0(t)*sqrt(x^3 + curveBPrime) + return (x, y) +end function +``` + +## Rationale +The BaseToG1 algorithm is based on the original Fouque and Tibouchi +[paper](../assets/eip-3068/latincrypt12.pdf) +with modifications based on Wahby and Boneh's +[paper](../assets/eip-3068/2019-403_BLS12_H2C.pdf). +There is freedom in choosing the HashToBase function +and this could easily be changed. +Within HashToBase, the particular hashing algorithm +(Keccak256 in our case) could also be modified. +It may be desired to change the call to `sign0` +at the end of BaseToG1 and BaseToTwist with `is_square`, +as this would result in the same deterministic map to curve from the +Fouque and Tibouchi +[paper](../assets/eip-3068/latincrypt12.pdf) +and ensure HashToG1 is indifferentiable from a random oracle; +they proved this result in their paper. +It may be possible to show that switching the `is_square` +call with `sign0` does not affect indifferentiability, +although this has not been proven. + +The HashToG2 algorithm follows from the Wahby and Boneh +[paper](../assets/eip-3068/2019-403_BLS12_H2C.pdf). +Algorithms for computing `inverse`, `is_square`, and `sqrt` +in finite field GF(fieldPrime^2) can be found +[here](../assets/eip-3068/2012-685_Square_Root_Even_Ext.pdf). + +We now discuss the potential gas cost for the HashToG1 +and HashToG2 operations. +On a local machine, ECMul was clocked at 68 microseconds +per operation. +The same machine clocked HashToG1 at 94 microseconds per operation +when hashing 32 bytes into G1 and 105 microseconds per operation +when hashing 1024 bytes into G1. +Given that it currently costs 6000 gas for ECMul, this gives us +an estimated gas cost for HashToG1 at `8500 + len(bytes)`. +Similarly, HashToG2 was clocked at 886 microseconds per operation +when hashing 32 bytes into G2 and 912 microseconds per operation when +hashing 1024 bytes into G2. +This allows us to estimate the gas cost at `80000 + 3*len(bytes)`. + +## Backwards Compatibility +There are no backward compatibility concerns. + +## Test Cases +TBD + +## Implementation +TBD + +## Security Considerations +Due to recent [work](../assets/eip-3068/2015-1027_exTNFS.pdf), the +128-bit security promised by the BN256 elliptic curve no longer applies; +this was mentioned in the Cloudflare BN256 +[library](https://github.com/cloudflare/bn256). +There has been some discussion on the exact security decrease +from this advancement; see these +[two](../assets/eip-3068/2016-1102_Assessing_NFS_Advances.pdf) +[papers](../assets/eip-3068/2017-334.pdf) +for different estimates. +The more conservative estimate puts the security of BN256 at +100-bits. +While this is likely still out of reach for many adversaries, +it should give us pause. +This reduced security was noted in the recent MadNet +[whitepaper](../assets/eip-3068/madnet.pdf), +and this security concern was partially mitigated by +requiring Secp256k1 signatures of the partial group signatures +in order for those partial signatures to be valid. +Full disclosure: the author of this EIP works for MadHive, +assisted in the development of MadNet, and +helped write the MadNet whitepaper. + +The security concerns of the BN256 elliptic curve +affect any operation using pairing check because it is +related to the elliptic curve pairing; +they are independent of this EIP. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3074.md b/EIPS/eip-3074.md new file mode 100644 index 0000000..0c04800 --- /dev/null +++ b/EIPS/eip-3074.md @@ -0,0 +1,331 @@ +--- +eip: 3074 +title: AUTH and AUTHCALL opcodes +description: Allow externally owned accounts to delegate control to a contract. +author: Sam Wilson (@SamWilsn), Ansgar Dietrichs (@adietrichs), Matt Garnett (@lightclient), Micah Zoltu (@micahzoltu) +discussions-to: https://ethereum-magicians.org/t/eip-3074-sponsored-transaction-precompile/4880 +status: Review +type: Standards Track +category: Core +created: 2020-10-15 +requires: 155 +--- + +## Abstract + +This EIP introduces two EVM instructions `AUTH` and `AUTHCALL`. The first sets a context variable `authorized` based on an ECDSA signature. The second sends a call as the `authorized` account. This essentially delegates control of the externally owned account (EOA) to a smart contract. + +## Motivation + +Adding more functionality to EOAs has been a long-standing feature request. The requests have spanned from implementing batching capabilities, allowing for gas sponsoring, expirations, scripting, and beyond. These changes often mean increased complexity and rigidity of the protocol. In some cases, it also means increased attack surfaces. + +This EIP takes a different approach. Instead of enshrining these capabilities in the protocol as transaction validity requirements, it allows users to *delegate* control of their EOA to a contract. This gives developers a flexible framework for developing novel transaction schemes for EOAs. A motivating use case of this EIP is that it allows any EOA to act like a smart contract wallet *without* deploying a contract. + +Although this EIP provides great benefit to individual users, the leading motivation for this EIP is "sponsored transactions". This is where the fee for a transaction is provided by a different account than the one that originates the call. + +With the extraordinary growth of tokens on Ethereum, it has become common for EOAs to hold valuable assets without holding any ether at all. Today, these assets must be converted to ether before they can be used to pay gas fees. However, without ether to pay for the conversion, it's impossible to convert them. Sponsored transactions break the circular dependency. + +## Specification + +### Conventions + + - **`top - N`** - the `N`th most recently pushed value on the EVM stack, where `top - 0` is the most recent. + - **`||`** - byte concatenation operator. + - **invalid execution** - execution that is invalid and must exit the current execution frame immediately, consuming all remaining gas (in the same way as a stack underflow or invalid jump). + +### Constants + +| Constant | Value | +| ---------------- | ------ | +| `MAGIC` | `0x03` | + +`MAGIC` is used for [EIP-3074](./eip-3074.md) signatures to prevent signature collisions with other signing formats. + +### Context Variables + +| Variable | Type | Initial Value | +| ------------------- | --------- |:------------- | +| `authorized` | `address` | unset | + +The context variable `authorized` shall indicate the active account for `AUTHCALL` instructions in the current frame of execution. If set, `authorized` shall only contain an account which has given the contract authorization to act on its behalf. An unset value shall indicate that no such account is set and that there is not yet an active account for `AUTHCALL` instructions in the current frame of execution. + +The variable has the same scope as the program counter -- `authorized` persists throughout a single frame of execution of the contract, but is not passed through any calls (including `DELEGATECALL`). If the same contract is being executed in separate execution frames (ex. a `CALL` to self), both frames shall have independent values for `authorized`. Initially in each frame of execution, `authorized` is always unset, even if a previous execution frame for the same contract has a value. + +### `AUTH` (`0xf6`) + +A new opcode `AUTH` shall be created at `0xf6`. It shall take three stack element inputs (the last two describing a memory range), and it shall return one stack element. + +#### Input + +##### Stack + +| Stack | Value | +| ---------- | ------------ | +| `top - 0` | `authority` | +| `top - 1` | `offset` | +| `top - 2` | `length` | + +##### Memory + +The final two stack arguments (`offset` and `length`) describe a range of memory. The format of the contents of that range is: + + - `memory[offset : offset+32 ]` - `yParity` + - `memory[offset+32 : offset+64 ]` - `r` + - `memory[offset+64 : offset+96 ]` - `s` + - `memory[offset+96 : offset+128]` - `commit` + +#### Output + +##### Stack + +| Stack | Value | +| ---------- | -------------| +| `top - 0` | `success` | + +##### Memory + +Memory is not modified by this instruction. + +#### Behavior + +If `length` is greater than 128, the extra bytes are ignored for signature verification (they still incur a gas cost as defined later). Bytes outside the range (in the event `length` is less than 128) are treated as if they had been zeroes. + +`authority` is the address of the account which generated the signature. + +The arguments (`yParity`, `r`, `s`) are interpreted as an ECDSA signature on the secp256k1 curve over the message `keccak256(MAGIC || chainId || paddedInvokerAddress || commit)`, where: + + - `chainId` is the current chain's [EIP-155](./eip-155.md) unique identifier padded to 32 bytes. + - `paddedInvokerAddress` is the address of the contract executing `AUTH` (or the active state address in the context of `CALLCODE` or `DELEGATECALL`), left-padded with zeroes to a total of 32 bytes (ex. `0x000000000000000000000000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`). + - `commit`, one of the arguments passed into `AUTH`, is a 32-byte value that can be used to commit to specific additional validity conditions in the invoker's pre-processing logic (e.g. a nonce for replay protection). + +Signature validity and signer recovery is handled analogously to transaction signatures, including the stricter `s` range for preventing ECDSA malleability. Note that `yParity` is expected to be `0` or `1`. + +If the signature is valid and the signer address is equal to `authority`, the context variable `authorized` is set to the `authority`. In particular, this is also true if `authority == tx.origin`, which used to be handled separately in earlier versions of this EIP (see Security Considerations). If the signature is instead invalid or the signer address does not equal `authority`, `authorized` is reset to an unset value. + +`AUTH` returns `1` if `authorized` is set, or `0` otherwise. + +#### Gas Cost + +The gas cost for `AUTH` is equal to the sum of: + + - fixed fee `3100`. + - memory expansion gas cost (`auth_memory_expansion_fee`) + +The fixed fee is equal to the cost for the `ecrecover` precompile, plus a bit extra to cover a keccak256 hash and some additional logic. + +The memory expansion gas cost (`auth_memory_expansion_fee`) shall be calculated in the same way as `RETURN`, where memory is expanded if the specified range is outside the current allocation. + +### `AUTHCALL` (`0xf7`) + +A new opcode `AUTHCALL` shall be created at `0xf7`. It shall take eight stack elements and return one stack element. It matches the behavior of the existing `CALL` (`0xF1`) instruction, except where noted below. + +#### Input + +| Stack | Value | +| --------- | ------------ | +| `top - 0` | `gas` | +| `top - 1` | `addr` | +| `top - 2` | `value` | +| `top - 3` | `valueExt` | +| `top - 4` | `argsOffset` | +| `top - 5` | `argsLength` | +| `top - 6` | `retOffset` | +| `top - 7` | `retLength` | + +#### Output + +| Stack | Value | +| ---------- | --------- | +| `top - 0` | `success` | + +#### Behavior + +`AUTHCALL` is interpreted the same as `CALL`, except for (note: this list is also the order of precedence for the logical checks): + + - If `authorized` is unset, execution is invalid (as defined above). Otherwise, the caller address for the call is set to `authorized`. + - The gas cost, including how much gas is available for the subcall, is specified in the Gas Cost section. + - If the `gas` operand is equal to `0`, the instruction will send all available gas as per [EIP-150](./eip-150). + - If the gas available for the subcall would be less than `gas`, execution is invalid. + - There is no gas stipend, even for non-zero `value`. + - `value` is deducted from the balance of the executing contract. It is not paid by `authorized`. If `value` is higher than the balance of the executing contract, execution is invalid. + - If `valueExt` is not zero, the instruction immediately returns 0. In this case the gas that would have been passed into the call is refunded, but not the gas consumed by the `AUTHCALL` opcode itself. In the future, this restriction may be relaxed to externally transfer value out of the `authorized` account. + +`AUTHCALL` must increase the call depth by one. `AUTHCALL` must not increase the call depth by two as it would if it first called into the authorized account and then into the target. + +The return data area accessed with `RETURNDATASIZE` (`0x3d`) and `RETURNDATACOPY` (`0x3e`) must be set in the same way as the `CALL` instruction. + +Importantly, `AUTHCALL` does not reset `authorized`, but leaves it unchanged. + +#### Gas Cost + +The gas cost for `AUTHCALL` shall be the **sum** of: + + - static gas cost (`warm_storage_read`) + - memory expansion gas cost (`memory_expansion_fee`) + - dynamic gas cost (`dynamic_gas`) + - gas available for execution in the subcall (`subcall_gas`) + +The memory expansion gas cost (`memory_expansion_fee`) shall be calculated in the same way as `CALL`. + +The dynamic gas portion (`dynamic_gas`), and the gas available for execution in the subcall (`subcall_gas`) shall be calculated as: + +``` +dynamic_gas = 0 + +if addr not in accessed_addresses: + dynamic_gas += 2500 # cold_account_access - warm_storage_read + +if value > 0: + dynamic_gas += 6700 # NB: Not 9000, like in `CALL` + if is_empty(addr): + dynamic_gas += 25000 + +remaining_gas = available_gas - dynamic_gas +all_but_one_64th = remaining_gas - (remaining_gas // 64) + +if gas == 0: + subcall_gas = all_but_one_64th +elif all_but_one_64th < gas: + raise # Execution is invalid. +else: + subcall_gas = gas +``` + +As with `CALL`, the full gas cost is charged immediately, independently of actually executing the call. + +## Rationale + +### Signature in Memory + +The signature format (`yParity`, `r`, and `s`) is fixed, so it might seem curious that `auth` accepts a dynamic memory range. The signature is placed in memory so that `auth` can be upgraded in the future to work with contract accounts (which might use non-ECDSA signatures) and not just EOAs. + +### Signing Address `auth` Argument + +Including `authority` (the signing address) as an argument to `auth` allows future upgrades to the instruction to work with contract accounts, and not just EOAs. + +If `authority` were not included and multiple signature schemes allowed, it would not be possible to compute the authorizing account's address from just the signature alone. + +### Reserving One Sixty-Fourth of Available Gas + +`AUTHCALL` will not pass more than 63/64th of the available gas for the reasons enumerated in [EIP-150](./eip-150.md). + +### Throwing for Unset `authorized` During `AUTHCALL` + +A well-behaved contract should never reach an `AUTHCALL` without having successfully set `authorized` beforehand. The safest behavior, therefore, is to exit the current frame of execution immediately. This is especially important in the context of transaction sponsoring / relaying, which is expected to be one of the main use cases for this EIP. In a sponsored transaction, the inability to distinguish between a sponsee-attributable fault (like a failing sub-call) and a sponsor-attributable fault (like a failing `AUTH`) is especially dangerous and should be prevented because it charges unfair fees to the sponsee. + +### Another Sponsored Transaction EIP + +There are two general approaches to separating the "fee payer" from the "action originator". + +The first is introducing a new transaction type. This requires significant changes to clients to support and is generally less upgradeable than other solutions (e.g. this EIP). This approach is also not immediately compatible with account abstraction (AA). These proposals require a *signed* transaction from the sponsor's account, which is not possible from an AA contract, because it has no private key to sign with. The main advantage of new transaction types is that the validity requirements are enforced by the protocol, therefore invalid transactions do not pollute block space. + +The other main approach is to introduce a new mechanism in the EVM to masquerade as other accounts. This EIP introduces `AUTH` and `AUTHCALL` to make calls as EOAs. There are many different permutations of this mechanism. An alternative mechanism would be add an opcode that can make arbitrary calls based on a similar address creation scheme as `CREATE2`. Although this mechanism would not benefit users today, it would immediately allow for those accounts to send and receive ether -- making it feel like a more first-class primitive. + +Besides better compatibility with AA, introducing a new mechanism into the EVM is a much less intrusive change than a new transaction type. This approach requires no changes in existing wallets, and little change in other tooling. + +`AUTHCALL`'s single deviation from `CALL` is to set `CALLER`. It implements the minimal functionality to enable sender abstraction for sponsored transactions. This single mindedness makes `AUTHCALL` significantly more composable with existing Ethereum features. + +More logic can be implemented around the `AUTHCALL` instruction, giving more control to invokers and sponsors without sacrificing security or user experience for sponsees. + +### What to Sign? + +As originally written, this proposal specified a precompile with storage to track nonces. Since a precompile with storage is unprecedented, a revision moved replay protection into the invoker contract, necessitating a certain level of user trust in the invoker. Expanding on this idea of trusted invokers, the other signed fields were eventually eliminated, one by one, until only `invoker` and `commit` remained. + +The `invoker` binds a particular signed message to a single invoker. If invoker was not part of the message, any invoker could reuse the signature to completely compromise the EOA. This allows users to trust that their message will be validated as they expect, particularly the values committed to in `commit`. + +### Understanding `commit` + +Earlier iterations of this EIP included mechanisms for replay protection, and also signed over value, gas, and other arguments to `AUTHCALL`. After further investigation, we revised this EIP to its current state: explicitly delegate these responsibilities to the invoker contract. + +A user will specifically interact with an invoker they trust. Because they trust this contract to execute faithfully, they will "commit" to certain properties of a call they would like to make by computing a hash of the call values. They can be certain that the invoker will only allow they call to proceed if it is able to verify the values committed to (e.g. a nonce to protect against replay attacks). This certainty arises from the `commit` value that is signed over by the user. This is the hash of values which the invoker will validate. A safe invoker should accept the values from the user and compute the commit hash itself. This ensures that invoker operated on the same input that user authorized. + +![auth message format](../assets/eip-3074/auth-msg.png) + +Using `commit` as a hash of values allows for invokers to implement arbitrary constraints. For example, they could allow accounts to have `N` parallel nonces. Or, they could allow a user to commit to multiple calls with a single signature. This would allow mult-tx flows, such as [ERC-20](./eip-20.md) `approve`-`transfer` actions, to be condensed into a single transaction with a single signature verification. A commitment to multiple calls would look something like the diagram below. + +![multi-call auth message](../assets/eip-3074/auth-msg-multi-call.png) + + +### Invoker Contracts + +The invoker contract is a trustless intermediary between the sponsor and sponsee. A sponsee signs over `invoker` to require they transaction to be processed only by a contract they trust. This allows them to interact with sponsors without needing to trust them. + +Choosing an invoker is similar to choosing a smart contract wallet implementation. It's important to choose one that has been thoroughly reviewed, tested, and accepted by the community as secure. We expect a few invoker designs to be utilized by most major transaction relay providers, with a few outliers that offer more novel mechanisms. + +An important note is that invoker contracts **MUST NOT** be upgradeable. If an invoker can be redeployed to the same address with different code, it would be possible to redeploy the invoker with code that does not properly verify `commit` and any account that signed a message over that invoker would be compromised. Although this sounds scary, it is no different than using a smart contract wallet via `DELEGATECALL`. If the wallet is redeployed with different logic, all wallets using its code could be compromised. + +### On Call Depth + +The EVM limits the maximum number of nested calls, and naively allowing a sponsor to manipulate the call depth before reaching the invoker would introduce a griefing attack against the sponsee. That said, with the 63/64th gas rule, and the cost of `AUTHCALL`, the stack is effectively limited to a much smaller depth than the hard maximum by the `gas` parameter. + +It is, therefore, sufficient for the invoker to guarantee a minimum amount of gas, because there is no way to reach the hard maximum call depth with any reasonable (i.e. less than billions) amount of gas. + +### Source of `value` + +Any non-zero `value` passed into an `AUTHCALL` is deducted from the invoker's balance. A natural alternative source for `value` would be the `authorized` account. However, deducting value from an EOA mid-execution is problematic, as it breaks important invariants for handling pending transactions. Specifically: + +- Transaction pools expect transactions for a given EOA to only turn invalid when other transactions from the same EOA are included into a block, increasing its nonce and (possibly) decreasing its balance. Deducting `value` from the `authorized` account would make transaction invalidation an unpredictable side effect of any smart contract execution. +- Similarly, miners rely on the ability to statically pick a set of valid transactions from their transaction pool to include into a new block. Deducting `value` from the `authorized` account would break this ability, increasing the overhead and thus the time for block creation. + +At the same time, the ability to directly take ether out of the `authorized` account is an important piece of functionality and thus a desired future addition via an additional opcode similar to `AUTHCALL`. For this reason, it is included as `valueExt`, an operand of `AUTHCALL`, which may be activated in a future fork. The prerequisite for that would be to find satisfying mitigations to the transaction invalidation concerns outlined above. One potential avenue for that could be the addition of account access lists similar to [EIP-2930](./eip-2930.md), used to signal accounts whose balance can be reduced as a side effect of the transaction (without on their own constituting authorization to do so). + +### Allowing `tx.origin` as Signer + +Allowing `authorized` to equal `tx.origin` enables simple transaction batching, where the sender of the outer transaction would be the signing account. The ERC-20 approve-then-transfer pattern, which currently requires two separate transactions, could be completed in a single transaction with this proposal. + +`AUTH` allows for signatures to be signed by `tx.origin`. For any such signatures, subsequent `AUTHCALL`s have `msg.sender == tx.origin` in their first layer of execution. Without EIP-3074, this situation can only ever arise in the topmost execution layer of a transaction. This EIP breaks that invariant and so affects smart contracts containing `require(msg.sender == tx.origin)` checks. This check can be used for at least three purposes: + + 1. Ensuring that `msg.sender` is an EOA (given that `tx.origin` always has to be an EOA). This invariant does not depend on the execution layer depth and, therefore, is not affected. + 2. Protecting against atomic sandwich attacks like flash loans, that rely on the ability to modify state before and after the execution of the target contract as part of the same atomic transaction. This protection would be broken by this EIP. However, relying on `tx.origin` in this way is considered bad practice, and can already be circumvented by miners conditionally including transactions in a block. + 3. Preventing re-entrancy. + +Examples of (1) and (2) can be found in contracts deployed on Ethereum mainnet, with (1) being more common (and unaffected by this proposal.) On the other hand, use case (3) is more severely affected by this proposal, but the authors of this EIP did not find any examples of this form of re-entrancy protection, though the search was non-exhaustive. + +This distribution of occurrences—many (1), some (2), and no (3)—is exactly what the authors of this EIP expect, because: + + - Determining if `msg.sender` is an EOA without `tx.origin` is difficult (if not impossible.) + - The only execution context which is safe from atomic sandwich attacks is the topmost context, and `tx.origin == msg.sender` is the only way to detect that context. + - In contrast, there are many direct and flexible ways of preventing re-entrancy (ex. using a storage variable.) Since `msg.sender == tx.origin` is only true in the topmost context, it would make an obscure tool for preventing re-entrancy, rather than other more common approaches. + +There are other approaches to mitigate this restriction which do not break the invariant: + + - Set `tx.origin` to a constant `ENTRY_POINT` address for `AUTHCALL`s. + - Set `tx.origin` to the invoker address for `AUTHCALL`s. + - Set `tx.origin` to a special address derived from any of the sender, invoker, and/or signer addresses. + - Disallow `authorized == tx.origin`. This would make the simple batching use cases impossible, but could be relaxed in the future. + +### `AUTHCALL` cheaper than `CALL` when sending value + +Sending non-zero value with `CALL` increases its cost by 9,000. Of that, 6,700 covers the increased overhead of the balance transfer and 2,300 is used as a stipend into the subcall to seed its gas counter. `AUTHCALL` does not provide a stipend and thus only charges the base 6,700. + +## Backwards Compatibility + +Although this EIP poses no issues for backwards compatibility, there are concerns that it limits future changes to accounts by further enshrining ECDSA signatures. For example, it might be desirable to erradicate the concept of EOAs altogether, and replace them with smart contract wallets that emulate the same behavior. This is fully compatible with the EIP as written, however, it gets tricky if users can then elect to "upgrade" their smart contract wallets to use other methods of authentication -- e.g. convert into a multisig. Without any changes, `AUTH` would not respect this new logic and continue allowing the old private key to perform actions on behalf of the account. + +A solution to this would be at the same time that EOAs are removed, to modify the logic of `AUTH` to actually call into the account with some standard message and allow the account to determine if the signature / witness is valid. Further research should be done to understand how invokers would need to change in this situation and how best to write them in a future-compatible manner. + +## Security Considerations + +### Secure Invokers + +The following is a non-exhaustive list of checks/pitfalls/conditions that invokers *should* be wary of: + + - Replay protection (ex. a nonce) should be implemented by the invoker, and included in `commit`. Without it, a malicious actor can reuse a signature, repeating its effects. + - `value` should be included in `commit`. Without it, a malicious sponsor could cause unexpected effects in the callee. + - `gas` should be included in `commit`. Without it, a malicious sponsor could cause the callee to run out of gas and fail, griefing the sponsee. + - `addr` and `calldata` should be included in `commit`. Without them, a malicious actor may call arbitrary functions in arbitrary contracts. + +A poorly implemented invoker can *allow a malicious actor to take near complete control over a signer's EOA*. + +### Allowing `tx.origin` as Signer + +Allowing `authorized` to equal `tx.origin` has the possibility to: + + - Break atomic sandwich protections which rely on `tx.origin`; + - Break re-entrancy guards of the style `require(tx.origin == msg.sender)`. + +The authors of this EIP believe the risks of allowing `authorized` to equal `tx.origin` are acceptable for the reasons outlined in the Rationale section. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3076.md b/EIPS/eip-3076.md new file mode 100644 index 0000000..9023b9d --- /dev/null +++ b/EIPS/eip-3076.md @@ -0,0 +1,263 @@ +--- +eip: 3076 +title: Slashing Protection Interchange Format +description: A JSON interchange format for proof of stake validators to migrate slashing protection data between clients. +author: Michael Sproul (@michaelsproul), Sacha Saint-Leger (@sachayves), Danny Ryan (@djrtwo) +discussions-to: https://ethereum-magicians.org/t/eip-3076-validator-client-interchange-format-slashing-protection/ +status: Last Call +type: Standards Track +category: Interface +created: 2020-10-27 +last-call-deadline: 2021-11-03 +--- + +## Abstract + +A standard format for transferring a key's signing history allows validators to easily switch between clients without the risk of signing conflicting messages. While a [common keystore format](https://eips.ethereum.org/EIPS/eip-2335) provides part of the solution, it does not contain any information about a key's signing history. For a validator moving their keys from client A to client B, this could lead to scenarios in which client B inadvertently signs a message that conflicts with an earlier message signed with client A. The interchange format described here provides a solution to this problem. + +## Motivation + +The proof of stake (PoS) protocol penalises validators for voting in ways that could result in two different versions of the chain being finalised. These types of penalties are called slashings. + +For a validator following the protocol correctly, there is, in principle, no risk of being slashed. However, changing clients (from client A to client B, say) can result in a slashing risk if client B is unaware of the blocks and attestations that were signed with client A. + +This can can occur if client A and client B do not agree on what the present time is. For example, say client A's time is accidentally set to a day in the future (225 epochs), and a validator switches from client A to client B without giving B a record of the blocks and attestations signed with A. The validator in question now runs the risk of attesting to two different blocks in the same epoch (a slashable offence) for the next 225 epochs (since they've already voted on these epochs with client A, and now stand to vote on them again with client B). Such time-skew bugs have been observed in the wild. + +Another situation in which slashing protection is critical is in the case of re-orgs. During a re-org it is possible for a validator to be assigned new attestation duties for an epoch in which it has already signed an attestation. In this case it is essential that the record of the previous attestation is available, even if the validator just moved from one client to another in the space of a single epoch. + +## Specification + +### JSON Schema + +A valid interchange file is one that adheres to the following JSON schema, and is interpreted according to the [Conditions](#conditions). + +```json +{ + "title": "Signing history", + "description": "This schema provides a record of the blocks and attestations signed by a set of validators", + "type": "object", + "properties": { + "metadata": { + "type": "object", + "properties": { + "interchange_format_version": { + "type": "string", + "description": "The version of the interchange format that this document adheres to" + }, + "genesis_validators_root": { + "type": "string", + "description": "Calculated at Genesis time; serves to uniquely identify the chain" + } + }, + "required": [ + "interchange_format_version", + "genesis_validators_root" + ] + }, + "data": { + "type": "array", + "items": [ + { + "type": "object", + "properties": { + "pubkey": { + "type": "string", + "description": "The BLS public key of the validator (encoded as a 0x-prefixed hex string)" + }, + "signed_blocks": { + "type": "array", + "items": [ + { + "type": "object", + "properties": { + "slot": { + "type": "string", + "description": "The slot number of the block that was signed" + }, + "signing_root": { + "type": "string", + "description": "The output of compute_signing_root(block, domain)" + } + }, + "required": [ + "slot" + ] + } + ] + }, + "signed_attestations": { + "type": "array", + "items": [ + { + "type": "object", + "properties": { + "source_epoch": { + "type": "string", + "description": "The attestation.data.source.epoch of the signed attestation" + }, + "target_epoch": { + "type": "string", + "description": "The attestation.data.target.epoch of the signed attestation" + }, + "signing_root": { + "type": "string", + "description": "The output of compute_signing_root(attestation, domain)" + } + }, + "required": [ + "source_epoch", + "target_epoch" + ] + } + ] + } + }, + "required": [ + "pubkey", + "signed_blocks", + "signed_attestations" + ] + } + ] + } + }, + "required": [ + "metadata", + "data" + ] +} +``` + +### Example JSON Instance + +```json +{ + "metadata": { + "interchange_format_version": "5", + "genesis_validators_root": "0x04700007fabc8282644aed6d1c7c9e21d38a03a0c4ba193f3afe428824b3a673" + }, + "data": [ + { + "pubkey": "0xb845089a1457f811bfc000588fbb4e713669be8ce060ea6be3c6ece09afc3794106c91ca73acda5e5457122d58723bed", + "signed_blocks": [ + { + "slot": "81952", + "signing_root": "0x4ff6f743a43f3b4f95350831aeaf0a122a1a392922c45d804280284a69eb850b" + }, + { + "slot": "81951" + } + ], + "signed_attestations": [ + { + "source_epoch": "2290", + "target_epoch": "3007", + "signing_root": "0x587d6a4f59a58fe24f406e0502413e77fe1babddee641fda30034ed37ecc884d" + }, + { + "source_epoch": "2290", + "target_epoch": "3008" + } + ] + } + ] +} +``` + +### Conditions + +After importing an interchange file with data field `data`, a signer must respect the following conditions: + +1. Refuse to sign any block that is slashable with respect to the blocks contained in `data.signed_blocks`. For details of what constitutes a slashable block, see [process_proposer_slashing][pps]. If the `signing_root` is absent from a block, a signer must assume that any new block with the same `slot` is slashable with respect to the imported block. + +2. Refuse to sign any block with `slot <= min(b.slot for b in data.signed_blocks if b.pubkey == proposer_pubkey)`, except if it is a repeat signing as determined by the `signing_root`. + +3. Refuse to sign any attestation that is slashable with respect to the attestations contained in `data.signed_attestations`. For details of what constitutes a slashable attestation, see [is_slashable_attestation_data][isad]. +4. Refuse to sign any attestation with source epoch less than the minimum source epoch present in that signer's attestations (as seen in `data.signed_attestations`). In pseudocode: +```python3 +source.epoch < + min(att.source_epoch + for att in data.signed_attestations + if att.pubkey == attester_pubkey) +``` + +5. Refuse to sign any attestation with target epoch less than or equal to the minimum target epoch present in that signer's attestations (as seen in `data.signed_attestations`). In pseudocode: + +```python3 +target_epoch <= + min(att.target_epoch + for att in data.signed_attestations + if att.pubkey == attester_pubkey) +``` + +### Additional Information + +- The `interchange_format_version` version is set to 5. + +- A signed block or attestation's `signing_root` refers to the message data (hash tree root) that gets signed with a BLS signature. It allows validators to re-sign and re-broadcast blocks or attestations if asked. + +- The `signed_blocks` `signing_root`s are calculated using [`compute_signing_root(block, domain)`][csr]: where `block` is the block (of type `BeaconBlock` or `BeaconBlockHeader`) that was signed, and `domain` is equal to [`compute_domain(DOMAIN_BEACON_PROPOSER, fork, metadata.genesis_validators_root)`][cd]. + +- The `signed_attestations` `signing_root`s are calculated using [`compute_signing_root(attestation, domain)`][csr]: where `attestation` is the attestation (of type `AttestationData`) that was signed, and `domain` is equal to [`compute_domain(DOMAIN_BEACON_ATTESTER, fork, metadata.genesis_validators_root)`][cd]. + +[pps]: https://github.com/ethereum/eth2.0-specs/blob/v1.0.0/specs/phase0/beacon-chain.md#proposer-slashings +[isad]: https://github.com/ethereum/eth2.0-specs/blob/v1.0.0/specs/phase0/beacon-chain.md#is_slashable_attestation_data +[csr]: https://github.com/ethereum/eth2.0-specs/blob/v1.0.0/specs/phase0/beacon-chain.md#compute_signing_root +[cd]: https://github.com/ethereum/eth2.0-specs/blob/v1.0.0/specs/phase0/beacon-chain.md#compute_domain + + +## Rationale + +### Supporting Different Strategies + +The interchange format is designed to be flexible enough to support the full variety of slashing protection strategies that clients may implement, which may be categorised into two main types: + +1. **Complete**: a database containing every message signed by each validator. +2. **Minimal**: a database containing only the latest messages signed by each validator. + +The advantage of the minimal strategy is its simplicity and succinctness. Using only the latest messages for each validator, safe slashing protection can be achieved by refusing to sign messages for slots or epochs prior. + +On the other hand, the complete strategy can provide safe slashing protection while also avoiding false positives (meaning that it only prevents a validator from signing if doing so would guarantee a slashing). + +The two strategies are unified in the interchange format through the inclusion of [conditions](#conditions) (2), (4) and (5). This allows the interchange to transfer detailed or succinct information, as desired. + +### Integer Representation + +Most fields in the JSON schema are strings. For fields in which it is possible to encode the value as either a string or an integer, strings were chosen. This choice was made in order to avoid issues with different languages supporting different ranges of integers (specifically JavaScript, where the `number` type is a 64-bit float). If a validator is yet to sign a block or attestation, the relevant list is simply left empty. + +### Versioning + +The `interchange_format_version` is set to 5 because the specification went through several breaking changes during its design, incorporating feedback from implementers. + + +## Backwards Compatibility + +This specification is not backwards-compatible with previous draft versions that used version numbers less than 5. + + +## Security Considerations + +In order to minimise risk and complexity, the format has been designed to map cleanly onto the internal database formats used by implementers. Nevertheless, there are a few pitfalls worth illuminating. + +### Advice for Complete Databases + +For implementers who use a complete record of signed messages to implement their slashing protection database, we make the following recommendations: + +* You MUST ensure that, in addition to importing all of the messages from an interchange, all the [conditions](#conditions) are enforced. In particular, conditions (2), (4) and (5) may not have been enforced by your implementation before adopting the interchange format. Our recommendation is to enforce these rules at all times, to keep the implementation clean and minimise the attack surface. For example: your slashing protection mechanism should not sign a block with a slot number less than, or equal to, the minimum slot number of a previously signed block, _irrespective_ of whether that minimum-slot block was imported from an interchange file, or inserted as part of your database's regular operation. +* If your database records the signing roots of messages in addition to their slot/epochs, you should ensure that imported messages without signing roots are assigned a suitable dummy signing root internally. We suggest using a special "null" value which is distinct from all other signing roots, although a value like `0x0` may be used instead (as it is extremely unlikely to collide with any real signing root). +* Care must be taken to avoid signing messages within a gap in the database (an area of unknown signing activity). This could occur if two interchanges were imported with a large gap between the last entry of the first and the first entry of the second. Signing in this gap is not safe, and would violate conditions (2), (4) and (5). It can be avoided by storing an explicit low watermark in addition to the actual messages of the slashing protection database, or by pruning on import so that the oldest messages from the interchange become the oldest messages in the database. + +### Advice for Minimal Databases + +For implementers who wish to implement their slashing protection database by storing only the latest block and attestation for each validator, we make the following recommendations: + +* During import, make sure you take the _maximum_ slot block and _maximum_ source and target attestations for each validator. Although the [conditions](#conditions) require the minimums to be enforced, taking the maximums from an interchange file and merging them with any existing values in the database is the recommended approach. For example, if the interchange file includes blocks for validator `V` at slots 4, 98 and 243, then the latest signed block for validator `V` should be updated to the one from slot 243. However, if the database has already included a block for this validator at a slot greater than 243, for example, slot 351, then the database's existing value should remain unchanged. + +### General Recommendations + +* To avoid exporting an outdated interchange file -- an action which creates a slashing risk -- your implementation should only allow the slashing protection database to be exported when the validator client or signer is _stopped_ -- in other words, when the client or signer is no longer adding new messages to the database. +* Similarly, your implementation should only allow an interchange file to be imported when the validator client is stopped. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3085.md b/EIPS/eip-3085.md new file mode 100644 index 0000000..1e081dd --- /dev/null +++ b/EIPS/eip-3085.md @@ -0,0 +1,249 @@ +--- +eip: 3085 +title: Wallet Add Ethereum Chain RPC Method (`wallet_addEthereumChain`) +author: Erik Marks (@rekmarks), Pedro Gomes (@pedrouid) +discussions-to: https://ethereum-magicians.org/t/eip-3085-wallet-addethereumchain/5469 +status: Stagnant +type: Standards Track +category: Interface +created: 2020-11-01 +requires: 155, 695 +--- + +## Simple Summary + +An RPC method for adding Ethereum chains to wallet applications. + +## Abstract + +The `wallet_addEthereumChain` RPC method allows Ethereum applications ("dapps") to suggest chains to be added to the user's wallet application. +The caller must specify a chain ID and some chain metadata. +The wallet application may arbitrarily refuse or accept the request. +`null` is returned if the chain was added, and an error otherwise. + +## Motivation + +All dapps require the user to interact with one or more Ethereum chains in order to function. +Any given chain may or may not be supported by the user's wallet application. +`wallet_addEthereumChain` enables dapps to request chains to be added to the user's wallet. +This enables UX improvements for both dapps and wallets. + +## Specification + +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). + +### `wallet_addEthereumChain` + +The method accepts a single object parameter, with a `chainId` and some chain metadata. +The method returns `null` if the chain was added to the wallet, and an error otherwise. + +The wallet **MAY** reject the request for any reason. + +> Note that this method makes **no** statement about whether the wallet should change the user's currently selected chain, if the wallet has a concept thereof. + +#### Parameters + +`wallet_addEthereumChain` accepts a single object parameter, specified by the following TypeScript interface: + +```typescript +interface AddEthereumChainParameter { + chainId: string; + blockExplorerUrls?: string[]; + chainName?: string; + iconUrls?: string[]; + nativeCurrency?: { + name: string; + symbol: string; + decimals: number; + }; + rpcUrls?: string[]; +} +``` + +Only the `chainId` is required per this specification, but a wallet **MAY** require any other fields listed, impose additional requirements on them, or ignore them outright. +If a field does not meet the requirements of this specification and the wallet does not ignore the field, the wallet **MUST** reject the request. + +- `chainId` + - **MUST** specify the integer ID of the chain as a hexadecimal string, per the [`eth_chainId`](./eip-695.md) Ethereum RPC method. + - The wallet **SHOULD** compare the specified `chainId` value with the `eth_chainId` return value from the endpoint. + If these values are not identical, the wallet **MUST** reject the request. +- `blockExplorerUrls` + - If provided, **MUST** specify one or more URLs pointing to block explorer web sites for the chain. +- `chainName` + - If provided, **MUST** specify a human-readable name for the chain. +- `iconUrls` + - If provided, **MUST** specify one or more URLs pointing to reasonably sized images that can be used to visually identify the chain. +- `nativeCurrency` + - If provided, **MUST** describe the native currency of the chain using the `name`, `symbol`, and `decimals` fields. + - `decimals` **MUST** be a non-negative integer. + - `name` and `symbol` **SHOULD** be human-readable strings. +- `rpcUrls` + - If provided, **MUST** specify one or more URLs pointing to RPC endpoints that can be used to communicate with the chain. + +All URL strings **MUST** include the protocol component of the URL. +HTTPS **SHOULD** always be used over HTTP. + +#### Returns + +The method **MUST** return `null` if the request was successful, and an error otherwise. + +A request to add a chain that was already added **SHOULD** be considered successful. + +The wallet **MUST NOT** allow the same `chainId` to be added multiple times. +See [Security Considerations](#security-considerations) for more information. + +### Examples + +These examples use JSON-RPC, but the method could be implemented using other RPC protocols. + +To add the Goerli test chain: + +```json +{ + "id": 1, + "jsonrpc": "2.0", + "method": "wallet_addEthereumChain", + "params": [ + { + "chainId": "0x5", + "chainName": "Goerli", + "rpcUrls": ["https://goerli.infura.io/v3/INSERT_API_KEY_HERE"], + "nativeCurrency": { + "name": "Goerli ETH", + "symbol": "gorETH", + "decimals": 18 + }, + "blockExplorerUrls": ["https://goerli.etherscan.io"] + } + ] +} +``` + +To add POA Network's xDAI chain: + +```json +{ + "id": 1, + "jsonrpc": "2.0", + "method": "wallet_addEthereumChain", + "params": [ + { + "chainId": "0x64", + "chainName": "xDAI Chain", + "rpcUrls": ["https://dai.poa.network"], + "iconUrls": [ + "https://xdaichain.com/fake/example/url/xdai.svg", + "https://xdaichain.com/fake/example/url/xdai.png" + ], + "nativeCurrency": { + "name": "xDAI", + "symbol": "xDAI", + "decimals": 18 + } + } + ] +} +``` + +In the above example, notice that the `iconUrls` array contains URLs pointing to two different image formats. + +A success response: + +```json +{ + "id": 1, + "jsonrpc": "2.0", + "result": null +} +``` + +A failure response: + +```json +{ + "id": 1, + "jsonrpc": "2.0", + "error": { + "code": 4001, + "message": "The user rejected the request." + } +} +``` + +## Rationale + +The design of `wallet_addEthereumChain` is deliberately ignorant of what it means to "add" a chain to a wallet. +The meaning of "adding" a chain to a wallet depends on the wallet implementation. + +When calling the method, specifying the `chainId` will always be necessary, since in the universe of Ethereum chains, the [EIP-155](./eip-155.md) chain ID is effectively the chain GUID. +The remaining parameters amount to what, in the estimation of the authors, a wallet will minimally require in order to effectively support a chain and represent it to the user. +The network ID (per the `net_version` RPC method) is omitted since it is effectively superseded by the chain ID. + +For [security reasons](#security-considerations), a wallet should always attempt to validate the chain metadata provided by the requester, and may choose to fetch the metadata elsewhere entirely. +Either way, only the wallet can know which chain metadata it needs from the requester in order to "add" the chain. +Therefore, all parameters except `chainId` are specified as optional, even though a wallet may require them in practice. + +This specification does not mandate that the wallet "switches" its "active" or "currently selected" chain after a successful request, if the wallet has a concept thereof. +Just like the meaning of "adding" a chain, "switching" between chains is a wallet implementation detail, and therefore out of scope. + +For related work, see [EIP-2015](./eip-2015.md). + +## Security Considerations + +`wallet_addEthereumChain` is a powerful method that exposes the end user to serious risks if implemented incorrectly. +Many of these risks can be avoided by validating the request data in the wallet, and clearly disambiguating different chains in the wallet UI. + +### Chain IDs + +Since the chain ID used for transaction signing determines which chain the transaction is valid for, handling the chain ID correctly is of utmost importance. +The wallet should: + +- Ensure that a submitted chain ID is valid. + - It should be a `0x`-prefixed hexadecimal string per [EIP-695](./eip-695.md), and parse to an integer number. +- Prevent the same chain ID from being added multiple times. + - See the next section for how to handle multiple RPC endpoints. +- Only use the submitted chain ID to sign transactions, **never** a chain ID received from an RPC endpoint. + - A malicious or faulty endpoint could return arbitrary chain IDs, and potentially cause the user to sign transactions for unintended chains. +- Verify that the specified chain ID matches the return value of `eth_chainId` from the endpoint, as described [above](#parameters). + +### RPC Endpoints and RPC URLs + +Wallets generally interact with chains via an RPC endpoint, identified by some URL. +Most wallets ship with a set of chains and corresponding trusted RPC endpoints. +The endpoints identified by the `rpcUrls` parameter cannot be assumed to be honest, correct, or even pointing to the same chain. +Moreover, even trusted endpoints can expose users to privacy risks depending on their data collection practices. + +Therefore, the wallet should: + +- Inform users that their on-chain activity and IP address will be exposed to RPC endpoints. +- If an endpoint is unknown to the wallet, inform users that the endpoint may behave in unexpected ways. +- Observe good web security practices when interacting with the endpoint, such as require HTTPS. +- Clearly inform the user which RPC URL is being used to communicate with a chain at any given moment, and inform the user of the risks of using multiple RPC endpoints to interact with the same chain. + +### Validating Chain Data + +A wallet that implements `wallet_addEthereumChain` should expect to encounter requests for chains completely unknown to the wallet maintainers. +That said, community resources exist that can be leveraged to verify requests for many Ethereum chains. +The wallet should maintain a list of known chains, and verify requests to add chains against that list. +Indeed, a wallet may even prefer its own chain metadata over anything submitted with a `wallet_addEthereumChain` request. + +### UX + +Adding a new chain to the wallet can have significant implications for the wallet's functionality and the experience of the user. +A chain should never be added without the explicit consent of the user, and different chains should be clearly differentiated in the wallet UI. +In service of these goals, the wallet should: + +- When receiving a `wallet_addEthereumChain` request, display a confirmation informing the user that a specific requester has requested that the chain be added. +- Ensure that any chain metadata, such as `nativeCurrency` and `blockExplorerUrls`, are validated and used to maximum effect in the UI. +- If any images are provided via `iconUrls`, ensure that the user understands that the icons could misrepresent the actual chain added. +- If the wallet UI has a concept of a "currently selected" or "currently active" chain, ensure that the user understands when a chain added using `wallet_addEthereumChain` becomes selected. + +### Preserving User Privacy + +Although a request to add a chain that was already added should generally be considered a success, treating such requests as _automatic_ successes leaks information to requesters about the chains a user has added to their wallet. +In the interest of preserving user privacy, implementers of `wallet_addEthereumChain` should consider displaying user confirmations even in these cases. +If the user denies the request, the wallet should return the same user rejection error as normal so that requesters cannot learn which chains are supported by the wallet without explicit permission to do so. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3091.md b/EIPS/eip-3091.md new file mode 100644 index 0000000..7393f57 --- /dev/null +++ b/EIPS/eip-3091.md @@ -0,0 +1,43 @@ +--- +eip: 3091 +title: Block Explorer API Routes +author: Pedro Gomes (@pedrouid) +discussions-to: https://ethereum-magicians.org/t/eip-3091-block-explorer-api-routes/4907 +status: Stagnant +type: Standards Track +category: Interface +created: 2020-11-02 +--- + +## Simple Summary +Standard API Routes for Blockchain explorers + +## Abstract +This proposal brings standardization between block explorers API routes when linking transactions, blocks, accounts and tokens. + +## Motivation +Currently wallets will link transactions and accounts to block explorers web pages but as chain diversity and layer two solutions grow it becomes harder to maintain a consistent user experience. Adding new chains or layer two solutions becomes harder given these endpoints are inconsistent. Standardizing the API routes to these links improves interoperability between wallets and block explorers. This EIP makes RPC endpoints like [EIP-2015](./eip-2015.md) more feasible. + +## Specification +Block explorers will route their webpages accordingly for the following data: + +### Blocks +`/block/` + +### Transactions +`/tx/` + +### Accounts +`/address/` + +### ERC-20 Tokens +`/token/` + +## Backward Compatibility +This EIP was designed with existing API routes in mind to reduce disruption. Incompatible block explorers should include either 301 redirects to their existing API routes to match this EIP. + +## Security Considerations +TBD + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3102.md b/EIPS/eip-3102.md new file mode 100644 index 0000000..00c50d9 --- /dev/null +++ b/EIPS/eip-3102.md @@ -0,0 +1,183 @@ +--- +eip: 3102 +title: Binary trie structure +author: Guillaume Ballet (@gballet), Vitalik Buterin (@vbuterin) +status: Draft +discussions-to: https://ethresear.ch/t/binary-trie-format/7621 +status: Draft +type: Standards Track +category: Core +created: 2020-09-01 +--- + +## Simple Summary + +Change the storage structure from hexary to binary, merge the account and storage tries, and use blake2b. + +## Abstract + +This proposal presents a binary structure and merkelization rule for the account and storage tries, which are merged into a single “state” trie. RLP and most of the MPT’s optimizations are dropped to simplify the design. Keccak256 is replaced with blake2b. + +## Motivation + +The current design of the Merkle Patricia Trie (MPT) uses an hexary trie. Hexary Merkle trees are more shallow than their binary counterparts, which means less hashing. +Over the course of the 5 years of Ethereum’s existence, it has become apparent that disk accesses are a greater bottleneck than hashing. Clients are therefore moving away from a storage model in which all internal nodes are stored, in favor of a flat (key, value) storage model first used by turbo-geth, in which the intermediate nodes are recalculated only when needed. + +There is a push for making Ethereum easier to use in a stateless fashion. Binary tries make for smaller (~4x) proofs than hexary tries, making it the design of choice for a stateless-friendly Ethereum. + +For that same reason, the account and storage tries are merged in order to have a single proof for all changes. + +The MPT design is also rife with uncanny optimizations for size, that have a limited effect - at the cost of prohibitive complexity. For example, nesting for children whose RLP is less than 32 bytes saves an estimated 1MB of disk space. A paltry compared to the 300GB required by a fast sync at the time of this writing. These optimizations are a significant source of errors, and therefore a consensus-breaking risk. +The reliance on RLP has also been criticized for its complexity, while the overhead of a general-purpose encoding scheme isn’t warranted for the rigid structure of a Merkle trie. + +The desire to switch the storage model from an hexary trie to a binary trie provides an opportunity to adopt a simpler trie architecture that pushes optimizations from the protocol level down to that of the client implementors. + +## Specification + +### Conventions + +| Code | Description | +| :-: | - | +| `u256(x)` | Big endian, 32-byte representation of number _x_ | +|`||` | Byte-wise concatenation operator| +| `++` | Bit-wise concatenation operator | +| `0b0101` | The binary string `0101` | +| `hash()` | The usual hashing function | +| `empty_hash` | The empty hash: `hash("")` | +| `length(x)` | The byte length of object `x` | +| `d[a..b]` | The big-endian bit sequence taken from byte sequence `d`, starting at bit index `a`, up to and including the bit at index `b`. | + +### Notable changes from the hexary structure + + * Account and storage tries are merged, with key length between 32 and 64 bytes; + * RLP is no longer used; + * The "leaf marker" bit used in the hex prefix is also dropped. Leaves are identified as nodes with no children; + * Serialized nodes are hashed, no matter how small the byte length of the serialized nodes. + +### The trie + +#### Structure + +The trie structure is made up of _nodes_. A node `N ≡ (N_l,N_r,N_p,N_v)` has the following 4 components: + + * `N_l` is the hash to the node's _left child_. If the node does not have a left child, then `N_l` is the empty hash `empty_hash`; + * `N_r` is the hash to the node's _right child_. If the node does not have a right child, then `N_r` is the empty hash `empty_hash`; + * the optional `N_p` is the node's _prefix_ : every key into the subtrie rooted at `N` is prefixed by this bit string; + * `N_v` is the _value_ stored at this node. The value is **only present in leaf nodes**. + +Nodes with `empty_hash` as both children are called _leaf nodes_, and the remaining nodes are known as _internal nodes_. + +#### Accessing account's balance, nonce, code, storage root and storage slots + +Assuming an account `A ≡ (A_b, A_n, A_c, A_s)` at address `A_a`, the following elements can be found at the following addresses: + + * The account balance `A_b` can be found at key `hash(A_a)[0..253] ++ 0b00` and is of type `uint256`; + * The account nonce `A_n` can be found at key `hash(A_a)[0..253] ++ 0b01` and is of type `uint64`; + * The code `A_c` is an arbitrary-length byte sequence that can be found at key `hash(A_a)[0..253] ++ 0b10`; + * The root of the storage trie `A_s` can be found at key `hash(A_a)[0..253] ++ 0b11` + * The storage slot number `k` can be found at key `hash(A_a)[0..253] ++ 0b11 ++ hash(k)`. + +After [EIP-2926](./eip-2926.md) has been rolled out, `A_c` will represent the root of the code merkelization tree. The key accessing code chunk number `c` is `hash(A_a)[0..253] ++ 0b10 ++ u256(c)`. + +In the unlikely future case that extra items are to be added to the trie at account level, a third bit can be reserved for future use. + +### Node merkelization rule + +Leaves and nodes that have no prefix are hashed according to the rule below: + +``` +internal_hash = hash(left_child_hash || right_child_hash) +leaf_hash = hash(hash(key) || hash(leaf_value)) +``` + +If a prefix is present, the length of the path from the root to the prefixed node is further concatenated with the output of the prefix-less rule, and hashed again: + +``` +internal_hash_with_prefix = hash(u256(path_length_u256 - 1) || internal_hash) +leaf_hash_with_prefix = hash(u256(path_length_u256 - 1) || leaf_hash) +``` + +## Rationale + +### blake2b + +BLAKE2 offers better performance, which is key to compensate for the loss of performance associated to a ~4x increase in the number of nodes. + +BLAKE3 offers even better performance. No official golang release exists at the time of the writing of this document. This presents a security risk, and therefore BLAKE2 is considered instead. + +### Merging of the account and storage tries + +The trend in clients is to store the keys and values in a "flat" database. Having the key of any storage slot prefixed with the address key of the account it belongs to helps grouping all of an account's data on disk, as well as simplifying the witness structure. + +### Prefixes and extension nodes + +An alternative proposal has been made, which provides optimal witnesses. The trade-off is that extension nodes must be removed. + +``` +node_hash = hash(left_child_hash || right_child_hash) +leaf_hash = hash(0 || leaf_value) +``` + +The removal of extension nodes induces 40x higher hashing costs (on the order of 25ms for a trie with only 1k leaves) and as a result they have been kept. + +An attempt to keep extension nodes for witness and not the merkelization rule can be found [here](https://notes.ethereum.org/m5VMkX8FRvi0Q_OOR7TF4A). + +Getting rid of complex methods like RLP, the hex prefix and children nesting is already offering great simplification. + +### 2x32-byte inputs + +It has been requested to keep each node hash calculation as a function that takes two 256-bit integer as an input and outputs one 256-bit integer. This property is expected to play nice with circuit constructions and is therefore expected to greatly help with future zero-knowledge applications. + +### Binary tries + +Binary tries have been chosen primarily because they reduce the witness size. In general, in an `N`-element tree with each element having `k` children, the average length of a branch is roughly `32 * (k-1) * log(N) / log(k)` plus a few percent for overhead. 32 is the length of a hash; the `k-1` refers to the fact that a Merkle proof needs to provide all `k-1` sister nodes at each level, and `log(N) / log(k)` is an approximation of the number of levels in the tree (eg. a tree where each node has 5 children, with 625 nodes total, would have depth 4, as `625 = 5**4` and so `log(625) / log(5) = 4`). + +For any `N`, the expression is minimized at `k = 2`. Here's a table of branch lengths for different `k` values assuming `N = 2**24`: + +| `k` (children per node) | Branch length (chunks) | Branch length (bytes) | +| - | - | - | +| 2 | 1 * 24 = 24 | 768 | +| 4 | 3 * 12 = 36 | 1152 | +| 8 | 7 * 8 = 56 | 1792 | +| 16 | 15 * 6 = 90 | 2880 | + +Actual branch lengths will be slightly larger than this due to uneven distribution and overhead, but the pattern of `k=2` being by far the best remains. + +The ethereum tree was originally hexary because this would reduce the number of database reads (eg. 6 instead of 24 in the above example). It is now understood that this reasoning was flawed, because nodes can still "pretend" that a binary tree is a hexary (or even 256-ary) tree at the database layer (eg. see https://ethresear.ch/t/optimizing-sparse-merkle-trees/3751), and thereby get the best-of-both-worlds of having the low proof sizes of the tree being binary from a hash-structure perspective and at the same time a much more efficient representation in the database. + +Additionally, binary trees are expected to be widely used in Eth2, so this path improves forward-compatibility and reduces long-run total complexity for the protocol. + +### Path length instead of bit prefix + +In order to remove the complexity associated with byte manipulation, only the bit-length of the extension is used to merkelize a node with a prefix. + +Storing the length of the path from the root node instead of that from the parent node has the nice property that siblings need not be hashed when deleting a leaf. + +![Sibling deletion diagram](../assets/eip-3102/sibling.svg) + +_On the left, a trie with the prefix length, and on the right, a trie with the full path length. Each both have values `10000100` and `10000000`. After deleting `10000100`,the sibling node has to be updated in the left tree, while it need not be in the case on the right._ + +### Value hashing + +Except for the code, all values in the trie are less than 32 bytes. EIP-2926 introduces code chunks, with `CHUNK_SIZE = 32 bytes`. The hashing of the leaf's value could therefore be saved. The authors of the EIP are however considering a future increase of `CHUNK_SIZE`, making `hash(value)` the future-proof choice. + +## Backwards Compatibility + +A hard fork is required in order for blocks to have a trie root using a different structure. + +## Test Cases + +TBD + +## Implementation + + * As of [commit 0db87e187dc0bfb96046a47e3d6768c93a2e3331](https://github.com/gballet/multiproof-rs/commit/6d22b1aef9548581826b3c04b3e00d6cc709388c), [multiproof-rs](https://github.com/gballet/multiproof-rs) implements this merkelization rule in the `hash_m5()` function, found in file `src/binary_tree.rs`. + * An implementation of this structure for go-ethereum is available [in this branch](https://github.com/gballet/go-ethereum/tree/rebased-binary-trie-m5-full-path). + +## Security Considerations + +Issues could arise when performing the transition. In particular, a heavy conversion process could incentivize clients to wait the transition out. This could lead to a lowered network security at the time of the transition. A transition process has been proposed with [EIP-2584](./eip-2584.md). + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3135.md b/EIPS/eip-3135.md new file mode 100644 index 0000000..bc7133d --- /dev/null +++ b/EIPS/eip-3135.md @@ -0,0 +1,268 @@ +--- +eip: 3135 +title: Exclusive Claimable Token +author: Zhenyu Sun (@Ungigdu) +discussions-to: https://github.com/ethereum/EIPs/issues/3132 +status: Stagnant +type: Standards Track +category: ERC +created: 2020-08-10 +requires: 20 +--- + +## Simple Summary + +This standard defines a token which can be claimed only by token issuer with payer's signature. + +## Abstract + +This EIP defines a set of additions to the default token standard such as ERC-20, that allows online/offline service providers establish micropayment channels with any number of users by signing and verifying messages about the consumption of token off chain. Using this mechanism will reduce interactions with blockchain to minimal for both participants, thus saving gas and improve performance. + +## Motivation + +There are two main purposes of this EIP, one is to reduce interactions with blockchain, the second is to link Ethereum to real-world payment problems. + +Many small businesses want to build payment system based on blockchain but find it difficult. There are basically two ways: + +1. Directly pay with token. There are many wallet can receive and transfer token but transactions on Ethereum cost gas and take time to confirm. +2. User lock token on payment smart contract and service provider use payment messages signed by user to release token, establishing a micropayment channel. The advantage is interactions with blockchain is reduced and the signing/verifying process is off-chain. But interact with payment contract needs service provider to build a DApp, which require resources many small businesses do not have. Even if they managed to build DApps, they are all different, not standardized. Also, user should have a wallet with DApp browser and has to learn how to use it. + +This EIP helps to standardize the interactions of micropayment system, and make it possible for wallet build a universal UI in the future. + +## Specification + +```solidity + +/// @return Image url of this token or descriptive resources +function iconUrl() external view returns (string memory); + +/// @return Issuer of this token. Only issuer can execute claim function +function issuer() external view returns (address); + +/** + * @notice Remove consumption from payer's deposite + * @dev Check if msg.sender == issuer + * @param from Payer's address + * @param consumption How many token is consumed in this epoch, specified + * @param epoch Epoch increased by 1 after claim or withdraw, at the beginning of each epoch, consumption goes back to 0 + * @param signature Signature of payment message signed by payer +*/ +function claim(address from, uint256 consumption, uint256 epoch, bytes calldata signature) external; + +function transferIssuer(address newIssuer) external; + +/// @notice Move amount from payer's token balance to deposite balance to ensure payment is sufficient +function deposit(uint256 amount) external; + +/** + * @notice Give remaining deposite balance back to "to" account, act as "refund" function + * @dev In prepayment module, withdraw is executed from issuer account + * In lock-release module, withdraw is executed from user account + * @param to the account receiving remaining deposite + * @param amount how many token is returned +*/ +function withdraw(address to, uint256 amount) external; + +function depositBalanceOf(address user) external view returns(uint256 depositBalance, uint256 epoch); + +event Deposit( + address indexed from, + uint256 amount +); + +event Withdraw( + address indexed to, + uint256 amount +); + +event TransferIssuer( + address indexed oldIssuer, + address indexed newIssuer +); + +event Claim( + address indexed from, + address indexed to, + uint256 epoch, + uint256 consumption +); + +``` + +### signature + +the pseudo code generating an ECDSA signature: +``` +sign(keccak256(abi_encode( + "\x19Ethereum Signed Message:\n32", + keccak256(abi_encode( + token_address, + payer_address, + token_issuer, + token_consumption, //calculated by user client + epoch + )) + )) +,private_key) + +``` + +### verification process + +the verification contains check about both signature and token_consumption + +the pseudo code run by verification server is as follows: + +``` + +serving_loop: + + for { + /** + * unpaied_consumption is calculated by provider + * signed_consumption is claimable amount + * tolerance allows payer "owes" provider to a certain degree + */ + //getSignedConsumption returns amount that are already claimable + if(unpaied_consumption < signed_consumption + tolerance){ + informUser("user need charge", unpaied_consumption) + interruptService() + }else{ + isServing() || recoverService() + } + } + +verification_loop: + + for { + message = incomingMessage() + if(recover_signer(message, signature) != payer_address){ + informUser("check signature failed", hash(message)) + continue + } + + /** + * optional: when using echo server to sync messages between verification servers + * more info about this in Security Considerations section + */ + if(query(message) != message){ + informUser("message outdate", hash(message)) + continue + } + + if(epoch != message.epoch || message.consumption > getDepositBalance()){ + informUser("invalid message", epoch, unpaied_consumption) + continue + } + + signed_consumption = message.consumption + save(message) + } + +claim_process: + + if(claim()){ + unpaied_consumption -= signed_consumption + signed_consumption = 0 + epoch+=1 + } + +``` +### About withdraw + +The withdraw function is slightly different based on business models + +1. prepayment model + +In prepayment business model such as using token as recharge card of general store, the user pays (crypto)currency to store in advance for claimable token as recharge card (with bonus or discount). When checking out, the customer signs a message with updated consumption (old consumption + consumption this time) to store and store verifies this message off chain. The shopping process loops without any blockchain involved, until the customer wants to return the card and get money back. Because the store already holds all currency, the withdraw function should be executed by token issuer (store) to return remaining deposit balance after claim. The prepayment model can easily be built into a wallet with QR-code scanning function. + +2. lock-release model + +If we run a paid end-to-end encrypted e-mail service that accepts token as payment, we can use lock-release model. Unlike prepayment, we charge X * N token for an e-mail sent to N recipients. In this "pay for usage" scenario, the counting of services happens on both client and server side. The client should not trust charge amount given by server in case the it's malfunctioning or malicious. When client decide not to trust server, it stops signing messages, but some of token is taken hostage in deposit balance. To fix this problem, the withdraw function should be executed by payer account with limitation such as epoch didn't change in a month. + +## Rationale + +This EIP targets on ERC-20 tokens due to its widespread adoption. However, this extension is designed to be compatible with other token standard. + +The reason we chose to implement those functions in token contract rather than a separate record contract is as follows: +- Token can transfer is more convenient and more general than interact with DApp +- Token is more standardized and has better UI support +- Token is equal to service, make token economy more prosperous +- Remove the approve process + +## Backwards Compatibility + +This EIP is fully backwards compatible as its implementation extends the functionality of [ERC-20](./eip-20.md). + +## Implementation + +```solidity + +mapping (address => StampBalance) private _depositBalance; + +struct StampBalance{ + uint256 balance; + uint256 epoch; +} + +function deposit(uint256 value) override external{ + require(value <= _balances[msg.sender]); + _balances[msg.sender] = _balances[msg.sender].sub(value); + _depositBalance[msg.sender].balance = _depositBalance[msg.sender].balance.add(value); + emit Deposit(msg.sender, value); +} + +function withdraw(address to, uint256 value) override onlyIssuer external{ + require(value <= _depositBalance[to].balance); + _depositBalance[to].balance = _depositBalance[to].balance.sub(value); + _depositBalance[to].epoch += 1; + _balances[to] = _balances[to].add(value); + emit Withdraw(to, value); +} + +function depositBalanceOf(address user) override public view returns(uint256 depositBalance, uint256 epoch){ + return (_depositBalance[user].balance, _depositBalance[user].epoch); +} + +// prepayment model +function claim(address from, uint credit, uint epoch, bytes memory signature) override onlyIssuer external{ + require(credit > 0); + require(_depositBalance[from].epoch + 1 == epoch); + require(_depositBalance[from].balance >= credit); + bytes32 message = keccak256(abi.encode(this, from, _issuer, credit, epoch)); + bytes32 msgHash = prefixed(message); + require(recoverSigner(msgHash, signature) == from); + _depositBalance[from].balance = _depositBalance[from].balance.sub(credit); + _balances[_issuer] = _balances[_issuer].add(credit); + _depositBalance[from].epoch += 1; + emit Claim(from, msg.sender, credit, epoch); +} + +function prefixed(bytes32 hash) internal pure returns (bytes32) { + return keccak256(abi.encode("\x19Ethereum Signed Message:\n32", hash)); +} + +function recoverSigner(bytes32 message, bytes memory sig) internal pure returns (address) { + (uint8 v, bytes32 r, bytes32 s) = splitSignature(sig); + return ecrecover(message, v, r, s); +} + +function splitSignature(bytes memory sig) internal pure returns (uint8 v, bytes32 r, bytes32 s) { + require(sig.length == 65); + assembly { + r := mload(add(sig, 32)) + s := mload(add(sig, 64)) + v := byte(0, mload(add(sig, 96))) + } + return (v, r, s); +} + +``` + +## Security Considerations + +By restricting claim function to issuer, there is no race condition on chain layer. However double spending problem may occur when the issuer use multiple verifiers and payer signs many payment messages simultaneously. Some of those messages may get chance to be checked valid though only the message with the largest consumption can be claimed. This problem can be fixed by introducing an echo server which accepts messages from verifiers, returns the message sequentially with largest consumption and biggest epoch number. If a verifier gets an answer different from the message he send, it updates the message from echo server as the last message it receives along with local storage of the status about this payer. Then the verifier asks the payer again for a new message. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). \ No newline at end of file diff --git a/EIPS/eip-3143.md b/EIPS/eip-3143.md new file mode 100644 index 0000000..1b48eac --- /dev/null +++ b/EIPS/eip-3143.md @@ -0,0 +1,53 @@ +--- +eip: 3143 +title: Increase block rewards to 5 ETH +author: Ben Tinner (@Terra854) +discussions-to: https://ethereum-magicians.org/t/eip-3143-increase-block-rewards-to-5-eth/5061 +status: Stagnant +type: Standards Track +category: Core +created: 2020-12-01 +--- + +## Simple Summary +Changes the block reward paid to proof-of-work (POW) miners to 5 ETH. + +## Abstract +Starting with `FORK_BLKNUM` block rewards will be increased to a base of 5 ETH, uncle and nephew rewards will be adjusted accordingly. + +## Motivation +Currently, the transaction fees (tx fees) portion of the mining rewards makes up a significant portion of the total rewards per block, at times almost exceeded the block reward of 2 ETH. This have resulted in situations where at times of low tx fees, POW miners decide to point their rigs away from ETH as they will always prefer to mine coins that are the most profitable at any point in time, reducing the security of the ETH network till transaction activity picks up again. By increasing the block rewards back to the original 5 ETH when the network first started, the voliatility will be reduced in terms of the percentage of tx fees that make up the mining rewards per block while increasing the total rewards per block, making it more financially attractive to POW miners to mine ETH barring any gigantic ETH price drops. The increase in block rewards will also allow smaller POW miners ample opporturnity to build up their stores of ETH so that when the time comes to fully transition to ETH 2.0, they may be more willing to become validators as they already have earned the requite amount of ETH needed to do so as opposed to having to spend tens of thousands of dollars to purchase the required ETH directly, increasing the number of validators in the network and therefore strengthening network security. + +Therefore, the ultimate end goal for this EIP is to give POW miners more incentive to switch to POS once ETH 2.0 is fully implemented since the transition will take a few years to complete and during that time, they will be incentivised to hold on to the tokens instead of selling it straight away in order to prepare to be a validator for ETH 2.0, reducing the selling pressure on ETH and increasing it's value in the long run. A side effect of miners staying on Ethereum is that network security will be assured during the transition period. + +## Specification +#### Adjust Block, Uncle, and Nephew rewards +Adjust the block reward to `new_block_reward`, where + + new_block_reward = 5_000_000_000_000_000_000 if block.number >= FORK_BLKNUM else block.reward + +(5E18 wei, or 5,000,000,000,000,000,000 wei, or 5 ETH). + +Analogue, if an uncle is included in a block for `block.number >= FORK_BLKNUM` such that `block.number - uncle.number = k`, the uncle reward is + + new_uncle_reward = (8 - k) * new_block_reward / 8 + +This is the existing formula for uncle rewards, simply adjusted with `new_block_reward`. + +The nephew reward for `block.number >= FORK_BLKNUM` is + + new_nephew_reward = new_block_reward / 32 + +This is the existing formula for nephew rewards, simply adjusted with `new_block_reward`. + +## Rationale +A 5 ETH base reward was chosen as a middle ground between wanting to prevent too high of an inflation rate (10.4% per annum for the first year at 5 ETH per block) and converting as many POW miners as possible into POS validators by making it easier to amass the required ETH needed through POW mining. + +## Backwards Compatibility +There are no known backward compatibility issues with the introduction of this EIP. + +## Security Considerations +There are no known security issues presented by this change. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3155.md b/EIPS/eip-3155.md new file mode 100644 index 0000000..e235e1e --- /dev/null +++ b/EIPS/eip-3155.md @@ -0,0 +1,167 @@ +--- +eip: 3155 +title: EVM trace specification +author: Martin Holst Swende (@holiman), Marius van der Wijden (@MariusVanDerWijden) +discussions-to: https://ethereum-magicians.org/t/eip-3155-create-evm-trace-specification/5007 +status: Stagnant +type: Standards Track +category: Interface +created: 2020-12-07 +--- + + +## Simple Summary +Introduce a new JSON standard for EVM traces during execution of state tests. + +## Motivation +The Ethereum Virtual Machine executes all smart contract code on ethereum. +In order to debug smart contracts and state tests better, a common format was introduced to log every execution step of the EVM. +This format was implemented by go-ethereum, parity, nethermind and Besu. +Since the common format was not well defined, the implementations differed slightly making it hard to develop adequate tooling which reduces the usefulness of tracing significantly. + +This EIP has multiple objectives: +- Move the specification to a more visible place to encourage new clients to implement it +- Strictly define corner cases that were not addressed in the previous version +- Allow for updates to the specification in case new fields are introduced during execution +- Provide sample output + +Implementing this EIP in all major clients allows us to create meaningful differential fuzzers that fuzz EVM implementations for the mainnet and all upcoming hardforks. +It also helps finding differences in execution quickly in the case of a chain split. + +This EIP will enable users to create better differential fuzzing infrastructure to compare the EVM implementations of all major Ethereum clients against each other. +This could help to find bugs that are currently present in the client implementations. + +## Specification +Clients should be able to execute simple transactions as well as code and return traces. In the following, we will call this client CUT (client under test) and use go-ethereums `evm` binary for code examples. + +### Datatypes + +| Type | Explanation | Example | +|---|---|---| +| Number | Plain json number | "pc":0 | +| Hex-Number | Hex-encoded number | "gas":"0x2540be400" | +| String | Plain string | "opName":"PUSH1" | +| Hex-String | Hex-encoded string | | +| Array of x | Array of x encoded values | | +| Key-Value | Key-Value structure with key and values encoded as hex strings | | +| Boolean | Json bool can either be true or false | "pass": true | + +### Output + +The CUT MUST output a `json` object for EACH operation. + +Required Fields: + +| Name | Type | Explanation | +|---|---|---| +| `pc` | Number | Program Counter | +| `op` | Number | OpCode | +| `gas` | Hex-Number | Gas left before executing this operation | +| `gasCost` | Hex-Number | Gas cost of this operation | +| `stack` | Array of Hex-Numbers | Array of all values on the stack | +| `depth` | Number | Depth of the call stack | +| `returnData` | Hex-String | Data returned by function call | +| `refund` | Hex-Number | Amount of **global** gas refunded | +| `memSize` | Number | Size of memory array | + +Optional Fields: + +| Name | Type | Explanation | +|---|---|---| +| `opName` | String | Name of the operation | +| `error` | Hex-String | Description of an error (should contain revert reason if supported) | +| `memory` | Array of Hex-Strings | Array of all allocated values | +| `storage` | Key-Value | Array of all stored values | +| `returnStack` | Array of Hex-Numbers | Array of values, Stack of the called function | + +*Example:* +``` +{"pc":0,"op":96,"gas":"0x2540be400","gasCost":"0x3","memory":"0x","memSize":0,"stack":[],"depth":1,"error":null,"opName":"PUSH1"} +``` + +The `stack`, `memory` and `memSize` are the values _before_ execution of the op. +All array attributes (`stack`, `returnStack`, `memory`) MUST be initialized to empty arrays ("stack":[],) NOT to null. +If the CUT will not output values for `memory` or `storage` then the `memory` and `storage` fields are omitted. +This can happen either because the CUT does not support tracing these fields or it has been configured not to trace it. +The `memSize` field MUST be present regardless of `memory` support. +Clients SHOULD implement a way to disable recording the storage as the stateroot includes all storage updates. +Clients SHOULD output the fields in the same order as listed in this EIP. + +The CUT MUST NOT output a line for the `STOP` operation if an error occurred: +*Example:* +``` +{"pc":2,"op":0,"gas":"0x2540be3fd","gasCost":"0x0","memory":"0x","memSize":0,"stack":["0x40"],"depth":1,"error":null,"opName":"STOP"} +``` + +### Summary and error handling + +At the end of execution, the CUT MUST print some summerical info, this info SHOULD have the following fields. +The summary should be a single `jsonl` object. + +Required Fields: + +| Name | Type | Explanation | +|---|---|---| +| `stateRoot` | Hex-String | Root of the state trie after executing the transaction | +| `output` | | Return values of the function | +| `gasUsed` | Hex-Number | All gas used by the transaction | +| `pass` | Boolean | Bool whether transaction was executed successfully | + +OptionalFields: + +| Name | Type | Explanation | +|---|---|---| +| `time` | Number | Time in nanoseconds needed to execute the transaction | +| `fork` | String | Name of the fork rules used for execution | +``` +{"stateRoot":"0xd4c577737f5d20207d338c360c42d3af78de54812720e3339f7b27293ef195b7","output":"","gasUsed":"0x3","successful":"true","time":141485} +``` + +## Rationale +This EIP is largely based on the previous non-official documentation for EVM tracing. +It tries to cover as many corner cases as possible to enable true client compatibility. +The datatypes and if a field is optional is chosen to be as compatible with current implementations as possible. + +## Backwards Compatibility +This EIP is fully backward compatible with ethereum as it only introduces a better tracing infrastructure that is optional for clients to implement. + +### Clients +This EIP is fully backward compatible with go-ethereum. OpenEthereum, Besu and Nethermind clients would have to change their JSON output of `openethereum-evm` `evmtool` and `nethtest` slightly do adhere to the new and stricter specs. New clients would need to implement this change if they want to be part of the differential fuzzing group. + +## Test Cases + +```bash + ~/go/src/github.com/ethereum/go-ethereum/build/bin/evm --code 604080536040604055604060006040600060025afa6040f3 --json run +{"pc":0,"op":96,"gas":"0x2540be400","gasCost":"0x3","memory":"0x","memSize":0,"stack":[],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"PUSH1","error":""} +{"pc":2,"op":128,"gas":"0x2540be3fd","gasCost":"0x3","memory":"0x","memSize":0,"stack":["0x40"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"DUP1","error":""} +{"pc":3,"op":83,"gas":"0x2540be3fa","gasCost":"0xc","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40","0x40"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"MSTORE8","error":""} +{"pc":4,"op":96,"gas":"0x2540be3ee","gasCost":"0x3","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":[],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"PUSH1","error":""} +{"pc":6,"op":96,"gas":"0x2540be3eb","gasCost":"0x3","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"PUSH1","error":""} +{"pc":8,"op":85,"gas":"0x2540be3e8","gasCost":"0x4e20","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40","0x40"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"SSTORE","error":""} +{"pc":9,"op":96,"gas":"0x2540b95c8","gasCost":"0x3","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":[],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"PUSH1","error":""} +{"pc":11,"op":96,"gas":"0x2540b95c5","gasCost":"0x3","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"PUSH1","error":""} +{"pc":13,"op":96,"gas":"0x2540b95c2","gasCost":"0x3","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40","0x0"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"PUSH1","error":""} +{"pc":15,"op":96,"gas":"0x2540b95bf","gasCost":"0x3","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40","0x0","0x40"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"PUSH1","error":""} +{"pc":17,"op":96,"gas":"0x2540b95bc","gasCost":"0x3","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40","0x0","0x40","0x0"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"PUSH1","error":""} +{"pc":19,"op":90,"gas":"0x2540b95b9","gasCost":"0x2","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40","0x0","0x40","0x0","0x2"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"GAS","error":""} +{"pc":20,"op":250,"gas":"0x2540b95b7","gasCost":"0x24abb676c","memory":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x40","0x0","0x40","0x0","0x2","0x2540b95b7"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"STATICCALL","error":""} +{"pc":21,"op":96,"gas":"0x2540b92a7","gasCost":"0x3","memory":"0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b00000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x1"],"returnStack":[],"returnData":"0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b","depth":1,"refund":0,"opName":"PUSH1","error":""} +{"pc":23,"op":243,"gas":"0x2540b92a4","gasCost":"0x0","memory":"0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b00000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000","memSize":96,"stack":["0x1","0x40"],"returnStack":[],"returnData":"0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b","depth":1,"refund":0,"opName":"RETURN","error":""} +{"stateRoot":"2eef130ec61805516c1f050720b520619787704a5dd826a39aeefb850f83acfd", "output":"40","gasUsed":"0x515c","time":350855} +``` + + +## Implementation +Implementation in [go-ethereum](https://github.com/ethereum/go-ethereum/tree/master/cmd/evm) +Implementation in [OpenEthereum](https://github.com/openethereum/openethereum/tree/master/evmbin) +Implementation in [Besu](https://github.com/hyperledger/besu/tree/master/ethereum/evmtool) +Implementation in [Nethermind](https://github.com/NethermindEth/nethermind/tree/master/src/Nethermind/Nethermind.State.Test.Runner) + + +## Security Considerations +Tracing is expensive. +Exposing an endpoint for creating traces publicly could open up a denial of service vector. +Clients should consider putting trace endpoints behind a separate flag from other endpoints. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3156.md b/EIPS/eip-3156.md new file mode 100644 index 0000000..07c46fa --- /dev/null +++ b/EIPS/eip-3156.md @@ -0,0 +1,517 @@ +--- +eip: 3156 +title: Flash Loans +author: Alberto Cuesta Cañada (@albertocuestacanada), Fiona Kobayashi (@fifikobayashi), fubuloubu (@fubuloubu), Austin Williams (@onewayfunction) +discussions-to: https://ethereum-magicians.org/t/erc-3156-flash-loans-review-discussion/5077 +status: Final +type: Standards Track +category: ERC +created: 2020-11-15 +--- + +## Simple Summary + +This ERC provides standard interfaces and processes for single-asset flash loans. + +## Abstract + +A flash loan is a smart contract transaction in which a lender smart contract lends assets to a borrower smart contract with the condition that the assets are returned, plus an optional fee, before the end of the transaction. This ERC specifies interfaces for lenders to accept flash loan requests, and for borrowers to take temporary control of the transaction within the lender execution. The process for the safe execution of flash loans is also specified. + +## Motivation + +Flash loans allow smart contracts to lend an amount of tokens without a requirement for collateral, with the condition that they must be returned within the same transaction. + +Early adopters of the flash loan pattern have produced different interfaces and different use patterns. The diversification is expected to intensify, and with it the technical debt required to integrate with diverse flash lending patterns. + +Some of the high level differences in the approaches across the protocols include: +- Repayment approaches at the end of the transaction, where some pull the principal plus the fee from the loan receiver, and others where the loan receiver needs to manually return the principal and the fee to the lender. +- Some lenders offer the ability to repay the loan using a token that is different to what was originally borrowed, which can reduce the overall complexity of the flash transaction and gas fees. +- Some lenders offer a single entry point into the protocol regardless of whether you're buying, selling, depositing or chaining them together as a flash loan, whereas other protocols offer discrete entry points. +- Some lenders allow to flash mint any amount of their native token without charging a fee, effectively allowing flash loans bounded by computational constraints instead of asset ownership constraints. + +## Specification + +A flash lending feature integrates two smart contracts using a callback pattern. These are called the LENDER and the RECEIVER in this EIP. + +### Lender Specification + +A `lender` MUST implement the IERC3156FlashLender interface. +``` +pragma solidity ^0.7.0 || ^0.8.0; +import "./IERC3156FlashBorrower.sol"; + + +interface IERC3156FlashLender { + + /** + * @dev The amount of currency available to be lent. + * @param token The loan currency. + * @return The amount of `token` that can be borrowed. + */ + function maxFlashLoan( + address token + ) external view returns (uint256); + + /** + * @dev The fee to be charged for a given loan. + * @param token The loan currency. + * @param amount The amount of tokens lent. + * @return The amount of `token` to be charged for the loan, on top of the returned principal. + */ + function flashFee( + address token, + uint256 amount + ) external view returns (uint256); + + /** + * @dev Initiate a flash loan. + * @param receiver The receiver of the tokens in the loan, and the receiver of the callback. + * @param token The loan currency. + * @param amount The amount of tokens lent. + * @param data Arbitrary data structure, intended to contain user-defined parameters. + */ + function flashLoan( + IERC3156FlashBorrower receiver, + address token, + uint256 amount, + bytes calldata data + ) external returns (bool); +} +``` + +The `maxFlashLoan` function MUST return the maximum loan possible for `token`. If a `token` is not currently supported `maxFlashLoan` MUST return 0, instead of reverting. + +The `flashFee` function MUST return the fee charged for a loan of `amount` `token`. If the token is not supported `flashFee` MUST revert. + +The `flashLoan` function MUST include a callback to the `onFlashLoan` function in a `IERC3156FlashBorrower` contract. + +``` +function flashLoan( + IERC3156FlashBorrower receiver, + address token, + uint256 amount, + bytes calldata data +) external returns (bool) { + ... + require( + receiver.onFlashLoan(msg.sender, token, amount, fee, data) == keccak256("ERC3156FlashBorrower.onFlashLoan"), + "IERC3156: Callback failed" + ); + ... +} +``` + +The `flashLoan` function MUST transfer `amount` of `token` to `receiver` before the callback to the receiver. + +The `flashLoan` function MUST include `msg.sender` as the `initiator` to `onFlashLoan`. + +The `flashLoan` function MUST NOT modify the `token`, `amount` and `data` parameter received, and MUST pass them on to `onFlashLoan`. + +The `flashLoan` function MUST include a `fee` argument to `onFlashLoan` with the fee to pay for the loan on top of the principal, ensuring that `fee == flashFee(token, amount)`. + +The `lender` MUST verify that the `onFlashLoan` callback returns the keccak256 hash of "ERC3156FlashBorrower.onFlashLoan". + +After the callback, the `flashLoan` function MUST take the `amount + fee` `token` from the `receiver`, or revert if this is not successful. + +If successful, `flashLoan` MUST return `true`. + +### Receiver Specification + +A `receiver` of flash loans MUST implement the IERC3156FlashBorrower interface: + +``` +pragma solidity ^0.7.0 || ^0.8.0; + + +interface IERC3156FlashBorrower { + + /** + * @dev Receive a flash loan. + * @param initiator The initiator of the loan. + * @param token The loan currency. + * @param amount The amount of tokens lent. + * @param fee The additional amount of tokens to repay. + * @param data Arbitrary data structure, intended to contain user-defined parameters. + * @return The keccak256 hash of "ERC3156FlashBorrower.onFlashLoan" + */ + function onFlashLoan( + address initiator, + address token, + uint256 amount, + uint256 fee, + bytes calldata data + ) external returns (bytes32); +} +``` + +For the transaction to not revert, `receiver` MUST approve `amount + fee` of `token` to be taken by `msg.sender` before the end of `onFlashLoan`. + +If successful, `onFlashLoan` MUST return the keccak256 hash of "ERC3156FlashBorrower.onFlashLoan". + +## Rationale + +The interfaces described in this ERC have been chosen as to cover the known flash lending use cases, while allowing for safe and gas efficient implementations. + +`flashFee` reverts on unsupported tokens, because returning a numerical value would be incorrect. + +`flashLoan` has been chosen as a function name as descriptive enough, unlikely to clash with other functions in the lender, and including both the use cases in which the tokens lent are held or minted by the lender. + +`receiver` is taken as a parameter to allow flexibility on the implementation of separate loan initiators and receivers. + +Existing flash lenders all provide flash loans of several token types from the same contract. Providing a `token` parameter in both the `flashLoan` and `onFlashLoan` functions matches closely the observed functionality. + +A `bytes calldata data` parameter is included for the caller to pass arbitrary information to the `receiver`, without impacting the utility of the `flashLoan` standard. + +`onFlashLoan` has been chosen as a function name as descriptive enough, unlikely to clash with other functions in the `receiver`, and following the `onAction` naming pattern used as well in EIP-667. + +A `initiator` will often be required in the `onFlashLoan` function, which the lender knows as `msg.sender`. An alternative implementation which would embed the `initiator` in the `data` parameter by the caller would require an additional mechanism for the receiver to verify its accuracy, and is not advisable. + +The `amount` will be required in the `onFlashLoan` function, which the lender took as a parameter. An alternative implementation which would embed the `amount` in the `data` parameter by the caller would require an additional mechanism for the receiver to verify its accuracy, and is not advisable. + +A `fee` will often be calculated in the `flashLoan` function, which the `receiver` must be aware of for repayment. Passing the `fee` as a parameter instead of appended to `data` is simple and effective. + +The `amount + fee` are pulled from the `receiver` to allow the `lender` to implement other features that depend on using `transferFrom`, without having to lock them for the duration of a flash loan. An alternative implementation where the repayment is transferred to the `lender` is also possible, but would need all other features in the lender to be also based in using `transfer` instead of `transferFrom`. Given the lower complexity and prevalence of a "pull" architecture over a "push" architecture, "pull" was chosen. + +## Backwards Compatibility + +No backwards compatibility issues identified. + +## Implementation + +### Flash Borrower Reference Implementation + +``` +pragma solidity ^0.8.0; + +import "./interfaces/IERC20.sol"; +import "./interfaces/IERC3156FlashBorrower.sol"; +import "./interfaces/IERC3156FlashLender.sol"; + + +contract FlashBorrower is IERC3156FlashBorrower { + enum Action {NORMAL, OTHER} + + IERC3156FlashLender lender; + + constructor ( + IERC3156FlashLender lender_ + ) { + lender = lender_; + } + + /// @dev ERC-3156 Flash loan callback + function onFlashLoan( + address initiator, + address token, + uint256 amount, + uint256 fee, + bytes calldata data + ) external override returns(bytes32) { + require( + msg.sender == address(lender), + "FlashBorrower: Untrusted lender" + ); + require( + initiator == address(this), + "FlashBorrower: Untrusted loan initiator" + ); + (Action action) = abi.decode(data, (Action)); + if (action == Action.NORMAL) { + // do one thing + } else if (action == Action.OTHER) { + // do another + } + return keccak256("ERC3156FlashBorrower.onFlashLoan"); + } + + /// @dev Initiate a flash loan + function flashBorrow( + address token, + uint256 amount + ) public { + bytes memory data = abi.encode(Action.NORMAL); + uint256 _allowance = IERC20(token).allowance(address(this), address(lender)); + uint256 _fee = lender.flashFee(token, amount); + uint256 _repayment = amount + _fee; + IERC20(token).approve(address(lender), _allowance + _repayment); + lender.flashLoan(this, token, amount, data); + } +} +``` + +### Flash Mint Reference Implementation + +``` +pragma solidity ^0.8.0; + +import "../ERC20.sol"; +import "../interfaces/IERC20.sol"; +import "../interfaces/IERC3156FlashBorrower.sol"; +import "../interfaces/IERC3156FlashLender.sol"; + + +/** + * @author Alberto Cuesta Cañada + * @dev Extension of {ERC20} that allows flash minting. + */ +contract FlashMinter is ERC20, IERC3156FlashLender { + + bytes32 public constant CALLBACK_SUCCESS = keccak256("ERC3156FlashBorrower.onFlashLoan"); + uint256 public fee; // 1 == 0.01 %. + + /** + * @param fee_ The percentage of the loan `amount` that needs to be repaid, in addition to `amount`. + */ + constructor ( + string memory name, + string memory symbol, + uint256 fee_ + ) ERC20(name, symbol) { + fee = fee_; + } + + /** + * @dev The amount of currency available to be lent. + * @param token The loan currency. + * @return The amount of `token` that can be borrowed. + */ + function maxFlashLoan( + address token + ) external view override returns (uint256) { + return type(uint256).max - totalSupply(); + } + + /** + * @dev The fee to be charged for a given loan. + * @param token The loan currency. Must match the address of this contract. + * @param amount The amount of tokens lent. + * @return The amount of `token` to be charged for the loan, on top of the returned principal. + */ + function flashFee( + address token, + uint256 amount + ) external view override returns (uint256) { + require( + token == address(this), + "FlashMinter: Unsupported currency" + ); + return _flashFee(token, amount); + } + + /** + * @dev Loan `amount` tokens to `receiver`, and takes it back plus a `flashFee` after the ERC3156 callback. + * @param receiver The contract receiving the tokens, needs to implement the `onFlashLoan(address user, uint256 amount, uint256 fee, bytes calldata)` interface. + * @param token The loan currency. Must match the address of this contract. + * @param amount The amount of tokens lent. + * @param data A data parameter to be passed on to the `receiver` for any custom use. + */ + function flashLoan( + IERC3156FlashBorrower receiver, + address token, + uint256 amount, + bytes calldata data + ) external override returns (bool){ + require( + token == address(this), + "FlashMinter: Unsupported currency" + ); + uint256 fee = _flashFee(token, amount); + _mint(address(receiver), amount); + require( + receiver.onFlashLoan(msg.sender, token, amount, fee, data) == CALLBACK_SUCCESS, + "FlashMinter: Callback failed" + ); + uint256 _allowance = allowance(address(receiver), address(this)); + require( + _allowance >= (amount + fee), + "FlashMinter: Repay not approved" + ); + _approve(address(receiver), address(this), _allowance - (amount + fee)); + _burn(address(receiver), amount + fee); + return true; + } + + /** + * @dev The fee to be charged for a given loan. Internal function with no checks. + * @param token The loan currency. + * @param amount The amount of tokens lent. + * @return The amount of `token` to be charged for the loan, on top of the returned principal. + */ + function _flashFee( + address token, + uint256 amount + ) internal view returns (uint256) { + return amount * fee / 10000; + } +} +``` + +### Flash Loan Reference Implementation + +``` +pragma solidity ^0.8.0; + +import "../interfaces/IERC20.sol"; +import "../interfaces/IERC3156FlashBorrower.sol"; +import "../interfaces/IERC3156FlashLender.sol"; + + +/** + * @author Alberto Cuesta Cañada + * @dev Extension of {ERC20} that allows flash lending. + */ +contract FlashLender is IERC3156FlashLender { + + bytes32 public constant CALLBACK_SUCCESS = keccak256("ERC3156FlashBorrower.onFlashLoan"); + mapping(address => bool) public supportedTokens; + uint256 public fee; // 1 == 0.01 %. + + + /** + * @param supportedTokens_ Token contracts supported for flash lending. + * @param fee_ The percentage of the loan `amount` that needs to be repaid, in addition to `amount`. + */ + constructor( + address[] memory supportedTokens_, + uint256 fee_ + ) { + for (uint256 i = 0; i < supportedTokens_.length; i++) { + supportedTokens[supportedTokens_[i]] = true; + } + fee = fee_; + } + + /** + * @dev Loan `amount` tokens to `receiver`, and takes it back plus a `flashFee` after the callback. + * @param receiver The contract receiving the tokens, needs to implement the `onFlashLoan(address user, uint256 amount, uint256 fee, bytes calldata)` interface. + * @param token The loan currency. + * @param amount The amount of tokens lent. + * @param data A data parameter to be passed on to the `receiver` for any custom use. + */ + function flashLoan( + IERC3156FlashBorrower receiver, + address token, + uint256 amount, + bytes calldata data + ) external override returns(bool) { + require( + supportedTokens[token], + "FlashLender: Unsupported currency" + ); + uint256 fee = _flashFee(token, amount); + require( + IERC20(token).transfer(address(receiver), amount), + "FlashLender: Transfer failed" + ); + require( + receiver.onFlashLoan(msg.sender, token, amount, fee, data) == CALLBACK_SUCCESS, + "FlashLender: Callback failed" + ); + require( + IERC20(token).transferFrom(address(receiver), address(this), amount + fee), + "FlashLender: Repay failed" + ); + return true; + } + + /** + * @dev The fee to be charged for a given loan. + * @param token The loan currency. + * @param amount The amount of tokens lent. + * @return The amount of `token` to be charged for the loan, on top of the returned principal. + */ + function flashFee( + address token, + uint256 amount + ) external view override returns (uint256) { + require( + supportedTokens[token], + "FlashLender: Unsupported currency" + ); + return _flashFee(token, amount); + } + + /** + * @dev The fee to be charged for a given loan. Internal function with no checks. + * @param token The loan currency. + * @param amount The amount of tokens lent. + * @return The amount of `token` to be charged for the loan, on top of the returned principal. + */ + function _flashFee( + address token, + uint256 amount + ) internal view returns (uint256) { + return amount * fee / 10000; + } + + /** + * @dev The amount of currency available to be lent. + * @param token The loan currency. + * @return The amount of `token` that can be borrowed. + */ + function maxFlashLoan( + address token + ) external view override returns (uint256) { + return supportedTokens[token] ? IERC20(token).balanceOf(address(this)) : 0; + } +} + +``` + +## Security Considerations + + +### Verification of callback arguments + +The arguments of `onFlashLoan` are expected to reflect the conditions of the flash loan, but cannot be trusted unconditionally. They can be divided in two groups, that require different checks before they can be trusted to be genuine. + +0. No arguments can be assumed to be genuine without some kind of verification. `initiator`, `token` and `amount` refer to a past transaction that might not have happened if the caller of `onFlashLoan` decides to lie. `fee` might be false or calculated incorrectly. `data` might have been manipulated by the caller. +1. To trust that the value of `initiator`, `token`, `amount` and `fee` are genuine a reasonable pattern is to verify that the `onFlashLoan` caller is in a whitelist of verified flash lenders. Since often the caller of `flashLoan` will also be receiving the `onFlashLoan` callback this will be trivial. In all other cases flash lenders will need to be approved if the arguments in `onFlashLoan` are to be trusted. +2. To trust that the value of `data` is genuine, in addition to the check in point 1, it is recommended to verify that the `initiator` belongs to a group of trusted addresses. Trusting the `lender` and the `initiator` is enough to trust that the contents of `data` are genuine. + +### Flash lending security considerations + +#### Automatic approvals +The safest approach is to implement an approval for `amount+fee` before the `flashLoan` is executed. + +Any `receiver` that keeps an approval for a given `lender` needs to include in `onFlashLoan` a mechanism to verify that the initiator is trusted. + +Any `receiver` that includes in `onFlashLoan` the approval for the `lender` to take the `amount + fee` needs to be combined with a mechanism to verify that the initiator is trusted. + +If an unsuspecting contract with a non-reverting fallback function, or an EOA, would approve a `lender` implementing ERC3156, and not immediately use the approval, and if the `lender` would not verify the return value of `onFlashLoan`, then the unsuspecting contract or EOA could be drained of funds up to their allowance or balance limit. This would be executed by an `initiator` calling `flashLoan` on the victim. The flash loan would be executed and repaid, plus any fees, which would be accumulated by the `lender`. For this reason, it is important that the `lender` implements the specification in full and reverts if `onFlashLoan` doesn't return the keccak256 hash for "ERC3156FlashBorrower.onFlashLoan". + +### Flash minting external security considerations + +The typical quantum of tokens involved in flash mint transactions will give rise to new innovative attack vectors. + +#### Example 1 - interest rate attack +If there exists a lending protocol that offers stable interests rates, but it does not have floor/ceiling rate limits and it does not rebalance the fixed rate based on flash-induced liquidity changes, then it could be susceptible to the following scenario: + +FreeLoanAttack.sol +1. Flash mint 1 quintillion STAB +2. Deposit the 1 quintillion STAB + $1.5 million worth of ETH collateral +3. The quantum of your total deposit now pushes the stable interest rate down to 0.00001% stable interest rate +4. Borrow 1 million STAB on 0.00001% stable interest rate based on the 1.5M ETH collateral +5. Withdraw and burn the 1 quint STAB to close the original flash mint +6. You now have a 1 million STAB loan that is practically interest free for perpetuity ($0.10 / year in interest) + +The key takeaway being the obvious need to implement a flat floor/ceiling rate limit and to rebalance the rate based on short term liquidity changes. + +#### Example 2 - arithmetic overflow and underflow +If the flash mint provider does not place any limits on the amount of flash mintable tokens in a transaction, then anyone can flash mint 2^256-1 amount of tokens. + +The protocols on the receiving end of the flash mints will need to ensure their contracts can handle this, either by using a compiler that embeds overflow protection in the smart contract bytecode, or by setting explicit checks. + +### Flash minting internal security considerations + +The coupling of flash minting with business specific features in the same platform can easily lead to unintended consequences. + +#### Example - Treasury draining +Assume a smart contract that flash lends its native token. The same smart contract borrows from a third party when users burn the native token. This pattern would be used to aggregate in the smart contract the collateralized debt of several users into a single account in the third party. The flash mint could be used to cause the lender to borrow to its limit, and then pushing interest rates in the underlying lender, liquidate the flash lender: +1. Flash mint from `lender` a very large amount of FOO. +2. Redeem FOO for BAR, causing `lender` to borrow from `underwriter` all the way to its borrowing limit. +3. Trigger a debt rate increase in `underwriter`, making `lender` undercollateralized. +4. Liquidate the `lender` for profit. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3198.md b/EIPS/eip-3198.md new file mode 100644 index 0000000..fdad719 --- /dev/null +++ b/EIPS/eip-3198.md @@ -0,0 +1,62 @@ +--- +eip: 3198 +title: BASEFEE opcode +author: Abdelhamid Bakhta (@abdelhamidbakhta), Vitalik Buterin (@vbuterin) +discussions-to: https://ethereum-magicians.org/t/eip-3198-basefeeopcode/5162 +status: Final +type: Standards Track +category: Core +created: 2021-01-13 +requires: 1559 +--- + +## Simple Summary +Adds an opcode that gives the EVM access to the block's base fee. + +## Abstract + +Add a `BASEFEE (0x48)` that returns the value of the base fee of the current block it is executing in. + +## Motivation +The intended use case would be for contracts to get the value of the base fee. This feature would enable or improve existing use cases, such as: +- Contracts that need to set bounties for anyone to "poke" them with a transaction could set the bounty to be `BASEFEE + x`, or `BASEFEE * (1 + x)`. This makes the mechanism more reliable, because they will always pay "enough" regardless of market conditions. +- Gas futures can be implemented based on it. This would be more precise than gastokens. +- Improve the security for state channels, plasma, optirolls and other fraud proof driven solutions. Having the `BASEFEE` as an input allows you to lengthen the challenge period automatically if you see that the `BASEFEE` is high. + +## Specification +Add a `BASEFEE` opcode at `(0x48)`, with gas cost `G_base`. + +| Op | Input | Output | Cost | +|:----: |:-----: |:------: |:----: | +| 0x48 | 0 | 1 | 2 | + +## Rationale + +### Gas cost +The value of the base fee is needed to process transactions. That means it's value is already available before running the EVM code. +The opcode does not add extra complexity and additional read/write operations, hence the choice of `G_base` gas cost. + +## Backwards Compatibility +There are no known backward compatibility issues with this opcode. + +## Test Cases + +### Nominal case +Assuming current block base fee is `7 wei`. +This should push the value `7` (left padded byte32) to the stack. + +Bytecode: `0x4800` (`BASEFEE, STOP`) + +| Pc | Op | Cost | Stack | RStack | +|-------|-------------|------|-----------|-----------| +| 0 | BASEFEE | 2 | [] | [] | +| 1 | STOP | 0 | [7] | [] | + +Output: 0x +Consumed gas: `2` + +## Security Considerations +The value of the base fee is not sensitive and is publicly accessible in the block header. There are no known security implications with this opcode. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3220.md b/EIPS/eip-3220.md new file mode 100644 index 0000000..949413a --- /dev/null +++ b/EIPS/eip-3220.md @@ -0,0 +1,76 @@ +--- +eip: 3220 +title: Crosschain Identifier Specification +author: Weijia Zhang (@weijia31415), Peter Robinson (@drinkcoffee) +discussions-to: https://ethereum-magicians.org/t/eip-3220-crosschain-id-specification/5446 +status: Stagnant +type: Standards Track +category: Core +created: 2020-10-21 +--- + +## Simple Summary + +A self-verifying unique blockchain identifier that deals with forks. + +## Abstract + +The crosschain-id is a 32 byte hex string and with some bytes extracted from blockchain hash and some manually defined to characterize a blockchain. +We also propose a registration and lookup service to retrieve blockchain metadata from the crosschain-id. + +## Motivation + +With the success of Bitcoin and Ethereum, various blockchains such as EOS, Ripple, Litecoin, Besu, Wanchain and the like have been developed and are growing at a fast pace. There are also other private and consortium blockchains such as Hyperledger Fabric, Hyperledger Besu, Stellar, Corda, Quorum that only allow nodes with permitted identities to join the blockchain network. The growth of public and private blockchains imposes challenges for inter-chain interoperability, particularly when these chains are heterogeneous and incompatible. Enterprise Ethereum Alliance formed Crosschain Interoperability Task Force (CITF) to look into common crosschain problems and solutions. CITF team noticed that there is a lack of unique identifier to charaterise and describe a blockchain. Several proprosals were discussed in EEA Crosschain Interoperability Task Force meetings and discussions. + +[EIP-155](./eip-155.md) provides a unique identifier to a blockchain to provide simple relay attack protection. This specification defines an integer for Chainid for a blockchain and sign the chainid into a transaction data and hence present attackers to send same transaction to different blockchains. This specification will require blockchains to define a chainid and register the chainid in a public repository. + +The challenge of using an integer for chainid is that it is not broad enough to cover all blockchains and it does not prevent different blockchains using the same chainid. Also, it does not address the issue for two forked blockchains having the same chainid. + +Hence there is need for a more robust blockchain identifier that will overcome these drawbacks, especially for crosschain operations where multiple chains are involved. A blockchain identifier (crosschain id) should be unique and satisfy the following requirements: + +* should provide identification, description, and discovery of blockchains. +* should provide unique identification of each blockchain in the crosschain service ecosystem. +* should provide descriptions for a blockchain identities such as chainId, name, type, consensus scheme etc. +* should provide discovery mechanism for supported blockchains and also for new blockchains in the ecosystem. +* should provide a mechanism for a joining blockchain to register to the ecosystem. +* should provide a mechanism for a blockchain to edit properties or unregister from the crosschain ecosystem. +* should provide a mechanism to get some critical information of the blockchain +* should provide a mechanism to differentiate an original blockchain and a forked blockchain +* should provide a mechanism to verify a chainid without external registration service + +## Specification + +### Definition of a 32 byte crosschain id + +| Name | Size(bytes) | Description | +|---------------|-------------|-------------| +| Truncated Block Hash | 16 | This is the block hash of the genesis block or the block hash of of the block immediate prior to the fork for a fork of a blockchain. The 16 bytes is the 16 least significant bytes, assuming network byte order.| +|Native Chain ID| 8 | This is the **Chain Id** value that should be used with the blockchain when signing transactions. For blockchains that do not have a concept of **Chain Id**, this value is zero.| +|Chain Type| 2 | Reserve 0x00 as undefined chaintype. 0x01 as mainnet type. 0x1[0-A]: testnet, 0x2[0-A]: private development network| +| Governance Identifier | 2 | For new blockchains, a governance_identifier can be specified to identify an original **owner** of a blockchain, to help settle forked / main chain disputes. For all existing blockchains and for blockchains that do not have the concept of an **owner**, this field is zero. | +| Reserved | 3 | Reserved for future use. Use 000000 for now. | +| Checksum | 1 | Used to verify the integrity of the identifier. This integrity check is targeted at detecting Crosschain Identifiers mis-typed by human users. The value is calculated as the truncated SHA256 message digest of the rest of the identifier, using the least significant byte, assuming network byte order. Note that this checksum byte only detects integrity with a probability of one in 256. This probability is adequate for the intended usage of detecting typographical errors by people manually entering the Crosschain Identifier. | + + +## Rationale + +We have considered various alternative specifications such as using a random unique hex string to represent a blockchain. The drawback of this method is that the random id can not be used to verify a blockchain's intrinsic identity such as the blockhash of the genesis block. A second alternative is simply using a genesis blockhash to represent a blockchain id for crosschain operations. The drawback of this is that this id does not have information about the property of the blockchain and it has problem when a blockchain is forked into two blockchain. + +## Backwards Compatibility + +Crosschainid can be backward compatible with EIP-155. The crosschain id contains an 8 byte segment to record the `Native Chain ID`. +For Ethereum chains, that can be used for a value intended to be used with EIP-155. + +## Security Considerations + +Collision of crosschain id: Two blockchains can contain the same crosschain id and hence making the mistakenly transfer assets to a wrong blockchain. +This security concern is addressed by comparing the hash of the crosschain id with the hash of the genesis block. If it matches, then the crosschain id is verified. If not, the crosschain id can be compared with the forked blockhash. If none of the blockhash match the crosschain id hash, then the crosschain id cannot be verified. + +Preventing relay attack: Although crosschain id by itself is different from chainid and it is not signed into blockchain transaction, the crosschain id can still be used for presenting relay attack. An application that handles crosschain transaction can verified the crosschain id with its blockhash and decide whether the transaction is valid or not. Any transaction with a non-verifiable crosschain id should be rejected. + +The crosschain-id are not required to be signed into blockchaid tx. +For blockchains that do not cryptographically sign crosschain id into the blocks, the crosschain id cannot be verified with the blocks itself and have to be verified with external smart contract address and offchain utilities implemented based on the crosschain id specification. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3224.md b/EIPS/eip-3224.md new file mode 100644 index 0000000..0d3ab96 --- /dev/null +++ b/EIPS/eip-3224.md @@ -0,0 +1,442 @@ +--- +eip: 3224 +title: Described Data +description: Contract method to compute human-readable descriptions for signable data. +author: Richard Moore (@ricmoo), Nick Johnson (@arachnid) +discussions-to: https://github.com/ethereum/EIPs/issues/3225 +status: Stagnant +type: Standards Track +category: ERC +created: 2021-01-23 +requires: 191 +--- + + +## Abstract + +Human-readable descriptions for machine executable operations, +described in higher level machine readable data, so that wallets +can provide meaningful feedback to the user describing the +action the user is about to perform. + + +## Motivation + +When using an Ethereum Wallet (e.g. MetaMask, Clef, Hardware +Wallets) users must accept and authorize signing messages or +sending transactions. + +Due to the complexity of Ethereum transactions, wallets are very +limitd in their ability to provide insight into the contents of +transactions user are approving; outside special-cased support +for common transactions such as ERC20 transfers, this often amounts +to asking the user to sign an opaque blob of binary data. + +This EIP presents a method for dapp developers to enable a more +comfortable user experience by providing wallets with a means +to generate a better description about what the contract claims +will happen. + +It does not address malicious contracts which wish to lie, it +only addresses honest contracts that want to make their user's +life better. We believe that this is a reasonable security model, +as transaction descriptions can be audited at the same time as +contract code, allowing auditors and code reviewers to check that +transaction descriptions are accurate as part of their review. + + +## Specification + +The **description string** and **described data** are generated +simultaneously by evaluating the contract +(i.e. the **describer**), passing the **describer inputs** to the +method: + +```solidity +function eipXXXDescribe(bytes describer_inputs) view returns (string description_string, bytes described_data); +``` + +The method must be executable in a static context, (i.e. any +side effects, such as logX, sstore, etc.), including through +indirect calls may be ignored. + +During evaluation, the `ADDRESS` (i.e. `to`), `CALLER` +(i.e. `from`), `VALUE`, and `GASPRICE` must be the same as the +values for the transaction being described, so that the +code generating the description can rely on them. For signing +**described messages**, `VALUE` should always be 0. + +When executing the bytecode, best efforts should be made to +ensure `BLOCKHASH`, `NUMBER`, `TIMESTAMP` and `DIFFICULTY` +match the `"latest"` block. The `COINBASE` should be the zero +address. + +The method may revert, in which case the signing must be aborted. + + +### New JSON-RPC Methods + +Clients which manage private keys should expose additional +methods for interacting with the related accounts. + +If an user interface is not present or expected for any other +account-based operations, the description strings should be +ignored and the described data used directly. + +These JSON-RPC methods will also be implemented in standard +Ethereum libraries, so the JSON-RPC description is meant more +of a canonical way to describe them. + + +### Signing Described Messages + +```solidity +eth_signDescribedMessage(address, describer, describerInput) +// Result: { +// description: "text/plain;Hello World", +// data: "0x...", // described data +// signature: "0x..." +// } +``` + +Compute the **description string** and **described data** by +evaluating the call to **describer**, with the +**describerInput** passed to the ABI encoded call to +`eipXXXDescription(bytes)`. The `VALUE` during execution must +be 0. + +If the wallet contains a user interface for accepting or +denying signing a message, it should present the description +string to the user. Optionally, a wallet may wish to +additionally provide a way to examine the described data. + +If accepted, the computed **described data** is signed +according to [EIP-191](./eip-191.md), with the *version +byte* of `0x00` and the *version specific data* of describer +address. + +That is: + +``` +0x19 0x00 DESCRIBER_ADDRESS 0xDESCRIBED_DATA +``` + +The returned result includes the **described data**, allowing +dapps that use parameters computed in the contract to be +available. + +### Sending Described Transactions + +```solidity +eth_sendDescribedTransaction(address, { + to: "0x...", + value: 1234, + nonce: 42, + gas: 42000, + gasPrice: 9000000000, + describerInput: "0x1234...", +}) +// Result: { +// description: "text/plain;Hello World", +// transaction: "0x...", // serialized signed transaction +// } +``` + +Compute the **description string** and **described data** by +evaluating the call to the **describer** `to`, with the +**describerInput** passed to the ABI encoded call to +`eipXXXDescription(bytes)`. + +If the wallet contains a user interface for accepting or +denying a transaction, it should present the description string +along with fee and value information. Optionally, a wallet may +wish to additionally provide a way to further examine the +transaction. + +If accepted, the transaction data is set to the computed +**described data**, the derived transaction is signed and sent, +and the **description string** and serialized signed +transaction is returned. + + +### Signing Described Transaction + +```solidity +eth_signDescribedTransaction(address, { + to: "0x...", + value: 1234, + nonce: 42, + gas: 42000, + gasPrice: 9000000000, + describerInput: "0x1234...", +}) +// Result: { +// description: "text/plain;Hello World", +// transaction: "0x...", // serialized signed transaction +// } +``` + +Compute the **description string** and **described data** by +evaluating the call to the **describer** `to`, with the +**describerInput** passed to the ABI encoded call to +`eipXXXDescription(bytes)`. + +If the wallet contains a user interface for accepting or +denying a transaction, it should present the description string +along with fee and value information. Optionally, a wallet may +wish to additionally provide a way to further examine the +transaction. + +If accepted, the transaction data is set to the computed +**described data**, the derived transaction is signed (and not +sent) and the **description string** and serialized signed +transaction is returned. + +### Description Strings + +A **description string** must begin with a mime-type followed +by a semi-colon (`;`). This EIP specifies only the `text/plain` +mime-type, but future EIPs may specify additional types to +enable more rich processing, such as `text/markdown` so that +addresses can be linkable within clients or to enable +multi-locale options, similar to multipart/form-data. + + +## Rationale + +### Meta Description + +There have been many attempts to solve this problem, many of +which attempt to examine the encoded transaction data or +message data directly. + +In many cases, the information that would be necessary for a +meaningful description is not present in the final encoded +transaction data or message data. + +Instead this EIP uses an indirect description of the data. + +For example, the `commit(bytes32)` method of ENS places a +commitement **hash** on-chain. The hash contains the +**blinded** name and address; since the name is blinded, the +encoded data (i.e. the hash) no longer contains the original +values and is insufficient to access the necessary values to +be included in a description. + +By instead describing the commitment indirectly (with the +original information intact: NAME, ADDRESS and SECRET) a +meaningful description can be computed (e.g. "commit to NAME for ADDRESS (with SECRET)") +and the matching data can be computed (i.e. `commit(hash(name, owner, secret))`). + +### Entangling the Contract Address + +To prevent data being signed from one contract being used +against another, the contract address is entanlged into +both the transaction (implicitly via the `to` field) and +in messages by the EIP-191 versions specific data. + +The use of the zero address is reserved. + +### Alternatives + +- NatSpec and company are a class of more complex languages that attempt to describe the encoded data directly. Because of the language complexity they often end up being quite large requiring entire runtime environments with ample processing power and memory, as well as additional sandboxing to reduce security concerns. One goal of this is to reduce the complexity to something that could execute on hardware wallets and other simple wallets. These also describe the data directly, which in many cases (such as blinded data), cannot adequately describe the data at all + +- Custom Languages; due to the complexity of Ethereum transactions, any language used would require a lot of expressiveness and re-inventing the wheel. The EVM already exists (it may not be ideal), but it is there and can handle everything necessary. + +- Format Strings (e.g. Trustless Signing UI Protocol; format strings can only operate on the class of regular languages, which in many cases is insufficient to describe an Ethereum transaction. This was an issue quite often during early attempts at solving this problem. + +- The signTypedData [EIP-712](./eip-712.md) has many parallels to what this EIP aims to solve + +- @TODO: More + + +## Backwards Compatibility + +All signatures for messages are generated using [EIP-191](./eip-191.md) +which had a previously compatible version byte of `0x00`, so +there should be no concerns with backwards compatibility. + + +## Test Cases + +All test cases operate against the published and verified contracts: + +- Formatter: Ropsten @ 0x7a89c0521604008c93c97aa76950198bca73d933 +- TestFormatter: Ropsten @ 0xab3045aa85cbcabb06ed3f3fe968fa5457727270 + +The private key used for signing messages and transactions is: + +``` +privateKey = "0x6283185307179586476925286766559005768394338798750211641949889184" +``` + + +### Messages + +**Example: login with signed message** + +- sends selector login() +- received data with selector doLogin(bytes32 timestamp) + +``` +Input: + Address: 0xab3045AA85cBCaBb06eD3F3FE968fA5457727270 + Describer Input: 0xb34e97e800000000000000000000000000000000000000000000000000000000 + i.e. encode( + [ "bytes4" ], + [ SEL("login()") ] + ) + +Output: + Description: text/plain;Log into ethereum.org? + Data: 0x14629d78000000000000000000000000000000000000000000000000000000006010d607 + i.e. encodeWithSelector("doLogin(bytes32)", "0x000000000000000000000000000000000000000000000000000000006010d607" ] + +Signing: + Preimage: 0x1900ab3045aa85cbcabb06ed3f3fe968fa545772727014629d78000000000000000000000000000000000000000000000000000000006010d607 + Signature: 0x8b9def29343c85797a580c5cd3607c06e78a53351219f9ba706b9985c1a3c91e702bf678e07f5daf5ef48b3e3cc581202de233904b72cf2c4f7d714ce92075b21c +``` + +### Transactions + +All transaction test cases use the ropsten network (chainId: 3) +and for all unspecified properties use 0. + +**Example: ERC-20 transfer** + +``` +Input: + Address: 0xab3045AA85cBCaBb06eD3F3FE968fA5457727270 + Describer Input: 0xa9059cbb000000000000000000000000000000000000000000000000000000000000000000000000000000008ba1f109551bd432803012645ac136ddd64dba720000000000000000000000000000000000000000000000002b992b75cbeb6000 + i.e. encode( + [ "bytes4", "address", "uint"], + [ SEL("transfer(address,uint256)"), "0x8ba1f109551bD432803012645Ac136ddd64DBA72", 3.14159e18 ] + ) +Output: + Description: text/plain;Send 3.14159 TOKN to "ricmoose.eth" (0x8ba1f109551bD432803012645Ac136ddd64DBA72)? + Described Data: 0xa9059cbb0000000000000000000000000000000000000000000000002b992b75cbeb60000000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72 + i.e. encodeWithSelector("transfer(address,uint256)", "0x8ba1f109551bD432803012645Ac136ddd64DBA72", 3.14159e18) + +Signing: + Signed Transaction: 0xf8a280808094ab3045aa85cbcabb06ed3f3fe968fa545772727080b844a9059cbb0000000000000000000000000000000000000000000000002b992b75cbeb60000000000000000000000000008ba1f109551bd432803012645ac136ddd64dba7229a0f33ea492d326ac32d9b7ae203c61bf7cf0ac576fb0cf8be8e4c63dc89c90de12a06c8efb28aaf3b70c032b3bd1edfc664578c49f040cf749bb19b000da56507fb2 +``` + +**Example: ERC-20 approve** + +``` +Input: + Address: 0xab3045AA85cBCaBb06eD3F3FE968fA5457727270 + Describer Input: 0x095ea7b3000000000000000000000000000000000000000000000000000000000000000000000000000000008ba1f109551bd432803012645ac136ddd64dba720000000000000000000000000000000000000000000000002b992b75cbeb6000 + i.e. encode( + [ "bytes4", "address", "uint"], + [ SEL("approve(address,uint256)"), "0x8ba1f109551bD432803012645Ac136ddd64DBA72", 3.14159e18 ] + ) + +Output: + Description: text/plain;Approve "ricmoose.eth" (0x8ba1f109551bD432803012645Ac136ddd64DBA72) to manage 3.14159 TOKN tokens? + Described Data: 0xa9059cbb0000000000000000000000000000000000000000000000002b992b75cbeb60000000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72 + i.e. encodeWithSelector("approve(address,uint256)", "0x8ba1f109551bD432803012645Ac136ddd64DBA72", 3.14159e18) + +Signing: + Signed Transaction: 0xf8a280808094ab3045aa85cbcabb06ed3f3fe968fa545772727080b844a9059cbb0000000000000000000000000000000000000000000000002b992b75cbeb60000000000000000000000000008ba1f109551bd432803012645ac136ddd64dba7229a0f33ea492d326ac32d9b7ae203c61bf7cf0ac576fb0cf8be8e4c63dc89c90de12a06c8efb28aaf3b70c032b3bd1edfc664578c49f040cf749bb19b000da56507fb2 +``` + +**Example: ENS commit** + +``` +Input: + Address: 0xab3045AA85cBCaBb06eD3F3FE968fA5457727270 + Describer Input: 0x0f0e373f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000e31f43c1d823afaa67a8c5fbb8348176d225a79e65462b0520ef7d3df61b9992ed3bea0c56ead753be7c8b3614e0ce01e4cac41b00000000000000000000000000000000000000000000000000000000000000087269636d6f6f7365000000000000000000000000000000000000000000000000 + i.e. encode( + [ "bytes4", "string", "address", "bytes32"], + [ SEL("commit(string,address,bytes32)"), "ricmoose", "0xE31f43C1d823AfAA67A8C5fbB8348176d225A79e", "0x65462b0520ef7d3df61b9992ed3bea0c56ead753be7c8b3614e0ce01e4cac41b" ] + ) + +Output: + Description: text/plain;Commit to the ENS name "ricmoose.eth" for 0xE31f43C1d823AfAA67A8C5fbB8348176d225A79e? + Described Data: 0xf14fcbc8e4a4f2bb818545497be34c7ab30e6e87e0001df4ba82e7c8b3f224fbaf255b91 + i.e. encodeWithSelector("commit(bytes32)", makeCommitment("ricmoose", "0xE31f43C1d823AfAA67A8C5fbB8348176d225A79e", "0x65462b0520ef7d3df61b9992ed3bea0c56ead753be7c8b3614e0ce01e4cac41b")) + +Signing: + Signed Transaction: 0xf88180808094ab3045aa85cbcabb06ed3f3fe968fa545772727080a4f14fcbc8e4a4f2bb818545497be34c7ab30e6e87e0001df4ba82e7c8b3f224fbaf255b912aa0a62b41d1ebda584fe84cf8a05f61b429fe4ec361e13c17f30a23281106b38a8da00bcdd896fe758d8f0cfac46445a48f76f5e9fe27790d67c51412cb98a12a0844 +``` + +**Example: WETH mint()** + +``` +Input: + Address: 0xab3045AA85cBCaBb06eD3F3FE968fA5457727270 + Describer Input: 0x1249c58b00000000000000000000000000000000000000000000000000000000 + i.e. encode( + [ "bytes4" ], + [ SEL("mint()") ] + ) + Value: 1.23 ether + +Output: + Description: text/plain;Mint 1.23 WETH (spending 1.23 ether)? + Described Data: 0x1249c58b + i.e. encodeWithSelector("mint()") + +Signing: + Signed Transaction: 0xf86980808094ab3045aa85cbcabb06ed3f3fe968fa5457727270881111d67bb1bb0000841249c58b29a012df802e1394a97caab23c15c3a8c931668df4b2d6d604ca23f3f6b836d0aafca0071a2aebef6a9848616b4d618912f2003fb4babde3dba451b5246f866281a654 +``` + +## Reference Implementation + +@TODO (consider adding it as one or more files in `../assets/eip-####/`) + +I will add examples in Solidity and JavaScript. + + +## Security Considerations + +### Escaping Text + +Wallets must be careful when displaying text provided by +contracts and proper efforts must be taken to sanitize +it, for example, be sure to consider: + +- HTML could be embedded to attempt to trick web-based wallets into executing code using the script tag (possibly uploading any private keys to a server) +- In general, extreme care must be used when rendering HTML; consider the ENS names `not-ricmoo.eth` or ` ricmoo.eth`, which if rendered without care would appear as `ricmoo.eth`, which it is not +- Other marks which require escaping could be included, such as quotes (`"`), formatting (`\n` (new line), `\f` (form feed), `\t` (tab), any of many non-standard whitespaces), back-slassh (`\`) +- UTF-8 has had bugs in the past which could allow arbitrary code execution and crashing renderers; consider using the UTF-8 replacement character (or *something*) for code-points outside common planes or common sub-sets within planes +- Homoglyphs attacks +- Right-to-left marks may affect rendering +- Many other things, deplnding on your environment + +### Distinguished Signed Data + +Applications implementing this EIP to sign message data should +ensure there are no collisions within the data which could +result in ambiguously signed data. + +@TODO: Expand on this; compare packed data to ABI encoded data? + +### Enumeration + +If an abort occurs during signing, the response from this call +should match the response from a declined signing request; +otherwise this could be used for enumeration attacks, etc. A +random interactive-scale delay should also be added, otherwise +a < 10ms response could be interpreted as an error. + +### Replayablility + +Transactions contain an explicit nonce, but signed messages do +not. + +For many purposes, such as signing in, a nonce could be +injected (using block.timestamp) into the data. The log in +service can verify this is a recent timestamp. The timestamp +may or may not be omitted from the description string in this +case, as it it largely useful internally only. + +In general, when signing messages a nonce often makes sense to +include to prevent the same signed data from being used in the +future. + + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3234.md b/EIPS/eip-3234.md new file mode 100644 index 0000000..df9e386 --- /dev/null +++ b/EIPS/eip-3234.md @@ -0,0 +1,226 @@ +--- +eip: 3234 +title: Batch Flash Loans +author: Alberto Cuesta Cañada (@albertocuestacanada), Fiona Kobayashi (@fifikobayashi), fubuloubu (@fubuloubu), Austin Williams (@onewayfunction) +discussions-to: https://ethereum-magicians.org/t/erc-3234-batch-flash-loans/5271 +status: Stagnant +type: Standards Track +category: ERC +created: 2021-01-31 +--- + +## Simple Summary + +This ERC provides standard interfaces and processes for multiple-asset flash loans. + +## Motivation + +Flash loans of multiple assets, or batch flash loans, are a common offering of flash lenders, and have a strong use case in the simultaneous refinance of several positions between platforms. At the same time, batch flash loans are more complicated to use than single asset flash loans (ER3156). This divergence of use cases and user profiles calls for independent, but consistent, standards for single asset flash loans and batch flash loans. + + +## Specification + +A batch flash lending feature integrates two smart contracts using a callback pattern. These are called the LENDER and the RECEIVER in this EIP. + +### Lender Specification + +A `lender` MUST implement the IERC3234BatchFlashLender interface. +``` +pragma solidity ^0.7.0 || ^0.8.0; +import "./IERC3234BatchFlashBorrower.sol"; + + +interface IERC3234BatchFlashLender { + + /** + * @dev The amount of currency available to be lended. + * @param tokens The currency for each loan in the batch. + * @return The maximum amount that can be borrowed for each loan in the batch. + */ + function maxFlashLoan( + address[] calldata tokens + ) external view returns (uint256[]); + + /** + * @dev The fees to be charged for a given batch loan. + * @param tokens The loan currencies. + * @param amounts The amounts of tokens lent. + * @return The amount of each `token` to be charged for each loan, on top of the returned principal. + */ + function flashFee( + address[] calldata tokens, + uint256[] calldata amounts + ) external view returns (uint256[]); + + /** + * @dev Initiate a batch flash loan. + * @param receiver The receiver of the tokens in the loan, and the receiver of the callback. + * @param tokens The loan currencies. + * @param amounts The amount of tokens lent. + * @param data Arbitrary data structure, intended to contain user-defined parameters. + */ + function batchFlashLoan( + IERC3234BatchFlashBorrower receiver, + address[] calldata tokens, + uint256[] calldata amounts, + bytes[] calldata data + ) external returns (bool); +} +``` + +The `maxFlashLoan` function MUST return the maximum loan possible for each `token`. If a `token` is not currently supported `maxFlashLoan` MUST return 0, instead of reverting. + +The `flashFee` function MUST return the fees charged for each loan of `amount` `token`. If a token is not supported `flashFee` MUST revert. + +The `batchFlashLoan` function MUST include a callback to the `onBatchFlashLoan` function in a `IERC3234BatchFlashBorrower` contract. + +``` +function batchFlashLoan( + IERC3234BatchFlashBorrower receiver, + address[] calldata tokens, + uint256[] calldata amounts, + bytes calldata data +) external returns (bool) { + ... + require( + receiver.onBatchFlashLoan( + msg.sender, + tokens, + amounts, + fees, + data + ) == keccak256("ERC3234BatchFlashBorrower.onBatchFlashLoan"), + "IERC3234: Callback failed" + ); + ... +} +``` + +The `batchFlashLoan` function MUST transfer `amounts[i]` of each `tokens[i]` to `receiver` before the callback to the borrower. + +The `batchFlashLoan` function MUST include `msg.sender` as the `initiator` to `onBatchFlashLoan`. + +The `batchFlashLoan` function MUST NOT modify the `tokens`, `amounts` and `data` parameters received, and MUST pass them on to `onBatchFlashLoan`. + +The `lender` MUST verify that the `onBatchFlashLoan` callback returns the keccak256 hash of "ERC3234BatchFlashBorrower.onBatchFlashLoan". + +The `batchFlashLoan` function MUST include a `fees` argument to `onBatchFlashLoan` with the fee to pay for each individual `token` and `amount` lent, ensuring that `fees[i] == flashFee(tokens[i], amounts[i])`. + +After the callback, for each `token` in `tokens`, the `batchFlashLoan` function MUST take the `amounts[i] + fees[i]` of `tokens[i]` from the `receiver`, or revert if this is not successful. + +If successful, `batchFlashLoan` MUST return `true`. + +### Receiver Specification + +A `receiver` of flash loans MUST implement the IERC3234BatchFlashBorrower interface: + +``` +pragma solidity ^0.7.0 || ^0.8.0; + + +interface IERC3234BatchFlashBorrower { + + /** + * @dev Receive a flash loan. + * @param initiator The initiator of the loan. + * @param tokens The loan currency. + * @param amounts The amount of tokens lent. + * @param fees The additional amount of tokens to repay. + * @param data Arbitrary data structure, intended to contain user-defined parameters. + * @return The keccak256 hash of "ERC3234BatchFlashBorrower.onBatchFlashLoan" + */ + function onBatchFlashLoan( + address initiator, + address[] calldata tokens, + uint256[] calldata amounts, + uint256[] calldata fees, + bytes calldata data + ) external returns (bytes32); +} +``` + +For the transaction to not revert, for each `token` in `tokens`, `receiver` MUST approve `amounts[i] + fees[i]` of `tokens[i]` to be taken by `msg.sender` before the end of `onBatchFlashLoan`. + +If successful, `onBatchFlashLoan` MUST return the keccak256 hash of "ERC3156BatchFlashBorrower.onBatchFlashLoan". + +## Rationale + +The interfaces described in this ERC have been chosen as to cover the known flash lending use cases, while allowing for safe and gas efficient implementations. + +`flashFee` reverts on unsupported tokens, because returning a numerical value would be incorrect. + +`batchFlashLoan` has been chosen as a function name as descriptive enough, unlikely to clash with other functions in the lender, and including both the use cases in which the tokens lended are held or minted by the lender. + +`receiver` is taken as a parameter to allow flexibility on the implementation of separate loan initiators and receivers. + +Existing flash lenders (Aave, dYdX and Uniswap) all provide flash loans of several token types from the same contract (LendingPool, SoloMargin and UniswapV2Pair). Providing a `token` parameter in both the `batchFlashLoan` and `onBatchFlashLoan` functions matches closely the observed functionality. + +A `bytes calldata data` parameter is included for the caller to pass arbitrary information to the `receiver`, without impacting the utility of the `batchFlashLoan` standard. + +`onBatchFlashLoan` has been chosen as a function name as descriptive enough, unlikely to clash with other functions in the `receiver`, and following the `onAction` naming pattern used as well in EIP-667. + +An `initiator` will often be required in the `onBatchFlashLoan` function, which the lender knows as `msg.sender`. An alternative implementation which would embed the `initiator` in the `data` parameter by the caller would require an additional mechanism for the receiver to verify its accuracy, and is not advisable. + +The `amounts` will be required in the `onBatchFlashLoan` function, which the lender took as a parameter. An alternative implementation which would embed the `amounts` in the `data` parameter by the caller would require an additional mechanism for the receiver to verify its accuracy, and is not advisable. + +The `fees` will often be calculated in the `batchFlashLoan` function, which the `receiver` must be aware of for repayment. Passing the `fees` as a parameter instead of appended to `data` is simple and effective. + +The `amount + fee` are pulled from the `receiver` to allow the `lender` to implement other features that depend on using `transferFrom`, without having to lock them for the duration of a flash loan. An alternative implementation where the repayment is transferred to the `lender` is also possible, but would need all other features in the lender to be also based in using `transfer` instead of `transferFrom`. Given the lower complexity and prevalence of a "pull" architecture over a "push" architecture, "pull" was chosen. + +## Security Considerations + +### Verification of callback arguments + +The arguments of `onBatchFlashLoan` are expected to reflect the conditions of the flash loan, but cannot be trusted unconditionally. They can be divided in two groups, that require different checks before they can be trusted to be genuine. + +0. No arguments can be assumed to be genuine without some kind of verification. `initiator`, `tokens` and `amounts` refer to a past transaction that might not have happened if the caller of `onBatchFlashLoan` decides to lie. `fees` might be false or calculated incorrectly. `data` might have been manipulated by the caller. +1. To trust that the value of `initiator`, `tokens`, `amounts` and `fees` are genuine a reasonable pattern is to verify that the `onBatchFlashLoan` caller is in a whitelist of verified flash lenders. Since often the caller of `batchFlashLoan` will also be receiving the `onBatchFlashLoan` callback this will be trivial. In all other cases flash lenders will need to be approved if the arguments in `onBatchFlashLoan` are to be trusted. +2. To trust that the value of `data` is genuine, in addition to the check in point 1, it is recommended that the `receiver` verifies that the `initiator` is in some list of trusted addresses. Trusting the `lender` and the `initiator` is enough to trust that the contents of `data` are genuine. + +### Flash lending security considerations + +#### Automatic approvals for untrusted borrowers +The safest approach is to implement an approval for `amount+fee` before the `batchFlashLoan` is executed. + +Including in `onBatchFlashLoan` the approval for the `lender` to take the `amount + fee` needs to be combined with a mechanism to verify that the borrower is trusted, such as those described above. + +If an unsuspecting contract with a non-reverting fallback function, or an EOA, would approve a `lender` implementing ERC3156, and not immediately use the approval, and if the `lender` would not verify the return value of `onBatchFlashLoan`, then the unsuspecting contract or EOA could be drained of funds up to their allowance or balance limit. This would be executed by a `borrower` calling `batchFlashLoan` on the victim. The flash loan would be executed and repaid, plus any fees, which would be accumulated by the `lender`. For this reason, it is important that the `lender` implements the specification in full and reverts if `onBatchFlashLoan` doesn't return the keccak256 hash for "ERC3156FlashBorrower.onBatchFlashLoan". + +### Flash minting external security considerations + +The typical quantum of tokens involved in flash mint transactions will give rise to new innovative attack vectors. + +#### Example 1 - interest rate attack +If there exists a lending protocol that offers stable interests rates, but it does not have floor/ceiling rate limits and it does not rebalance the fixed rate based on flash-induced liquidity changes, then it could be susceptible to the following scenario: + +FreeLoanAttack.sol +1. Flash mint 1 quintillion DAI +2. Deposit the 1 quintillion DAI + $1.5 million worth of ETH collateral +3. The quantum of your total deposit now pushes the stable interest rate down to 0.00001% stable interest rate +4. Borrow 1 million DAI on 0.00001% stable interest rate based on the 1.5M ETH collateral +5. Withdraw and burn the 1 quint DAI to close the original flash mint +6. You now have a 1 million DAI loan that is practically interest free for perpetuity ($0.10 / year in interest) + +The key takeaway being the obvious need to implement a flat floor/ceiling rate limit and to rebalance the rate based on short term liquidity changes. + +#### Example 2 - arithmetic overflow and underflow +If the flash mint provider does not place any limits on the amount of flash mintable tokens in a transaction, then anyone can flash mint 2^256-1 amount of tokens. + +The protocols on the receiving end of the flash mints will need to ensure their contracts can handle this. One obvious way is to leverage OpenZeppelin's SafeMath libraries as a catch-all safety net, however consideration should be given to when it is or isn't used given the gas tradeoffs. + +If you recall there was a series of incidents in 2018 where exchanges such as OKEx, Poloniex, HitBTC and Huobi had to shutdown deposits and withdrawls of ERC20 tokens due to integer overflows within the ERC20 token contracts. + + +### Flash minting internal security considerations + +The coupling of flash minting with business specific features in the same platform can easily lead to unintended consequences. + +#### Example - Treasury draining +In early implementations of the Yield Protocol flash loaned fyDai could be redeemed for Dai, which could be used to liquidate the Yield Protocol CDP vault in MakerDAO: +1. Flash mint a very large amount of fyDai. +2. Redeem for Dai as much fyDai as the Yield Protocol collateral would allow. +3. Trigger a stability rate increase with a call to `jug.drip` which would make the Yield Protocol uncollateralized. +4. Liquidate the Yield Protocol CDP vault in MakerDAO. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3238.md b/EIPS/eip-3238.md new file mode 100644 index 0000000..3fa3a6c --- /dev/null +++ b/EIPS/eip-3238.md @@ -0,0 +1,39 @@ +--- +eip: 3238 +title: Difficulty Bomb Delay to Q2/2022 +author: Afri Schoedon (@q9f) +discussions-to: https://github.com/ethereum/EIPs/issues/3239 +type: Standards Track +category: Core +status: Stagnant +created: 2021-01-25 +--- + +## Simple Summary +Delays the difficulty bomb so 30 second blocks won't happen until around Q2/2022. + +## Abstract +Starting with `FORK_BLOCK_NUMBER` the client will calculate the difficulty based on a fake block number suggesting to the client that the difficulty bomb is adjusting eleven million blocks later than the actual block number. + +## Motivation +Even after the Ethereum 2.0 mainnet launch, Ethash proof-of-work mining on the legacy chain should be feasible. It should allow miners sealing new blocks every 13~15 seconds on average for another ten months and allow both Ethereum 1.x and Ethereum 2.0 developers to conclude the merge. + +## Specification +#### Relax Difficulty with Fake Block Number +For the purposes of `calc_difficulty`, simply replace the use of `block.number`, as used in the exponential ice age component, with the formula: + + fake_block_number = max(0, block.number - 11_000_000) if block.number >= FORK_BLOCK_NUMBER else block.number + +## Rationale +This will delay the ice age by another ~26 million seconds (approximately ~9.89 months), so the chain would be back at ~30 second block times in Q2/2022. Hopefully, by then the Eth1-to-Eth2 merge will be concluded and the ice age fulfilled its task. + +## Backwards Compatibility +This EIP is not forward compatible and introduces backwards incompatibilities in the difficulty calculation. Therefore, it should be included in a scheduled hardfork at a certain block number. It's suggested to consider this EIP either with or shortly after the Berlin hard-fork but not later than July 2021. + +Alternatively, in order to maintain stability of the system, a it can be considered to activate this EIP along with EIP-1559 fee market changes in a bundle. With the delay of the ice age, there is a desire to no further increase inflation and rather incentivize users to participate in proof-of-stake consensus instead. + +## Security Considerations +There are no known security issues with this proposal. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3267.md b/EIPS/eip-3267.md new file mode 100644 index 0000000..c44fe3e --- /dev/null +++ b/EIPS/eip-3267.md @@ -0,0 +1,69 @@ +--- +eip: 3267 +title: Giving Ethereum fees to Future Salaries +author: Victor Porton (@vporton), Victor Porton +discussions-to: https://ethereum-magicians.org/t/discussion-of-eip-3267/5343 +status: Stagnant +type: Standards Track +category: Core +created: 2021-02-13 +--- + +## Simple Summary +Transfer a part of Ethereum transfer/mining fees to Future Salaries contract + +## Abstract +Transfer a part (exact fractions - TBD) of mining/transfer fees + (probably: TBD) some minted ETH to the `DonateETH` contract configured to transfer to `SalaryWithDAO` contract. + +## Motivation +This proposal solves two problems at once: + +1. It provides a big amount of "money" to common good producers. That obviously personally benefits common good producers, allowing them to live better human lives, it increases peoples' and organizations' both abilities and incentives to produce common goods. That benefits the humanity as a whole and the Ethereum ecosystem in particular. See more in the discussion why it's crucial. + +2. This would effectively decrease circulating ETH supply. The necessity to decrease the (circulating) ETH supply (by locking ETH in Future Salaries system for a long time) is a well-known important thing to be done. + +Paradoxically, it will directly benefit miners/validators, see the discussion. + +## Specification +(TBD) + +`SalaryWithDAO` = `TBD` (`address`) + +`DefaultDAOInterface` = `TBD` (`address`) + +`MintPerPeriod` = `TBD` (`uint256`) + +`TransferFraction` = `TBD` (0..1) + +`MineFraction` = `TBD` (0..1) + +[The contract's source](../assets/eip-3267/contracts/README.md) + +Prior to `FORK_BLOCK_NUMBER`, `SalaryWithDAO` and `DefaultDAOInterface` contracts will be deployed to the network and exist at the above specified addresses. + +Change the Ethereum clients to transfer at every ETH transfer and every ETH mine a fixed fraction `TransferFraction` of the transferred ETH and `MineFraction` of the mined ETH to a fixed account (decide the account number, it can be for example `0x00000000000000000000000000000000000000001` or even `0x00000000000000000000000000000000000000000` or a random account). + +Change the Ethereum clients to mint `MintPerPeriod` ETH to the contract `DonateETH` every some time (e.g. the first transaction of the first block every UTC day - TBD how often). + +Change the Ethereum clients to every some time (e.g. the second transaction of the first block every UTC day - TBD how often) transfer the entire ETH from this account to the contract `DonateETH`. + +Because this EIP solves a similar problem, cancel any other EIPs that burn ETH (except gas fees) during transfers or mining. (TBD: We should transfer more ETH in this EIP than we burned accordingly older accepted EIPs, because this EIP has the additional advantages of: 1. funding common goods; 2. better aligning values of ETH and values of tokens). + +## Rationale +The Future Salaries is the _only_ known system of distributing significant funds to common good producers. (Quadratic funding aimed to do a similar thing, but in practice as we see on GitCoin it favors a few developers, ignores project of highly advanced scientific research that is hard to explain to an average developer, and encourages colluding, and it just highly random due to small number of donors. Also quadratic funding simply does not gather enough funds to cover common good needs). So this EIP is the only known way to recover the economy. + +The economical model of Future Salaries is described in [this research article preprint](../assets/eip-3267/science-salaries.pdf). + +Funding multiple oracles with different finish time would alleviate the future trouble that the circulating ETH (or other tokens) supply would suddenly increase when the oracle finishes. It would effectively exclude some ETH from the circulation forever. + +## Backwards Compatibility +Because transferring to the aforementioned account is neither mining nor a transaction, we get a new kinds of ETH transfers, so there may be some (expected moderate impact) troubles with applications that have made assumptions about ETH transfers all occurring either as miner payments or transactions. + +## Security Considerations +The security considerations are: +- The DAO that controls account restoration may switch to a non-effective or biased way of voting (for example to being controlled by one human) thus distributing funds unfairly. This problem could be solved by a future fork of Ethereum that would "confiscate" control from the DAO. + +See more in the discussion. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3298.md b/EIPS/eip-3298.md new file mode 100644 index 0000000..daa52e7 --- /dev/null +++ b/EIPS/eip-3298.md @@ -0,0 +1,95 @@ +--- +eip: 3298 +title: Removal of refunds +author: Vitalik Buterin (@vbuterin), Martin Swende (@holiman) +discussions-to: https://ethereum-magicians.org/t/eip-3298-removal-of-refunds/5430 +status: Stagnant +type: Standards Track +category: Core +created: 2021-02-26 +--- + +## Simple Summary + +Remove gas refunds for SSTORE and SELFDESTRUCT. + +## Motivation + +Gas refunds for SSTORE and SELFDESTRUCT were originally introduced to motivate application developers to write applications that practice "good state hygiene", clearing storage slots and contracts that are no longer needed. However, they are not widely used for this, and poor state hygiene continues to be the norm. It is now widely accepted that the only solution to state growth is some form of [statelessness or state expiry](https://hackmd.io/@HWeNw8hNRimMm2m2GH56Cw/state_size_management), and if such a solution is implemented, then disused storage slots and contracts would start to be ignored automatically. + +Gas refunds additionally have multiple harmful consequences: + +* Refunds give rise to [GasToken](https://gastoken.io/). GasToken has benefits in moving gas space from low-fee periods to high-fee periods, but it also has downsides to the network, particularly in exacerbating state size (as state slots are effectively used as a "battery" to save up gas) and inefficiently clogging blockchain gas usage +* Refunds increase block size variance. The theoretical maximum amount of actual gas consumed in a block is nearly twice the on-paper gas limit (as refunds add gas space for subsequent transactions in a block, though refunds are capped at 50% of a transaction's gas used). This is [not fatal](https://notes.ethereum.org/@vbuterin/eip_1559_spikes), but is still undesirable, especially given that refunds can be used to maintain 2x usage spikes for far longer than EIP 1559 can. + +## Specification + +### Parameters + +| Constant | Value | +| - | - | +| `FORK_BLOCK` | TBD | + +For blocks where `block.number >= FORK_BLOCK`, the following changes apply. + +Do not apply the `refund`. + +The description above is sufficient to describe the change, but for the sake of clarity we enumerate all places where gas refunds are currently used and which should/could be removed within a node implementation. + +1. Remove all use of the "refund counter" in SSTORE gas accounting, as defined in [EIP 2200](https://eips.ethereum.org/EIPS/eip-2200). Particularly: + + * If a storage slot is changed and the _current value_ equals the _original value_, but does not equal the _new value_, `SSTORE_RESET_GAS` is deducted (plus `COLD_SLOAD_COST` if [prescribed by EIP 2929 rules](https://eips.ethereum.org/EIPS/eip-2929#sstore-changes)), but no modifications to the refund counter are made. + * If a storage slot is changed and the _current value_ equals neither the _new value_ nor the _original value_ (regardless of whether or not the latter two are equal), `SLOAD_GAS` is deducted (plus `COLD_SLOAD_COST` if [prescribed by EIP 2929 rules](https://eips.ethereum.org/EIPS/eip-2929#sstore-changes)), but no modifications to the refund counter are made. + +2. Remove the `SELFDESTRUCT` refund. + +## Rationale + +A full removal of refunds is the simplest way to solve the issues with refunds; any gains from partial retention of the refund mechanism are not worth the complexity that that would leave remaining in the Ethereum protocol. + +## Backwards Compatibility + +Refunds are currently only applied _after_ transaction execution, so they cannot affect how much gas is available to any particular call frame during execution. Hence, removing them will not break the ability of any code to execute, though it will render some applications economically nonviable. + +[GasToken](https://gastoken.io/) in particular will become valueless. DeFi arbitrage bots, which today frequently use either established GasToken schemes or a custom alternative to reduce on-chain costs, would benefit from rewriting their code to remove calls to these no-longer-functional gas storage mechanisms. + +## Implementation + +An implementation can be found here: https://gist.github.com/holiman/460f952716a74eeb9ab358bb1836d821#gistcomment-3642048 + +## Test case changes + +* The "original", "1st", "2nd", "3rd" columns refer to the value of storage slot 0 before the execution and after each SSTORE. +* The "Berlin (cold)" column gives the post-Berlin (EIP 2929) gas cost assuming the storage slot had not yet been accessed. +* The "Berlin (hot)" column gives the post-Berlin gas cost assuming the storage slot has already been accessed. +* The "Berlin (hot) + norefund" column gives the post-Berlin gas cost assuming the storage slot has already been accessed, **and assuming this EIP has been implemented**. + +Gas costs are provided with refunds subtracted; if the number is negative this means that refunds exceed gas costs. The 50% refund limit is not applied (due to the implied assumption that this code is only a small fragment of a much larger execution). + +If refunds were to be removed, this would be the comparative table +| Code | Original | 1st | 2nd | 3rd | Istanbul | Berlin (cold) | Berlin (hot)| Berlin (hot)+norefund | +| -- | -- | -- | -- | -- | -- | -- | -- | -- | +| `0x60006000556000600055` | 0 | 0 | 0 | | 1612 | 2312 | 212 | 212 | +| `0x60006000556001600055` | 0 | 0 | 1 | | 20812 | 22212 | 20112 | 20112 | +| `0x60016000556000600055` | 0 | 1 | 0 | | 1612 | 2312 | 212 | 20112 | +| `0x60016000556002600055` | 0 | 1 | 2 | | 20812 | 22212 | 20112 | 20112 | +| `0x60016000556001600055` | 0 | 1 | 1 | | 20812 | 22212 | 20112 | 20112 | +| `0x60006000556000600055` | 1 | 0 | 0 | | -9188 | -9888 | -11988 | 3012 | +| `0x60006000556001600055` | 1 | 0 | 1 | | 1612 | 2312 | 212 | 3012 | +| `0x60006000556002600055` | 1 | 0 | 2 | | 5812 | 5112 | 3012 | 3012 | +| `0x60026000556000600055` | 1 | 2 | 0 | | -9188 | -9888 | -11988 | 3012 | +| `0x60026000556003600055` | 1 | 2 | 3 | | 5812 | 5112 | 3012 | 3012 | +| `0x60026000556001600055` | 1 | 2 | 1 | | 1612 | 2312 | 212 | 3012 | +| `0x60026000556002600055` | 1 | 2 | 2 | | 5812 | 5112 | 3012 | 3012 | +| `0x60016000556000600055` | 1 | 1 | 0 | | -9188 | -9888 | -11988 | 3012 | +| `0x60016000556002600055` | 1 | 1 | 2 | | 5812 | 5112 | 3012 | 3012 | +| `0x60016000556001600055` | 1 | 1 | 1 | | 1612 | 2312 | 212 | 212 | +| `0x600160005560006000556001600055` | 0 | 1 | 0 | 1 | 21618 | 22318 | 20218 | 40118 | +| `0x600060005560016000556000600055` | 1 | 0 | 1 | 0 | -8382 | -9782 | -11882 | 5918 | + +## Security Considerations + +TBD + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3300.md b/EIPS/eip-3300.md new file mode 100644 index 0000000..d120916 --- /dev/null +++ b/EIPS/eip-3300.md @@ -0,0 +1,78 @@ +--- +eip: 3300 +title: Phase out refunds +author: William Morriss (@wjmelements) +discussions-to: https://ethereum-magicians.org/t/eip-3300-phase-out-refunds/5434 +status: Stagnant +type: Standards Track +category: Core +created: 2020-02-26 +--- + +## Simple Summary +Phases out the `SSTORE` and `SELFDESTRUCT` gas refunds. + +## Abstract +This EIP would define a block when the `SSTORE` and `SELFDESTRUCT` refunds would begin to diminish. +The refund would step linearly downward, eroding the implicit value of such refunds at an accelerating pace. + +## Motivation +Refunds increase block elasticity, so the block gas target can exceed the number established by miners by up to 2x. +This can cause hesitancy for miners to increase the block gas target. + +Refunds, tokenized or not, are valuable to their holders, especially during congestion. +If refunds must be removed, a gradual change in their value would be less-disruptive to the gas market than sudden abolition. +Refund consumption would proceed, especially during periods of congestion, and the refunds would be cleaned up from the state. +Refund creation, driven by demand, would naturally diminish as the efficiency of the refunds fall. +As the refund value approaches the activation cost, the implicit value of the refunds will approach zero, but in periods of congestion they will be cleaned up. + +This change is less work for the protocol developers than compensation and cleanup, while likely still achieving cleanup. + + +## Specification +Parameters: +* `FORK_BLOCK_NUM`: EIP-3300 activation block +* `REFUND_DECAY_STEP`: 1 gas +* `REFUND_DECAY_FREQUENCY`: 100 blocks + +Computed: +* `REFUND_DECAY`: `REFUND_DECAY_STEP * ceil((block.number + 1 - FORK_BLOCK_NUM) / REFUND_DECAY_FREQUENCY)` + + +On the block this EIP activates, and again every `REFUND_DECAY_FREQUENCY` blocks, all gas refunds, including `SELFDESTRUCT` and `SSTORE` would diminish by `REFUND_DECAY_STEP`, until 0. +The current difference is called the `REFUND_DECAY`, which shall be subtracted from each gas refund. + +For gas-cost regimes with refund removals that cancel prior refunds, the invariant that the refund counter cannot go negative will be preserved by diminishing the magnitude of those removals by `REFUND_DECAY`, until 0. + + +### EIP-2929 +The refunds as of EIP-2929 are as follows: + +* 24000 for SELFDESTRUCT +* `SSTORE_RESET_GAS - SLOAD_GAS` (5000 - 100) +* `SSTORE_SET_GAS - SLOAD_GAS` (20000 - 100) +* `SSTORE_SET_GAS - SLOAD_GAS` (20000 - 100) +* `SSTORE_CLEARS_SCHEDULE` (15000) + + +Each of these refunds would be decreased by the current `REFUND_DECAY`. + +There is also a case where `SSTORE_CLEARS_SCHEDULE` is removed from the refund counter. +That removal will also diminish by `REFUND_DECAY_STEP` until 0, maintaining the non-negative refund counter invariant. + + +## Rationale +Persisted refunds would become worthless before they fall below their activation cost. +Once the refunds are worthless, they can be removed by another hard fork without waiting for 0. +The rate of diminishing specified would currently require (24000-5000) * 100 = 1,900,000 blocks for `SELFDESTRUCT` and (15000-5000) * 100 = 1,000,000 blocks for `SSTORE`. +This timeframe is currently about a year, which should be enough flexibility for the remaining refunds to be consumed. + + +## Backwards Compatibility +This proposal breaks gas refunds, which contribute to block elasticity. +The effect of this will be increased gas price volatility: higher highs and lower lows. + +Because the refund counter is separate from the gas counter, the block-to-block gas changes will not break `eth_estimateGas`. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3322.md b/EIPS/eip-3322.md new file mode 100644 index 0000000..e865e06 --- /dev/null +++ b/EIPS/eip-3322.md @@ -0,0 +1,68 @@ +--- +eip: 3322 +title: Account gas storage opcodes +author: William Morriss (@wjmelements) +discussions-to: https://ethereum-magicians.org/t/eip-3322-efficient-gas-storage/5470 +status: Stagnant +type: Standards Track +category: Core +created: 2020-03-04 +--- + +## Simple Summary +Allows contract accounts to store gas that can be transferred to the refund counter. + +## Abstract +Contracts can persist gas for later transfer to the refund counter. +Three opcodes are introduced to read, add to, and use this gas counter. + +## Motivation +The refund mechanism is currently being used by gas tokens to arbitrage gas price. +This brings gas supply elasticity and price stability by moving gas from blocks with less demand to blocks with more demand. +Unfortunately this rewards unnecessary state growth. +By introducing a superior gas storage mechanism, the gas market will require less storage and computation. + +## Specification +Contract accounts gain an unsigned gas refund counter, initially zero. + +Three new opcodes are introduced to manage this state. + +* `SELFGAS` (`0x49`): Pushes the current account's gas refund counter onto the stack. +Shares gas pricing with `SELFBALANCE`. +* `USEGAS` (`0x4a`): Pops `amount` from the stack. +The minimum of `amount` and the current account's gas refund counter is transferred to the execution context's refund counter. +Costs `5000` gas. +* `STOREGAS` (`0x4b`): Pops `amount` from the stack. +Costs `5000 + amount` gas. +Increases the current account's gas refund counter by `amount`. + +## Rationale +By reusing the execution context's refund counter we can reuse its 50% DoS protection, which limits its block elasticity contribution to 2x. + +The gas costs are based on similar opcodes `SELFBALANCE` and `SSTORE`. + +Most accounts will store no gas, so the per-account storage overhead should be minimal or even zero in the normal case. + +The opcode numbers chosen are in the same `0x4X` range as `SELFBALANCE` and `GASLIMIT`. + +## Backwards Compatibility +Because the gas is added to the refund counter, no compatibility issues are anticipated. + +## Test Cases +| Code | Used Gas | Refund | Original | Final | +|------------------|----------|--------|----------|-------| +| 0x60004900 | 5003 | 0 | 0 | 0 | +| 0x60034900 | 5003 | 2 | 2 | 0 | +| 0x60034900 | 5003 | 3 | 3 | 0 | +| 0x60034900 | 5003 | 3 | 4 | 1 | +| 0x60034960034900 | 10006 | 4 | 4 | 0 | +| 0x60034960034900 | 10006 | 6 | 6 | 0 | +| 0x484900 | 5010 | 100000 | 100000 | 0 | +| 0x61ffff4a00 | 70538 | 0 | 0 | 65535 | + + +## Security Considerations +DoS is already limited by the 50% refund limit. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3326.md b/EIPS/eip-3326.md new file mode 100644 index 0000000..20113eb --- /dev/null +++ b/EIPS/eip-3326.md @@ -0,0 +1,130 @@ +--- +eip: 3326 +title: Wallet Switch Ethereum Chain RPC Method (`wallet_switchEthereumChain`) +author: Erik Marks (@rekmarks) +discussions-to: https://ethereum-magicians.org/t/eip-3326-wallet-switchethereumchain +status: Stagnant +type: Standards Track +category: Interface +created: 2021-03-04 +requires: 155, 695 +--- + +## Simple Summary + +An RPC method for switching the wallet's active Ethereum chain. + +## Abstract + +The `wallet_switchEthereumChain` RPC method allows Ethereum applications ("dapps") to request that the wallet switches its active Ethereum chain, if the wallet has a concept thereof. +The caller must specify a chain ID. +The wallet application may arbitrarily refuse or accept the request. +`null` is returned if the active chain was switched, and an error otherwise. + +Important cautions for implementers of this method are included in the [Security Considerations](#security-considerations) section. + +## Motivation + +All dapps require the user to interact with one or more Ethereum chains in order to function. +Some wallets only supports interacting with one chain at a time. +We call this the wallet's "active chain". +`wallet_switchEthereumChain` enables dapps to request that the wallet switches its active chain to whichever one is required by the dapp. +This enables UX improvements for both dapps and wallets. + +## Specification + +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). + +### `wallet_switchEthereumChain` + +The method accepts a single object parameter with a `chainId` field. +The method returns `null` if the wallet switched its active chain, and an error otherwise. + +The method presupposes that the wallet has a concept of a single "active chain". +The active chain is defined as the chain that the wallet is forwarding RPC requests to. + +#### Parameters + +`wallet_switchEthereumChain` accepts a single object parameter, specified by the following TypeScript interface: + +```typescript +interface SwitchEthereumChainParameter { + chainId: string; +} +``` + +If a field does not meet the requirements of this specification, the wallet **MUST** reject the request. + +- `chainId` + - **MUST** specify the integer ID of the chain as a hexadecimal string, per the [`eth_chainId`](./eip-695.md) Ethereum RPC method. + - The chain ID **MUST** be known to the wallet. + - The wallet **MUST** be able to switch to the specified chain and service RPC requests to it. + +#### Returns + +The method **MUST** return `null` if the request was successful, and an error otherwise. + +If the wallet does not have a concept of an active chain, the wallet **MUST** reject the request. + +### Examples + +These examples use JSON-RPC, but the method could be implemented using other RPC protocols. + +To switch to Mainnet: + +```json +{ + "id": 1, + "jsonrpc": "2.0", + "method": "wallet_switchEthereumChain", + "params": [ + { + "chainId": "0x1", + } + ] +} +``` + +To switch to the Goerli test chain: + +```json +{ + "id": 1, + "jsonrpc": "2.0", + "method": "wallet_switchEthereumChain", + "params": [ + { + "chainId": "0x5", + } + ] +} +``` + +## Rationale + +The purpose `wallet_switchEthereumChain` is to provide dapps with a way of requesting to switch the wallet's active chain, which they would otherwise have to ask the user to do manually. + +The method accepts a single object parameter to allow for future extensibility at virtually no cost to implementers and consumers. + +For related work, see [EIP-3085: `wallet_addEthereumChain`](./eip-3085.md) and [EIP-2015: `wallet_updateEthereumChain`](./eip-2015.md). +`wallet_switchEthereumChain` intentionally forgoes the chain metadata parameters included in those EIPs, since it is purely concerned with switching the active chain, regardless of RPC endpoints or any other metadata associated therewith. + +## Security Considerations + +For wallets with a concept of an active chain, switching the active chain has significant implications for pending RPC requests and the user's experience. +If the active chain switches without the user's awareness, a dapp could induce the user to take actions for unintended chains. + +In light of this, the wallet should: + +- Display a confirmation whenever a `wallet_switchEthereumChain` is received, clearly identifying the requester and the chain that will be switched to. + - The confirmation used in [EIP-1102](./eip-1102.md) may serve as a point of reference. +- When switching the active chain, cancel all pending RPC requests and chain-specific user confirmations. + +### Preserving User Privacy + +Automatically rejecting requests for chains that aren't supported or have yet to be added by the wallet allows requesters to infer which chains are supported by the wallet. +Wallet implementers should consider whether this communication channel violates any security properties of the wallet, and if so, take appropriate steps to mitigate it. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3332.md b/EIPS/eip-3332.md new file mode 100644 index 0000000..082ceaa --- /dev/null +++ b/EIPS/eip-3332.md @@ -0,0 +1,97 @@ +--- +eip: 3332 +title: MEDGASPRICE Opcode +author: Justice Hudson (@jchancehud) +discussions-to: https://ethereum-magicians.org/t/medgasprice-opcode-eip/5480 +status: Withdrawn +type: Standards Track +category: Core +created: 2021-03-05 +--- + +## Simple Summary + +An opcode for getting the median gas price of the parent block. + +## Abstract + +Adds `MEDGASPRICE (0x46)` opcode that returns the median gas price for the parent block. + +## Motivation + +With the emergence of rollups as core mechanisms in scaling Ethereum there are a number of common transactions that can be front-run. Optimistic rollups rely on the submission of fraud proofs to maintain the integrity of their systems. As a result actors submitting fraud proofs typically receive a financial reward for doing so. This opens a trivial front-running strategy of watching the mempool for fraud proof submissions and copying such transactions with a much higher gas price to reap the reward. Such front-runners do not perform validation independently and de-incentivize others from performing validation. Adding a mechanism enforcing an upper bound on gas prices for a transaction could be an effective defense against such front-running attacks. + +Consider a smart contract that wants to implement a first come first serve mechanism. Such a mechanism must defeat the inherently pay-to-win nature of the gas price market. Enforcing a maximum gas price for a transaction relies on the fact that transactions of the same gas price are generally processed in a first in first out way by Ethereum miners. A contract currently has few options for setting a max gas price: + +- Set a constant value at a reasonable rate given the current gas prices +- Allow an individual or group of individuals to adjust a max gas price over time + +More elaborate schemes could likely be constructed but all would involve storing gas price information on chain increasing the number of transactions and costing Ether. + +Given a median gas price opcode a contract can set a maximum gas price as a function of the last blocks gas price. This can easily be implemented using a strategy such as the following: + +``` +// Assume that block.medgasprice is bound to MEDGASPRICE (0x46) + +function submitFraudProof(bytes calldata proof) public { + require(tx.gasprice <= maxGasPrice()); + // process the fraud proof and provide a reward (if valid) +} + +function maxGasPrice() public view returns (uint) { + return 3 * block.medgasprice; +} +``` + +Given the contract implementation above a client would simply call `maxGasPrice` to determine the gas price to use when submitting a fraud proof. This particular implementation allows up to 3x the median gas price of the last block to be used. + +### Forwards Compatibility + +[EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) plans to change the fee market in a number of ways. Most notably is the creation of a base fee that is burned. In this context an "inclusion fee" still exists as a part of the total fee. Consider the following two cases: + +#### Block sizes are increasing (all available gas is being consumed) + +In this case there will be bidding contention in the inclusion fee to incentivize miners to include transactions. A median gas price operator would still be helpful as an attacker could supply a high inclusion fee to bump honest transactions. + +#### Block sizes are decreasing (excess gas is available) + +In this case an attacker could specify a high inclusion fee to incentivize miners to include their transaction early in the block. Miners are incentivized to do so as including expensive transactions first reduces the risk of a revert (and partial refund) occurring. + +Given these two cases this EIP seems relevant in the context of EIP-1559. + +Post EIP-1559 `MEDGASPRICE (0x46)` should return the median `effective_gas_price` of the previous block. + +[EIP-3198](https://eips.ethereum.org/EIPS/eip-3198) is required for the above strategies to be implemented. With the inclusion of `BASEFEE (0x48)` a contract can subtract the `base_fee_per_gas` from the `effective_gas_price` to determine the inclusion fee per gas being paid for the transaction and thus implement an upper bound. + +## Specification + +If `block.number >= TBD`, add a new opcode `MEDGASPRICE (0x46)`: + +Pushes the median gas price of the parent block onto the stack. + +| Op | Input | Output | Cost | +|:----: |:-----: |:------: |:----: | +| 0x46 | 0 | 1 | 8 | + +## Rationale + +Having access to the current gas price economy allows contracts to implement more robust and automated logic surrounding acceptable transaction gas prices. + +### Naming note + +The name `MEDGASPRICE` was chosen because the median gas price of the network can only be calculated from the latest complete block. Thus transactions being executed should expect the median gas price to be calculated from the previous block. + +## Backwards Compatibility + +There are no known backwards incompabitility issues. + +## Security Considerations + +The strategy described for preventing front-running by setting an upper bound on the gas price of transactions has a few caveats: + +1. It relies on miners being impartial. Reordering transactions with the same gas price is a trivial means of defeating this strategy. +2. The value returned by `MEDGASPRICE (0x46)` may fluctuate rapidly between blocks. If a transaction is not included immediately it may either fail (if the gas price drops) or become vulnerable to front-running (if the gas price increases). + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3336.md b/EIPS/eip-3336.md new file mode 100644 index 0000000..81015e9 --- /dev/null +++ b/EIPS/eip-3336.md @@ -0,0 +1,71 @@ +--- +eip: 3336 +title: Paged memory allocation for the EVM +author: Nick Johnson (@arachnid) +discussions-to: https://ethereum-magicians.org/t/eips-3336-and-3337-improving-the-evms-memory-model/5482 +status: Stagnant +type: Standards Track +category: Core +created: 2021-03-06 +--- + +## Simple Summary +Changes the memory model for the EVM to use pagination. + +## Abstract +Presently, the EVM charges for memory as a linear array starting at address 0 and extending to the highest address that has been read from or written to. This suffices for simple uses, but means that compilers have to generate programs that use memory compactly, which leads to wasted gas with reallocation of memory elements, and makes some memory models such as separate heap and stack areas impractical. This EIP proposes changing to a page-based billing model, which adds minimal complexity to implementations, while providing for much more versatility in EVM programs. + +## Motivation +Most modern computers implement "virtual memory" for userspace programs, where programs have access to a large address space, with pages of RAM that are allocated as needed by the OS. This allows them to distribute data throughout memory in ways that minimises the amount of reallocation and copying that needs to go on, and permits flexible use of memory for data with different lifetimes. Implementing a simple version of paged memory inside the EVM will provide the same flexibility to compilers targeting the EVM. + +## Specification +### Parameters + +| Constant | Value | +| - | - | +| `FORK_BLOCK` | TBD | +| `PAGE_BITS` | 10 | +| `PAGE_BASE_COST` | 96 | + +For blocks where `block.number >= FORK_BLOCK`, the following changes apply. + +### Changes to memory allocation in EVM implementations +Memory is now allocated in pages of `2**PAGE_BITS` bytes each. The most significant `256 - PAGE_BITS` bits of each memory address denote the page number, while the least significant `PAGE_BITS` bits of the memory address denote the location in the page. Pages are initialized to contain all zero bytes and allocated when the first byte from a page is read or written. + +EVM implementations are encouraged to store the pagetable as an associative array (eg, hashtable or dict) mapping from page number to an array of bytes for the page. + +### Changes to memory expansion gas cost +Presently, the total cost to extend the memory to `a` words long is `Cmem(a) = 3 * a + floor(a ** 2 / 512)`. If the memory is already `b` words long, the incremental cost is `Cmem(a) - Cmem(b)`. `a` is the number of words required to cover the range from memory address 0 to the last word that has been read or written by the EVM. + +Under this EIP, we define a new memory cost function, based on the number of allocated pages. This function is `Cmem'(p) = max(PAGE_BASE_COST * (p - 1) + floor(2 * (p - 1) ** 2), 0)`. As above, if the memory already contains `q` pages, the incremental cost is `Cmem'(p) - Cmem'(q)`. + +### Changes to `MLOAD` and `MSTORE` +Loading a word from memory or storing a word to memory requires instantiating any pages that it touches that do not already exist, with the resulting gas cost as described above. If the word being loaded or stored resides in a single page, the gas cost remains unchanged at 3 gas. If the word being loaded spans two pages, the cost is 6 gas. + +### Changes to other memory-touching opcodes +`CALLDATACOPY`, `CODECOPY`, `EXTCODECOPY`, `CALL`, `CALLCODE`, `DELEGATECALL`, `STATICCALL`, `CREATE`, `MSTORE8` and any other opcodes that read or write memory are modified as follows: + - Any page they touch for reading or writing is instantiated if it is not already. + - Memory expansion gas is charged as described above. + +## Rationale +### Memory expansion gas cost +The new gas cost follows the same curve as the previous one, while ensuring that the new gas cost is always less than or equal to the previous cost. This prevents existing programs that make assumptions about memory allocation gas costs from resulting in errors, without unduly discounting memory below what it costs today. Intuitively, a program that uses up to a page boundary pays for one page less than they would under the old model, while a program that uses one word more than a page boundary pays for one word less than they would under the old model. + +We believe that this incremental reduction will not have a significant impact on the effective gas limit, as it gets proportionally smaller as programs use more RAM. + +### Additional cost for MLOADs and MSTOREs spanning two pages +Loading or storing data spanning two memory pages requires more work from the EVM implementation, which must split the word at the page boundary and update the two (possibly disjoint) pages. Since we cannot guarantee loads and stores in existing EVM programs are page-aligned, we cannot prohibit this behaviour for efficiency. Instead, we propose treating each as two loads or stores for gas accounting purposes. This discourages the use of this functionality, and accounts for the additional execution cost, without prohibiting it outright. + +This will result in additional gas costs for any programs that perform these operations. We believe this to be minimal, and hope to do future analysis to confirm this. + +## Backwards Compatibility +The new function for memory expansion gas cost is designed specifically to avoid backwards compatibility issues by always charging less than or equal to the amount the current EVM would charge. Under some circumstances existing programs will be charged more for MLOADs and MSTOREs that span page boundaries as described above; we believe these changes will affect a minimum of programs and have only a small impact on their gas consumption. + +## Test Cases +TBD + +## Security Considerations +Potential CPU DoS issues arising from additional work done under the new model are alleviated by charging more for non-page-aligned reads and writes. Charges for memory expansion asymptotically approach those currently in force, so this change will not permit programs to allocate substantially more memory than they can today. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3337.md b/EIPS/eip-3337.md new file mode 100644 index 0000000..73ac304 --- /dev/null +++ b/EIPS/eip-3337.md @@ -0,0 +1,99 @@ +--- +eip: 3337 +title: Frame pointer support for memory load and store operations +author: Nick Johnson (@arachnid) +discussions-to: https://ethereum-magicians.org/t/eips-3336-and-3337-improving-the-evms-memory-model/5482 +status: Stagnant +type: Standards Track +category: Core +created: 2021-03-06 +requires: 3336 +--- + +## Simple Summary +Introduces four new opcodes for loading data from and storing data to memory offset by a frame pointer. + +## Abstract +This EIP introduces four new opcodes, `MLOADFP`, `MSTOREFP`, `GETFP` and `SETFP` that allow for more efficient memory access offset by a user-controlled quantity called the "frame pointer". This permits compilers to more efficiently offload ephemeral data such as local variables to memory instead of the EVM's evaluation stack, which has a number of benefits, including the effective elimination of restrictions on the number of local variables in a function. + +## Motivation +In most commonly used VMs, ephemeral data such as local variables, function arguments, and return addresses is stored in a region of memory called the stack. In contrast to the EVM's evaluation stack, this area of memory is randomly accessible, and thus can store an arbitrary amount of data, which can be referenced from anywhere they remain in scope. Although this model is possible in the current EVM design, it is made difficult by the linear model of memory (addressed in [EIP-3336](./eip-3336.md)) and by the lack of opcodes for relative memory access commonly found in other architectures. This EIP proposes new opcodes that permit this form of memory use, without imposing undue burden on EVM implementers or on runtime efficiency. + +In the current EVM model, a compiler wishing to use this pattern would have to store the frame pointer - which points to the start or end of the current memory stack frame - in memory, and load it each time they wish to reference it. For example, loading a value from memory offset by the frame pointer would require the following sequence of operations: + +| Opcode | Gas used | +|-----------|----------| +| `PUSHn x` | 3 | +| `PUSH1 0` | 3 | +| `MLOAD` | 3 | +| `ADD` | 3 | +| `MLOAD` | 3 | + +This consumes a total of 15 gas, and takes up at least 7 bytes of bytecode each time it is referenced. In contrast, after this EIP, the equivalent sequence of operations is: + +| Opcode | Gas used | +|-----------|----------| +| `PUSH1 x` | 3 | +| `MLOADFP` | 3 | + +This consumes only 6 gas, and takes at least 3 bytes of bytecode. The effort required from the EVM implementation is equivalent, costing only one extra addition operation over a regular `MLOAD`. The alternative of storing values on the stack, which requires 3 gas and 1 byte of bytecode for a `DUPn` operation, but it is now at most twice as efficient rather than 5 times as efficient, making storing values in memory a viable alternative. + +Likewise, before this EIP a frame-pointer relative store requires the following sequence of operations: +| Opcode | Gas used | +|-----------|----------| +| `PUSHn x` | 3 | +| `PUSH1 0` | 3 | +| `MLOAD` | 3 | +| `ADD` | 3 | +| `MSTORE` | 3 | + +This consumes 15 gas and at least 7 bytes of bytecode. After this EIP, the equivalent sequence of operations is: + +| Opcode | Gas used | +|-----------|----------| +| `PUSHn x` | 3 | +| `MSTOREFP`| 3 | + +Consuming only 6 gas and at least 3 bytes of bytecode, while once again only requiring EVM implementations to do one extra addition operation. The alternative of storing values on the stack requires 6 gas and 2 bytes of bytecode for the sequence `SWAPn POP`, making it no more efficient than memory storage. + +## Specification +### Parameters + +| Constant | Value | +| - | - | +| `FORK_BLOCK` | TBD | + +For blocks where `block.number >= FORK_BLOCK`, the following changes apply. + +### Frame pointer +A new EVM internal state variable called the "frame pointer" is introduced. This is a signed integer that starts at 0. + +### `SETFP` opcode +A new opcode, `SETFP` is introduced with value `0x5c`. This opcode costs `G_low` (3 gas) and takes one argument from the stack. The argument is stored as the new value of the frame pointer. + +### `GETFP` opcode +A new opcode, `GETFP` is introduced with value `0x5d`. This opcode costs `G_low` (3 gas) and takes no arguments. It takes the current value of the frame pointer and pushes it to the stack. + +### `MLOADFP` opcode +A new opcode `MLOADFP` is introduced with value `0x5e`. This opcode acts in all ways identical to `MLOAD`, except that the value of the frame pointer is added to the address before loading data from memory. An attempt to load data from a negative address should be treated identically to an invalid opcode, consuming all gas and reverting the current execution context. + +### `MSTOREFP` opcode +A new opcode `MSTOREFP` is introduced with value `0x5f`. This opcode acts in all ways identical to `MSTORE`, except that the value of the frame pointer is added to the address before storing data to memory. An attempt to store data to a negative address should be treated identically to an invalid opcode, consuming all gas and reverting the current execution context. + +## Rationale +### Cost of new opcodes +The cost of the new opcodes `MLOADFP` and `MSTOREFP` reflects the cost of `MLOAD` and `MSTORE`. They are generally equivalent in cost with the exception of an extra addition operation, which imposes negligible cost. + +The cost of the new opcodes `SETFP` and `GETFP` is based on other common low-cost opcodes such as `PUSH` and `POP`. + +### Absence of `MSTORE8FP` +`MSTORE8FP` opcode was not included because it is expected that it would be used infrequently, and there is a desire to minimise the size of the instruction set and to conserve opcodes for future use. + +## Backwards Compatibility +This EIP exclusively introduces new opcodes, and as a result should not impact any existing programs unless they operate under the assumption that these opcodes are undefined, which we believe will not be the case. + +## Security Considerations +DoS risks are mitigated by correct pricing of opcodes to reflect current execution costs. No other security considerations pertain to this EIP. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3338.md b/EIPS/eip-3338.md new file mode 100644 index 0000000..0b61fe1 --- /dev/null +++ b/EIPS/eip-3338.md @@ -0,0 +1,55 @@ +--- +eip: 3338 +title: Limit account nonce to 2^52 +author: Micah Zoltu (@MicahZoltu), Alex Beregszaszi (@axic) +discussions-to: https://ethereum-magicians.org/t/eip-2681-limit-account-nonce-to-2-64-1/4324 +status: Withdrawn +withdrawal-reason: Withdrawn in favor of [EIP-2681](./eip-2681.md) +type: Standards Track +category: Core +created: 2021-03-07 +--- + +## Abstract + +Limit account nonce to be between `0` and `2^52`. + +## Motivation + +Account nonces are currently specified to be arbitrarily long unsigned integers. Dealing with arbitrary length data in the state witnesses is not optimal, therefore this EIP will allow proofs to represent the nonce in a more optimized way. + +Additionally it could prove beneficial to transaction formats, where some improvements are potentially sought by at least three other proposals. + +Lastly, this facilitates a minor optimisation in clients, because the nonce no longer needs to be kept as a 256-bit number. + +## Specification + +If `block.number >= FORK_BLOCK` introduce two new restrictions: + +1. Consider any transaction invalid, where the nonce exceeds `2^52`. +2. The `CREATE` instruction to abort with an exceptional halt, where the account nonce is `2^52`. + +## Rationale + +1. It is unlikely for any nonce to reach or exceed the proposed limit. If one would want to reach that limit via external transactions, it would cost at least `21000 * (2^64-1) = 387_381_625_547_900_583_915_000` gas. + +2. It must be noted that in the past, in the Morden testnet, each new account had a starting nonce of `2^20` in order to differentiate transactions from mainnet transactions. +This mode of replay protection is out of fashion since [EIP-155](./eip-155.md) introduced a more elegant way using chain identifiers. + +3. Most clients already consider the nonce field to be 64-bit, such as go-ethereum. + +4. All integer values <= 2^52 can be encoded in a 64-bit floating point without any loss of precision, making this value easy to work with in languages that only have floating point number support. + +## Backwards Compatibility + +While this is a breaking change, no actual effect should be visible: + +1. There is no account in the state currently which would have a nonce exceeding that value. As of November 2020, the account `0xea674fdde714fd979de3edf0f56aa9716b898ec8` is responsible for the highest account nonce at approximately 29 million. + +## Security Considerations + +None. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3368.md b/EIPS/eip-3368.md new file mode 100644 index 0000000..6c186b2 --- /dev/null +++ b/EIPS/eip-3368.md @@ -0,0 +1,50 @@ +--- +eip: 3368 +title: Increase block rewards to 3 ETH, with 2 Year Decay to 1 ETH Scheduled +author: Michael D. Carter (@BitsBeTrippin) +discussions-to: https://ethereum-magicians.org/t/eip-3368-block-reward-increase-w-decay-for-next-two-years/5550 +status: Stagnant +type: Standards Track +category: Core +created: 2021-03-12 +--- + +## Simple Summary +Changes the block reward paid to proof-of-work (POW) miners to 3 ETH from existing 2 ETH and starts a decay schedule for next two years to 1 ETH Block Reward. + +## Abstract +Set the block reward to 3 ETH and then decrease it slightly every block for 4,724,000 blocks (approximately 2 years) until it reaches 1 ETH. + +## Motivation +A sudden drop in PoW mining rewards could result in a sudden precipitous decrease in mining profitability that may drive miners to auction off their hashrate to the highest bidder while they figure out what to do with their now "worthless" hardware. If enough hashrate is auctioned off in this way at the same time, an attacker will be able to rent a large amount of hashing power for a short period of time at relatively low cost vs. reward and potentially attack the network. By setting the block reward to X (where X is enough to offset the sudden profitability decrease) and then decreasing it over time to Y (where Y is a number below the sudden profitability decrease), we both avoid introducing long term inflation while at the same time spreading out the rate that individual miners cross into a transitional range. This approach offers a higher level of confidence and published schedule of yield, while allowing mining participants time to gracefully repurpose/sell their hardware. This greatly increases ethereums PoW security by keeping incentives aligned to ethereum and not being force projected to short term brokerage for the highest bidder. Additionally the decay promotes a known schedule of a deflationary curve, aligning to the overall Minimal Viable Issuance directive aligned to a 2 year transition schedule for Proof of Stake, consensus replacement of Proof of Work. Security is paramount in cryptocurrency blockchains and the risk to a 51% non-resistant chain is real. The scope of Ethereum's current hashrate has expanded to hundreds of thousands of new participants and over 2.5x original ATH hashrate/difficulty. While the largest by hashrate crypto is bitcoin, ethereum is not far behind the total network size in security aspects. This proposal is focused to keep that superiority in security one of the key aspects. + +## Specification +Adjust block, uncle, and nephew rewards +### Constants +* `TRANSITION_START_BLOCK_NUMBER: TBD` +* `TRANSITION_DURATION: 4_724_000` (about two years) +* `TRANSITION_END_BLOCK_NUMBER: FORK_BLOCK_NUMBER + TRANSITION_DURATION` +* `STARTING_REWARD: 3_000_000_000_000_000_000` +* `ENDING_REWARD: 1_000_000_000_000_000_000` +* `REWARD_DELTA: STARTING_REWARD - ENDING_REWARD` +### Block Reward +```py +if block.number >= TRANSITION_END_BLOCK_NUMBER: + block_reward = ENDING_REWARD +elif block.number = TRANSITION_START_BLOCK_NUMBER: + block_reward = STARTING_REWARD +elif block.number > TRANSITION_START_BLOCK_NUMBER: + block_reward = STARTING_REWARD - REWARD_DELTA * TRANSITION_DURATION / (block.number - TRANSITION_START_BLOCK_NUMBER) +``` + +## Rationale +2 years was chosen because it gives miners sufficient time to find alternative uses for their hardware and/or get their hardware back out onto the open market (e.g., in the form of gaming GPUs) without flooding the market suddenly. This proposal should ONLY be considered as a last resort as something we have in our pocket should the "network need to attract hashing power quickly and then bleed it off over time" rather than "something that is scheduled to be included in X hard fork" ; Recommendation to have in a fast track state, but NOT deployed to mainnet unless needed. + +## Backwards Compatibility +There are no known backward compatibility issues with the introduction of this EIP. + +## Security Considerations +There are no known security issues with the introduction of this EIP. + +## Copyright +Copyright and related rights waived via CC0. diff --git a/EIPS/eip-3372.md b/EIPS/eip-3372.md new file mode 100644 index 0000000..9ac605a --- /dev/null +++ b/EIPS/eip-3372.md @@ -0,0 +1,123 @@ +--- +eip: 3372 +title: 5 FNV primes for ethash +status: Stagnant +type: Standards Track +category: Core +author: mineruniter969 (@mineruniter969), mineruniter969 +discussions-to: https://ethereum-magicians.org/t/eip-3372-apply-minor-modifications-to-the-ethash-algorithm-to-break-current-asic-implementations-eip-969-resubmission/5655 +created: 2021-03-13 +--- + +## Simple Summary + +Introduce 5 new FNV primes into the ethash algorithm. + +## Abstract + +This EIP is to kick current ASIC implementations out of the network to keep the Ethereum network secure and healthy by changing the `fnv` constants. + +## Motivation + +ASICs provide a severe centralization risk for the Ethereum network. If we do not get rid of them, small GPU miners will be forced to exit the Ethereum mining because EIP-1559 will make them mining at a loss. Furthermore, ASIC production will be concentrated only at one or two parties, which will make the Ethereum hashrate centralized. Also, it is worth noting that Ethash ASIC machines cost 10k+ USD, while GPUs are priced less than 1000 USD. + +With GPUs, miners can switch to other mining algorithms, but with ASICs, it is impossible. Leaving everything as-is will almost certainly a very tough (from the side of miners) integration of the Ethereum 2.0. + +In short, this EIP is required to keep the Ethereum network stable and decentralized by keeping the ASIC business away. + +## Specification + +If `block.number >= ETHASH11_BLKNUM`, activate the `ethash1.1` algorithm version. + +### ethash1.1 + +Prior to this change, `fnv` hash function is used throughout the `hashimoto` function. `fnv` is identical for all steps, `ethash1.1` will introduce additional `fnvA`, `fnvB`, `fnvC`, `fnvD`, and `fnvE` functions. All those functions will have different FNV constants. + +``` +// Previously used FNV prime +#define FNV_PRIME_0 0x1000193 + +// New FNV primes +#define FNV_PRIME_A 0x10001a7 +#define FNV_PRIME_B 0x10001ab +#define FNV_PRIME_C 0x10001cf +#define FNV_PRIME_D 0x10001e3 +#define FNV_PRIME_E 0x10001f9 +``` + +Prior to this EIP, all parts of Ethash are using the `fnv` (hereinafter referenced as `fnv0`) function. As of the introduction of this EIP: +* `fnvA` replaces `fnv0` in the DAG item selection step +* `fnvB` replaces `fnv0` in the DAG item mix step +* `fnvC(fnvD(fnvE` replaces `fnv0(fnv0(fnv0(` in the compress mix step +* `fnv0` in the DAG generation step should remain unchanged. + +## Rationale + +ASIC Miners have become a threat to the future of Ethereum and a hard fork is required to remove them from the network before additional damage is caused. EIP-3372 proposes the minimum necessary to do so and will not affect ETH stakeholders or the network like Ethash 2.0 would. The threat ASICs pose is legal, social, moral, technical, monetary, and environmental. As we continue to come closer to the merge ASICs will attack the network and the developers personally as they have done in the past because miners will always pursue profits. + +Legally and socially ASIC's have previously been a threat and a nuisance. As Hudson describes Linzhi attacked the EF and developers personally seeking to spread lies and misinformation while sending legal threats during discussions around EIP-1057. His account is [here](https://github.com/Souptacular/linzhi) and in his own words + +> ASIC manufacturer Linzhi has both pressured me and told lies + +With their attacks and harassment on staff demonstrated in the below image: +![](upload://n8TJsS8hEEH4DXYJ2WDzblm5qJo.jpeg) + +Socially and morally the Ethereum community must adopt a no-tolerance policy towards personal attacks on its developers. It is only because of the core developers that Ethereum has value, the community cannot allow large companies free reign to attack them personally and must seek to protect each developer as they are a resource that keeps Ethereum viable. Multiple developers were "burned" during this event. As we accelerate the merge, it is likely that ASIC companies will repeat their actions and again attack the developers personally while pursuing legal options. This is seen not only by their actions during EIP-1057 but recent discussion around EIP-969 where legal threats from them caused the champion of that EIP to dropout and forcing me to submit this EIP anonymously. Ethereum cannot allow its actors to be threatened without consequence and this is a fight that must happen now while they are weak rather than pre-merge where they are stronger which will result in a delayed merge and hurt the value of Ethereum. + +ASICs have the greatest incentives and resources to commit bad acts because they are centralized in farms this is why Vitalik designed ETH to be ASIC-resistant because ASICs had ruined BTC's principles of decentralization. Each day their power and control over the network grows. ASICs are against the founding principles of Ethereum which promotes a decentralized system run by common people, not a single owner of large warehouses. F2Pool which consists largely of ASIC farms has become the #3 largest pool controlling around 10% of hashrate. Their farms can be viewed on their webpage. In November 2020 they were 23TH/s yet today they are 45.6 TH/s. That's a doubling in 4 months and their growth is accelerating as additional ASICs come online. ASICs are becoming a threat that will soon dominate the network and action must be taken now to head them off. + +ASICs on F2Pool have long been known to be "bad actors" on the BTC network. They are known for market manipulation and dumping BTC to manipulate prices (I could not post the source link as this is a new account). What will these ASICs do once they find out that they are about to lose millions prior to the merge? Ethereum is not just a network it is a community and they will use their financial resources and pour millions into delaying the merge as they launch legal case after legal case. They will attack the developers and the community as they seek to squeeze every last dollar. + +The reason Ethereum was founded on the principle of being anti-ASIC is because Vitalik had seen the damage ASICs had caused to the BTC network as they pursued profits rather than the betterment of the network. GPU miners are decentralized and disorganized which makes them a much lower threat than warehouses under one central corporation that is outside the legal system and thus cannot be held to account for bad acts. + +EIP-3372 also works to protect the environment. Post merge, gpus will go into the secondary market or move to other coins. However, ASICs will become junk. As more ASICs are produced, Ethereum increases its environmental footprint. These ASICs are being mass produced in greater numbers despite it being public that the merge is being accelerated. It is clear that these ASIC manufacturers and buyers must either be ignorant of the accelerated merge or plan to delay it. Because they dump their ETH they have no stake in the network except the money they can squeeze from it and if by making trouble they can give themselves another day than they will do it. + +Finally, Ethereum has always sought to pursue "minimum issuance". By reducing the amount of miners that can pose a threat to the network, Ethereum also decreases how much it needs to pay for protection. Some EIP's are being prepared to increase miner incomes post EIP-1559 should a threat appear. EIP-3372 eliminates the need to pay more for security and allows miners to be paid less without compromising the network's security. As we go forward closer to the merge, the community must reduce attack vectors so as to reduce the cost of the merge itself and maximize the security of the network. The community already pays too much for protection and by reducing threats we can reduce this cost. ASIC warehouse farms are dumping all the ETH they make which is suppressing the price of ETH. Although rare, several individual GPU miners are taking part in staking or have gone on to join the community in development or our financial endeavors. They thus are more valuable to the community than a warehouse of future junk. There is no need for the Ethereum community to continue to pay for soon-to-be obsolete hardware that will end up in landfills. + +### Technical Overview + +Ethash 1.1 will replace the only FNV prime with five new primes at the stage of the hash computation. The prime used for the DAG generation is remained unchanged, while all others are be changed. This will not prevent ASIC companies from creating new ASICs that adapt but so close to the merge it is unlikely they will do so, and even if they do they are unlikely to be able to produce enough to again be a threat. +The choice of FNV constants are based on the formal specification that is available on [https://tools.ietf.org/html/draft-eastlake-fnv-14#section-2.1](https://ietf) + +We apologize for the delay in submitting the justification for this EIP. As the community may or may not be aware, the original champion was forced to stop working on this EIP due to legal attacks from ASIC companies. It has taken us this long to review his work and find a new champion. To protect ourselves we are submitting this anonymously and would request the community's aid in our endeavor to maintain our anonymity and some lenience given the circumstances and threats we face pursuing this EIP. + +## Backwards Compatibility + +Mining hardware that is optimized for ethash may no longer work if the `fnv` constants are baked into the hardware and cannot be changed. + +## Test Vectors + +```json +{ + "first": { + "nonce": "4242424242424242", + "mixHash": "aa6a928db9b548ebf20fc9a74e9200321426f1c2db1571636cdd3a33eb162b36", + "header": "f901f3a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a09178d0f23c965d81f0834a4c72c6253ce6830f4022b1359aaebfc1ecba442d4ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000080830f4240808080a058f759ede17a706c93f13030328bcea40c1d1341fb26f2facd21ceb0dae57017884242424242424242", + "seed": "0000000000000000000000000000000000000000000000000000000000000000", + "result": "3972318778d2af9d3c5c3dfc463bc2a5ebeebd1a7a04392708ff94d29aa18c5f", + "cache_size": 16776896, + "full_size": 1073739904, + "header_hash": "2a8de2adf89af77358250bf908bf04ba94a6e8c3ba87775564a41d269a05e4ce", + "cache_hash": "35ded12eecf2ce2e8da2e15c06d463aae9b84cb2530a00b932e4bbc484cde353" + }, + "second": { + "nonce": "307692cf71b12f6d", + "mixHash": "4a2ef8287dc21f5def0d4e9694208c56e574b1692d7b254822a3f4704d8ad1ba", + "header": "f901f7a01bef91439a3e070a6586851c11e6fd79bbbea074b2b836727b8e75c7d4a6b698a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794ea3cb5f94fa2ddd52ec6dd6eb75cf824f4058ca1a00c6e51346be0670ce63ac5f05324e27d20b180146269c5aab844d09a2b108c64a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302004002832fefd880845511ed2a80a0e55d02c555a7969361cf74a9ec6211d8c14e4517930a00442f171bdb1698d17588307692cf71b12f6d", + "seed": "0000000000000000000000000000000000000000000000000000000000000000", + "result": "5ab98957ba5520d4e367080f442e37a047cfd9d2857b6e00dd12d82900d108a6", + "cache_size": 16776896, + "full_size": 1073739904, + "header_hash": "100cbec5e5ef82991290d0d93d758f19082e71f234cf479192a8b94df6da6bfe", + "cache_hash": "35ded12eecf2ce2e8da2e15c06d463aae9b84cb2530a00b932e4bbc484cde353" + } +} +``` + +## Security Considerations + +There are no known security issues with this change. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3374.md b/EIPS/eip-3374.md new file mode 100644 index 0000000..d77d8cf --- /dev/null +++ b/EIPS/eip-3374.md @@ -0,0 +1,58 @@ +--- +eip: 3374 +title: Predictable Proof-of-Work (POW) Sunsetting +author: Query0x (@Query0x) +discussions-to: https://ethereum-magicians.org/t/eip-3374-predictable-proof-of-work-sunsetting +status: Withdrawn +type: Standards Track +category: Core +created: 2021-03-13 +--- + +## Simple Summary +Sets block reward to 3 and reduces it to 1 linearly over the course of about 1 year. + +## Abstract +Sets the block reward to 3 ETH and then incrementally decreases it every block for 2,362,000 blocks (approximately 1 year) until it reaches 1 ETH. + +## Motivation +Unnecessarily abrupt changes to the Ethereum ecosystem cause disruption and disharmony resulting in the disenfranchisement of community members while undermining stability and confidence. While moves from Proof-of-Work to Proof-of-Stake will undoubtedly cause friction between those community members vested in either, all benefit from a measured, predictable transition. + +This proposal: + +1) Is issuance neutral over 1 year, and reduces issuance beyond that. +2) Sets an initial block reward of 3; +3) Introduces an ongoing, predictable reduction in future mining rewards down to 1, effectively "sunsetting" POW and codifying the move to POS; +4) Reduces economic incentives for continued development of ASICs; +5) Allows the impacts of decreasing miner rewards to be measured and monitored rather than relying on conjecture and game theory, so adjustments can be made if necessary. + + +## Specification +### Constants +* `TRANSITION_START_BLOCK_NUMBER: TBD` +* `TRANSITION_DURATION: 2_362_000` // (about one year) +* `TRANSITION_END_BLOCK_NUMBER: FORK_BLOCK_NUMBER + TRANSITION_DURATION` +* `STARTING_REWARD: 3_000_000_000_000_000_000` +* `ENDING_REWARD: 1_000_000_000_000_000_000` +* `REWARD_DELTA: STARTING_REWARD - ENDING_REWARD` +### Block Reward +```py +if block.number >= TRANSITION_END_BLOCK_NUMBER: + block_reward = ENDING_REWARD +elif block.number == TRANSITION_START_BLOCK_NUMBER: + block_reward = STARTING_REWARD +elif block.number > TRANSITION_START_BLOCK_NUMBER: + block_reward = STARTING_REWARD - REWARD_DELTA * TRANSITION_DURATION / (block.number - TRANSITION_START_BLOCK_NUMBER) +``` + +## Rationale +Picking starting and ending block reward values that are equidistant from the current block reward rate of 2 ensures the impact of this EIP will be issuance neutral over the one year time frame. Temporarily raising the block reward to 3 blunts the initial impact of a sudden miner revenue decrease and the continual reductions thereafter codify Ethereum's move to POS by increasingly disincentivizing POW. Importantly, this approach moderates the rate of change so impacts and threats can be measured and monitored. + +## Backwards Compatibility +There are no known backward compatibility issues with the introduction of this EIP. + +## Security Considerations +There are no known security issues with the introduction of this EIP. + +## Copyright +Copyright and related rights waived via CC0. diff --git a/EIPS/eip-3382.md b/EIPS/eip-3382.md new file mode 100644 index 0000000..06788cb --- /dev/null +++ b/EIPS/eip-3382.md @@ -0,0 +1,51 @@ +--- +eip: 3382 +title: Hardcoded Block Gas Limit +author: Philippe Castonguay (@PhABC) +discussions-to: https://ethereum-magicians.org/t/eip-3382-hardcoded-gas-limit +status: Withdrawn +withdrawal-reason: The author prefers [EIP-3756](./eip-3756.md) +type: Standards Track +category: Core +created: 2021-03-13 +--- + +## Simple Summary + +Hardcode the block gas limit to `12,500,000` gas per block. + +## Abstract + +Updates the block validation rules such that a block is invalid if the `gas_limit` header field is not equal to `12,500,000`. + +## Motivation + +Both Ethereum's Proof of Work and Proof of Stake designs assume that block producers are financially rational, but does not assume block producers to be benevolent. There is one exception however, and it is when block producers choose the gas limit of a block where it is assumed that block producers care about the long term health and decentralisation of the chain. Indeed, the block gas limit is one of the only parameters in Ethereum that is not dictated by node consensus, but instead is chosen by block producers. This decision was initially made to allow urgent changes in the block gas limit if necessary. Both drastically increasing or decreasing this parameter could have serious consequences that may not be desired. It is therefore a critical parameter that should require node consensus to avoid any sudden harmful change imposed by a small number of actors on the rest of the network. + +## Specification +Refer to `gasLimit` as `gasTarget` post EIP-1559. + +### Added Consensus Constraint + +As of `FORK_BLOCK_NUMBER`, the `header.gasLimit` **MUST** be equal to `BLOCK_GAS_LIMIT`, where `BLOCK_GAS_LIMIT` is a hardcoded constant set to `12,500,000`. + +## Rationale + +### Keeping gasLimit in Block Headers + +While it would be possible to remove the `gasLimit` field from block headers, it would change the data structure to be hashed, which could lead to unintended consequences. It is therefore easier to leave the gasLimit as part of block headers. + +### Chosen Gas Limit + +The `12,500,000` value is being proposed as it's the current block gas limit as of time of writing this EIP. The actual amount could be altered with a subsequent EIP to avoid deviating from the core intent of this EIP. + +## Backwards Compatibility + +This EIP is backward compatible. + +## Security Considerations +Rapid changes to the gas limit will likely be more difficult to execute, which could be problematic if an urgent situation arise that required changing the gas limit. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3386.md b/EIPS/eip-3386.md new file mode 100644 index 0000000..81558c2 --- /dev/null +++ b/EIPS/eip-3386.md @@ -0,0 +1,274 @@ +--- +eip: 3386 +title: ERC-721 and ERC-1155 to ERC-20 Wrapper +author: Calvin Koder (@ashrowz) +discussions-to: https://github.com/ethereum/EIPs/issues/3384 +status: Stagnant +type: Standards Track +category: ERC +created: 2021-03-12 +requires: 165 +--- + +## Simple Summary +A standard interface for contracts that create generic ERC-20 tokens which derive from a pool of unique ERC-721/ERC-1155 tokens. + +## Abstract + +This standard outlines a smart contract interface to wrap identifiable tokens with fungible tokens. This allows for derivative [ERC-20](./eip-20.md) tokens to be minted by locking the base [ERC-721](./eip-721.md) non-fungible tokens and [ERC-1155](./eip-1155.md) multi tokens into a pool. The derivative tokens can be burned to redeem base tokens out of the pool. These derivatives have no reference to the unique id of these base tokens, and should have a proportional rate of exchange with the base tokens. As representatives of the base tokens, these generic derivative tokens can be traded and otherwise utilized according to ERC-20, such that the unique identifier of each base token is irrelevant. + +ERC-721 and ERC-1155 tokens are considered valid base, tokens because they have unique identifiers and are transferred according to similar rules. This allows for both ERC-721 NFTs and ERC-1155 Multi-Tokens to be wrapped under a single common interface. + +## Motivation + +The ERC-20 token standard is the most widespread and liquid token standard on Ethereum. ERC-721 and ERC-1155 tokens on the other hand can only be transferred by their individual ids, in whole amounts. Derivative tokens allow for exposure to the base asset while benefiting from contracts which utilize ERC-20 tokens. This allows for the base tokens to be fractionalized, traded and pooled generically on AMMs, collateralized, and be used for any other ERC-20 type contract. Several implementations of this proposal already exist without a common standard. + +Given a fixed exchange rate between base and derivative tokens, the value of the derivative token is proportional to the floor price of the pooled tokens. With the derivative tokens being used in AMMs, there is opportunity for arbitrage between derived token markets and the base NFT markets. By specifying a subset of base tokens which may be pooled, the difference between the lowest and highest value token in the pool may be minimized. This allows for higher value tokens within a larger set to be poolable. Additionally, price calculations using methods such as Dutch auctions, as implemented by NFT20, allow for price discovery of subclasses of base tokens. This allows the provider of a higher value base token to receive a proportionally larger number of derivative tokens than a token worth the floor price would receive. + +## Specification + +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). + +**Every IWrapper compliant contract must implement the `IWrapper` and `ERC165` interfaces** : + + +```solidity +pragma solidity ^0.8.0; + +/** + @title IWrapper Identifiable Token Wrapper Standard + @dev {Wrapper} refers to any contract implementing this interface. + @dev {Base} refers to any ERC-721 or ERC-1155 contract. It MAY be the {Wrapper}. + @dev {Pool} refers to the contract which holds the {Base} tokens. It MAY be the {Wrapper}. + @dev {Derivative} refers to the ERC-20 contract which is minted/burned by the {Wrapper}. It MAY be the {Wrapper}. + @dev All uses of "single", "batch" refer to the number of token ids. This includes individual ERC-721 tokens by id, and multiple ERC-1155 by id. An ERC-1155 `TransferSingle` event may emit with a `value` greater than `1`, but it is still considered a single token. + @dev All parameters named `_amount`, `_amounts` refer to the `value` parameters in ERC-1155. When using this interface with ERC-721, `_amount` MUST be 1, and `_amounts` MUST be either an empty list or a list of 1 with the same length as `_ids`. +*/ +interface IWrapper /* is ERC165 */ { + /** + * @dev MUST emit when a mint occurs where a single {Base} token is received by the {Pool}. + * The `_from` argument MUST be the address of the account that sent the {Base} token. + * The `_to` argument MUST be the address of the account that received the {Derivative} token(s). + * The `_id` argument MUST be the id of the {Base} token transferred. + * The `_amount` argument MUST be the number of {Base} tokens transferred. + * The `_value` argument MUST be the number of {Derivative} tokens minted. + */ + event MintSingle (address indexed _from, address indexed _to, uint256 _id, uint256 _amount, uint256 _value); + + /** + * @dev MUST emit when a mint occurs where multiple {Base} tokens are received by the {Wrapper}. + * The `_from` argument MUST be the address of the account that sent the {Base} tokens. + * The `_to` argument MUST be the address of the account that received the {Derivative} token(s). + * The `_ids` argument MUST be the list ids of the {Base} tokens transferred. + * The `_amounts` argument MUST be the list of the numbers of {Base} tokens transferred. + * The `_value` argument MUST be the number of {Derivative} tokens minted. + */ + event MintBatch (address indexed _from, address indexed _to, uint256[] _ids, uint256[] _amounts, uint256 _value); + + /** + * @dev MUST emit when a burn occurs where a single {Base} token is sent by the {Wrapper}. + * The `_from` argument MUST be the address of the account that sent the {Derivative} token(s). + * The `_to` argument MUST be the address of the account that received the {Base} token. + * The `_id` argument MUST be the id of the {Base} token transferred. + * The `_amount` argument MUST be the number of {Base} tokens transferred. + * The `_value` argument MUST be the number of {Derivative} tokens burned. + */ + event BurnSingle (address indexed _from, address indexed _to, uint256 _id, uint256 _amount, uint256 _value); + + /** + * @dev MUST emit when a mint occurs where multiple {Base} tokens are sent by the {Wrapper}. + * The `_from` argument MUST be the address of the account that sent the {Derivative} token(s). + * The `_to` argument MUST be the address of the account that received the {Base} tokens. + * The `_ids` argument MUST be the list of ids of the {Base} tokens transferred. + * The `_amounts` argument MUST be the list of the numbers of {Base} tokens transferred. + * The `_value` argument MUST be the number of {Derivative} tokens burned. + */ + event BurnBatch (address indexed _from, address indexed _to, uint256[] _ids, uint256[] _amounts, uint256 _value); + + /** + * @notice Transfers the {Base} token with `_id` from `msg.sender` to the {Pool} and mints {Derivative} token(s) to `_to`. + * @param _to Target address. + * @param _id Id of the {Base} token. + * @param _amount Amount of the {Base} token. + * + * Emits a {MintSingle} event. + */ + function mint( + address _to, + uint256 _id, + uint256 _amount + ) external; + + /** + * @notice Transfers `_amounts[i]` of the {Base} tokens with `_ids[i]` from `msg.sender` to the {Pool} and mints {Derivative} token(s) to `_to`. + * @param _to Target address. + * @param _ids Ids of the {Base} tokens. + * @param _amounts Amounts of the {Base} tokens. + * + * Emits a {MintBatch} event. + */ + function batchMint( + address _to, + uint256[] calldata _ids, + uint256[] calldata _amounts + ) external; + + /** + * @notice Burns {Derivative} token(s) from `_from` and transfers `_amounts` of some {Base} token from the {Pool} to `_to`. No guarantees are made as to what token is withdrawn. + * @param _from Source address. + * @param _to Target address. + * @param _amount Amount of the {Base} tokens. + * + * Emits either a {BurnSingle} or {BurnBatch} event. + */ + function burn( + address _from, + address _to, + uint256 _amount + ) external; + + /** + * @notice Burns {Derivative} token(s) from `_from` and transfers `_amounts` of some {Base} tokens from the {Pool} to `_to`. No guarantees are made as to what tokens are withdrawn. + * @param _from Source address. + * @param _to Target address. + * @param _amounts Amounts of the {Base} tokens. + * + * Emits either a {BurnSingle} or {BurnBatch} event. + */ + function batchBurn( + address _from, + address _to, + uint256[] calldata _amounts + ) external; + + /** + * @notice Burns {Derivative} token(s) from `_from` and transfers `_amounts[i]` of the {Base} tokens with `_ids[i]` from the {Pool} to `_to`. + * @param _from Source address. + * @param _to Target address. + * @param _id Id of the {Base} token. + * @param _amount Amount of the {Base} token. + * + * Emits either a {BurnSingle} or {BurnBatch} event. + */ + function idBurn( + address _from, + address _to, + uint256 _id, + uint256 _amount + ) external; + + /** + * @notice Burns {Derivative} tokens from `_from` and transfers `_amounts[i]` of the {Base} tokens with `_ids[i]` from the {Pool} to `_to`. + * @param _from Source address. + * @param _to Target address. + * @param _ids Ids of the {Base} tokens. + * @param _amounts Amounts of the {Base} tokens. + * + * Emits either a {BurnSingle} or {BurnBatch} event. + */ + function batchIdBurn( + address _from, + address _to, + uint256[] calldata _ids, + uint256[] calldata _amounts + ) external; +} +``` + +## Rationale + +### Naming + +The ERC-721/ERC-1155 tokens which are pooled are called {Base} tokens. Alternative names include: +- Underlying. +- NFT. However, ERC-1155 tokens may be considered "semi-fungible". + +The ERC-20 tokens which are minted/burned are called {Derivative} tokens. Alternative names include: +- Wrapped. +- Generic. + +The function names `mint` and `burn` are borrowed from the minting and burning extensions to ERC-20. Alternative names include: +- `mint`/`redeem` ([NFTX](https://nftx.org)) +- `deposit`/`withdraw` ([WrappedKitties](https://wrappedkitties.com/)) +- `wrap`/`unwrap` ([MoonCatsWrapped](https://etherscan.io/address/0x7c40c393dc0f283f318791d746d894ddd3693572)) + +The function names `*idBurn` are chosen to reduce confusion on what is being burned. That is, the {Derivative} tokens are burned in order to redeem the id(s). + +The wrapper/pool itself can be called an "Index fund" according to NFTX, or a "DEX" according to [NFT20](https://nft20.io). However, the {NFT20Pair} contract allows for direct NFT-NFT swaps which are out of the scope of this standard. + +### Minting +Minting requires the transfer of the {Base} tokens into the {Pool} in exchange for {Derivative} tokens. The {Base} tokens deposited in this way MUST NOT be transferred again except through the burning functions. This ensures the value of the {Derivative} tokens is representative of the value of the {Base} tokens. + +Alternatively to transferring the {Base} tokens into the {Pool}, the tokens may be locked as collateral in exchange for {Derivative} loans, as proposed in NFTX litepaper, similarly to Maker vaults. This still follows the general minting pattern of removing transferability of the {Base} tokens in exchange for {Derivative} tokens. + +### Burning +Burning requires the transfer of {Base} tokens out of the {Pool} in exchange for burning {Derivative} tokens. The burn functions are distinguished by the quantity and quality of {Base} tokens redeemed. +- For burning without specifying the `id`: `burn`, `batchBurn`. +- For burning with specifying the `id`(s): `idBurn`, `batchIdBurn`. + +By allowing for specific ids to be targeted, higher value {Base} tokens may be selected out of the pool. NFTX proposes an additional fee to be applied for such targeted withdrawals, to offset the desire to drain the {Pool} of {Base} tokens worth more than the floor price. + +### Pricing +Prices should not be necessarily fixed. therefore, Mint/Burn events MUST include the ERC-20 `_value` minted/burned. + +Existing pricing implementations are as follows (measured in base:derivative): +- Equal: Every {Base} costs 1 {Derivative} + - NFTX + - Wrapped Kitties +- Proportional + - NFT20 sets a fixed rate of 100 {Base} tokens per {Derivative} token. +- Variable + - NFT20 also allows for Dutch auctions when minting. + - NFTX proposes an additional fee to be paid when targeting the id of the {Base} token. + +Due to the variety of pricing implementations, the Mint\* and Burn\* events MUST include the number {Derivative} tokens minted/burned. + +### Inheritance +#### ERC-20 +The {Wrapper} MAY inherit from {ERC20}, in order to directly call `super.mint` and `super.burn`. +If the {Wrapper} does not inherit from {ERC20}, the {Derivative} contract MUST be limited such that the {Wrapper} has the sole power to `mint`, `burn`, and otherwise change the supply of tokens. + +#### ERC721Receiver, ERC1155Receiver +If not inheriting from {ERC721Receiver} and/or {ERC1155Receiver}, the pool MUST be limited such that the base tokens can only be transferred via the Wrapper's `mint`, `burn`. + +There exists only one of each ERC-721 token of with a given (address, id) pair. However, ERC-1155 tokens of a given (address, id) may have quantities greater than 1. Accordingly, the meaning of "Single" and "Batch" in each standard varies. In both standards, "single" refers to a single id, and "batch" refers to multiple ids. In ERC-1155, a single id event/function may involve multiple tokens, according to the `value` field. + +In building a common set of events and functions, we must be aware of these differences in implementation. The current implementation treats ERC-721 tokens as a special case where, in reference to the quantity of each {Base} token: +- All parameters named `_amount`, MUST be `1`. +- All parameters named `_amounts` MUST be either an empty list or a list of `1` with the same length as `_ids`. + +This keeps a consistent enumeration of tokens along with ERC-1155. Alternative implementations include: +- A common interface with specialized functions. EX: `mintFromERC721`. +- Separate interfaces for each type. EX: `ERC721Wrapper`, `ERC1155Wrapper`. + +#### ERC721, ERC1155 +The {Wrapper} MAY inherit from {ERC721} and/or {ERC1155} in order to call `super.mint`, directly. This is optional as minting {Base} tokens is not required in this standard. An "Initial NFT Offering" could use this to create a set of {Base} tokens within the contract, and directly distribute {Derivative} tokens. + +If the {Wrapper} does not inherit from {ERC721} or {ERC1155}, it MUST include calls to {IERC721} and {IERC1155} in order to transfer {Base} tokens. + +### Approval +All of the underlying transfer methods are not tied to the {Wrapper}, but rather call the ERC-20/721/1155 transfer methods. Implementations of this standard MUST: +- Either implement {Derivative} transfer approval for burning, and {Base} transfer approval for minting. +- Or check for Approval outside of the {Wrapper} through {IERC721} / {IERC1155} before attempting to execute. + +## Backwards Compatibility +Most existing implementations inherit from ERC-20, using functions `mint` and `burn`. +Events: +- Mint + - WK: DepositKittyAndMintToken + - NFTX: Mint + +- Burn + - WK: BurnTokenAndWithdrawKity + - NFTX: Redeem + +## Reference Implementation +[ERC-3386 Reference Implementation](https://github.com/ashrowz/erc-3386) + +## Security Considerations +Wrapper contracts are RECOMMENDED to inherit from burnable ERC-20 tokens. If they are not, the supply of the {Derivative} tokens MUST be controlled by the Wrapper. Similarly, price implementations MUST ensure that the supply of {Base} tokens is reflected by the {Derivative} tokens. + +With the functions `idBurn`, `idBurns`, users may target the most valuable NFT within the generic lot. If there is a significant difference between tokens values of different ids, the contract SHOULD consider creating specialized pools (NFTX) or pricing (NFT20) to account for this. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3403.md b/EIPS/eip-3403.md new file mode 100644 index 0000000..bee317c --- /dev/null +++ b/EIPS/eip-3403.md @@ -0,0 +1,134 @@ +--- +eip: 3403 +title: Partial removal of refunds +author: Vitalik Buterin (@vbuterin), Martin Swende (@holiman) +discussions-to: https://ethereum-magicians.org/t/eip-3298-removal-of-refunds/5430 +status: Stagnant +type: Standards Track +category: Core +created: 2021-03-16 +--- + +## Simple Summary + +Remove gas refunds for SELFDESTRUCT, and restrict gas refunds for SSTORE to one specific case. + +## Motivation + +Gas refunds for SSTORE and SELFDESTRUCT were originally introduced to motivate application developers to write applications that practice "good state hygiene", clearing storage slots and contracts that are no longer needed. However, they are not widely used for this, and poor state hygiene continues to be the norm. It is now widely accepted that the only solution to state growth is some form of statelessness or state expiry, and if such a solution is implemented, then disused storage slots and contracts would start to be ignored automatically. + +Gas refunds additionally have multiple harmful consequences: + +* Refunds give rise to GasToken. GasToken has benefits in moving gas space from low-fee periods to high-fee periods, but it also has downsides to the network, particularly in exacerbating state size (as state slots are effectively used as a "battery" to save up gas) and inefficiently clogging blockchain gas usage +* Refunds increase block size variance. The theoretical maximum amount of actual gas consumed in a block is nearly twice the on-paper gas limit (as refunds add gas space for subsequent transactions in a block, though refunds are capped at 50% of a transaction's gas used). This is not fatal, but is still undesirable, especially given that refunds can be used to maintain 2x usage spikes for far longer than EIP 1559 can. + +### The mutex usecase + +There are two typical ways to implement mutexes: '0-1-0' and '1-2-1. Let's see how they differ + +- '0-1-0': + - Istanbul: 1612 + - Berlin: 212 + - NoRefund: 20112 + - EIP-3403: 1112 +- '1-2-1': + - Istanbul: 1612 + - Berlin: 212 + - NoRefund: 3012 + - EIP-3403: 3012 + + +**Note**: In reality, there are never a negative gas cost, since the refund is capped at 0.5 * gasUsed. +However, these tables show the negative values, since a more real-world scenario would likely spend the +extra gas on other operations.' + +## Specification + +### Parameters + +| Constant | Value | +| - | - | +| `FORK_BLOCK` | TBD | +| `SSTORE_REFUND_GAS` | 19000 | + +For blocks where `block.number >= FORK_BLOCK`, the following changes apply. + +1. Remove the `SELFDESTRUCT` refund. +2. Remove the `SSTORE` refund in all cases except for one specific case: if the _new value_ and _original value_ of the storage slot both equal 0 but the _current value_ does not (those terms being defined as in [EIP-1283](https://eips.ethereum.org/EIPS/eip-1283)), refund `SSTORE_REFUND_GAS` gas. + +## Rationale + +Preserving refunds in the `new = original = 0 != current` case ensures that a few key use cases that deserve favorable gas cost treatment continue to receive favorable gas cost treatment, particularly: + +* Anti-reentrancy locks (typically flipped from 0 to 1 right before a child call begins, and then flipped back to 0 when the child call ends) +* ERC20 approve-and-send (the "approved value" goes from zero to nonzero when the token transfer is approved, and then back to zero when the token transfer processes) + +It also preserves two key goals of EIP 3298: + +1. Gas tokens continue to be non-viable, because each 19000 refund is only possible because of 19000 extra gas that was paid for flipping that storage slot from zero to nonzero earlier in the same transaction, so you can't clear some storage slots and use that saved gas to fill others. +2. The total amount of gas _spent on execution_ is capped at the gas limit. Every 19000 refund for flipping a storage slot non from zero -> zero is only possible because of 19000 extra gas paid for flipping that slot from zero -> nonzero earlier in the same transaction; that gas paid for a storage write and expansion that were both reverted and so do not actually need to be applied to the Merkle tree. Hence, this extra gas does not contribute to risk. + +## Backwards Compatibility + +Refunds are currently only applied _after_ transaction execution, so they cannot affect how much gas is available to any particular call frame during execution. Hence, removing them will not break the ability of any code to execute, though it will render some applications economically nonviable. + +Gas tokens in particular will become valueless. DeFi arbitrage bots, which today frequently use either established gas token schemes or a custom alternative to reduce on-chain costs, would benefit from rewriting their code to remove calls to these no-longer-functional gas storage mechanisms. + +## Test Cases + +### 2929 Gas Costs + +Note, there is a difference between 'hot' and 'cold' slots. This table shows the values as of [EIP-2929](./eip-2929.md) assuming that all touched storage slots were already 'hot' (the difference being a one-time cost of `2100` gas). + +| Code | Used Gas | Refund | Original | 1st | 2nd | 3rd | Effective gas (after refund) +| -- | -- | -- | -- | -- | -- | -- | -- | +| `0x60006000556000600055` | 212 | 0| 0 | 0 | 0 | | 212 | +| `0x60006000556001600055` | 20112 | 0| 0 | 0 | 1 | | 20112 | +| `0x60016000556000600055` | 20112 | 19900| 0 | 1 | 0 | | 212 | +| `0x60016000556002600055` | 20112 | 0| 0 | 1 | 2 | | 20112 | +| `0x60016000556001600055` | 20112 | 0| 0 | 1 | 1 | | 20112 | +| `0x60006000556000600055` | 3012 | 15000| 1 | 0 | 0 | | -11988 | +| `0x60006000556001600055` | 3012 | 2800| 1 | 0 | 1 | | 212 | +| `0x60006000556002600055` | 3012 | 0| 1 | 0 | 2 | | 3012 | +| `0x60026000556000600055` | 3012 | 15000| 1 | 2 | 0 | | -11988 | +| `0x60026000556003600055` | 3012 | 0| 1 | 2 | 3 | | 3012 | +| `0x60026000556001600055` | 3012 | 2800| 1 | 2 | 1 | | 212 | +| `0x60026000556002600055` | 3012 | 0| 1 | 2 | 2 | | 3012 | +| `0x60016000556000600055` | 3012 | 15000| 1 | 1 | 0 | | -11988 | +| `0x60016000556002600055` | 3012 | 0| 1 | 1 | 2 | | 3012 | +| `0x60016000556001600055` | 212 | 0| 1 | 1 | 1 | | 212 | +| `0x600160005560006000556001600055` | 40118 | 19900| 0 | 1 | 0 | 1 | 20218 | +| `0x600060005560016000556000600055` | 5918 | 17800| 1 | 0 | 1 | 0 | -11882 | + +### With EIP-3403 partial refunds + +If refunds were to be partially removed, as specified [here](https://github.com/ethereum/EIPs/pull/3403/), this would be the comparative table. **This table also assumes touched storage slots were already 'hot'**. + +| Code | Used Gas | Refund | Original | 1st | 2nd | 3rd | Effective gas (after refund) +| -- | -- | -- | -- | -- | -- | -- | -- | +| `0x60006000556000600055` | 212 | 0| 0 | 0 | 0 | | 212 | +| `0x60006000556001600055` | 20112 | 0| 0 | 0 | 1 | | 20112 | +| `0x60016000556000600055` | 20112 | 19000| 0 | 1 | 0 | | 1112 | +| `0x60016000556002600055` | 20112 | 0| 0 | 1 | 2 | | 20112 | +| `0x60016000556001600055` | 20112 | 0| 0 | 1 | 1 | | 20112 | +| `0x60006000556000600055` | 3012 | 0| 1 | 0 | 0 | | 3012 | +| `0x60006000556001600055` | 3012 | 0| 1 | 0 | 1 | | 3012 | +| `0x60006000556002600055` | 3012 | 0| 1 | 0 | 2 | | 3012 | +| `0x60026000556000600055` | 3012 | 0| 1 | 2 | 0 | | 3012 | +| `0x60026000556003600055` | 3012 | 0| 1 | 2 | 3 | | 3012 | +| `0x60026000556001600055` | 3012 | 0| 1 | 2 | 1 | | 3012 | +| `0x60026000556002600055` | 3012 | 0| 1 | 2 | 2 | | 3012 | +| `0x60016000556000600055` | 3012 | 0| 1 | 1 | 0 | | 3012 | +| `0x60016000556002600055` | 3012 | 0| 1 | 1 | 2 | | 3012 | +| `0x60016000556001600055` | 212 | 0| 1 | 1 | 1 | | 212 | +| `0x600160005560006000556001600055` | 40118 | 19000| 0 | 1 | 0 | 1 | 21118 | +| `0x600060005560016000556000600055` | 5918 | 0| 1 | 0 | 1 | 0 | 5918 | + +## Security Considerations + +Refunds are not visible to transaction execution, so this should not have any impact on transaction execution logic. + +The maximum amount of gas that can be spent on execution in a block is limited to the gas limit, if we do not count zero-to-nonzero SSTOREs that were later reset back to zero. It is okay to not count those, because if such an SSTORE is reset, storage is not expanded and the client does not need to actually adjust the Merke tree; the gas consumption is refunded, but the effort normally required by the client to process those opcodes is also cancelled. **Clients should make sure to not do a storage write if `new_value = original_value`; this was a prudent optimization since the beginning of Ethereum but it becomes more important now.** + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3416.md b/EIPS/eip-3416.md new file mode 100644 index 0000000..d84650c --- /dev/null +++ b/EIPS/eip-3416.md @@ -0,0 +1,104 @@ +--- +eip: 3416 +title: Median Gas Premium +author: HexZorro (@hexzorro), Mojtaba Tefagh (@mtefagh) +discussions-to: https://ethereum-magicians.org/t/eip-3416-median-gas-premium/5755 +status: Stagnant +type: Standards Track +category: Core +created: 2021-03-18 +--- + +## Simple Summary + +A transaction pricing mechanism with a fixed-per-block network fee and a median inclusion fee with additive updates. + +## Abstract + +There is a base fee per gas in protocol, which can move up or down by a maximum of 1/8 in each block. The base fee per gas is adjusted by the protocol to target an average gas usage per block instead of an absolute gas usage per block. The base fee is increased when blocks are over the gas limit target and decreases when blocks are under the gas limit target. Transaction senders specify their fees by providing *only one value*: + +* The fee cap which represents the maximum total (base fee + gas premium) that the transaction sender would be willing to pay to get their transaction included, resembles the current maximum gas price specified by senders but in this protocol change proposal the final gas price paid, most of the time, will be lower than the proposed by the transaction sender. + +Then there is a gas premium that is directly computed as 50% of (fee cap - base fee). This gas premium gets added onto the base fee to calculate the gas price that will be used in the weighted median computation. The gas premium, determined directly by a specified fee cap, can either be set to a fairly low value to compensate miners for uncle rate risk only with the base fee, or to a high value to compete during sudden bursts of activity. Using all transactions that the miner wants to include in the block, a **weighted median gas premium** is computed, not considering in the computation 5% of gas price outliers on the upper-side for extra robustness against miner manipulation. + +## Motivation + +We target the following goals: + +* Gas prices spikes are mathematically smoothed out. EIP1559 does not seems to really tackle gas premium volatility and UX. +* Maintain gas price preference, i.e. transaction senders willing to pay extra in fees will be rewarded with early preferential inclusion in the blocks, because the miners want to maximize their profits and include transactions with higher fee caps first to maximize the median. +* Final gas price paid by the sender is, most of the time, smaller than the maximum gas price specified by sender. +* Gas pricing is more robust to sender manipulation or miner manipulation. + +Ethereum currently prices transaction fees using a simple auction mechanism, where users send transactions with bids ("gasprices") and miners choose transactions with the highest bids, and transactions that get included pay the bid that they specify. This leads to several large sources of inefficiency: + +* **Current extreme volatility of gas prices is hurting user experience**: if you observe online gas price metrics, the current trends in recommended gas prices can change substantially by the minute, making the user experience in the network very awkward. Also, gas volatility makes the mining business more unpredictable and costly, because miners need to spend money hedging the risks. +* **Mismatch between volatility of transaction fee levels and social cost of transactions**: bids to include transactions on mature public blockchains, that have enough usage so that blocks are full, tend to be extremely volatile. On Ethereum, minimum bids range between 1 nanoeth (10^9 nanoeth = 1 ETH), but sometimes go over 100 nanoeth and have reached over 200 nanoeth. This clearly creates many inefficiencies, because it's absurd to suggest that the cost incurred by the network from accepting one more transaction into a block actually is 200x more when gas prices are 200 nanoeth than when they are 1 nanoeth; in both cases, it's a difference between 8 million gas and 8.02 million gas. +* **Needless delays for users**: because of the hard per-block gas limit coupled with natural volatility in transaction volume, transactions often wait for several blocks before getting included, but this is socially unproductive; no one significantly gains from the fact that there is no "slack" mechanism that allows one block to be bigger and the next block to be smaller to meet block-by-block differences in demand. +* **Inefficiencies of first price auctions**: The current approach, where transaction senders publish a transaction with a bid a maximum fee, miners choose the highest-paying transactions, and everyone pays what they bid. This is well-known in mechanism design literature to be highly inefficient, and so complex fee estimation algorithms are required. But even these algorithms often end up not working very well, leading to frequent fee overpayment. We need a more stable fee metric that is computed inside the protocol. + +The proposal in this EIP is to start with a base fee amount which is adjusted up and down by the protocol based on how congested the network is. When the network exceeds the target per-block gas usage, the base fee increases slightly and when capacity is below the target, it decreases slightly. Because these base fee changes are constrained, the maximum difference in base fee from block to block is predictable. This then allows wallets to auto-set the gas fees for users in a highly reliable fashion. It is expected that most users will not have to manually adjust gas fees, even in periods of high network activity. For most users the base fee will be estimated by their wallet and a small gas premium related to the urgency and the priority they want to instill into the transaction. + +## Specification + +### Definitions + +This is a classic fork without a long migration time. + +* `FORK_BLOCK_NUMBER`: TBD. Block number at or after which EIP-3416 transactions are valid. +* `GAS_TARGET_MAX_CHANGE`: `1 // 1024`. +* `BLOCK_GAS_USED`: total gas consumed by transaction included in the block. +* `PARENT_GAS_USED`: same as `BLOCK_GAS_USED` for parent block. +* `CURRENT_BLOCK`: The current block that is being worked with (either being validated, or being produced). +* `BASE_FEE`: 16th item in the block header. Represents the amount of attoeth burned for every unit of gas a transaction uses. +* `PARENT_BASE_FEE`: same as `BASE_FEE` for parent block. +* `BASE_FEE_MAX_CHANGE`: `1 // 8` +* `INITIAL_BASE_FEE` : Median gas price in `FORK_BLOCK_NUMBER - 1`. + +### Process + +* At `block.number == FORK_BLOCK_NUMBER` we set `BASE_FEE = INITIAL_BASE_FEE` +* `BASE_FEE` is set, from `FORK_BLOCK_NUMBER + 1`, as follows + * Let `GAS_DELTA = (PARENT_GAS_USED - PARENT_GAS_TARGET) // PARENT_GAS_TARGET` (possibly negative). + * Set `BASE_FEE = PARENT_BASE_FEE + GSA_DELTA * BASE_FEE_MAX_CHANGE` +* Transactions since `FORK_BLOCK_NUMBER` are encoded the same as the current ones `rlp([nonce, gasPrice, gasLimit, to, value, data, v, r, s])` where `v,r,s` is a signature of `rlp([nonce, gasPrice, gasLimit, to, value, data])` and `gasPrice` is the `FEE_CAP` specified by the sender according to this proposal. +* To produce transactions since `FORK_BLOCK_NUMBER`, the new `FEE_CAP` field (maintaining legacy name of `gasPrice` in the transaction) is set as follows (and the `GAS_PREMIUM` is computed as specified): + * `FEE_CAP`: `tx.gasPrice`, serves as the absolute maximum that the transaction sender is willing to pay. + * `GAS_PREMIUM = (FEE_CAP - BASE_FEE) / 2` serves as a sender-preferred median premium to the miner, beyond the base fee. + * If `FEE_CAP < BASE_FEE` then the transaction is considered invalid and cannot be included in the current block, but might be included in future blocks. +* During transaction execution, for EIP3416 transactions we calculate the cost to the `tx.origin` and the gain to the `block.coinbase` as follows: + * Set `GASPRICE = BASE_FEE + median((tx_i.gasPrice - BASE_FEE) / 2)` among all transactions `tx_i` included in the same block, *weighted by gas consumed* and not including the top 5% of outlier gas price in calculation. By weighted median without 5% of the upper-side outliers, we mean that each gas unit spent is ordered according to the corresponding transaction by `BASE_FEE + tx.gasPrice / 2` and then the value chosen will be the one separating the lower 95% in two parts. + * Let `GASUSED` be the gas used during the transaction execution/state transition. + * The `tx.origin` initially pays `GASPRICE * tx.gasLimit`, and gets refunded `GASPRICE * (tx.gasLimit - GASUSED)`. + +The miners can still use a `greedy` strategy to include new transactions in the proposed blocks by adding the transactions ordered by larger `FEE_CAP` first. This is similar to how current blocks are filled, and is a consequence of `FEE_CAP` and `GAS_PREMIUM` being a positive linear function of each other. + +## Rationale + +The rationale behind the premium being 50% of (fee cap - base fee) is that at any given point the average network sender has an average fee cap, and we assume that between base fee and fee cap the sender has no specific preference, as long as the transaction is included in some block. Then, the sender is happy with a median premium among this uniform range. Another justification can be that the user also knows that this new version of the pricing protocol for the complete block uses a median, then is fair for him to apply a median within his preferential range, assuming an uniform sampling there. Simulations ([here](https://hackmd.io/c6kyRNMuTnKf_SlolmevRg#An-improvement-for-the-premium)) with Ethereum gas data shows indeed that median one of the most robust metric.s + +The 5% top outliers removal, not considered in the median, or similar number, is to give extra robustness against miner manipulation, because as current network utilization has been around 97% for the last 6 months the miners can include their own transactions on the empty 3% to try to manipulate and increase the median price (even this manipulation effect will be very small on the final price). + +The rationale for the `BASE_FEE` update formula is that we are using an additive version (`PARENT_BASE_FEE + GAS_DELTA * BASE_FEE_MAX_CHANGE`) to avoid an attack of senders sending this fee to zero. This attack was simulated and observed for multiplicative formula proposed in previous version (`PARENT_BASE_FEE + PARENT_BASE_FEE * GAS_DELTA * BASE_FEE_MAX_CHANGE`). See an article about the attack and the simulation [here](https://mtefagh.github.io/fee/). + +Another rationale for the additive `BASE_FEE` update formula is that it guarantees (see [this](https://pdfs.semanticscholar.org/3d2d/773983c5201b58586af463f045befae5bbf2.pdf) article) that the optimal execution strategy (scheduling broadcasts in order to pay less fee) for a batch of nonurgent transactions is to spread the transactions across different blocks which in turn helps to avoid network congestion and lowers volatility. For the multiplicative formula, it is exactly the reverse, that is, spikes (dumping all your transactions at once) are incentivized as described [here](https://ethresear.ch/t/path-dependence-of-eip-1559-and-the-simulation-of-the-resulting-permanent-loss/8964). + +The rationale for the `BASE_FEE_MAX_CHANGE` being `1 // 8` is that the `BASE_FEE` is designed to be very adaptative to block utilization changes. + +## Backwards Compatibility + +The backward compatibility is very straightforward because there are no new fields added to the transactions. Pricing of the gas per block on the miner/validator side is still fast to compute but a little more complex. Changes only affect miners/validators. Wallets are no affected by this proposal. + +## Test Cases + +TBD. + +## Security Considerations + +* Senders cannot manipulate the minimum fee because the minimum `BASE_FEE` is controlled by the miners with small increments or decrements on each new block proposed. +* Above the `BASE_FEE` the senders have a very limited ability to manipulate and lower the final gas price they pay because they have to move the weighted median close to `BASE_FEE` and, as we know, this is a very robust statistic. +* Miners have a very limited ability to manipulate and raise the final gas price paid by senders above `BASE_FEE` because to influence the final gas price they have to stuff fake transactions beyond the top 5% of the blocks. In average and currently, only the top 3% of the block is empty, so to fill-up 5% of the block they need to start dropping profitable transactions to reach 5%. Only beyond 5% of the top block gas they can start moving the median a little and the median is still a very robust statistic, not liable to being easily manipulated. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3436.md b/EIPS/eip-3436.md new file mode 100644 index 0000000..b2476b0 --- /dev/null +++ b/EIPS/eip-3436.md @@ -0,0 +1,116 @@ +--- +eip: 3436 +title: Expanded Clique Block Choice Rule +author: Danno Ferrin (@shemnon) +discussions-to: https://ethereum-magicians.org/t/eip-3436-expanded-clique-block-choice-rule/5809 +status: Stagnant +type: Standards Track +category: Core +created: 2021-03-25 +requires: 225 +--- + +## Simple Summary + +Add a four step block rule to Clique that should reduce block production deadlocks + +## Abstract + +The current specification of Clique allows for multiple competing blocks from producers but does not +provide any strategies to pick blocks aside from the current "highest total difficulty" rule. This +EIP proposes a four step choice rule of highest total difficulty, shortest chain, most recently +in-turn, and lowest hash. This would prevent deadlocks that have occurred in production systems. + +## Motivation + +There has been more than one deadlock in the Goerli multi-client Clique network. The number of +active validators was greater than 1/2 of the available validators so a chain halt should not have +occurred. The halt was resolved by an inactive validator coming back on line. The state of the chain +was in one of two configurations of 8 validators that can result in a chain halt. Three of the four +clients observed a choice sequence of lowest total difficulty followed by first observed block. Geth +added one extra rule of preferring the shortest chain before preferring the first observed block. +This fork would have resolved itself with Geth's rule, but there is still a configuration where the +chain can halt with a shortest chain rule. + +## Specification + +When a Clique validator is arbitrating the canonical status between two different chain head blocks, +they should choose the canonical block with the following ordered priorities. + +1. Choose the block with the most total difficulty. +2. Then choose the block with the lowest block number. +3. Then choose the block whose validator had the least recent in-turn block assignment. +4. Then choose the block with the lowest hash. + +When resolving rule 3 clients should use the following formula, where `validator_index` is the integer +index of the validator that signed the block when sorted as per epoch checkpointing, +`header_number` is the number of the header, and `validator_count` is the count of the current +validators. Clients should choose the block with the **largest** value. Note that an in-turn block +is considered to be the most recent in-turn block. + +``` +(header_number - validator_index) % validator_count +``` + +When resolving rule 4 the hash should be converted into an unsigned 256 bit integer. + +## Rationale + +Two scenarios of a halted chain are known based on the current total difficulty then first observed +rule. One of the scenarios is also resistant to the shortest chain rule. + +For the first scenario where chains of different lengths can halt consider a block with 8 +validators, whose addresses sort to the same order as their designation in this example. A fully +in-order chain exists and validator number 8 has just produced an in-turn block and then validators +5, 7 and 8 go offline, leaving validators 1 to 6 to produce blocks. Two forks form, one with an +in-order block from validator 1 and then an out of order block from validator 3. The second fork +forms from validators 2, 4, and 6 in order. Both have a net total difficulty of 3 more than the +common ancestor. So in this case if both forks become aware of the other fork then both are +considered equally viable and neither set of validators should switch to the newly observed fork. In +this case, adding a shortest chain rule would break the deadlock as the even numbered validators +would adopt the shorter chain. + +For the second scenario with the same validator set and in-order chain with validator 7 having just +produced an in order block, then validators 7 and 8 go offline. Two forks form, 1,3,5 on one side +and 2,4,6 on the other. Both forks become aware of the other fork after producing their third block. +In this case both forks have equal total difficulty and equal length. So Geth's rule would not break +the tie and only the arrival of one of the missing validators fix the chain. In a worst case +scenario the odd and even chains would produce a block for 7 and 8 respectively, and chain halt +would result with no validators that have not chosen a fork. Only a manual rollback would fix this. + +One consideration when formulating the rules is that the block choice should be chosen so that it +would encourage the maximum amount of in-order blocks. Selecting a chain based on shortest chain +implicitly prefers the chain with more in-order blocks. When selecting between competing out of +order chains the validator who is closest to producing an in-order block in the future should have +their chain declined so that they are available to produce an in-order block sooner. + +At least one client has been observed producing multiple blocks at the same height with the same +difficulty, so a final catch-all standard of lowest block hash should break any remaining ties. + +## Backwards Compatibility + +The current block choice rules are a mix of most total difficulty and most total difficulty plus +shortest chain. + +As long as the majority of the active validators implement the block choice rules then a client who +only implements the existing difficulty based rule will eventually align with the chain preferred by +these rules. If less than a majority implement these rules then deadlocks can still occur, and +depend on the first observation of problematic blocks, which is no worse than the current situation. + +If clients only partially implement the rule as long as every higher ranked rule is also implemented +then the situation will be no worse than today. + +## Security Considerations + +Malicious and motivated attackers who are participating in the network can force the chain to halt +with well crafted block production. With a fully deterministic choice rule the opportunity to halt +is diminished. Attackers still have the same opportunities to flood the network with multiple blocks +at the same height. A deterministic rule based on the lowest hash reduces the impact of such a +flooding attack. A malicious validator could exploit this deterministic rule to produce a +replacement block. Such an attack exists in current implementations but a deterministic hash rule +makes such replacements more likely. However the impact of such an attack seems low to trivial at +this time. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3440.md b/EIPS/eip-3440.md new file mode 100644 index 0000000..a343038 --- /dev/null +++ b/EIPS/eip-3440.md @@ -0,0 +1,300 @@ +--- +eip: 3440 +title: ERC-721 Editions Standard +author: Nathan Ginnever (@nginnever) +discussions-to: https://ethereum-magicians.org/t/eip-3340-nft-editions-standard-extension/6044 +status: Stagnant +type: Standards Track +category: ERC +created: 2021-04-20 +requires: 712, 721 +--- + +## Simple Summary + +This standard addresses an extension to the [ERC-721 specification](./eip-721.md) by allowing signatures on NFTs representing works of art. This provides improved provenance by creating functionality for an artist to designate an original and signed limited-edition prints of their work. + +## Abstract + +ERC-3440 is an ERC-721 extension specifically designed to make NFTs more robust for works of art. This extends the original ERC-721 spec by providing the ability to designate the original and limited-edition prints with a specialized enumeration extension similar to the [original 721 extension](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/extensions/ERC721Enumerable.sol) built-in. The key improvement of this extension is allowing artists to designate the limited nature of their prints and provide a signed piece of data that represents their unique signature to a given token Id, much like an artist would sign a print of their work. + +## Motivation +Currently the link between a NFT and the digital work of art is only enforced in the token metadata stored in the shared `tokenURI` state of a NFT. While the blockchain provides an immutable record of history back to the origin of an NFT, often the origin is not a key that an artist maintains as closely as they would a hand written signature. + +An edition is a printed replica of an original piece of art. ERC-721 is not specifically designed to be used for works of art, such as digital art and music. ERC-721 (NFT) was originally created to handle deeds and other contracts. Eventually ERC-721 evolved into gaming tokens, where metadata hosted by servers may be sufficient. This proposal takes the position that we can create a more tangible link between the NFT, digital art, owner, and artist. By making a concise standard for art, it will be easier for an artist to maintain a connection with the Ethereum blockchain as well as their fans that purchase their tokens. + +The use cases for NFTs have evolved into works of digital art, and there is a need to designate an original NFT and printed editions with signatures in a trustless manner. ERC-721 contracts may or may not be deployed by artists, and currently, the only way to understand that something is uniquely touched by an artist is to display it on 3rd party applications that assume a connection via metadata that exists on servers, external to the blockchain. This proposal helps remove that distance with readily available functionality for artists to sign their work and provides a standard for 3rd party applications to display the uniqueness of a NFT for those that purchase them. The designation of limited-editions combined with immutable signatures, creates a trustlessly enforced link. This signature is accompanied by view functions that allow applications to easily display these signatures and limited-edition prints as evidence of uniqueness by showing that artists specifically used their key to designate the total supply and sign each NFT. + +## Specification +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. + +ERC-721 compliant contracts MAY implement this ERC for editions to provide a standard method for designating the original and limited-edition prints with signatures from the artist. + +Implementations of ERC-3440 MUST designate which token Id is the original NFT (defaulted to Id 0), and which token Id is a unique replica. The original print SHOULD be token Id number 0 but MAY be assigned to a different Id. The original print MUST only be designated once. The implementation MUST designate a maximum number of minted editions, after which new Ids MUST NOT be printed / minted. + +Artists MAY use the signing feature to sign the original or limited edition prints but this is OPTIONAL. A standard message to sign is RECOMMENDED to be simply a hash of the integer of the token Id. + +Signature messages MUST use the [EIP-712](https://eips.ethereum.org/EIPS/eip-712) standard. + +A contract that is compliant with ERC-3440 shall implement the following abstract contract (referred to as ERC3440.sol): + +```solidity +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; +import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; + +/** + * @dev ERC721 token with editions extension. + */ +abstract contract ERC3440 is ERC721URIStorage { + + // eip-712 + struct EIP712Domain { + string name; + string version; + uint256 chainId; + address verifyingContract; + } + + // Contents of message to be signed + struct Signature { + address verificationAddress; // ensure the artists signs only address(this) for each piece + string artist; + address wallet; + string contents; + } + + // type hashes + bytes32 constant EIP712DOMAIN_TYPEHASH = keccak256( + "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" + ); + + bytes32 constant SIGNATURE_TYPEHASH = keccak256( + "Signature(address verifyAddress,string artist,address wallet, string contents)" + ); + + bytes32 public DOMAIN_SEPARATOR; + + // Optional mapping for signatures + mapping (uint256 => bytes) private _signatures; + + // A view to display the artist's address + address public artist; + + // A view to display the total number of prints created + uint public editionSupply = 0; + + // A view to display which ID is the original copy + uint public originalId = 0; + + // A signed token event + event Signed(address indexed from, uint256 indexed tokenId); + + /** + * @dev Sets `artist` as the original artist. + * @param `address _artist` the wallet of the signing artist (TODO consider multiple + * signers and contract signers (non-EOA) + */ + function _designateArtist(address _artist) internal virtual { + require(artist == address(0), "ERC721Extensions: the artist has already been set"); + + // If there is no special designation for the artist, set it. + artist = _artist; + } + + /** + * @dev Sets `tokenId as the original print` as the tokenURI of `tokenId`. + * @param `uint256 tokenId` the nft id of the original print + */ + function _designateOriginal(uint256 _tokenId) internal virtual { + require(msg.sender == artist, "ERC721Extensions: only the artist may designate originals"); + require(_exists(_tokenId), "ERC721Extensions: Original query for nonexistent token"); + require(originalId == 0, "ERC721Extensions: Original print has already been designated as a different Id"); + + // If there is no special designation for the original, set it. + originalId = _tokenId; + } + + + /** + * @dev Sets total number printed editions of the original as the tokenURI of `tokenId`. + * @param `uint256 _maxEditionSupply` max supply + */ + function _setLimitedEditions(uint256 _maxEditionSupply) internal virtual { + require(msg.sender == artist, "ERC721Extensions: only the artist may designate max supply"); + require(editionSupply == 0, "ERC721Extensions: Max number of prints has already been created"); + + // If there is no max supply of prints, set it. Leaving supply at 0 indicates there are no prints of the original + editionSupply = _maxEditionSupply; + } + + /** + * @dev Creates `tokenIds` representing the printed editions. + * @param `string memory _tokenURI` the metadata attached to each nft + */ + function _createEditions(string memory _tokenURI) internal virtual { + require(msg.sender == artist, "ERC721Extensions: only the artist may create prints"); + require(editionSupply > 0, "ERC721Extensions: the edition supply is not set to more than 0"); + for(uint i=0; i < editionSupply; i++) { + _mint(msg.sender, i); + _setTokenURI(i, _tokenURI); + } + } + + /** + * @dev internal hashing utility + * @param `Signature memory _message` the signature message struct to be signed + * the address of this contract is enforced in the hashing + */ + function _hash(Signature memory _message) internal view returns (bytes32) { + return keccak256(abi.encodePacked( + "\x19\x01", + DOMAIN_SEPARATOR, + keccak256(abi.encode( + SIGNATURE_TYPEHASH, + address(this), + _message.artist, + _message.wallet, + _message.contents + )) + )); + } + + /** + * @dev Signs a `tokenId` representing a print. + * @param `uint256 _tokenId` id of the NFT being signed + * @param `Signature memory _message` the signed message + * @param `bytes memory _signature` signature bytes created off-chain + * + * Requirements: + * + * - `tokenId` must exist. + * + * Emits a {Signed} event. + */ + function _signEdition(uint256 _tokenId, Signature memory _message, bytes memory _signature) internal virtual { + require(msg.sender == artist, "ERC721Extensions: only the artist may sign their work"); + require(_signatures[_tokenId].length == 0, "ERC721Extensions: this token is already signed"); + bytes32 digest = hash(_message); + address recovered = ECDSA.recover(digest, _signature); + require(recovered == artist, "ERC721Extensions: artist signature mismatch"); + _signatures[_tokenId] = _signature; + emit Signed(artist, _tokenId); + } + + + /** + * @dev displays a signature from the artist. + * @param `uint256 _tokenId` NFT id to verify isSigned + * @returns `bytes` gets the signature stored on the token + */ + function getSignature(uint256 _tokenId) external view virtual returns (bytes memory) { + require(_signatures[_tokenId].length != 0, "ERC721Extensions: no signature exists for this Id"); + return _signatures[_tokenId]; + } + + /** + * @dev returns `true` if the message is signed by the artist. + * @param `Signature memory _message` the message signed by an artist and published elsewhere + * @param `bytes memory _signature` the signature on the message + * @param `uint _tokenId` id of the token to be verified as being signed + * @returns `bool` true if signed by artist + * The artist may broadcast signature out of band that will verify on the nft + */ + function isSigned(Signature memory _message, bytes memory _signature, uint _tokenId) external view virtual returns (bool) { + bytes32 messageHash = hash(_message); + address _artist = ECDSA.recover(messageHash, _signature); + return (_artist == artist && _equals(_signatures[_tokenId], _signature)); + } + + /** + * @dev Utility function that checks if two `bytes memory` variables are equal. This is done using hashing, + * which is much more gas efficient then comparing each byte individually. + * Equality means that: + * - 'self.length == other.length' + * - For 'n' in '[0, self.length)', 'self[n] == other[n]' + */ + function _equals(bytes memory _self, bytes memory _other) internal pure returns (bool equal) { + if (_self.length != _other.length) { + return false; + } + uint addr; + uint addr2; + uint len = _self.length; + assembly { + addr := add(_self, /*BYTES_HEADER_SIZE*/32) + addr2 := add(_other, /*BYTES_HEADER_SIZE*/32) + } + assembly { + equal := eq(keccak256(addr, len), keccak256(addr2, len)) + } + } +} +``` + +## Rationale + +A major role of NFTs is to display uniqueness in digital art. Provenance is a desired feature of works of art, and this standard will help improve a NFT by providing a better way to verify uniqueness. Taking this extra step by an artist to explicitly sign tokens provides a better connection between the artists and their work on the blockchain. Artists can now retain their private key and sign messages in the future showing that the same signature is present on a unique NFT. + +## Backwards Compatibility + +This proposal combines already available 721 extensions and is backwards compatible with the ERC-721 standard. + +## Test Cases +An example implementation including tests can be found [here](https://github.com/nginnever/NFT-editions). + +## Reference Implementation +```solidity +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "./ERC3440.sol"; + +/** + * @dev ERC721 token with editions extension. + */ +contract ArtToken is ERC3440 { + + /** + * @dev Sets `address artist` as the original artist to the account deploying the NFT. + */ + constructor ( + string memory _name, + string memory _symbol, + uint _numberOfEditions, + string memory tokenURI, + uint _originalId + ) ERC721(_name, _symbol) { + _designateArtist(msg.sender); + _setLimitedEditions(_numberOfEditions); + _createEditions(tokenURI); + _designateOriginal(_originalId); + + DOMAIN_SEPARATOR = keccak256(abi.encode( + EIP712DOMAIN_TYPEHASH, + keccak256(bytes("Artist's Editions")), + keccak256(bytes("1")), + 1, + address(this) + )); + } + + /** + * @dev Signs a `tokenId` representing a print. + */ + function sign(uint256 _tokenId, Signature memory _message, bytes memory _signature) public { + _signEdition(_tokenId, _message, _signature); + } +} + +``` + +## Security Considerations +This extension gives an artist the ability to designate an original edition, set the maximum supply of editions as well as print the editions and uses the `tokenURI` extension to supply a link to the art work. To minimize the risk of an artist changing this value after selling an original piece this function can only happen once. Ensuring that these functions can only happen once provides consistency with uniqueness and verifiability. Due to this, the reference implementation handles these features in the constructor function. An edition may only be signed once, and care should be taken that the edition is signed correctly before release of the token/s. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3448.md b/EIPS/eip-3448.md new file mode 100644 index 0000000..e28d15f --- /dev/null +++ b/EIPS/eip-3448.md @@ -0,0 +1,208 @@ +--- +eip: 3448 +title: MetaProxy Standard +description: A minimal bytecode implementation for creating proxy contracts with immutable metadata attached to the bytecode +author: pinkiebell (@pinkiebell) +discussions-to: https://ethereum-magicians.org/t/erc-3448-metaproxy-factory/5834 +status: Final +type: Standards Track +category: ERC +created: 2021-03-29 +--- + +## Abstract +By standardizing on a known minimal bytecode proxy implementation with support for immutable metadata, this standard allows users and third party tools (e.g. Etherscan) to: +(a) simply discover that a contract will always redirect in a known manner and +(b) depend on the behavior of the code at the destination contract as the behavior of the redirecting contract and +(c) verify/view the attached metadata. + +Tooling can interrogate the bytecode at a redirecting address to determine the location of the code that will run along with the associated metadata - and can depend on representations about that code (verified source, third-party audits, etc). +This implementation forwards all calls via `DELEGATECALL` and any (calldata) input plus the metadata at the end of the bytecode to the implementation contract and then relays the return value back to the caller. +In the case where the implementation reverts, the revert is passed back along with the payload data. + +## Motivation +This standard supports use-cases wherein it is desirable to clone exact contract functionality with different parameters at another address. + +## Specification +The exact bytecode of the MetaProxy contract is: +``` + 20 bytes target contract address + ---------------------------------------- +363d3d373d3d3d3d60368038038091363936013d7300000000000000000000000000000000000000005af43d3d93803e603457fd5bf3 +``` +wherein the bytes at indices 21 - 41 (inclusive) are replaced with the 20 byte address of the master functionality contract. +Additionally, everything after the MetaProxy bytecode can be arbitrary metadata and the last 32 bytes (one word) of the bytecode must indicate the length of the metadata in bytes. + +``` +<54 bytes metaproxy> +``` + +## Rationale +The goals of this effort have been the following: +- a cheap way of storing immutable metadata for each child instead of using storage slots +- inexpensive deployment of clones +- handles error return bubbling for revert messages + +## Backwards Compatibility +There are no backwards compatibility issues. + +## Test Cases +Tested with: +- invocation with no arguments +- invocation with arguments +- invocation with return values +- invocation with revert (confirming reverted payload is transferred) + +A solidity contract with the above test cases can be found [in the EIP asset directory](../assets/eip-3448/MetaProxyTest.sol). + +## Reference Implementation +A reference implementation can be found [in the EIP asset directory](../assets/eip-3448/MetaProxyFactory.sol). + +### Deployment bytecode +A annotated version of the deploy bytecode: +``` +// PUSH1 11; +// CODESIZE; +// SUB; +// DUP1; +// PUSH1 11; +// RETURNDATASIZE; +// CODECOPY; +// RETURNDATASIZE; +// RETURN; +``` + +### MetaProxy +A annotated version of the MetaProxy bytecode: +``` +// copy args +// CALLDATASIZE; calldatasize +// RETURNDATASIZE; 0, calldatasize +// RETURNDATASIZE; 0, 0, calldatasize +// CALLDATACOPY; + +// RETURNDATASIZE; 0 +// RETURNDATASIZE; 0, 0 +// RETURNDATASIZE; 0, 0, 0 +// RETURNDATASIZE; 0, 0, 0, 0 + +// PUSH1 54; 54, 0, 0, 0, 0 +// DUP1; 54, 54, 0, 0, 0, 0 +// CODESIZE; codesize, 54, 54, 0, 0, 0, 0 +// SUB; codesize-54, 54, 0, 0, 0, 0 +// DUP1; codesize-54, codesize-54, 54, 0, 0, 0, 0 +// SWAP2; 54, codesize-54, codesize-54, 0, 0, 0, 0 +// CALLDATASIZE; calldatasize, 54, codesize-54, codesize-54, 0, 0, 0, 0 +// CODECOPY; codesize-54, 0, 0, 0, 0 + +// CALLDATASIZE; calldatasize, codesize-54, 0, 0, 0, 0 +// ADD; calldatasize+codesize-54, 0, 0, 0, 0 +// RETURNDATASIZE; 0, calldatasize+codesize-54, 0, 0, 0, 0 +// PUSH20 0; addr, 0, calldatasize+codesize-54, 0, 0, 0, 0 - zero is replaced with shl(96, address()) +// GAS; gas, addr, 0, calldatasize+codesize-54, 0, 0, 0, 0 +// DELEGATECALL; (gas, addr, 0, calldatasize() + metadata, 0, 0) delegatecall to the target contract; +// +// RETURNDATASIZE; returndatasize, retcode, 0, 0 +// RETURNDATASIZE; returndatasize, returndatasize, retcode, 0, 0 +// SWAP4; 0, returndatasize, retcode, 0, returndatasize +// DUP1; 0, 0, returndatasize, retcode, 0, returndatasize +// RETURNDATACOPY; (0, 0, returndatasize) - Copy everything into memory that the call returned + +// stack = retcode, 0, returndatasize # this is for either revert(0, returndatasize()) or return (0, returndatasize()) + +// PUSH1 _SUCCESS_; push jumpdest of _SUCCESS_ +// JUMPI; jump if delegatecall returned `1` +// REVERT; (0, returndatasize()) if delegatecall returned `0` +// JUMPDEST _SUCCESS_; +// RETURN; (0, returndatasize()) if delegatecall returned non-zero (1) +``` + +### Examples +The following code snippets serve only as suggestions and are not a discrete part of this standard. + +#### Proxy construction with bytes from abi.encode +```solidity +/// @notice MetaProxy construction via abi encoded bytes. +function createFromBytes ( + address a, + uint256 b, + uint256[] calldata c +) external payable returns (address proxy) { + // creates a new proxy where the metadata is the result of abi.encode() + proxy = MetaProxyFactory._metaProxyFromBytes(address(this), abi.encode(a, b, c)); + require(proxy != address(0)); + // optional one-time setup, a constructor() substitute + MyContract(proxy).init{ value: msg.value }(); +} +``` + +#### Proxy construction with bytes from calldata +```solidity +/// @notice MetaProxy construction via calldata. +function createFromCalldata ( + address a, + uint256 b, + uint256[] calldata c +) external payable returns (address proxy) { + // creates a new proxy where the metadata is everything after the 4th byte from calldata. + proxy = MetaProxyFactory._metaProxyFromCalldata(address(this)); + require(proxy != address(0)); + // optional one-time setup, a constructor() substitute + MyContract(proxy).init{ value: msg.value }(); +} +``` + +#### Retrieving the metadata from calldata and abi.decode +```solidity +/// @notice Returns the metadata of this (MetaProxy) contract. +/// Only relevant with contracts created via the MetaProxy standard. +/// @dev This function is aimed to be invoked with- & without a call. +function getMetadataWithoutCall () public pure returns ( + address a, + uint256 b, + uint256[] memory c +) { + bytes memory data; + assembly { + let posOfMetadataSize := sub(calldatasize(), 32) + let size := calldataload(posOfMetadataSize) + let dataPtr := sub(posOfMetadataSize, size) + data := mload(64) + // increment free memory pointer by metadata size + 32 bytes (length) + mstore(64, add(data, add(size, 32))) + mstore(data, size) + let memPtr := add(data, 32) + calldatacopy(memPtr, dataPtr, size) + } + return abi.decode(data, (address, uint256, uint256[])); +} +``` + +#### Retrieving the metadata via a call to self +```solidity +/// @notice Returns the metadata of this (MetaProxy) contract. +/// Only relevant with contracts created via the MetaProxy standard. +/// @dev This function is aimed to to be invoked via a call. +function getMetadataViaCall () public pure returns ( + address a, + uint256 b, + uint256[] memory c +) { + assembly { + let posOfMetadataSize := sub(calldatasize(), 32) + let size := calldataload(posOfMetadataSize) + let dataPtr := sub(posOfMetadataSize, size) + calldatacopy(0, dataPtr, size) + return(0, size) + } +} +``` + +Apart from the examples above, it is also possible to use Solidity Structures or any custom data encoding. + +## Security Considerations +This standard only covers the bytecode implementation and does not include any serious side effects of itself. +The reference implementation only serves as a example. It is highly recommended to research side effects depending on how the functionality is used and implemented in any project. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3450.md b/EIPS/eip-3450.md new file mode 100644 index 0000000..7030877 --- /dev/null +++ b/EIPS/eip-3450.md @@ -0,0 +1,190 @@ +--- +eip: 3450 +title: Standardized Shamir Secret Sharing Scheme for BIP-39 Mnemonics +author: Daniel Streit (@danielstreit) +discussions-to: https://ethereum-magicians.org/t/erc-3450-standard-for-applying-shamirs-to-bip-39-mnemonics/5844 +status: Stagnant +type: Standards Track +category: ERC +created: 2021-03-29 +--- + +## Simple Summary + +A standardized algorithm for applying Shamir's Secret Sharing Scheme to BIP-39 mnemonics. + +## Abstract + +A standardized approach to splitting a BIP-39 mnemonic into _N_ BIP-39 mnemonics, called shares, so that _T_ shares are required to recover the original mnemonic and no information about the original mnemonic, other than its size, is leaked with less than _T_ shares. + +## Motivation + +We'd like to make it easier for less-technical users to store keys securely. + +Currently, many users use BIP-39 mnemonics to store entropy values underlying their keys. These mnemonics are a single point of failure. If lost, the user may never regain access to the assets locked by the keys. If stolen, a malicious actor can steal the assets. + +Shamir's Secret Sharing Scheme addresses this concern directly. It creates "shares" of the secret, such that a subset can be used to recover the secret, but only if a minimum threshold of shares is reached. Without the minimum, no information about the original secret is leaked. + +One concern with Shamir's Secret Sharing Scheme is there is no canonical, standard implementation. This puts recovery at risk, as tooling may change over time. + +Here, we propose a standardized implementation of Shamir's Secret Sharing Scheme applied specifically to BIP-39 mnemonics, so users can easily create shares of their mnemonic, destroy the original, store the shares appropriately, and confidently recover the original mnemonic at a later date. + +## Specification + +### Shamir's Secret Sharing Scheme + +Shamir's Secret Sharing Scheme is a cryptographic method to split a secret into _N_ unique parts, where any _T_ of them are required to reconstruct the secret. + +First, a polynomial _f_ of degree _T_ − 1 is constructed. Then, each share is a point on the polynomial's curve: an integer _x_, and its corresponding _y_ point _f_(_x_). + +With any set of _T_ shares (or points), the initial polynomial can be recovered using polynomial interpolation. + +When constructing the initial polynomial, the secret is stored as the coefficient of x0 and the rest of the coefficients are randomly generated. + +### BIP-39 Mnemonics + +BIP-39 is a common standard for storing entropy as a list of words. It is easier to work with for human interactions than raw binary or hexadecimal representations of entropy. + +BIP-39 mnemonics encode two pieces of data: the original entropy and a checksum of that entropy. The checksum allows the mnemonic to be validated, ensuring that the user entered it correctly. + +#### Generating the Mnemonic + +The mnemonic must encode entropy in a multiple of 32 bits. With more entropy security is improved but the sentence length increases. We refer to the initial entropy length as ENT. The allowed size of ENT is 128-256 bits. + +First, an initial entropy of ENT bits is generated. A checksum is generated by taking the first `ENT / 32` bits of its SHA256 hash. This checksum is appended to the end of the initial entropy. Next, these concatenated bits are split into groups of 11 bits, each encoding a number from 0-2047, serving as an index into a word list. Finally, we convert these numbers into words and use the joined words as a mnemonic sentence. + +The following table describes the relation between the initial entropy length (ENT), the checksum length (CS), and the length of the generated mnemonic sentence (MS) in words. + +``` +CS = ENT / 32 +MS = (ENT + CS) / 11 + +| ENT | CS | ENT+CS | MS | ++-------+----+--------+------+ +| 128 | 4 | 132 | 12 | +| 160 | 5 | 165 | 15 | +| 192 | 6 | 198 | 18 | +| 224 | 7 | 231 | 21 | +| 256 | 8 | 264 | 24 | +``` + +#### Recovering the Entropy + +The initial entropy can be recovered by reversing the process above. The mnemonic is converted to bits, where each word is converted to 11 bits representing its index in the word list. The entropy portion is defined in the table above, based on the size of the mnemonic. + +#### Word List + +This specification only supports the BIP-39 English word list, but this may be expanded in the future. + +See [word list](../assets/eip-3450/wordlist.txt). + +### Applying Shamir's Scheme to BIP-39 Mnemonics + +To ensure that the shares are valid BIP-39 mnemonics, we: + +1. Convert the target BIP-39 mnemonic to its underlying entropy +2. Apply Shamir's Scheme to the entropy +3. Convert each resulting share's _y_ value to a BIP-39 mnemonic + +By converting to entropy before applying Shamir's Scheme, we omit the checksum from the initial secret, allowing us to calculate a new checksum for each share when converting the share _y_ values to mnemonics, ensuring that they are valid according to BIP-39. + +When applying Shamir's Scheme to the entropy, we apply it separately to each byte of the entropy and GF(256) is used as the underlying finite field. Bytes are interpreted as elements of GF(256) using polynomial representation with operations modulo the Rijndael irreducible polynomial _x_8 + _x_4 + _x_3 + _x_ + 1, following AES. + +### Share Format + +A share represents a point on the curve described by the underlying polynomial used to split the secret. It includes two pieces of data: + +- An ID: the _x_ value of the share +- A BIP-39 mnemonic: the _y_ value of the share represented by a mnemonic + +### Creating Shares + +Inputs: BIP-39 mnemonic, number of shares (_N_), threshold (_T_) + +Output: N Shares, each share including an ID, { _x_ | 0 < _x_ < 256 }, and a BIP-39 mnemonic of the same length as the input one + +1. Check the following conditions: + - 1 < T <= N < 256 + - The mnemonic is valid according to [BIP-39](#generating-the-mnemonic) +2. [Recover the underlying entropy of the mnemonic](#recovering-the-entropy) as a vector of bytes +3. Define values: + - Let _E_ be the byte-vector representation of the mnemonic's entropy + - Let _n_ be the length of _E_ + - Let _coeff1_, ... , _coeffT - 1_ be byte-vectors belonging to GF(256)_n_ generated randomly, independently with uniform distribution from a source suitable for generating cryptographic keys +4. Evaluate the polynomial for each share + - For each _x_ from 1 to _N_, evaluate the polynomial _f(x)_ = _E_ + _coeff1x1_ + ... + _coeffT - 1xT - 1_, where _x_ is the share ID and _f(x)_ is the share value (as a vector of bytes) +5. Using _f(x)_ as the underlying entropy, [generate a mnemonic](#generating-the-mnemonic) for each share +6. Return the ID and mnemonic for each share + +### Recovering the Mnemonic + +To recover the original mnemonic, we interpolate a polynomial _f_ from the given set of shares (or points on the polynomial) and evaluate _f(0)_. + +#### Polynomial Interpolation + +Given a set of _m_ points (_xi_, _yi_), 1 ≤ _i_ ≤ _m_, such that no two _xi_ values equal, there exists a polynomial that assumes the value _yi_ at each point _xi_. The polynomial of lowest degree that satisfies these conditions is uniquely determined and can be obtained using the Lagrange interpolation formula given below. + +Since Shamir's Secret Sharing Scheme is applied separately to each of the _n_ bytes of the shared mnemonic's entropy, we work with _yi_ as a vector of _n_ values, where _yi_[_k_] = _fk_(_xi_), 1 ≤ _k_ ≤ _n_, and _fk_ is the polynomial in the _k_-th instance of the scheme. + +#### Interpolate(_x_, {(_xi_, _yi_), 1 ≤ _i_ ≤ _m_}) + +Input: the desired index _x_, a set of index/value-vector pairs {(_xi_, _y__i_), 1 ≤ _i_ ≤ _m_} ⊆ GF(256) × GF(256)_n_ + +Output: the value-vector (_f_1(_x_), ... , _fn_(_x_)) + +![f_k(x) = \sum_{i=1}^m y_i[k] \prod_{\underset{j \neq i}{j=1}}^m \frac{x - x_j}{x_i - x_j}](../assets/eip-3450/lagrange.gif) + +#### Recover the Mnemonic + +Input: A set of _m_ Shares + +Output: The original mnemonic + +1. [Recover the underlying entropy of each share's mnemonic](#recovering-the-entropy) as a vector of bytes +2. Calculate _E_ = Interpolate(0, [(_x1_, _y1_),...,(_xm_, _ym_)]), where _x_ is the share ID and _y_ is the byte-vector of the share's mnemonic's entropy +3. Using _E_ as the underlying entropy, [generate a mnemonic](#generating-the-mnemonic) and return it + +## Rationale + +### Choice of Field + +The field GF(256) was chosen, because the field arithmetic is easy to implement in any programming language and many implementations are already available since it is used in the AES cipher. Although using GF(256) requires that we convert the mnemonic to its underlying entropy as a byte-vector, this is also easy to implement and many implementations of it exist in a variety of programming languages. + +GF(2048) was also considered. Using GF(2048), we could have applied Shamir's Scheme directly to the mnemonic, using the word indexes as the values. This would have allowed us to avoid converting the mnemonic to its underlying entropy. But, the resulting shares would not have been valid BIP-39 mnemonics - the checksum portion would not be a valid checksum of the entropy. And, working around this would add considerable complexity. + +Another option was GF(2_n_) where _n_ is the size of the entropy in bits. We'd still convert the mnemonic to entropy, but then apply Shamir's Scheme over the entire entropy rather than on a vector of values. The downside of this approach is we'd need a different field for each mnemonic strength along with an associated irreducible polynomial. Additionally, this would require working with very large numbers that can be cumbersome to work with in some languages. + +### Valid Share Mnemonics and Share IDs + +The shares produced by the specification include an ID, in addition to the BIP-39 mnemonic. + +Other options could have encoded the share ID into the mnemonic, simplifying storage - only the mnemonic would need to be stored. + +One possibility would be to store the ID instead of the checksum in the mnemonic. The downside of this approach is that the shares would not be _valid_ BIP-39 mnemonics because the "checksum" section of the mnemonic would not match the "entropy" section. Shares with valid BIP-39 mnemonics are useful because they are indistinguishable from any other. And users could store the ID in a variety of ways that obscure it. + +### Validation on Recovery + +We decided _not_ to include a validation mechanism on recovering the original mnemonic. This leaks less information to a potential attacker. There is no indication they've gotten the requisite number of shares until they've obtained _T_ + 1 shares. + +We could provide recovery validation by replacing one of the random coefficients with a checksum of the original mnemonic. Then, when recovering the original mnemonic and the polynomial, we could validate that the checksum coefficient is the valid checksum of recovered mnemonic. + +## Test Cases + +Coming soon. + +All implementations must be able to: + +- Split and recover each `mnemonic` with the given `numShares` and `threshold`. +- Recover the `mnemonic` from the given `knownShares`. + +## Security Considerations + +The shares produced by the specification include an ID in addition to the BIP-39 mnemonic. This raises two security concerns: + +Users **must** keep this ID in order to recover the original mnemonic. If the ID is lost, or separated from the share mnemonic, it may not be possible to recover the original. (Brute force recovery may or may not be possible depending on how much is known about the number of shares and threshold) + +The additional data may hint to an attacker of the existence of other keys and the scheme under which they are stored. Therefore, the ID should be stored in a way that obscures its use. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3455.md b/EIPS/eip-3455.md new file mode 100644 index 0000000..076766d --- /dev/null +++ b/EIPS/eip-3455.md @@ -0,0 +1,71 @@ +--- +eip: 3455 +title: SUDO Opcode +description: A new opcode is introduced to allow calling from an arbitrary sender address. +author: William Morriss (@wjmelements), Baptiste Vauthey (@thabaptiser) +discussions-to: https://ethereum-magicians.org/t/eip-3455-sudo-opcode/5860 +status: Draft +type: Standards Track +category: Core +created: 2021-04-01 +--- + +## Abstract +A new opcode, `SUDO`, is introduced with the same parameters as `CALL`, plus another parameter to specify the sender address. + +## Motivation +There are many use cases for being able to set the sender. + +Many tokens are stuck irretrievably because nobody has the key for the owner address. +In particular, at address zero there is approximately 17 billion USD in tokens and ether, according to etherscan. +With `SUDO`, anyone could free that value, leading to an economic boom that would end poverty and world hunger. +Instead it is sitting there idle like the gold in Fort Knox. +`SUDO` fixes this. + +It is a common mistake to send [ERC-20](./eip-20.md) tokens to the token address instead of the intended recipient. +This happens because users paste the token address into the recipient fields. +Currently there is no way to recover these tokens. +`SUDO` fixes this. + +Many scammers have fraudulently received tokens and ETH via trust-trading. +Their victims currently have no way to recover their funds. +`SUDO` fixes this. + +Large amounts of users have accidentally locked up tokens and ether by losing their private keys. +This is inefficient and provides a bad user experience. +To accommodate new and inexperienced users, there needs to be a way to recover funds after the private key has been lost. +`SUDO` fixes this. + +Finally, there are many tokens and ether sitting in smart contracts locked due to a bug. +We could finally close EIP issue #156. +We cannot currently reclaim ether from stuck accounts. +`SUDO` fixes this. + +## Specification +Adds a new opcode (`SUDO`) at `0xf8`. +`SUDO` pops 8 parameters from the stack. +Besides the sender parameter, the parameters shall match `CALL`. + +1. Gas: Integer; Maximum gas allowance for message call, safely using current gas counter if the counter is lower +2. Sender: Address, truncated to lower 40 bytes; Sets `CALLER` inside the call frame +3. To: Address, truncated to lower 40 bytes; sets `ADDRESS` +4. Value: Integer, raises exception amount specified is less than the value in Sender account; transferred with call to recipient balance, sets `CALLVALUE` +5. InStart: Integer; beginning of memory to use for `CALLDATA` +6. InSize: Integer; length of memory to use for `CALLDATA` +7. OutStart: Integer; beginning of memory to replace with `RETURNDATA` +8. OutSize: Integer; maximum `RETURNDATA` to place in memory + +Following execution, `SUDO` pushes a result value to the stack, indicating success or failure. +If the call ended with `STOP`, `RETURN`, or `SELFDESTRUCT`, `1` is pushed. +If the call ended with `REVERT`, `INVALID`, or an EVM assertion, `0` is pushed. + +## Rationale +The `GAS` parameter is first so that callers can tediously compute how much of their remaining gas to send at the last possible moment. +The remaining parameters inherited from `CALL` are in the same order, with sender inserted between. + + +## Security Considerations +It will be fine. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3475.md b/EIPS/eip-3475.md new file mode 100644 index 0000000..f55b6dc --- /dev/null +++ b/EIPS/eip-3475.md @@ -0,0 +1,445 @@ +--- +eip: 3475 +title: Abstract Storage Bonds +description: Interface for creating tokenized obligations with abstract on-chain metadata storage +author: Yu Liu (@yuliu-debond), Varun Deshpande (@dr-chain), Cedric Ngakam (@drikssy), Dhruv Malik (@dhruvmalik007), Samuel Gwlanold Edoumou (@Edoumou), Toufic Batrice (@toufic0710) +discussions-to: https://ethereum-magicians.org/t/eip-3475-multiple-callable-bonds-standard/8691 +status: Final +type: Standards Track +category: ERC +created: 2021-04-05 +requires: 20, 721, 1155 +--- + +## Abstract + +- This EIP allows the creation of tokenized obligations with abstract on-chain metadata storage. Issuing bonds with multiple redemption data cannot be achieved with existing token standards. + +- This EIP enables each bond class ID to represent a new configurable token type and corresponding to each class, corresponding bond nonces to represent an issuing condition or any other form of data in uint256. Every single nonce of a bond class can have its metadata, supply, and other redemption conditions. + +- Bonds created by this EIP can also be batched for issuance/redemption conditions for efficiency on gas costs and UX side. And finally, bonds created from this standard can be divided and exchanged in a secondary market. + +## Motivation + +Current LP (Liquidity Provider) tokens are simple [EIP-20](./eip-20.md) tokens with no complex data structure. To allow more complex reward and redemption logic to be stored on-chain, we need a new token standard that: + +- Supports multiple token IDs +- Can store on-chain metadata +- Doesn't require a fixed storage pattern +- Is gas-efficient. + +Also Some benefits: + +- This EIP allows the creation of any obligation with the same interface. +- It will enable any 3rd party wallet applications or exchanges to read these tokens' balance and redemption conditions. +- These bonds can also be batched as tradeable instruments. Those instruments can then be divided and exchanged in secondary markets. + +## Specification + +**Definition** + +Bank: an entity that issues, redeems, or burns bonds after getting the necessary amount of liquidity. Generally, a single entity with admin access to the pool. + +**Functions** + +```solidity +pragma solidity ^0.8.0; + +/** +* transferFrom +* @param _from argument is the address of the bond holder whose balance is about to decrease. +* @param _to argument is the address of the bond recipient whose balance is about to increase. +* @param _transactions is the `Transaction[] calldata` (of type ['classId', 'nonceId', '_amountBonds']) structure defined in the rationale section below. +* @dev transferFrom MUST have the `isApprovedFor(_from, _to, _transactions[i].classId)` approval to transfer `_from` address to `_to` address for given classId (i.e for Transaction tuple corresponding to all nonces). +e.g: +* function transferFrom(0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef, 0x82a55a613429Aeb3D01fbE6841bE1AcA4fFD5b2B, [IERC3475.Transaction(1,14,500)]); +* transfer from `_from` address, to `_to` address, `500000000` bonds of type class`1` and nonce `42`. +*/ + +function transferFrom(address _from, address _to, Transaction[] calldata _transactions) external; + +/** +* transferAllowanceFrom +* @dev allows the transfer of only those bond types and nonces being allotted to the _to address using allowance(). +* @param _from is the address of the holder whose balance is about to decrease. +* @param _to is the address of the recipient whose balance is about to increase. +* @param _transactions is the `Transaction[] calldata` structure defined in the section `rationale` below. +* @dev transferAllowanceFrom MUST have the `allowance(_from, msg.sender, _transactions[i].classId, _transactions[i].nonceId)` (where `i` looping for [ 0 ...Transaction.length - 1] ) +e.g: +* function transferAllowanceFrom(0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef, 0x82a55a613429Aeb3D01fbE6841bE1AcA4fFD5b2B, [IERC3475.Transaction(1,14,500)]); +* transfer from `_from` address, to `_to` address, `500000000` bonds of type class`1` and nonce `42`. +*/ + +function transferAllowanceFrom(address _from,address _to, Transaction[] calldata _transactions) public ; + +/** +* issue +* @dev allows issuing any number of bond types (defined by values in Transaction tuple as param) to an address. +* @dev it MUST be issued by a single entity (for instance, a role-based ownable contract that has integration with the liquidity pool of the deposited collateral by `_to` address). +* @param `_to` argument is the address to which the bond will be issued. +* @param `_transactions` is the `Transaction[] calldata` (ie array of issued bond class, bond nonce and amount of bonds to be issued). +* @dev transferAllowanceFrom MUST have the `allowance(_from, msg.sender, _transactions[i].classId, _transactions[i].nonceId)` (where `i` looping for [ 0 ...Transaction.length - 1] ) +e.g: +example: issue(0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef,[IERC3475.Transaction(1,14,500)]); +issues `1000` bonds with a class of `0` to address `0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef` with a nonce of `5`. +*/ +function issue(address _to, Transaction[] calldata _transaction) external; + +/** +* redeem +* @dev permits redemption of bond from an address. +* @dev the calling of this function needs to be restricted to the bond issuer contract. +* @param `_from` is the address from which the bond will be redeemed. +* @param `_transactions` is the `Transaction[] calldata` structure (i.e., array of tuples with the pairs of (class, nonce and amount) of the bonds that are to be redeemed). Further defined in the rationale section. +* @dev redeem function for a given class, and nonce category MUST BE done after certain conditions for maturity (can be end time, total active liquidity, etc.) are met. +* @dev furthermore, it SHOULD ONLY be called by the bank or secondary market maker contract. +e.g: +* redeem(0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef, [IERC3475.Transaction(1,14,500)]); +means “redeem from wallet address(0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef), 500000000 of bond class1 and nonce 42. +*/ + +function redeem(address _from, Transaction[] calldata _transactions) external; + +/** +* burn +* @dev permits nullifying of the bonds (or transferring given bonds to address(0)). +* @dev burn function for given class and nonce MUST BE called by only the controller contract. +* @param _from is the address of the holder whose bonds are about to burn. +* @param `_transactions` is the `Transaction[] calldata` structure (i.e., array of tuple with the pairs of (class, nonce and amount) of the bonds that are to be burned). further defined in the rationale. +* @dev burn function for a given class, and nonce category MUST BE done only after certain conditions for maturity (can be end time, total active liquidity, etc). +* @dev furthermore, it SHOULD ONLY be called by the bank or secondary market maker contract. +* e.g: +* burn(0x82a55a613429Aeb3D01fbE6841bE1AcA4fFD5b2B,[IERC3475.Transaction(1,14,500)]); +* means burning 500000000 bonds of class 1 nonce 42 owned by address 0x82a55a613429Aeb3D01fbE6841bE1AcA4fFD5b2B. +*/ +function burn(address _from, Transaction[] calldata _transactions) external; + +/** +* approve +* @dev Allows `_spender` to withdraw from the msg.sender the bonds of `_amount` and type (classId and nonceId). +* @dev If this function is called again, it overwrites the current allowance with the amount. +* @dev `approve()` should only be callable by the bank, or the owner of the account. +* @param `_spender` argument is the address of the user who is approved to transfer the bonds. +* @param `_transactions` is the `Transaction[] calldata` structure (ie array of tuple with the pairs of (class,nonce, and amount) of the bonds that are to be approved to be spend by _spender). Further defined in the rationale section. +* e.g: +* approve(0x82a55a613429Aeb3D01fbE6841bE1AcA4fFD5b2B,[IERC3475.Transaction(1,14,500)]); +* means owner of address 0x82a55a613429Aeb3D01fbE6841bE1AcA4fFD5b2B is approved to manage 500 bonds from class 1 and Nonce 14. +*/ + +function approve(address _spender, Transaction[] calldata _transactions) external; + +/** +* SetApprovalFor +* @dev enable or disable approval for a third party (“operator”) to manage all the Bonds in the given class of the caller’s bonds. +* @dev If this function is called again, it overwrites the current allowance with the amount. +* @dev `approve()` should only be callable by the bank or the owner of the account. +* @param `_operator` is the address to add to the set of authorized operators. +* @param `classId` is the class id of the bond. +* @param `_approved` is true if the operator is approved (based on the conditions provided), false meaning approval is revoked. +* @dev contract MUST define internal function regarding the conditions for setting approval and should be callable only by bank or owner. +* e.g: setApprovalFor(0x82a55a613429Aeb3D01fbE6841bE1AcA4fFD5b2B,0,true); +* means that address 0x82a55a613429Aeb3D01fbE6841bE1AcA4fFD5b2B is authorized to transfer bonds from class 0 (across all nonces). +*/ + +function setApprovalFor(address _operator, bool _approved) external returns(bool approved); + +/** +* totalSupply +* @dev Here, total supply includes burned and redeemed supply. +* @param classId is the corresponding class Id of the bond. +* @param nonceId is the nonce Id of the given bond class. +* @return the supply of the bonds +* e.g: +* totalSupply(0, 1); +* it finds the total supply of the bonds of classid 0 and bond nonce 1. +*/ +function totalSupply(uint256 classId, uint256 nonceId) external view returns (uint256); + +/** +* redeemedSupply +* @dev Returns the redeemed supply of the bond identified by (classId,nonceId). +* @param classId is the corresponding class id of the bond. +* @param nonceId is the nonce id of the given bond class. +* @return the supply of bonds redeemed. +*/ +function redeemedSupply(uint256 classId, uint256 nonceId) external view returns (uint256); + +/** +* activeSupply +* @dev Returns the active supply of the bond defined by (classId,NonceId). +* @param classId is the corresponding classId of the bond. +* @param nonceId is the nonce id of the given bond class. +* @return the non-redeemed, active supply. +*/ +function activeSupply(uint256 classId, uint256 nonceId) external view returns (uint256); + +/** +* burnedSupply +* @dev Returns the burned supply of the bond in defined by (classId,NonceId). +* @param classId is the corresponding classId of the bond. +* @param nonceId is the nonce id of the given bond class. +* @return gets the supply of bonds for given classId and nonceId that are already burned. +*/ +function burnedSupply(uint256 classId, uint256 nonceId) external view returns (uint256); + +/** +* balanceOf +* @dev Returns the balance of the bonds (nonReferenced) of given classId and bond nonce held by the address `_account`. +* @param classId is the corresponding classId of the bond. +* @param nonceId is the nonce id of the given bond class. +* @param _account address of the owner whose balance is to be determined. +* @dev this also consists of bonds that are redeemed. +*/ +function balanceOf(address _account, uint256 classId, uint256 nonceId) external view returns (uint256); + +/** +* classMetadata +* @dev Returns the JSON metadata of the classes. +* @dev The metadata SHOULD follow a set of structures explained later in the metadata.md +* @param metadataId is the index-id given bond class information. +* @return the JSON metadata of the nonces. — e.g. `[title, type, description]`. +*/ +function classMetadata(uint256 metadataId) external view returns (Metadata memory); + +/** +* nonceMetadata +* @dev Returns the JSON metadata of the nonces. +* @dev The metadata SHOULD follow a set of structures explained later in metadata.md +* @param classId is the corresponding classId of the bond. +* @param nonceId is the nonce id of the given bond class. +* @param metadataId is the index of the JSON storage for given metadata information. more is defined in metadata.md. +* @returns the JSON metadata of the nonces. — e.g. `[title, type, description]`. +*/ +function nonceMetadata(uint256 classId, uint256 metadataId) external view returns (Metadata memory); + +/** +* classValues +* @dev allows anyone to read the values (stored in struct Values for different class) for given bond class `classId`. +* @dev the values SHOULD follow a set of structures as explained in metadata along with correct mapping corresponding to the given metadata structure +* @param classId is the corresponding classId of the bond. +* @param metadataId is the index of the JSON storage for given metadata information of all values of given metadata. more is defined in metadata.md. +* @returns the Values of the class metadata. — e.g. `[string, uint, address]`. +*/ +function classValues(uint256 classId, uint256 metadataId) external view returns (Values memory); + +/** +* nonceValues +* @dev allows anyone to read the values (stored in struct Values for different class) for given bond (`nonceId`,`classId`). +* @dev the values SHOULD follow a set of structures explained in metadata along with correct mapping corresponding to the given metadata structure +* @param classId is the corresponding classId of the bond. +* @param metadataId is the index of the JSON storage for given metadata information of all values of given metadata. More is defined in metadata.md. +* @returns the Values of the class metadata. — e.g. `[string, uint, address]`. +*/ +function nonceValues(uint256 classId, uint256 nonceId, uint256 metadataId) external view returns (Values memory); + +/** +* getProgress +* @dev Returns the parameters to determine the current status of bonds maturity. +* @dev the conditions of redemption SHOULD be defined with one or several internal functions. +* @param classId is the corresponding classId of the bond. +* @param nonceId is the nonceId of the given bond class . +* @returns progressAchieved defines the metric (either related to % liquidity, time, etc.) that defines the current status of the bond. +* @returns progressRemaining defines the metric that defines the remaining time/ remaining progress. +*/ +function getProgress(uint256 classId, uint256 nonceId) external view returns (uint256 progressAchieved, uint256 progressRemaining); + +/** +* allowance +* @dev Authorizes to set the allowance for given `_spender` by `_owner` for all bonds identified by (classId, nonceId). +* @param _owner address of the owner of bond(and also msg.sender). +* @param _spender is the address authorized to spend the bonds held by _owner of info (classId, nonceId). +* @param classId is the corresponding classId of the bond. +* @param nonceId is the nonceId of the given bond class. +* @notice Returns the _amount which spender is still allowed to withdraw from _owner. +*/ +function allowance(address _owner, address _spender, uint256 classId, uint256 nonceId) external returns(uint256); + +/** +* isApprovedFor +* @dev returns true if address _operator is approved for managing the account’s bonds class. +* @notice Queries the approval status of an operator for a given owner. +* @dev _owner is the owner of bonds. +* @dev _operator is the EOA /contract, whose status for approval on bond class for this approval is checked. +* @returns “true” if the operator is approved, “false” if not. +*/ +function isApprovedFor(address _owner, address _operator) external view returns (bool); +``` + +### Events + +```solidity +/** +* Issue +* @notice Issue MUST trigger when Bonds are issued. This SHOULD not include zero value Issuing. +* @dev This SHOULD not include zero value issuing. +* @dev Issue MUST be triggered when the operator (i.e Bank address) contract issues bonds to the given entity. +* eg: emit Issue(_operator, 0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef,[IERC3475.Transaction(1,14,500)]); +* issue by address(operator) 500 Bonds(nonce14,class 1) to address 0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef. +*/ + +event Issue(address indexed _operator, address indexed _to, Transaction[] _transactions); + +/** +* Redeem +* @notice Redeem MUST trigger when Bonds are redeemed. This SHOULD not include zero value redemption. +*e.g: emit Redeem(0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef,0x492Af743654549b12b1B807a9E0e8F397E44236E,[IERC3475.Transaction(1,14,500)]); +* emit event when 5000 bonds of class 1, nonce 14 owned by address 0x492Af743654549b12b1B807a9E0e8F397E44236E are being redeemed by 0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef. +*/ + +event Redeem(address indexed _operator, address indexed _from, Transaction[] _transactions); + + +/** +* Burn. +* @dev `Burn` MUST trigger when the bonds are being redeemed via staking (or being invalidated) by the bank contract. +* @dev `Burn` MUST trigger when Bonds are burned. This SHOULD not include zero value burning. +* e.g : emit Burn(0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef,0x492Af743654549b12b1B807a9E0e8F397E44236E,[IERC3475.Transaction(1,14,500)]); +* emits event when 500 bonds of owner 0x492Af743654549b12b1B807a9E0e8F397E44236E of type (class 1, nonce 14) are burned by operator 0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef. +*/ + +event burn(address _operator, address _owner, Transaction[] _transactions); + +/** +* Transfer +* @dev its emitted when the bond is transferred by address(operator) from owner address(_from) to address(_to) with the bonds transferred, whose params are defined by _transactions struct array. +* @dev Transfer MUST trigger when Bonds are transferred. This SHOULD not include zero value transfers. +* @dev Transfer event with the _from `0x0` MUST not create this event(use `event Issued` instead). +* e.g emit Transfer(0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef, 0x492Af743654549b12b1B807a9E0e8F397E44236E, _to, [IERC3475.Transaction(1,14,500)]); +* transfer by address(_operator) amount 500 bonds with (Class 1 and Nonce 14) from 0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef, to address(_to). +*/ + +event Transfer(address indexed _operator, address indexed _from, address indexed _to, Transaction[] _transactions); + +/** +* ApprovalFor +* @dev its emitted when address(_owner) approves the address(_operator) to transfer his bonds. +* @notice Approval MUST trigger when bond holders are approving an _operator. This SHOULD not include zero value approval. +* eg: emit ApprovalFor(0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef, 0x492Af743654549b12b1B807a9E0e8F397E44236E, true); +* this means 0x2d03B6C79B75eE7aB35298878D05fe36DC1fE8Ef gives 0x492Af743654549b12b1B807a9E0e8F397E44236E access permission for transfer of its bonds. +*/ + +event ApprovalFor(address indexed _owner, address indexed _operator, bool _approved); +``` + +**Metadata**: +The metadata of a bond class or nonce is stored as an array of JSON objects, represented by the following types. + +**NOTE: all of the metadata schemas are referenced from [here](../assets/eip-3475/Metadata.md)** + +### 1. Description: + +This defines the additional information about the nature of data being stored in the nonce/class metadata structures. They are defined using the structured explained [here](../assets/eip-3475/Metadata.md#1-description-metadata). this will then be used by the frontend of the respective entities participating in the bond markets to interpret the data which is compliant with their jurisdiction. + +### 2. Nonce: + +The key value for indexing the information is the 'class' field. Following are the rules: + +- The title can be any alphanumeric type that is differentiated by the description of metadata (although it can be dependent on certain jurisdictions). +- The title SHOULD not be EMPTY. + +Some specific examples of metadata can be the localization of bonds, jurisdiction details etc., and they can be found in the [metadata.md](../assets/eip-3475/Metadata.md) example description. + +### 3. Class metadata: + +This structure defines the details of the class information (symbol, risk information, etc.). the example is explained [here](../assets/eip-3475/Metadata.md) in the class metadata section. + +### 4. Decoding data + +First, the functions for analyzing the metadata (i.e `ClassMetadata` and `NonceMetadata`) are to be used by the corresponding frontend to decode the information of the bond. + +This is done via overriding the function interface for functions `classValues` and `nonceValues` by defining the key (which SHOULD be an index) to read the corresponding information stored as a JSON object. + +```JSON +{ +"title": "symbol", +"_type": "string", +"description": "defines the unique identifier name in following format: (symbol, bondType, maturity in months)", +"values": ["Class Name 1","Class Name 2","DBIT Fix 6M"], +} +``` + +e.g. In the above example, to get the `symbol` of the given class id, we can use the class id as a key to get the `symbol` value in the values, which then can be used for fetching the detail for instance. + +## Rationale + +### Metadata structure + +Instead of storing the details about the class and their issuances to the user (ie nonce) externally, we store the details in the respective structures. Classes represent the different bond types, and nonces represent the various period of issuances. Nonces under the same class share the same metadata. Meanwhile, nonces are non-fungible. Each nonce can store a different set of metadata. Thus, upon transfer of a bond, all the metadata will be transferred to the new owner of the bond. + +```solidity + struct Values{ + string stringValue; + uint uintValue; + address addressValue; + bool boolValue; + bytes bytesValue; + } +``` + +```solidity + struct Metadata { + string title; + string _type; + string description; + } +``` + +### Batch function + + This EIP supports batch operations. It allows the user to transfer different bonds along with their metadata to a new address instantaneously in a single transaction. After execution, the new owner holds the right to reclaim the face value of each of the bonds. This mechanism helps with the "packaging" of bonds–helpful in use cases like trades on a secondary market. + +```solidity + struct Transaction { + uint256 classId; + uint256 nonceId; + uint256 _amount; + } +``` + +Where: +The `classId` is the class id of the bond. + +The `nonceId` is the nonce id of the given bond class. This param is for distinctions of the issuing conditions of the bond. + +The `_amount` is the amount of the bond for which the spender is approved. + +### AMM optimization + + One of the most obvious use cases of this EIP is the multilayered pool. The early version of AMM uses a separate smart contract and an [EIP-20](./eip-20.md) LP token to manage a pair. By doing so, the overall liquidity inside of one pool is significantly reduced and thus generates unnecessary gas spent and slippage. Using this EIP standard, one can build a big liquidity pool with all the pairs inside (thanks to the presence of the data structures consisting of the liquidity corresponding to the given class and nonce of bonds). Thus by knowing the class and nonce of the bonds, the liquidity can be represented as the percentage of a given token pair for the owner of the bond in the given pool. Effectively, the [EIP-20](./eip-20.md) LP token (defined by a unique smart contract in the pool factory contract) is aggregated into a single bond and consolidated into a single pool. + +- The reason behind the standard's name (abstract storage bond) is its ability to store all the specifications (metadata/values and transaction as defined in the following sections) without needing external storage on-chain/off-chain. + +## Backwards Compatibility + +Any contract that inherits the interface of this EIP is compatible. This compatibility exists for issuer and receiver of the bonds. Also any client EOA wallet can be compatible with the standard if they are able to sign `issue()` and `redeem()` commands. + +However, any existing [EIP-20](./eip-20.md) token contract can issue its bonds by delegating the minting role to a bank contract with the interface of this standard built-in. Check out our reference implementation for the correct interface definition. + +To ensure the indexing of transactions throughout the bond lifecycle (i.e "Issue", "Redeem" and "Transfer" functions), events cited in specification section MUST be emitted when such transaction is passed. + +**Note that the this standard interface is also compatible with [EIP-20](./eip-20.md) and [EIP-721](./eip-721.md) and [EIP-1155](./eip-1155.md)interface.** + +However, creating a separate bank contract is recommended for reading the bonds and future upgrade needs. + +Acceptable collateral can be in the form of fungible (like [EIP-20](./eip-20.md)), non-fungible ([EIP-721](./eip-721.md), [EIP-1155](./eip-1155.md)) , or other bonds represented by this standard. + +## Test Cases + +Test-case for the minimal reference implementation is [here](../assets/eip-3475/ERC3475.test.ts). Use the Truffle box to compile and test the contracts. + +## Reference Implementation + +- [Interface](../assets/eip-3475/interfaces/IERC3475.sol). + +- [Basic Example](../assets/eip-3475/ERC3475.sol). + - This demonstration shows only minimalist implementation. + +## Security Considerations + +- The `function setApprovalFor(address _operatorAddress)` gives the operator role to `_operatorAddress`. It has all the permissions to transfer, burn and redeem bonds by default. + +- If the owner wants to give a one-time allocation to an address for specific bonds(classId,bondsId), he should call the `function approve()` giving the `Transaction[]` allocated rather than approving all the classes using `setApprovalFor`. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3508.md b/EIPS/eip-3508.md new file mode 100644 index 0000000..8c22e23 --- /dev/null +++ b/EIPS/eip-3508.md @@ -0,0 +1,100 @@ +--- +eip: 3508 +title: Transaction Data Opcodes +author: Alex Papageorgiou (@alex-ppg) +discussions-to: https://ethereum-magicians.org/t/eip-draft-transaction-data-opcodes/6017 +status: Stagnant +type: Standards Track +category: Core +created: 2021-04-16 +--- + +## Simple Summary + +Provide access to original transaction data. + +## Abstract + +This EIP introduces the following three EVM instructions: `ORIGINDATALOAD`, `ORIGINDATASIZE`, and `ORIGINDATACOPY`. + +These three instructions are meant to provide access to the original transaction's `data` payload, enabling a gas-efficient way of accessing large data payloads in cross-contract calls. + +## Motivation + +As the Ethereum development scene matures, more ambitious and complex features are introduced into smart contracts more often than not requiring the utilization of complex and at times large data structures. Given the inherent limits of the EVM, however, transporting large data structures in between contracts is a costly task that can at times lead to even futile scenarios whereby the gas consumption of such an operation is impossible to execute within the gas limit bounds as well as without sacrificing a large chunk of ETH to facilitate its gas cost. + +The purpose of this EIP is to render these features viable by introducing a way via which multi-contract systems are able to access the same in-memory data source without necessarily transmitting the full payload between them. + +This EIP enables elaborate smart contract features to become part of a larger call-chain by efficiently reading data from the original transaction payload rather than requiring the data to be passed in as call-level data. Its inclusion will mainly benefit advanced trustless schemes to manifest, such as efficient verification of Merkle Patricia trees validating the storage value of a particular Ethereum block or EVM-based layer 2 solutions. + +A side-effect of this change is that smart contract systems relying entirely on origin data inherently guarantee that the data they receive has not been malformed by an intermediate smart contract call. + +## Specification + +### ORIGINDATALOAD (`0x47`), ORIGINDATASIZE (`0x48`) and ORIGINDATACOPY (`0x49`) + +These instructions are meant to operate similarly to their call-prefixed counterparts with the exception that they instead operate on the original `data` of a transaction instead of the current call's data. In detail: + +- ORIGINDATALOAD (`0x47`) performs similarly to CALLDATALOAD (`0x35`) +- ORIGINDATASIZE (`0x48`) performs similarly to CALLDATASIZE (`0x36`) +- ORIGINDATACOPY (`0x49`) performs similarly to CALLDATACOPY (`0x37`) + +As the data is retrieved once again from the execution environment, the costs for the three instructions will be `G_verylow`, `G_base` and `G_base + G_verylow * (number of words copied, rounded up)` respectively. + +The transaction data the `ORIGINDATA*` opcodes operate on will be equivalent to the `calldata` specified in the `args*` parameter to the nearest `AUTHCALL` (`0xf7`) up the stack. If there is no `AUTHCALL` in the stack then `ORIGINDATA*` will operate on the transaction's original `data` field. + +This interaction ensures full compatibility with [EIP-3074](./eip-3074.md) and ensures that no form of discrimination is introduced back into the system by this EIP e.g. by contracts entirely relying on `ORIGINDATA*` and thus allowing only EOAs to supply data to them. + +## Rationale + +### AUTHCALL (`0xf7`) Interaction + +The [EIP-3074](./eip-3074.md) that will be part of the London fork has introduced a new call instruction called `AUTHCALL` (`0xf7`) that will replace a transaction's `ORIGIN` (`0x32`) with the context variable `authorized`. The intention of `AUTHCALL` is to prevent discrimination between smart contracts and EOAs which `ORIGIN` initially facilitated and as a result, it is sensible also replace the values retrieved by the `ORIGINDATA*` opcodes to the ones used in the `AUTHCALL`. + +### Naming Conventions + +The `ORIGIN`-prefixed instructions attempted to conform to the existing naming convention of `CALL`-prefixed instructions given the existence of the `ORIGIN` (`0x32`) instruction which is equivalent to the `CALLER` (`0x33`) instruction but on the original transaction's context. + +### Instruction Address Space + +The instruction address space of the `0x30-0x3f` has been exhausted by calls that already provide information about the execution context of a call so a new range had to be identified that is suitable for the purposes of the EIP. + +Given that the [EIP-1344](./eip-1344.md) `CHAINID` opcode was included at `0x46`, it made sense to include additional transaction-related data beyond it since the Chain ID is also included in transaction payloads apart from the blocks themselves, rendering the `0x46-0x4f` address space reserved for more transaction-related data that may be necessary in the future, such as the EOA's nonce. + +### Gas Costs + +The opcodes ORIGINDATALOAD (`0x47`), ORIGINDATASIZE (`0x48`), and ORIGINDATACOPY (`0x49`) essentially perform the same thing as opcodes CALLDATALOAD (`0x35`), CALLDATASIZE (`0x36`), and CALLDATACOPY (`0x37`) respectively and thus share the exact same gas costs. + +### Instruction Space Pollution + +One can argue that multiple new EVM instructions pollute the EVM instruction address space and could cause issues in assigning sensible instruction codes to future instructions. This particular issue was assessed and a methodology via which the raw RLP encoded transaction may be accessible to the EVM was ideated. This would _future-proof_ the new instruction set as it would be usable for other members of the transaction that may be desired to be accessible on-chain in the future, however, it would also cause a redundancy in the `ORIGIN` opcode. + +## Backwards Compatibility + +The EIP does not alter or adjust existing functionality provided by the EVM and as such, no known issues exist. + +## Test Cases + +TODO. + +## Security Considerations + +### Introspective Contracts + +Atomically, the `ORIGINDATALOAD` and `ORIGINDATACOPY` values should be considered insecure as they can easily be spoofed by creating an entry smart contract with the appropriate function signature and arguments that consequently invokes other contracts within the call chain. In brief, one should always assume that `tx.data != calldata` and these instructions should not be used as an introspection tool alone. + +### Denial-of-Service Attack + +An initial concern that may arise from this EIP is the additional contextual data that must be provided at the software level of nodes to the EVM in order for it to be able to access the necessary data via the `ORIGINDATALOAD` and `ORIGINDATACOPY` instructions. + +This would lead to an increase in memory consumption, however, this increase should be negligible if at all existent given that the data of a transaction should already exist in memory as part of its execution process; a step in the overall inclusion of a transaction within a block. + +### Multi-Contract System Gas Reduction + +Given that most complex smart contract systems deployed on Ethereum today rely on cross-contract interactions whereby values are passed from one contract to another via function calls, the `ORIGIN`-prefixed instruction set would enable a way for smart contract systems to acquire access to the original transaction data at any given step in the call chain execution which could result in cross-contract calls ultimately consuming less gas if the data passed between them is reduced as a side-effect of this change. + +The gas reduction, however, would be an implementation-based optimization that would also be solely applicable for rudimentary memory arguments rather than storage-based data, the latter of which is most commonly utilized in these types of calls. As a result, the overall gas reduction observed by this change will be negligible for most implementations. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3520.md b/EIPS/eip-3520.md new file mode 100644 index 0000000..8e83f74 --- /dev/null +++ b/EIPS/eip-3520.md @@ -0,0 +1,89 @@ +--- +eip: 3520 +title: Transaction Destination Opcode +author: Alex Papageorgiou (@alex-ppg) +discussions-to: https://ethereum-magicians.org/t/eip-3520-transaction-destination-opcode/6058 +status: Stagnant +type: Standards Track +category: Core +created: 2021-04-16 +requires: 3508 +--- + +## Simple Summary + +Provide access to the original recipient of a transaction. + +## Abstract + +This EIP introduces the following EVM instruction: `ENTRYPOINT`. + +This instruction is meant to provide access to the original recipient of the transaction, the `to` address, enabling new ways of introspection to be applied in conjunction with [EIP-3508](./eip-3508.md). + +## Motivation + +It is undeniable that smart contracts are becoming more interconnected than ever. Up until this point, smart contracts have entirely relied on compliant interfaces and introspection to introduce a new step in the call chain of a complex multi-contract interaction. However, this presents a forwards-only approach which limits the types of interactions that can manifest. + +The purpose of this EIP is to provide a way via which a contract is able to identify the entry-point of a transaction on the blockchain and deduce what was the original intention of the transaction by applying introspection on the original transaction data itself. + +This EIP enables the development of new types of smart contracts as it can open new pathways for [EIP-721](./eip-721) NFTs and [EIP-20](./eip-20) tokens to detect which action their transaction is part of, such as detecting a liquidity provision to a decentralized exchange or a loan within a collateralized lending protocol. + +## Specification + +### ENTRYPOINT (`0x4a`) + +The `ENTRYPOINT` instruction uses 0 stack arguments and pushes the original `to` member of the transaction onto the stack. The address yielded by the instruction is a 160-bit value padded to 256-bits. The operation costs `G_base` to execute, similarly to `ORIGIN` (`0x32`). + +The address returned by the `ENTRYPOINT` opcode will be equivalent to the `to` address parameter specified in the nearest `AUTHCALL` up the stack. If there is no `AUTHCALL` in the stack then `ENTRYPOINT` will retrieve the original transaction's `to` field. + +## Rationale + +### AUTHCALL (`0xf7`) Interaction + +The [EIP-3074](./eip-3074.md) introduced a new call instruction called `AUTHCALL` (`0xf7`) that will replace a transaction's `ORIGIN` (`0x32`) with the context variable `authorized`. The intention of `AUTHCALL` is to prevent discrimination between smart contracts and EOAs which `ORIGIN` initially facilitated. The `ENTRYPOINT` opcode by itself re-introduces discrimination into the system as it indirectly allows one to evaluate whether the smart contract code being executed is done so by an EOA by validating that `ENTRYPOINT == ADDRESS` where `ADDRESS` (`0x30`) retrieves the currently executing account address. Therefore, it is sensible also replace the values retrieved by the `ENTRYPOINT` opcode to the target of an `AUTHCALL`. + +This interaction ensures full compatibility with [EIP-3074](./eip-3074.md) and ensures that no form of discrimination is introduced back into the system by this EIP. + +### Naming Conventions + +The `ENTRYPOINT` instruction came to be by defining a sensible name that immediately and clearly depicts what it is meant to achieve by signaling the first interaction of a particular call, i.e. the entry-point. + +### Instruction Address Space + +Equivalent to [EIP-3508](./eip-3508). + +### Gas Cost + +The opcode ENTRYPOINT (`0x4a`) essentially performs the same thing as the opcode ORIGIN (`0x32`) and thus shares the exact same gas cost. + +### Dependency on EIP-3508 + +The `ENTRYPOINT` (`0x4a`) instruction alone has no perceivable benefit as it can be replaced by the `AUTHCALL` (`0xf7`) instruction and as such should solely be introduced to the system in conjunction with the `ORIGINDATA*` opcodes defined in [EIP-3508](./eip-3508.md). + +## Backwards Compatibility + +The EIP does not alter or adjust existing functionality provided by the EVM and as such, no known issues exist. + +## Test Cases + +TODO. + +## Security Considerations + +### Introspective Contracts + +The `ENTRYPOINT` instruction allows the association of the `ORIGINDATALOAD` and `ORIGINDATACOPY` values with a particular smart contract address and interface, enabling introspection to be applied based on the function signature invoked and the arguments provided to reliably deduce the call-path via which a particular smart contract was invoked and allowing a more granular level of interaction to be defined in such special cases. + +However, this type of introspection should solely be applied on pre-approved contracts rather than user-defined ones as the value stemming from this type of introspection entirely relies on a contract's code immutability and proper function, both of which a user supplied contract can easily bypass. + +### Caller Discrimination + +The instructions of this EIP should not be utilized as a way to discriminate between EOA callers and smart contracts, as this type of differentiation can be broken by an `AUTHCALL` as defined in the specification chapter. + +### Contract Creation Behaviour + +The behaviour of the `ENTRYPOINT` opcode during a contract creation will result in the opcode yielding the zero-address as the first address interacted with in the transaction. This should be taken into account by contract implementations in a similar fashion to how `ecrecover` invalid signatures are handled to prevent software misbehaviours from arising. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3521.md b/EIPS/eip-3521.md new file mode 100644 index 0000000..b88a08b --- /dev/null +++ b/EIPS/eip-3521.md @@ -0,0 +1,105 @@ +--- +eip: 3521 +title: Reduce access list cost +author: Matt Garnett (@lightclient) +discussions-to: https://ethereum-magicians.org/t/eip-3521-reduce-access-list-cost/6072 +status: Stagnant +type: Standards Track +category: Core +created: 2021-04-15 +requires: 2028, 2930 +--- + +## Simple Summary + +Reduce the cost of declaring `tx.to` storage keys in access lists. + +## Motivation + +Currently, a transaction must read at least 25 distinct storage slots in `tx.to` +before it's more expensive to forego an access list. + +``` +ACCESS_LIST_ADDRESS_COST + (ACCESS_LIST_STORAGE_KEY_COST + WARM_STORAGE_READ_COST) * x = COLD_SLOAD_COST * x +x = 24 +``` + +EIP-2930 requires the address under which the storage keys reside be declared +explicitly, since it must be added to the EIP-2929 `accessed_addresses` list. +However, `tx.to` is a special case that is added by default, so paying +`ACCESS_LIST_ADDRESS_COST` for `tx.to` is essentially paying twice for the same +address. Avoiding overpayment here will reduce the differential to just 5 unique +reads before using an access list is cheaper -- making them a more attractive +option. + +## Specification + +Treat the first occurrence of `tx.to` in an access list as `calldata` for gas +accounting purposes. Do not charge `ACCESS_LIST_ADDRESS_COST` for it. Storage +keys underneath the address are unaffected. + +If `tx.to == nil`, `tx.to` is defined be the derived contract address created by +the transaction. + +## Rationale + +### Why charge at all? + +EIP-2930 is specifically written to make access lists simple to reason about and +validate. It may be possible to modify the structure of the access list to avoid +including `tx.to` explicitly, but this would renege on the spirit of EIP-2930. + +### Why charge as `calldata`? + +The cost of `calldata` was thoroughly analyzed in EIP-2028 to determine +a fair value that is not susceptible to denial-of-service attacks. We consider +this the lower bound on how much transaction data should cost. Since there is +no computation burden imposed for adding `tx.to` to the `accessed_addresses` +map (it's added by default by [EIP-2929](./eip-2929.md)), there is no reason to charge more than +the absolute minimum for the data. + +## Test Cases +``` +{ + "0xffffffffffffffffffffffffffffffffffffffff": [] +} +cost = 320 + +{ + "0x00ffffffffffffffffffffffffffffffffffffff": [] +} +cost = 308 + +{ + "0xffffffffffffffffffffffffffffffffffffffff": [] + "0xffffffffffffffffffffffffffffffffffffffff": [] +} +cost = 2720 + +{ + "0xffffffffffffffffffffffffffffffffffffffff": [ + "0x00" + ] + "0xffffffffffffffffffffffffffffffffffffffff": [] +} +cost = 4620 + +{ + "0xffffffffffffffffffffffffffffffffffffffff": [ + "0x00" + ] + "0xffffffffffffffffffffffffffffffffffffffff": [ + "0x00" + ] +} +cost = 6520 +``` + +## Backwards Compatibility +No issues. + +## Security Considerations +None. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3525.md b/EIPS/eip-3525.md new file mode 100644 index 0000000..93bb1fc --- /dev/null +++ b/EIPS/eip-3525.md @@ -0,0 +1,575 @@ +--- +eip: 3525 +title: Semi-Fungible Token +description: Defines a specification where ERC-721 compatible tokens with the same SLOT and different IDs are fungible. +author: Will Wang (@will42w), Mike Meng , Yi Cai (@YeeTsai) , Ryan Chow , Zhongxin Wu (@Nerverwind), AlvisDu (@AlvisDu) +discussions-to: https://ethereum-magicians.org/t/eip-3525-the-semi-fungible-token +status: Final +type: Standards Track +category: ERC +created: 2020-12-01 +requires: 20, 165, 721 +--- + +## Abstract + +This is a standard for semi-fungible tokens. The set of smart contract interfaces described in this document defines an [ERC-721](./eip-721.md) compatible token standard. This standard introduces an `` triple scalar model that represents the semi-fungible structure of a token. It also introduces new transfer models as well as approval models that reflect the semi-fungible nature of the tokens. + +Token contains an ERC-721 equivalent ID property to identify itself as a universally unique entity, so that the tokens can be transferred between addresses and approved to be operated in ERC-721 compatible way. + +Token also contains a `value` property, representing the quantitative nature of the token. The meaning of the 'value' property is quite like that of the 'balance' property of an [ERC-20](./eip-20.md) token. Each token has a 'slot' attribute, ensuring that the value of two tokens with the same slot be treated as fungible, adding fungibility to the value property of the tokens. + +This EIP introduces new token transfer models for semi-fungibility, including value transfer between two tokens of the same slot and value transfer from a token to an address. + +## Motivation + +Tokenization is one of the most important trends by which to use and control digital assets in crypto. Traditionally, there have been two approaches to do so: fungible and non-fungible tokens. Fungible tokens generally use the ERC-20 standard, where every unit of an asset is identical to each other. ERC-20 is a flexible and efficient way to manipulate fungible tokens. Non-fungible tokens are predominantly ERC-721 tokens, a standard capable of distinguishing digital assets from one another based on identity. + +However, both have significant drawbacks. For example, ERC-20 requires that users create a separate ERC-20 contract for each individual data structure or combination of customizable properties. In practice, this results in an extraordinarily large amount of ERC-20 contracts that need to be created. On the other hand, ERC-721 tokens provide no quantitative feature, significantly undercutting their computability, liquidity, and manageability. For example, if one was to create financial instruments such as bonds, insurance policy, or vesting plans using ERC-721, no standard interfaces are available for us to control the value in them, making it impossible, for example, to transfer a portion of the equity in the contract represented by the token. + +A more intuitive and straightforward way to solve the problem is to create a semi-fungible token that has the quantitative features of ERC-20 and qualitative attributes of ERC-721. The backwards-compatibility with ERC-721 of such semi-fungible tokens would help utilize existing infrastructures already in use and lead to faster adoption. + +## Specification + +The keywords "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. + +**Every [ERC-3525](./eip-3525.md) compliant contract must implement the ERC-3525, ERC-721 and [ERC-165](./eip-165.md) interfaces** + +```solidity +pragma solidity ^0.8.0; + +/** + * @title ERC-3525 Semi-Fungible Token Standard + * Note: the ERC-165 identifier for this interface is 0xd5358140. + */ +interface IERC3525 /* is IERC165, IERC721 */ { + /** + * @dev MUST emit when value of a token is transferred to another token with the same slot, + * including zero value transfers (_value == 0) as well as transfers when tokens are created + * (`_fromTokenId` == 0) or destroyed (`_toTokenId` == 0). + * @param _fromTokenId The token id to transfer value from + * @param _toTokenId The token id to transfer value to + * @param _value The transferred value + */ + event TransferValue(uint256 indexed _fromTokenId, uint256 indexed _toTokenId, uint256 _value); + + /** + * @dev MUST emit when the approval value of a token is set or changed. + * @param _tokenId The token to approve + * @param _operator The operator to approve for + * @param _value The maximum value that `_operator` is allowed to manage + */ + event ApprovalValue(uint256 indexed _tokenId, address indexed _operator, uint256 _value); + + /** + * @dev MUST emit when the slot of a token is set or changed. + * @param _tokenId The token of which slot is set or changed + * @param _oldSlot The previous slot of the token + * @param _newSlot The updated slot of the token + */ + event SlotChanged(uint256 indexed _tokenId, uint256 indexed _oldSlot, uint256 indexed _newSlot); + + /** + * @notice Get the number of decimals the token uses for value - e.g. 6, means the user + * representation of the value of a token can be calculated by dividing it by 1,000,000. + * Considering the compatibility with third-party wallets, this function is defined as + * `valueDecimals()` instead of `decimals()` to avoid conflict with ERC-20 tokens. + * @return The number of decimals for value + */ + function valueDecimals() external view returns (uint8); + + /** + * @notice Get the value of a token. + * @param _tokenId The token for which to query the balance + * @return The value of `_tokenId` + */ + function balanceOf(uint256 _tokenId) external view returns (uint256); + + /** + * @notice Get the slot of a token. + * @param _tokenId The identifier for a token + * @return The slot of the token + */ + function slotOf(uint256 _tokenId) external view returns (uint256); + + /** + * @notice Allow an operator to manage the value of a token, up to the `_value`. + * @dev MUST revert unless caller is the current owner, an authorized operator, or the approved + * address for `_tokenId`. + * MUST emit the ApprovalValue event. + * @param _tokenId The token to approve + * @param _operator The operator to be approved + * @param _value The maximum value of `_toTokenId` that `_operator` is allowed to manage + */ + function approve( + uint256 _tokenId, + address _operator, + uint256 _value + ) external payable; + + /** + * @notice Get the maximum value of a token that an operator is allowed to manage. + * @param _tokenId The token for which to query the allowance + * @param _operator The address of an operator + * @return The current approval value of `_tokenId` that `_operator` is allowed to manage + */ + function allowance(uint256 _tokenId, address _operator) external view returns (uint256); + + /** + * @notice Transfer value from a specified token to another specified token with the same slot. + * @dev Caller MUST be the current owner, an authorized operator or an operator who has been + * approved the whole `_fromTokenId` or part of it. + * MUST revert if `_fromTokenId` or `_toTokenId` is zero token id or does not exist. + * MUST revert if slots of `_fromTokenId` and `_toTokenId` do not match. + * MUST revert if `_value` exceeds the balance of `_fromTokenId` or its allowance to the + * operator. + * MUST emit `TransferValue` event. + * @param _fromTokenId The token to transfer value from + * @param _toTokenId The token to transfer value to + * @param _value The transferred value + */ + function transferFrom( + uint256 _fromTokenId, + uint256 _toTokenId, + uint256 _value + ) external payable; + + + /** + * @notice Transfer value from a specified token to an address. The caller should confirm that + * `_to` is capable of receiving ERC-3525 tokens. + * @dev This function MUST create a new ERC-3525 token with the same slot for `_to`, + * or find an existing token with the same slot owned by `_to`, to receive the transferred value. + * MUST revert if `_fromTokenId` is zero token id or does not exist. + * MUST revert if `_to` is zero address. + * MUST revert if `_value` exceeds the balance of `_fromTokenId` or its allowance to the + * operator. + * MUST emit `Transfer` and `TransferValue` events. + * @param _fromTokenId The token to transfer value from + * @param _to The address to transfer value to + * @param _value The transferred value + * @return ID of the token which receives the transferred value + */ + function transferFrom( + uint256 _fromTokenId, + address _to, + uint256 _value + ) external payable returns (uint256); +} +``` + +The slot's enumeration extension is OPTIONAL. This allows your contract to publish its full list of `SLOT`s and make them discoverable. + +```solidity +pragma solidity ^0.8.0; + +/** + * @title ERC-3525 Semi-Fungible Token Standard, optional extension for slot enumeration + * @dev Interfaces for any contract that wants to support enumeration of slots as well as tokens + * with the same slot. + * Note: the ERC-165 identifier for this interface is 0x3b741b9e. + */ +interface IERC3525SlotEnumerable is IERC3525 /* , IERC721Enumerable */ { + + /** + * @notice Get the total amount of slots stored by the contract. + * @return The total amount of slots + */ + function slotCount() external view returns (uint256); + + /** + * @notice Get the slot at the specified index of all slots stored by the contract. + * @param _index The index in the slot list + * @return The slot at `index` of all slots. + */ + function slotByIndex(uint256 _index) external view returns (uint256); + + /** + * @notice Get the total amount of tokens with the same slot. + * @param _slot The slot to query token supply for + * @return The total amount of tokens with the specified `_slot` + */ + function tokenSupplyInSlot(uint256 _slot) external view returns (uint256); + + /** + * @notice Get the token at the specified index of all tokens with the same slot. + * @param _slot The slot to query tokens with + * @param _index The index in the token list of the slot + * @return The token ID at `_index` of all tokens with `_slot` + */ + function tokenInSlotByIndex(uint256 _slot, uint256 _index) external view returns (uint256); +} +``` + +The slot level approval is OPTIONAL. This allows any contract that wants to support approval for slots, which allows an operator to manage one's tokens with the same slot. + +```solidity +pragma solidity ^0.8.0; + +/** + * @title ERC-3525 Semi-Fungible Token Standard, optional extension for approval of slot level + * @dev Interfaces for any contract that wants to support approval of slot level, which allows an + * operator to manage one's tokens with the same slot. + * See https://eips.ethereum.org/EIPS/eip-3525 + * Note: the ERC-165 identifier for this interface is 0xb688be58. + */ +interface IERC3525SlotApprovable is IERC3525 { + /** + * @dev MUST emit when an operator is approved or disapproved to manage all of `_owner`'s + * tokens with the same slot. + * @param _owner The address whose tokens are approved + * @param _slot The slot to approve, all of `_owner`'s tokens with this slot are approved + * @param _operator The operator being approved or disapproved + * @param _approved Identify if `_operator` is approved or disapproved + */ + event ApprovalForSlot(address indexed _owner, uint256 indexed _slot, address indexed _operator, bool _approved); + + /** + * @notice Approve or disapprove an operator to manage all of `_owner`'s tokens with the + * specified slot. + * @dev Caller SHOULD be `_owner` or an operator who has been authorized through + * `setApprovalForAll`. + * MUST emit ApprovalSlot event. + * @param _owner The address that owns the ERC-3525 tokens + * @param _slot The slot of tokens being queried approval of + * @param _operator The address for whom to query approval + * @param _approved Identify if `_operator` would be approved or disapproved + */ + function setApprovalForSlot( + address _owner, + uint256 _slot, + address _operator, + bool _approved + ) external payable; + + /** + * @notice Query if `_operator` is authorized to manage all of `_owner`'s tokens with the + * specified slot. + * @param _owner The address that owns the ERC-3525 tokens + * @param _slot The slot of tokens being queried approval of + * @param _operator The address for whom to query approval + * @return True if `_operator` is authorized to manage all of `_owner`'s tokens with `_slot`, + * false otherwise. + */ + function isApprovedForSlot( + address _owner, + uint256 _slot, + address _operator + ) external view returns (bool); +} +``` + + +### ERC-3525 Token Receiver + +If a smart contract wants to be informed when they receive values from other addresses, it should implement all of the functions in the `IERC3525Receiver` interface, in the implementation it can decide whether to accept or reject the transfer. See "Transfer Rules" for further detail. + +```solidity + pragma solidity ^0.8.0; + +/** + * @title ERC-3525 token receiver interface + * @dev Interface for a smart contract that wants to be informed by ERC-3525 contracts when receiving values from ANY addresses or ERC-3525 tokens. + * Note: the ERC-165 identifier for this interface is 0x009ce20b. + */ +interface IERC3525Receiver { + /** + * @notice Handle the receipt of an ERC-3525 token value. + * @dev An ERC-3525 smart contract MUST check whether this function is implemented by the recipient contract, if the + * recipient contract implements this function, the ERC-3525 contract MUST call this function after a + * value transfer (i.e. `transferFrom(uint256,uint256,uint256,bytes)`). + * MUST return 0x009ce20b (i.e. `bytes4(keccak256('onERC3525Received(address,uint256,uint256, + * uint256,bytes)'))`) if the transfer is accepted. + * MUST revert or return any value other than 0x009ce20b if the transfer is rejected. + * @param _operator The address which triggered the transfer + * @param _fromTokenId The token id to transfer value from + * @param _toTokenId The token id to transfer value to + * @param _value The transferred value + * @param _data Additional data with no specified format + * @return `bytes4(keccak256('onERC3525Received(address,uint256,uint256,uint256,bytes)'))` + * unless the transfer is rejected. + */ + function onERC3525Received(address _operator, uint256 _fromTokenId, uint256 _toTokenId, uint256 _value, bytes calldata _data) external returns (bytes4); + +} +``` + +### Token Manipulation + +#### Scenarios + +**_Transfer:_** + +Besides ERC-721 compatible token transfer methods, this EIP introduces two new transfer models: value transfer from ID to ID, and value transfer from ID to address. + +```solidity +function transferFrom(uint256 _fromTokenId, uint256 _toTokenId, uint256 _value) external payable; + +function transferFrom(uint256 _fromTokenId, address _to, uint256 _value) external payable returns (uint256 toTokenId_); +``` + +The first one allows value transfers from one token (specified by `_fromTokenId`) to another token (specified by `_toTokenId`) within the same slot, resulting in the `_value` being subtracted from the value of the source token and added to the value of the destination token; + +The second one allows value transfers from one token (specified by `_fromTokenId`) to an address (specified by `_to`), the value is actually transferred to a token owned by the address, and the id of the destination token should be returned. Further explanation can be found in the 'design decision' section for this method. + +#### Rules + +**_approving rules:_** + +This EIP provides four kinds of approving functions indicating different levels of approvals, which can be described as full level approval, slot level approval, token ID level approval as well as value level approval. + +- `setApprovalForAll`, compatible with ERC-721, SHOULD indicate the full level of approval, which means that the authorized operators are capable of managing all the tokens, including their values, owned by the owner. +- `setApprovalForSlot` (optional) SHOULD indicate the slot level of approval, which means that the authorized operators are capable of managing all the tokens with the specified slot, including their values, owned by the owner. +- The token ID level `approve` function, compatible with ERC-721, SHOULD indicate that the authorized operator is capable of managing only the specified token ID, including its value, owned by the owner. +- The value level `approve` function, SHOULD indicate that the authorized operator is capable of managing the specified maximum value of the specified token owned by the owner. +- For any approving function, the caller MUST be the owner or has been approved with a higher level of authority. + +**_transferFrom rules:_** + +- The `transferFrom(uint256 _fromTokenId, uint256 _toTokenId, uint256 _value)` function, SHOULD indicate value transfers from one token to another token, in accordance with the rules below: + + - MUST revert unless `msg.sender` is the owner of `_fromTokenId`, an authorized operator or an operator who has been approved the whole token or at least `_value` of it. + - MUST revert if `_fromTokenId` or `_toTokenId` is zero token id or does not exist. + - MUST revert if slots of `_fromTokenId` and `_toTokenId` do not match. + - MUST revert if `_value` exceeds the value of `_fromTokenId` or its allowance to the operator. + - MUST check for the `onERC3525Received` function if the owner of _toTokenId is a smart contract, if the function exists, MUST call this function after the value transfer, MUST revert if the result is not equal to 0x009ce20b; + - MUST emit `TransferValue` event. + +- The `transferFrom(uint256 _fromTokenId, address _to, uint256 _value)` function, which transfers value from one token ID to an address, SHOULD follow the rule below: + + - MUST either find a ERC-3525 token owned by the address `_to` or create a new ERC-3525 token, with the same slot of `_fromTokenId`, to receive the transferred value. + - MUST revert unless `msg.sender` is the owner of `_fromTokenId`, an authorized operator or an operator who has been approved the whole token or at least `_value` of it. + - MUST revert if `_fromTokenId` is zero token id or does not exist. + - MUST revert if `_to` is zero address. + - MUST revert if `_value` exceeds the value of `_fromTokenId` or its allowance to the operator. + - MUST check for the `onERC3525Received` function if the _to address is a smart contract, if the function exists, MUST call this function after the value transfer, MUST revert if the result is not equal to 0x009ce20b; + - MUST emit `Transfer` and `TransferValue` events. + + +### Metadata + +#### Metadata Extensions + +ERC-3525 metadata extensions are compatible ERC-721 metadata extensions. + +This optional interface can be identified with the ERC-165 Standard Interface Detection. + +```solidity +pragma solidity ^0.8.0; + +/** + * @title ERC-3525 Semi-Fungible Token Standard, optional extension for metadata + * @dev Interfaces for any contract that wants to support query of the Uniform Resource Identifier + * (URI) for the ERC-3525 contract as well as a specified slot. + * Because of the higher reliability of data stored in smart contracts compared to data stored in + * centralized systems, it is recommended that metadata, including `contractURI`, `slotURI` and + * `tokenURI`, be directly returned in JSON format, instead of being returned with a url pointing + * to any resource stored in a centralized system. + * See https://eips.ethereum.org/EIPS/eip-3525 + * Note: the ERC-165 identifier for this interface is 0xe1600902. + */ +interface IERC3525Metadata is + IERC3525 /* , IERC721Metadata */ +{ + /** + * @notice Returns the Uniform Resource Identifier (URI) for the current ERC-3525 contract. + * @dev This function SHOULD return the URI for this contract in JSON format, starting with + * header `data:application/json;`. + * See https://eips.ethereum.org/EIPS/eip-3525 for the JSON schema for contract URI. + * @return The JSON formatted URI of the current ERC-3525 contract + */ + function contractURI() external view returns (string memory); + + /** + * @notice Returns the Uniform Resource Identifier (URI) for the specified slot. + * @dev This function SHOULD return the URI for `_slot` in JSON format, starting with header + * `data:application/json;`. + * See https://eips.ethereum.org/EIPS/eip-3525 for the JSON schema for slot URI. + * @return The JSON formatted URI of `_slot` + */ + function slotURI(uint256 _slot) external view returns (string memory); +} +``` + +#### ERC-3525 Metadata URI JSON Schema + +This is the "ERC-3525 Metadata JSON Schema for `contractURI()`" referenced above. + +```json +{ + "title": "Contract Metadata", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Contract Name" + }, + "description": { + "type": "string", + "description": "Describes the contract" + }, + "image": { + "type": "string", + "description": "Optional. Either a base64 encoded imgae data or a URI pointing to a resource with mime type image/* representing what this contract represents." + }, + "external_link": { + "type": "string", + "description": "Optional. A URI pointing to an external resource." + }, + "valueDecimals": { + "type": "integer", + "description": "The number of decimal places that the balance should display - e.g. 18, means to divide the token value by 1000000000000000000 to get its user representation." + } + } +} +``` + +This is the "ERC-3525 Metadata JSON Schema for `slotURI(uint)`" referenced above. + +```json +{ + "title": "Slot Metadata", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Identifies the asset category to which this slot represents" + }, + "description": { + "type": "string", + "description": "Describes the asset category to which this slot represents" + }, + "image": { + "type": "string", + "description": "Optional. Either a base64 encoded imgae data or a URI pointing to a resource with mime type image/* representing the asset category to which this slot represents." + }, + "properties": { + "type": "array", + "description": "Each item of `properties` SHOULD be organized in object format, including name, description, value, order (optional), display_type (optional), etc." + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The name of this property." + }, + "description": { + "type": "string", + "description": "Describes this property." + } + "value": { + "description": "The value of this property, which may be a string or a number." + }, + "is_intrinsic": { + "type": "boolean", + "description": "According to the definition of `slot`, one of the best practice to generate the value of a slot is utilizing the `keccak256` algorithm to calculate the hash value of multi properties. In this scenario, the `properties` field should contain all the properties that are used to calculate the value of `slot`, and if a property is used in the calculation, is_intrinsic must be TRUE." + }, + "order": { + "type": "integer", + "description": "Optional, related to the value of is_intrinsic. If is_intrinsic is TRUE, it must be the order of this property appeared in the calculation method of the slot." + }, + "display_type": { + "type": "string", + "description": "Optional. Specifies in what form this property should be displayed." + } + } + } + } + } +} +``` + + +This is the "ERC-3525 Metadata JSON Schema for `tokenURI(uint)`" referenced above. + +```json +{ + "title": "Token Metadata", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Identifies the asset to which this token represents" + }, + "description": { + "type": "string", + "description": "Describes the asset to which this token represents" + }, + "image": { + "type": "string", + "description": "Either a base64 encoded imgae data or a URI pointing to a resource with mime type image/* representing the asset to which this token represents." + }, + "balance": { + "type": "integer", + "description": "THe value held by this token." + }, + "slot": { + "type": "integer", + "description": "The id of the slot that this token belongs to." + }, + "properties": { + "type": "object", + "description": "Arbitrary properties. Values may be strings, numbers, objects or arrays. Optional, you can use the same schema as the properties section of ERC-3525 Metadata JSON Schema for slotURI(uint) if you need a better description attribute." + } + } +} +``` + + +## Rationale + +### Metadata generation + +This token standard is designed to represent semi-fungible assets, which are most suited for financial instruments rather than collectibles or in-game items. For maximum transparency and safety of digital assets, we strongly recommend that all implementations should generate metadata directly from contract code rather than giving out an off-chain server URL. + +### Design decision: Value transfer from token to address + +The 'value' of a token is a property of the token and is not linked to an address, so to transfer the value to an address would be actually transferring it to a token owned by that address, not the address itself. + +From the implementation perspective, the process of transferring values from token to address could be done as follows: (1) create a new token for the recipient's address, (2) transfer the value to the new token from the 'source token'. So that this method is not fully independent from the ID-to-ID transfer method, and can be viewed as syntactic sugar that wraps the process described above. + +In a special case, if the destination address owns one or more tokens with the same slot value as the source token, this method will have an alternative implementation as follows: (1) find one token owned by the address with the same slot value of the source token, (2) transfer the value to the found token. + +Both implementations described above should be treated as compliant with this standard. + +The purpose of maintaining id-to-address transfer function is to maximize the compatibility with most wallet apps, since for most of the token standards, the destination of token transfer are addresses. This syntactic wrapping will help wallet apps easily implement the value transfer function from a token to any address. + +### Design decision: Notification/acceptance mechanism instead of 'Safe Transfer' + +ERC-721 and some later token standards introduced 'Safe Transfer' model, for better control of the 'safety' when transferring tokens, this mechanism leaves the choice of different transfer modes (safe/unsafe) to the sender, and may cause some potential problems: + +1. In most situations the sender does not know how to choose between two kinds of transfer methods (safe/unsafe); +2. If the sender calls the `safeTransferFrom` method, the transfer may fail if the recipient contract did not implement the callback function, even if that contract is capable of receiving and manipulating the token without issue. + +This EIP defines a simple 'Check, Notify and Response' model for better flexibility as well as simplicity: + +1. No extra `safeTransferFrom` methods are needed, all callers only need to call one kind of transfer; +2. All ERC-3525 contracts MUST check for the existence of `onERC3525Received` on the recipient contract and call the function when it exists; +3. Any smart contract can implement `onERC3525Received` function for the purpose of being notified after receiving values; this function MUST return 0x009ce20b (i.e. `bytes4(keccak256('onERC3525Received(address,uint256,uint256,uint256,bytes)'))`) if the transfer is accepted, or any other value if the transfer is rejected. + +There is a special case for this notification/acceptance mechanism: since ERC-3525 allows value transfer from an address to itself, when a smart contract which implements `onERC3525Received` transfers value to itself, `onERC3525Received` will also be called. This allows for the contract to implement different rules of acceptance between self-value-transfer and receiving value from other addresses. + +### Design decision: Relationship between different approval models + +For semantic compatibility with ERC-721 as well as the flexibility of value manipulation of tokens, we decided to define the relationships between some of the levels of approval like that: + +1. Approval of an id will lead to the ability to partially transfer values from this id by the approved operator; this will simplify the value approval for an id. However, the approval of total values in a token should not lead to the ability to transfer the token entity by the approved operator. +2. `setApprovalForAll` will lead to the ability to partially transfer values from any token, as well as the ability to approve partial transfer of values from any token to a third party; this will simplify the value transfer and approval of all tokens owned by an address. + +## Backwards Compatibility + +As mentioned in the beginning, this EIP is backward compatible with ERC-721. + +## Reference Implementation + +- [ERC-3525 implementation](../assets/eip-3525/contracts/ERC3525.sol) + +## Security Considerations + +The value level approval and slot level approval (optional) is isolated from ERC-721 approval models, so that approving value should not affect ERC-721 level approvals. Implementations of this EIP must obey this principle. + +Since this EIP is ERC-721 compatible, any wallets and smart contracts that can hold and manipulate standard ERC-721 tokens will have no risks of asset loss for ERC-3525 tokens due to incompatible standards implementations. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3529.md b/EIPS/eip-3529.md new file mode 100644 index 0000000..f962230 --- /dev/null +++ b/EIPS/eip-3529.md @@ -0,0 +1,140 @@ +--- +eip: 3529 +title: Reduction in refunds +author: Vitalik Buterin (@vbuterin), Martin Swende (@holiman) +discussions-to: https://ethereum-magicians.org/t/eip-3529-reduction-in-refunds-alternative-to-eip-3298-and-3403-that-better-preserves-existing-clearing-incentives/6097 +status: Final +type: Standards Track +category: Core +created: 2021-04-22 +requires: 2200, 2929, 2930 +--- + +## Simple Summary + +Remove gas refunds for `SELFDESTRUCT`, and reduce gas refunds for `SSTORE` to a lower level where the refunds are still substantial, but they are no longer high enough for current "exploits" of the refund mechanism to be viable. + +## Motivation + +Gas refunds for `SSTORE` and `SELFDESTRUCT` were originally introduced to motivate application developers to write applications that practice "good state hygiene", clearing storage slots and contracts that are no longer needed. However, the benefits of this technique have proven to be far lower than anticipated, and gas refunds have had multiple unexpected harmful consequences: + +* Refunds give rise to GasToken. GasToken has benefits in moving gas space from low-fee periods to high-fee periods, but it also has downsides to the network, particularly in exacerbating state size (as state slots are effectively used as a "battery" to save up gas) and inefficiently clogging blockchain gas usage +* Refunds increase block size variance. The theoretical maximum amount of actual gas consumed in a block is nearly twice the on-paper gas limit (as refunds add gas space for subsequent transactions in a block, though refunds are capped at 50% of a transaction's gas used). This is not fatal, but is still undesirable, especially given that refunds can be used to maintain 2x usage spikes for far longer than EIP-1559 can. + + +## Specification + +### Parameters + +| Constant | Value | +| - | - | +| `FORK_BLOCK` | TBD | +| `MAX_REFUND_QUOTIENT` | 5 | + +For blocks where `block.number >= FORK_BLOCK`, the following changes apply. + +1. Remove the `SELFDESTRUCT` refund. +2. Replace `SSTORE_CLEARS_SCHEDULE` (as defined in [EIP-2200](./eip-2200.md)) with `SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST` (4,800 gas as of [EIP-2929](./eip-2929.md) + [EIP-2930](./eip-2930.md)) +3. Reduce the max gas refunded after a transaction to `gas_used // MAX_REFUND_QUOTIENT` + +Remark: Previously _max gas refunded_ was defined as `gas_used // 2`. Here we +name the constant `2` as `MAX_REFUND_QUOTIENT` and change its value to `5`. + +## Rationale + +In [EIP-2200](./eip-2200.md#specification), three cases for refunds were introduced: + +1. If the original value is nonzero, and the new value is zero, add `SSTORE_CLEARS_SCHEDULE` (currently 15,000) gas to the refund counter +2. If the original value is zero, the current value is nonzero, and the new value is zero, add `SSTORE_SET_GAS - SLOAD_GAS` (currently 19,900) gas to the refund counter +3. If the original value is nonzero, the current value is a different nonzero value, and the new value equals the original value, add `SSTORE_RESET_GAS - SLOAD_GAS` (currently 4,900) gas to the refund counter + +Of these three, only (1) enables gastokens and allows a block to expend more gas on execution than the block gas limit. (2) does not have this property, because for the 19,900 refund to be obtained, _the same storage slot_ must have been changed from zero to nonzero previously, costing 20,000 gas. The inability to obtain gas from clearing one storage slot and use it to edit another storage slot means that it cannot be used for gas tokens. Additionally, obtaining the refund requires _reverting_ the effect of the storage write and expansion, so the refunded gas does not contribute to a client's load in processing a block. (3) behaves similarly: the 4,900 refund can only be obtained when 5,000 gas had previously been spent on the same storage slot. + +This EIP deals with case (1). We can establish under what conditions a gastoken is nonviable (ie. you cannot get more gas out of a storage slot than you put in) by using a similar "pairing" argument, mapping each refund to a previous expenditure in the same transaction on the same storage slot. lf a storage slot is changed to zero when its original value is nonzero, there are two possibilities: + +1. This could be the first time that the storage slot is set to zero. In this case, we can pair this event with the `SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST` minimum cost of reading and editing the storage slot for the first time. +2. This could be the second or later time that the storage slot is set to zero. In this case, we can pair this event with the most recent previous time that the value was set _away_ from zero, in which `SSTORE_CLEARS_SCHEDULE` gas is _removed_ from the refund. + +For the second and later event, it does not matter what value `SSTORE_CLEARS_SCHEDULE` has, because every refund of that size is paired with a refund _removal_ of the same size. This leaves the first event. For the total gas expended on the slot to be guaranteed to be positive, we need `SSTORE_CLEARS_SCHEDULE <= SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST`. And so this EIP simply decreases `SSTORE_CLEARS_SCHEDULE` to the sum of those two costs. + +One alternative intuition for this EIP is that there will not be a net refund for clearing data that has not yet been read (which is often "useless" data), but there will continue to be a net refund for clearing data that has been read (which is likely to be "useful" data). + +## Backwards Compatibility + +Refunds are currently only applied _after_ transaction execution, so they cannot affect how much gas is available to any particular call frame during execution. Hence, removing them will not break the ability of any code to execute, though it will render some applications economically nonviable. + +Gas tokens will become valueless. DeFi arbitrage bots, which today frequently use either established gas token schemes or a custom alternative to reduce on-chain costs, would benefit from rewriting their code to remove calls to these no-longer-functional gas storage mechanisms. + +However, fully preserving refunds in the `new = original = 0 != current` case, and keeping _some_ refund in the other `nonzero -> zero` cases, ensures that a few key use cases that receive (and deserve) favorable gas cost treatment continue to do so. For example, `zero -> nonzero -> zero` storage set patterns continue to cost only ~100 gas. Two important examples of such patterns include: + +* Anti-reentrancy locks (typically flipped from 0 to 1 right before a child call begins, and then flipped back to 0 when the child call ends) +* ERC20 approve-and-send (the "approved value" goes from zero to nonzero when the token transfer is approved, and then back to zero when the token transfer processes) + +### Effect on storage clearing incentives + +A criticism of earlier refund removal EIPs ([EIP-3298](./eip-3298.md) and [EIP-3403](./eip-3403.md)) is that these EIPs fully remove the incentive to set a value to zero, encouraging users to not fully clear a storage slot if they expect even the smallest probability that they will want to use that storage slot again. + +For example, if you have 1 unit of an ERC20 token and you are giving away or selling your entire balance, you could instead only give away 0.999999 units and leave the remainder behind. If you ever decide to re-acquire more of that token with the same account in the future, you would only have to pay 5000 gas (2100 for the read + 2900 for nonzero-to-nonzero set) for the `SSTORE` instead of 22100 (20000 for the zero-to-nonzero set). Today, this is counterbalanced by the 15000 refund for clearing, so you only have an incentive to do this if you are more than `15000 / 17100 = 87.7%` sure that you will use the slot again; with EIP-3298 or EIP-3403 the counterbalancing incentive would not exist, so setting to nonzero is better if your chance of using the slot again is _any_ value greater than 0%. + +A refund of 4800 gas remains, so there is only be an incentive to keep a storage slot nonzero if you expect a probability of more than `4800 / 17100 = 28.1%` that you will use that slot again. This is not perfect, but it is likely higher than the average person's expectations of later re-acquiring a token with the same address if they clear their entire balance of it. + +The capping of refunds to 1/5 of gas expended means that this refund can only be used to increase the amount of storage write operations needed to process a block by at most 25%, limiting the ability to use this mechanic for storage-write-focused denial-of-service attacks. + +## Test Cases + +### EIP-2929 Gas Costs + +Note, there is a difference between 'hot' and 'cold' slots. This table shows the values as of [EIP-2929](./eip-2929.md) assuming that all touched storage slots were already 'hot' (the difference being a one-time cost of `2100` gas). + +| Code | Used Gas | Refund | Original | 1st | 2nd | 3rd | Effective gas (after refund) +| -- | -- | -- | -- | -- | -- | -- | -- | +| `0x60006000556000600055` | 212 | 0| 0 | 0 | 0 | | 212 | +| `0x60006000556001600055` | 20112 | 0| 0 | 0 | 1 | | 20112 | +| `0x60016000556000600055` | 20112 | 19900| 0 | 1 | 0 | | 212 | +| `0x60016000556002600055` | 20112 | 0| 0 | 1 | 2 | | 20112 | +| `0x60016000556001600055` | 20112 | 0| 0 | 1 | 1 | | 20112 | +| `0x60006000556000600055` | 3012 | 15000| 1 | 0 | 0 | | -11988 | +| `0x60006000556001600055` | 3012 | 2800| 1 | 0 | 1 | | 212 | +| `0x60006000556002600055` | 3012 | 0| 1 | 0 | 2 | | 3012 | +| `0x60026000556000600055` | 3012 | 15000| 1 | 2 | 0 | | -11988 | +| `0x60026000556003600055` | 3012 | 0| 1 | 2 | 3 | | 3012 | +| `0x60026000556001600055` | 3012 | 2800| 1 | 2 | 1 | | 212 | +| `0x60026000556002600055` | 3012 | 0| 1 | 2 | 2 | | 3012 | +| `0x60016000556000600055` | 3012 | 15000| 1 | 1 | 0 | | -11988 | +| `0x60016000556002600055` | 3012 | 0| 1 | 1 | 2 | | 3012 | +| `0x60016000556001600055` | 212 | 0| 1 | 1 | 1 | | 212 | +| `0x600160005560006000556001600055` | 40118 | 19900| 0 | 1 | 0 | 1 | 20218 | +| `0x600060005560016000556000600055` | 5918 | 17800| 1 | 0 | 1 | 0 | -11882 | + +### With reduced refunds + +If refunds were to be partially removed, by changing `SSTORE_CLEARS_SCHEDULE` from 15000 to 4800 (and removing selfdestruct refund) this would be the comparative table. + +| Code | Used Gas | Refund | Original | 1st | 2nd | 3rd | Effective gas (after refund) +| -- | -- | -- | -- | -- | -- | -- | -- | +| `0x60006000556000600055` | 212 | 0| 0 | 0 | 0 | | 212 | +| `0x60006000556001600055` | 20112 | 0| 0 | 0 | 1 | | 20112 | +| `0x60016000556000600055` | 20112 | 19900| 0 | 1 | 0 | | 212 | +| `0x60016000556002600055` | 20112 | 0| 0 | 1 | 2 | | 20112 | +| `0x60016000556001600055` | 20112 | 0| 0 | 1 | 1 | | 20112 | +| `0x60006000556000600055` | 3012 | 4800| 1 | 0 | 0 | | -1788 | +| `0x60006000556001600055` | 3012 | 2800| 1 | 0 | 1 | | 212 | +| `0x60006000556002600055` | 3012 | 0| 1 | 0 | 2 | | 3012 | +| `0x60026000556000600055` | 3012 | 4800| 1 | 2 | 0 | | -1788 | +| `0x60026000556003600055` | 3012 | 0| 1 | 2 | 3 | | 3012 | +| `0x60026000556001600055` | 3012 | 2800| 1 | 2 | 1 | | 212 | +| `0x60026000556002600055` | 3012 | 0| 1 | 2 | 2 | | 3012 | +| `0x60016000556000600055` | 3012 | 4800| 1 | 1 | 0 | | -1788 | +| `0x60016000556002600055` | 3012 | 0| 1 | 1 | 2 | | 3012 | +| `0x60016000556001600055` | 212 | 0| 1 | 1 | 1 | | 212 | +| `0x600160005560006000556001600055` | 40118 | 19900| 0 | 1 | 0 | 1 | 20218 | +| `0x600060005560016000556000600055` | 5918 | 7600| 1 | 0 | 1 | 0 | -1682 | + +## Security Considerations + +Refunds are not visible to transaction execution, so this should not have any impact on transaction execution logic. + +The maximum amount of gas that can be spent on execution in a block is limited to the gas limit, if we do not count zero-to-nonzero `SSTORE`s that were later reset back to zero. It is okay to not count those, because if such an `SSTORE` is reset, storage is not expanded and the client does not need to actually adjust the Merke tree; the gas consumption is refunded, but the effort normally required by the client to process those opcodes is also cancelled. **Clients should make sure to not do a storage write if `new_value = original_value`; this was a prudent optimization since the beginning of Ethereum but it becomes more important now.** + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3534.md b/EIPS/eip-3534.md new file mode 100644 index 0000000..2876b58 --- /dev/null +++ b/EIPS/eip-3534.md @@ -0,0 +1,312 @@ +--- +eip: 3534 +title: Restricted Chain Context Type Transactions +author: Isaac Ardis (@whilei) +discussions-to: https://ethereum-magicians.org/t/eip-3534-restricted-chain-context-transaction-type/6112 +status: Stagnant +type: Standards Track +category: Core +created: 2021-04-20 +requires: 2718, 2930 +--- + +## Simple Summary + +Defines a new transaction type with constraints on ancestor block hash, block author, and/or block timestamp. + +## Abstract + +We introduce a new EIP-2718 transaction type with the format `0x4 || rlp([chainId, chainContext, nonce, gasPrice, gasLimit, to, value, data, access_list, yParity, senderR, senderS])`. + +This proposed `chainContext` element adds a constraint on the validity of a transaction to a chain segment meeting the referenced value(s). Four contexts are defined as subclasses of this type: + +- `segmentId` +- `eligibleMinerList` +- `ineligibleMinerList` +- `expiry` + +These contexts can be used in arbitrary combinations. Annotated context value combinations are referenced by a composite integer prefix on the annotation. + +## Motivation + +Establish a protocol-based mechanism with which transactions are able to articulate constraints on eligible chain contexts. +Generally, these constraints give the consumer (the transactor) an ability to express requirements about the transaction's relationship to blockchain data and its provenance. + +- Restrict transaction applicability to a chain context that is currently available and reasoned about under some subjective view. + - Introduces a way for transactions to describe a dependency on their current view of a chain. +- Restrict transaction applicability to a chain context following some foregoing block (and its transactions). + - Introduces a way for transactions to describe ancestral dependencies at a "macro" (block) level. + Indirectly, this offers a way for a transaction to depend on the presence of another, so long as the dependent transaction is in a different block. +- Restrict transaction applicability to blocks benefitting, or _not_ benefitting, a preferred/spurned miner address or addresses. + - Introduces an opportunity/market for miners to compete for consumers' transactions; under the status quo, the current miner-transaction processing service is almost perfectly homogeneous from the consumer perspective. +- Restrict transaction applicability time span. + - Introduces an alternative (to the status quo) way for consumers/transactors to have transactions invalidated/ejected from the transaction pool. + +## Specification + +### Parameters + +- `FORK_BLOCK_NUMBER` `TBD` +- `TRANSACTION_TYPE_NUMBER` `0x4`. See EIP-2718. + +As of `FORK_BLOCK_NUMBER`, a new EIP-2718 transaction is introduced with `TransactionType` `TRANSACTION_TYPE_NUMBER`. + +The EIP-2718 `TransactionPayload` for this transaction is `rlp([chainId, chainContext, nonce, gasPrice, gasLimit, to, value, data, access_list, yParity, senderR, senderS])`. + +The EIP-2718 `ReceiptPayload` for this transaction is `rlp([status, cumulativeGasUsed, logsBloom, logs])`. + +### Definitions + +- `chainContext`. The transaction is only valid for blockchain data satisfying ALL OF the annotations. +- `ANNOTATION_COMPOSITE_PREFIX`. A positive integer between `1` and `0xff` that represents the set of subclass annotations in the `chainContext` (_ie._ _which_ chain context subclasses should the provided values be applied to). This value should be the sum of the subclass' `ANNOTATION_PREFIX`s. +- `ANNOTATION_PREFIX`s are defined for Subclasses as octal-derived positive integers, limited to the set `2^0,2^1,2^2,2^3,2^4,2^5,2^6,2^7`. + +The `chainContext` value should be of the form `ANNOTATION_COMPOSITE_PREFIX || [{subclass value}...]`, where +- `...` means "zero or more of the things to the left," and +- `||` denotes the byte/byte-array concatenation operator. + +The `chainContext` value should be encoded as `ANNOTATION_COMPOSITE_PREFIX || rlp[{subclass value}...]`. + +### Validation + +The values defined as subclasses below acts as constraints on transaction validity for specific chain contexts. +Transactions defining constraints which are not satisfied by their chain context should be rejected as invalid. +Blocks containing invalid transactions should be rejected as invalid themselves, per the _status quo_. + +### Subclass Combination + +`chainContext` values annotating more than one subclass reference should provide those values in the following sequential order: + +1. `ANCESTOR_ID` +2. `ELIGIBLE_MINER_LIST` +3. `INELIGIBLE_MINER_LIST` +4. `EXPIRY` + +As above, the `ANNOTATION_COMPOSITE_PREFIX` should be the sum of the designated subclass' `ANNOTATION_PREFIX`s. +### Subclasses + +- An `ANNOTATION_PREFIX` value is used to represent each of the available context subclasses. + +#### `ancestorId` + +- `ANNOTATION_PREFIX` `1`. +- `ANCESTOR_ID` `bytes`. A byte array between 4 and 12 bytes in length. + +The `ANCESTOR_ID` is a reference to a specific block by concatenating the byte representation of a block number and the first 4 bytes of its hash. +The block number's should be encoded as a big endian value and should have left-padding 0's removed. +The block number value may be omitted in case of reference to the genesis block. + +The `ANCESTOR_ID` value should be RLP encoded as a byte array for hashing and transmission. + +#### `eligibleMinerList` + +- `ANNOTATION_PREFIX` `2`. +- `ELIGIBLE_MINER_LIST` `[address...]`. A list of addresses. +- `MAX_ELEMENTS` `3`. The maximum number of addresses that can be provided. + +The `ELIGIBLE_MINER_LIST` value is an array of unique, valid addresses. +Any block containing a transaction using this value must have a block beneficiary included in this set. + +The `ELIGIBLE_MINER_LIST` value should be of the type `[{20 bytes}+]`, where `+` means "one or more of the thing to the left." +Non-unique values are not permitted. + +The `ELIGIBLE_MINER_LIST` value should be RLP encoded for hashing and transmission. + +An `ELIGIBLE_MINER_LIST` value may NOT be provided adjacent to an `INELIGIBLE_MINER_LIST` value. + +#### `ineligibleMinerList` + +- `ANNOTATION_PREFIX` `4`. +- `INELIGIBLE_MINER_LIST` `[address...]`. A list of addresses. +- `MAX_ELEMENTS` `3`. The maximum number of addresses that can be provided. + +The `INELIGIBLE_MINER_LIST` value is an array of unique, valid addresses. +Any block containing a transaction using this value must not have a block beneficiary included in this set. + +The `INELIGIBLE_MINER_LIST` value should be of the type `[{20 bytes}+]`, where `+` means "one or more of the thing to the left." +Non-unique values are not permitted. + +The `INELIGIBLE_MINER_LIST` value should be RLP encoded for hashing and transmission. + +An `INELIGIBLE_MINER_LIST` value may NOT be provided adjacent to an `ELIGIBLE_MINER_LIST` value. + +#### `expiry` + +- `ANNOTATION_PREFIX` `8`. +- `EXPIRY` `integer`. A positive, unsigned scalar. + +The `EXPIRY` value is a scalar equal to the maximum valid block `timestamp` for a block including this transaction. + +The `EXPIRY` value should be RLP encoded as an integer for hashing and transmission. + +## Rationale + +### Subclasses + +Subclasses are defined with a high level of conceptual independence, and can be modified and/or extended independently from this EIP. +Their specification definitions allow arbitrary mutual (`AND`) combinations. + +This design is intended to form a proposal which offers a concrete set of specifics while doing so with enough flexibility for extension or modification later. + +#### `ANNOTATION_PREFIX` + +`ANNOTATION_PREFIX` values' use of octal-derived values, ie. `1, 2, 4, 8, 16, 32, 64, 128`, follows a conventional pattern of representing combinations from a limited set uniquely and succinctly, eg. Unix-style file permissions. +This EIP defines four of the eight possible context subclasses; this seems to leave plenty of room for future growth in this direction if required. +If this limit is met or exceeded, doing so will require a hard fork _de facto_ (by virtue of making consensus protocol facing changes to transaction validation schemes), so revising this scheme as needed should be only incidental and trivial. + +#### `ancestorId` + +Constrains the validity of a transaction by referencing a prior canonical block by number and hash. +The transaction is only valid when included in a block which has the annotated block as an ancestor. + +Practically, the "designated allowable chain segment" can be understood as the segment of blocks from `0..ancestorId` inclusive. + +##### Redundancy to `chainId` + +This pattern can be understood as a correlate of [EIP-155](./eip-155)'s `chainId` specification. +EIP155 defines the restriction of transactions between chains; limiting the applicability of any EIP-155 transaction to a chain with the annotated ChainID. +`ancestorId` further restricts transaction application to one subsection ("segment") of one chain. + +From this constraint hierarchy, we note that an implementation of `ancestorId` can make `chainId` conceptually redundant. + +##### So why keep `chainId`? + +`chainId` is maintained as an invariant because: + +- The use of the transaction type proposed by this EIP is optional, implying the continued necessity of `chainId` in the protocol infrastructure and tooling for legacy and other transaction types. +- The presence of `ancestorId` in the transaction type proposed by this EIP is optional. If the value is not filled by an RCC transaction, the demand for `chainId` remains. +- A `chainId` value is not necessarily redundant to `ancestorId`, namely in cases where forks result in living chains. For example, an `ancestorId` reference to block `1_919_999` would be ambiguous between Ethereum and Ethereum Classic. +- It would be possible to specify the omission of `chainId` in case of `ancestorId`'s use. This would add infrastructural complexity for the sake of removing the few bytes `chainId` typically requires; we do not consider this trade-off worth making. + - `chainId` is used as the `v` value (of `v,r,s`) in the transaction signing scheme; removing or modifying this incurs complexity at a level below encoded transaction fields, demanding additional infrastructural complexity for implementation. +- The proposed design for `ancestorId` does not provide perfect precision (at the benefit of byte-size savings). + In the small chance that the value is ambiguous, the `chainId` maintains an infallible guarantee for a transaction's chain specificity. + +#### `eligibleMinerList` + +The transaction is only valid when included in a block having an `etherbase` contained in the annotated list of addresses. +The use of "whitelist" (`eligibleMinerList`) in conjunction with a "blacklist" (`ineligibleMinerList`) is logically inconsistent; their conjunction is not allowed. + +A `MAX_ELEMENTS` limit of `3` is chosen to balance the interests of limiting the potential size of transactions, and to provide a sufficient level of articulation for the user. At the time of writing, the top 3 miners of Ethereum (by block, measured by known public addresses) account for 52% of all blocks produced. + +#### `ineligibleMinerList` + +The transaction is only valid when included in a block having an `etherbase` _not_ contained in the annotated list of addresses. +The use of "blacklist" (`ineligibleMinerList`) in conjunction with a "whitelist" (`eligibleMinerList`) is logically inconsistent; their conjunction is not allowed. + +A `MAX_ELEMENTS` limit of `3` is chosen to balance the interests of limiting the potential size of transactions, and to provide a sufficient level of articulation for the user. At the time of writing, the top 3 miners of Ethereum (by block, measured by known public addresses) account for 52% of all blocks produced. + +#### `expiry` + +The transaction is only valid when included in a block having a `timestamp` less than the value annotated. +A positive integer is used because that corresponds to the specified type of block `timestamp` header values. + +### Subclass Combination + +Since subclasses use octal-based values for `ANNOTATION_PREFIX`, they can be distinguishably combined as sums, provided as we assume annotation cardinality (ie ordering). +For example: + +- `ANNOTATION_PREFIX` `1` signals `ancestorId` exclusively. +- `ANNOTATION_PREFIX` `2` signals `eligibleMinerList` exclusively. +- `ANNOTATION_PREFIX` `4` signals `ineligibleMinerList` exclusively. +- `ANNOTATION_PREFIX` `8` signals `expiry` exclusively. +- `ANNOTATION_PREFIX` `1+2=3` combines `ancestorId` and `eligibleMinerList`. +- `ANNOTATION_PREFIX` `1+4=5` combines `ancestorId` and `ineligibleMinerList`. +- `ANNOTATION_PREFIX` `1+8=9` combines `ancestorId` and `expiry`. +- `ANNOTATION_PREFIX` `1+2+8=11` combines `ancestorId` and `eligibleMinerList` and `expiry`. +- `ANNOTATION_PREFIX` `1+4+8=13` combines `ancestorId` and `ineligibleMinerList` and `expiry`. +- `ANNOTATION_PREFIX` `2+4=6` is NOT PERMITTED. It would combine `eligibleMinerList` and `ineligibleMinerList`. +- `ANNOTATION_PREFIX` `1+2+4+8=15` is NOT PERMITTED. It would combine `eligibleMinerList` and `ineligibleMinerList` (and `ancestorId` and `expiry`). + +Since ordering is defined and demanded for multiple values, annotated references remain distinguishable. For example: + +- `chainContext` `3[e4e1c0e78b1ec3,[Df7D7e053933b5cC24372f878c90E62dADAD5d42]]` - Transaction can only be included in a block having a canonical ancestor block numbered `15_000_000` and with a hash prefixed with the bytes `e78b1ec3`, and if the containing block uses `Df7D7e053933b5cC24372f878c90E62dADAD5d42` as the beneficiary. +- `chainContext` `10[[Df7D7e053933b5cC24372f878c90E62dADAD5d42],1619008030]` - Transaction can only be included in a block naming `Df7D7e053933b5cC24372f878c90E62dADAD5d42` as the `etherbase` beneficiary, and which has a timestamp greater than `1619008030` (Wed Apr 21 07:27:10 CDT 2021). + + +### EIP-2930 Inheritance +The [EIP-2930 Optional Access List Type Transaction](https://eips.ethereum.org/EIPS/eip-2930) is used as an assumed "base" transaction type for this proposal. +However, this is NOT a conceptual dependency; the included `accessList` portion of this proposal (the only differential from post-EIP-155 legacy transaction fields) can readily be removed. +Standing on the shoulders of EIP-2930 is only intended to support and further the adoption of next-generation transactions. + +### Signature target + +The signature signs over the transaction type as well as the transaction data. +This is done to ensure that the transaction cannot be “re-interpreted” as a transaction of a different type. + +## Backwards Compatibility + +There are no known backward compatibility issues. + +## Test Cases + +| Segment ID | Block Number | Canonical Block Hash | +| --- | --- | --- | +| `e78b1ec3` | `0` | `0xe78b1ec31bcb535548ce4b6ef384deccad1e7dc599817b65ab5124eeaaee3e58` | +| `01e78b1ec3` | `1` | `0xe78b1ec31bcb535548ce4b6ef384deccad1e7dc599817b65ab5124eeaaee3e58` | +| `e4e1c0e78b1ec3` | `15_000_000` | `0xe78b1ec31bcb535548ce4b6ef384deccad1e7dc599817b65ab5124eeaaee3e58` | +| `e8d4a50fffe78b1ec3` | `999_999_999_999` | `0xe78b1ec31bcb535548ce4b6ef384deccad1e7dc599817b65ab5124eeaaee3e58` | +| `7fffffffffffffffe78b1ec3` | `9223372036854775807` | `0xe78b1ec31bcb535548ce4b6ef384deccad1e7dc599817b65ab5124eeaaee3e58` | + +Further test cases, TODO. + +## Security Considerations + +### Why 4 bytes of a block hash is "safe enough" for the `ancestorId` + +__TL;DR__: The chance of an ineffectual `ancestorId` is about 1 in between ~4 billion and ~40 billion, with the greater chance for intentional duplication scenarios, eg. malicious reorgs. + +__If a collision _does_ happen__, that means the transaction will be valid on both segments (as is the case under the status quo). + +Four bytes, instead of the whole hash (32 bytes), was chosen only to reduce the amount of information required to cross the wire to implement this value. +Using the whole hash would result in a "perfectly safe" implementation, and every additional byte reduces the chance of collision exponentially. + +The goal of the `ancestorId` is to disambiguate one chain segment from another, and in doing so, enable a transaction to define with adequate precision which chain it needs to be on. +When a transaction's `ancestorId` references a block, we want to be pretty sure that that reference won't get confused with a different block than the one the author of the transaction had in mind. + +We assume the trait of collision resistance is uniformly applicable to all possible subsets of the block hash value, so our preference of using the _first_ 4 bytes is arbitrary and functionally equivalent to any other subset of equal length. + +For the sake of legibility and accessibility, the following arguments will reference the hex representation of 4 bytes, which is 8 characters in length, eg. `e78b1ec3`. + +The chance of a colliding `ancestorId` is `1/(16^8=4_294_967_296)` times whatever we take the chance of the existence of an equivalently-numbered block (on an alternative chain) to be. Assuming a generous ballpark chance of 10% (`1/10`) for any given block having a public uncle, this yields `(1/(16^8=4_294_967_296) * 1/10`. Note that this ballpark assumes "normal" chain and network behavior. In the case of an enduring competing chain segment, this value rises to 100% (`1`). + +### `eligibleMinerList` + +Miners who do not find themselves listed in an annotated `eligibleMinerList` should be expected to immediately remove the transaction from their transaction pool. + +In a pessimistic outlook, we should also expect that these ineligible nodes would not offer rebroadcasts of these transactions, potentially impacting the distribution (and availability) of the transactions to their intended miners. On the other hand, miners are incentivized to make themselves available for reception of such transactions, and there are many ways this is feasible both on-network and off-. + +The author of a transaction using the `eligibleMinerList` must assume that the "general availability" of the blockchain state database for such a transaction will be lower than a nonrestrictive transaction (since only a subset of miners will be able to process the transaction). + +A final consideration is the economics of a whitelisted miner concerning the processing order of transactions in which they are whitelisted and those without whitelists. +Transactions without whitelists would appear at first glean to be more competitive, and thus should be processed with priority. +However, miners following such a strategy may find their reputation diminished, and, in the worst case, see the assertive preferences of transaction authors shift to their competitors and beyond their reach. + +### `ineligibleMinerList` + +In addition to the concerns and arguments presented by `eligibleMinerList` above, there is a unique concern for `ineligibleMinerList`: in order for a miner entity to avoid ineligibility by a blacklist, they only need to use an alternative adhoc address as the block beneficiary. +In principle, this is ineluctable. + +However, there are associated costs to the "dodging" miner that should be considered. + +- The creation of an account requires time and energy. But indeed, this work can be done at any convenient time and circumstance. Probably marginal, but non-zero. +- The transfer of funds from multiple accounts requires a commensurate number of transactions. Block rewards are applied after transactions are processed, so the miner is unable to simultaneously shift funds from an adhoc account to a target account in the same block they mine (which would otherwise be a "free" transaction). +- In using an adhoc address to dodge a blacklist, the miner may also cause their ineligibility from contemporary whitelist transactions. + +### Validation costs + +Miner lists and expiry depend on easily cached and contextually available conditions (ie. the containing block header). The infrastructural overhead costs for enforcing these validations are expected to be nominal. + +Validation of `ancestorId` demands the assertion of a positive database hit by block number (thereby cross-referencing a stored block's hash). +This necessary lookup can be (and maybe already is) cached, but we must expect less than 100% hits on cached values, since the lookup value is arbitrary. +With that in mind, however, the value provided to a transaction using a deep `ancestorId` is increasingly marginal, so we should expect +most transactions using this field to use a relatively small set of common, shallow, cache-friendly values. + +### Transaction size increase + +The proposed additional fields potentially increase transaction size. +The proposed fields are not associated with any gas costs, establishing no protocol-defined economic mitigation for potential spam. +However, transactions which are considered by a miner to be undesirable can be simply dropped from the transaction pool and ignored. + +## Copyright + +Copyright and related rights waved via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3540.md b/EIPS/eip-3540.md new file mode 100644 index 0000000..80439fd --- /dev/null +++ b/EIPS/eip-3540.md @@ -0,0 +1,305 @@ +--- +eip: 3540 +title: EOF - EVM Object Format v1 +description: EOF is an extensible and versioned container format for EVM bytecode with a once-off validation at deploy time. +author: Alex Beregszaszi (@axic), Paweł Bylica (@chfast), Andrei Maiboroda (@gumb0), Matt Garnett (@lightclient) +discussions-to: https://ethereum-magicians.org/t/evm-object-format-eof/5727 +status: Review +type: Standards Track +category: Core +created: 2021-03-16 +requires: 3541, 3860, 4750, 5450 +--- + +## Abstract + +We introduce an extensible and versioned container format for the EVM with a once-off validation at deploy time. The version described here brings the tangible benefit of code and data separation, and allows for easy introduction of a variety of changes in the future. This change relies on the reserved byte introduced by [EIP-3541](./eip-3541.md). + +To summarise, EOF bytecode has the following layout: + +``` +magic, version, (section_kind, section_size)+, 0,
+``` + +## Motivation + +On-chain deployed EVM bytecode contains no pre-defined structure today. Code is typically validated in clients to the extent of `JUMPDEST` analysis at runtime, every single time prior to execution. This poses not only an overhead, but also a challenge for introducing new or deprecating existing features. + +Validating code during the contract creation process allows code versioning without an additional version field in the account. Versioning is a useful tool for introducing or deprecating features, especially for larger changes (such as significant changes to control flow, or features like account abstraction). + +The format described in this EIP introduces a simple and extensible container with a minimal set of changes required to both clients and languages, and introduces validation. + +The first tangible feature it provides is separation of code and data. This separation is especially beneficial for on-chain code validators (like those utilised by layer-2 scaling tools, such as Optimism), because they can distinguish code and data (this includes deployment code and constructor arguments too). Currently, they a) require changes prior to contract deployment; b) implement a fragile method; or c) implement an expensive and restrictive jump analysis. Code and data separation can result in ease of use and significant gas savings for such use cases. Additionally, various (static) analysis tools can also benefit, though off-chain tools can already deal with existing code, so the impact is smaller. + +A non-exhaustive list of proposed changes which could benefit from this format: + +- Including a `JUMPDEST`-table (to avoid analysis at execution time) and/or removing `JUMPDEST`s entirely. +- Introducing static jumps (with relative addresses) and jump tables, and disallowing dynamic jumps at the same time. +- Requiring the execution of a code section ends with a terminating instruction. (Assumptions like this can provide significant speed improvements in interpreters, such as a speed-up of ~7% seen in evmone (ethereum/evmone#295). +- Multibyte opcodes without any workarounds. +- Representing functions as individual code sections instead of subroutines. +- Introducing special sections for different use cases, notably Account Abstraction. + +## Specification + +The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “NOT RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119 and RFC 8174. + +In order to guarantee that every EOF-formatted contract in the state is valid, we need to prevent already deployed (and not validated) contracts from being recognized as such format. This is achieved by choosing a byte sequence for the *magic* that doesn't exist in any of the already deployed contracts. + +### Remarks + +The *initcode* is the code executed in the context of the *create* transaction, `CREATE`, or `CREATE2` instructions. The *initcode* returns *code* (via the `RETURN` instruction), which is inserted into the account. See section 7 ("Contract Creation") in the Yellow Paper for more information. + +The opcode `0xEF` is currently an undefined instruction, therefore: *It pops no stack items and pushes no stack items, and it causes an exceptional abort when executed.* This means *initcode* or already deployed *code* starting with this instruction will continue to abort execution. + +Unless otherwised specified, all integers are encoded in big-endian byte order. + +### Code validation + +We introduce *code validation* for new contract creation. To achieve this, we define a format called EVM Object Format (EOF), containing a version indicator, and a ruleset of validity tied to a given version. + +At `block.number == HF_BLOCK` new contract creation is modified: + +- if *initcode* or *code* starts with the `MAGIC`, it is considered to be EOF formatted and will undergo validation specified in the following sections, +- else if *code* starts with `0xEF`, creation continues to result in an exceptional abort (the rule introduced in EIP-3541), +- otherwise code is considered *legacy code* and the following rules do not apply to it. + +For a create transaction, if *initcode* or *code* is invalid, the contract creation results in an exceptional abort. Such a transaction is valid and may be included in a block. Therefore, the transaction sender's nonce is increased. + +For the `CREATE` and `CREATE2` instructions, if *initcode* or *code* is invalid, instructions' execution ends with the result `0` pushed on stack. The *initcode* validation happens just before its execution and validation failure is observable as if execution results in an exceptional abort. I.e. in case *initcode* or returned *code* is invalid the caller's nonce remains increased and all creation gas is deducted. + +### Container specification + +EOF container is a binary format with the capability of providing the EOF version number and a list of EOF sections. + +The container starts with the EOF header: + +| description | length | value | | +|-------------|----------|------------|--------------------| +| magic | 2-bytes | 0xEF00 | | +| version | 1-byte | 0x01–0xFF | EOF version number | + +The EOF header is followed by at least one section header. Each section header contains two fields, `section_kind` and either `section_size` or `section_size_list`, depending on the kind. `section_size_list` is a list of size values when multiple sections of this kind are allowed. + +| description | length | value | | +|-------------------|---------|---------------|-------------------| +| section_kind | 1-byte | 0x01–0xFF | `uint8` | +| section_size | 2-bytes | 0x0000–0xFFFF | `uint16` | +| section_size_list | dynamic | n/a | `uint16, uint16+` | + +The list of section headers is terminated with the *section headers terminator byte* `0x00`. The body content follows immediately after. + +#### Container validation rules + +1. `version` MUST NOT be `0`.[^1](#eof-version-range-start-with-1) +2. `section_kind` MUST NOT be `0`. The value `0` is reserved for *section headers terminator byte*. +3. There MUST be at least one section (and therefore section header). +4. Section content size MUST be equal to size declared in its header. +5. Stray bytes outside of sections MUST NOT be present. This includes trailing bytes after the last section. + +### EOF version 1 + +EOF version 1 is made up of 5 EIPs, including this one: [EIP-3540](./eip-3540.md), [EIP-3670](./eip-3670.md), [EIP-4200](./eip-4200.md), [EIP-4750](./eip-4750.md), and [EIP-5450](./eip-5450.md). Some values in this specification are only discussed briefly. To understand the full scope of EOF, it is necessary to review each EIP in-depth. + +#### Container + +The EOF version 1 container consists of a `header` and `body`. + +``` +container := header, body +header := magic, version, kind_type, type_size, kind_code, num_code_sections, code_size+, kind_data, data_size, terminator +body := type_section, code_section+, data_section +type_section := (inputs, outputs, max_stack_height)+ +``` + +*note: `,` is a concatenation operator and `+` should be interpreted as "one or more" of the preceding item* + +##### Header + +| name | length | value | description | +|-------------------|----------|---------------|------------------------------------------------------------------------------------| +| magic | 2 bytes | 0xEF00 | EOF prefix | +| version | 1 byte | 0x01 | EOF version | +| kind_type | 1 byte | 0x01 | kind marker for EIP-4750 type section header | +| type_size | 2 bytes | 0x0004-0xFFFC | uint16 denoting the length of the type section content, 4 bytes per code segment | +| kind_code | 1 byte | 0x02 | kind marker for code size section | +| num_code_sections | 2 bytes | 0x0001-0x0400 | uint16 denoting the number of the code sections | +| code_size | 2 bytes | 0x0001-0xFFFF | uint16 denoting the length of the code section content | +| kind_data | 1 byte | 0x03 | kind marker for data size section | +| data_size | 2 bytes | 0x0000-0xFFFF | uint16 integer denoting the length of the data section content | +| terminator | 1 byte | 0x00 | marks the end of the header | + +##### Body + +| name | length | value | description | +|------------------|----------|--------------|----------------------------------------------------| +| type_section | variable | n/a | stores EIP-4750 and EIP-5450 code section metadata | +| inputs | 1 byte | 0x00-0x7F | number of stack elements the code section consumes | +| outputs | 1 byte | 0x00-0x7F | number of stack elements the code section returns | +| max_stack_height | 2 bytes | 0x0000-0x3FF | max height of operand stack during execution | +| code_section | variable | n/a | arbitrary bytecode | +| data_section | variable | n/a | arbitrary sequence of bytes | + +See [EIP-4750](./eip-4750.md) for more information on the type section content. + +#### EOF version 1 validation rules + +1. In addition to general validation rules above, EOF version 1 bytecode conforms to the rules specified below: + - Exactly one type section header MUST be present immediately following the EOF version. Each code section MUST have a specified type signature in the type body. + - Exactly one code section header MUST be present immediately following the type section. A maximum of 1024 individual code sections are allowed. + - Exactly one data section header MUST be present immediately following the code section. +2. Any version other than `0x01` is invalid. + +(*Remark:* Contract creation code SHOULD set the section size of the data section so that the constructor arguments fit it.) + +### Changes to execution semantics + +For clarity, the *container* refers to the complete account code, while *code* refers to the contents of the code section only. + +1. Execution starts at the first byte of the first code section, and PC is set to 0. +2. Execution stops if `PC` goes outside the code section bounds. +3. `PC` returns the current position within the *code*. +4. `CODECOPY`/`CODESIZE`/`EXTCODECOPY`/`EXTCODESIZE`/`EXTCODEHASH` keeps operating on the entire *container*. +5. The input to `CREATE`/`CREATE2` is still the entire *container*. +6. The size limit for deployed code as specified in [EIP-170](./eip-170.md) and for initcode as specified in [EIP-3860](./eip-3860.md) is applied to the entire *container* size, not to the *code* size. This also means if initcode validation fails, it is still charged the EIP-3860 `initcode_cost`. + +(*Remark:* Due to [EIP-4750](./eip-4750.md), `JUMP` and `JUMPI` are disabled and therefore are not discussed in relation to EOF.) + +### Changes to contract creation semantics + +For clarity, the *EOF prefix* together with a version number *n* is denoted as the *EOFn prefix*, e.g. *EOF1 prefix*. + +1. If *initcode's container* has EOF1 prefix it MUST be valid EOF1 code. +2. If *code's container* has EOF1 prefix it MUST be valid EOF1 code. +3. If *initcode's container* is valid EOF1 code the resulting *code's container* MUST be valid EOF1 code (i.e. it MUST NOT be empty and MUST NOT produce legacy code). +4. If `CREATE` or `CREATE2` instruction is executed in an EOF1 code the instruction's *initcode* MUST be valid EOF1 code (i.e. EOF1 contracts MUST NOT produce legacy code). + +See [Code validation](#code-validation) above for specification of behaviour in case one of these conditions is not satisfied. + +## Rationale + +EVM and/or account versioning has been discussed numerous times over the past years. This proposal aims to learn from them. +See "Ethereum account versioning" on the Fellowship of Ethereum Magicians Forum for a good starting point. + +### Execution vs. creation time validation + +This specification introduces creation time validation, which means: + +- All created contracts with *EOFn* prefix are valid according to version *n* rules. This is very strong and useful property. The client can trust that the deployed code is well-formed. +- In the future, this allows to serialize `JUMPDEST` map in the EOF container and eliminate the need of implicit `JUMPDEST` analysis required before execution. +- Or to completely remove the need for `JUMPDEST` instructions. +- This helps with deprecating EVM instructions and/or features. +- The biggest disadvantage is that deploy-time validation of EOF code must be enabled in two hard-forks. However, the first step ([EIP-3541](./eip-3541.md)) is already deployed in London. + +The alternative is to have execution time validation for EOF. This is performed every single time a contract is executed, however clients may be able to cache validation results. This *alternative* approach has the following properties: + +- Because the validation is consensus-level execution step, it means the execution always requires the entire code. This makes *code merkleization impractical*. +- Can be enabled via a single hard-fork. +- Better backwards compatibility: data contracts starting with the `0xEF` byte or the *EOF prefix* can be deployed. This is a dubious benefit, however. + +### Contract creation restrictions + +The [Changes to contact creation semantics](#changes-to-contract-creation-semantics) section defines +minimal set of restrictions related to the contract creation: if *initcode* or *code* has the EOF1 +container prefix it must be validated. This adds two validation steps in the contract creation, +any of it failing will result in contract creation failure. + +Moreover, it is not allowed to create legacy contracts from EOF1 ones. And the EOF version of *initcode* must match the EOF version of the produced *code*. +The rule can be generalized in the future: EOFn contract must only create EOFm contracts, where m ≥ n. + +This guarantees that a cluster of EOF contracts will never spawn new legacy contracts. +Furthermore, some exotic contract creation combinations are eliminated (e.g. EOF1 contract creating new EOF1 contract with legacy *initcode*). + +Finally, create transaction must be allowed to contain legacy *initcode* and deploy legacy *code* because otherwise there is no transition period allowing upgrading transaction signing tools. Deprecating such transactions may be considered in the future. + +### The MAGIC + +1. The first byte `0xEF` was chosen because it is reserved for this purpose by [EIP-3541](./eip-3541.md). + +2. The second byte `0x00` was chosen to avoid clashes with three contracts which were deployed on **Mainnet**: + - `0xca7bf67ab492b49806e24b6e2e4ec105183caa01`: `EFF09f918bf09f9fa9` + - `0x897da0f23ccc5e939ec7a53032c5e80fd1a947ec`: `EF` + - `0x6e51d4d9be52b623a3d3a2fa8d3c5e3e01175cd0`: `EF` + +3. No contracts starting with `0xEF` bytes exist on public testnets: Goerli, Ropsten, Rinkeby, Kovan and Sepolia at their London fork block. + +### EOF version range start with 1 + +The version number 0 will never be used in EOF, so we can call legacy code *EOF0*. +Also, implementations may use APIs where 0 version number denotes legacy code. + +### Section structure + +We have considered different questions for the sections: + +- Streaming headers (i.e. `section_header, section_data, section_header, section_data, ...`) are used in some other formats (such as WebAssembly). They are handy for formats which are subject to editing (adding/removing sections). That is not a useful feature for EVM. One minor benefit applicable to our case is that they do not require a specific "header terminator". On the other hand they seem to play worse with code chunking / merkleization, as it is better to have all section headers in a single chunk. +- Whether to have a header terminator or to encode `number_of_sections` or `total_size_of_headers`. Both raise the question of how large of a value these fields should be able to hold. A terminator byte seems to avoid the problem of choosing a size which is too small without any perceptible downside, so it is the path taken. +- Whether to encode `section_size` as a fixed 16-bit value or some kind of variable length field (e.g. LEB128). We have opted for fixed size, because it simplifies client implementations, and 16-bit seems enough, because of the currently exposed code size limit of 24576 bytes (see [EIP-170](./eip-170.md) and [EIP-3860](./eip-3860.md)). Should this be limiting in the future, a new EOF version could change the format. Besides simplifying client implementations, not using LEB128 also greatly simplifies on-chain parsing. + +### Data-only contracts + +The EOF prevents deploying contracts with arbitrary bytes (data-only contracts: their purpose is to store data not execution). **EOF1 requires** presence of a **code section** therefore the minimal overhead EOF data contract consist of a data section and one code section with single instruction. We recommend to use `INVALID` instruction in this case. In total there are 20 additional bytes required. + +``` +EF0001 010004 020001 0001 03 00 00000000 FE +``` + +It is possible in the future that this data will be accessible with data-specific opcodes, such as `DATACOPY` or `EXTDATACOPY`. Until then, callers will need to determine the data offset manually. + +### PC starts with 0 at the code section + +The value for `PC` is specified to start at 0 and to be within the active *code* section. We considered keeping `PC` to operate on the whole *container* and be consistent with `CODECOPY`/`EXTCODECOPY` but in the end decided otherwise. This also feels more natural and easier to implement in EVM: the new EOF EVM should only care about traversing *code* and accessing other parts of the *container* only on special occasions (e.g. in `CODECOPY` instruction). + +## Backwards Compatibility + +This is a breaking change given that any code starting with `0xEF` was not deployable before (and resulted in exceptional abort if executed), but now some subset of such codes can be deployed and executed successfully. + +The choice of `MAGIC` guarantees that none of the contracts existing on the chain are affected by the new rules. + +## Test Cases + +### Contract creation + +All cases should be checked for creation transaction, `CREATE` and `CREATE2`. + +- Legacy init code + - Returns legacy code + - Returns valid EOF1 code + - Returns invalid EOF1 code, contract creation fails + - Returns 0xEF not followed by EOF1 code, contract creation fails +- Valid EOF1 init code + - Returns legacy code, contract creation fails + - Returns valid EOF1 code + - Returns invalid EOF1 code, contract creation fails + - Returns 0xEF not followed by EOF1 code, contract creation fails +- Invalid EOF1 init code + +### Contract execution + +- EOF code containing `PC` opcode - offset inside code section is returned +- EOF code containing `CODECOPY/CODESIZE` - works as in legacy code + - `CODESIZE` returns the size of entire container + - `CODECOPY` can copy from code section + - `CODECOPY` can copy from data section + - `CODECOPY` can copy from the EOF header + - `CODECOPY` can copy entire container +- `EXTCODECOPY/EXTCODESIZE/EXTCODEHASH` with the EOF *target* contract - works as with legacy target contract + - `EXTCODESIZE` returns the size of entire target container + - `EXTCODEHASH` returns the hash of entire target container + - `EXTCODECOPY` can copy from target's code section + - `EXTCODECOPY` can copy from target's data section + - `EXTCODECOPY` can copy from target's EOF header + - `EXTCODECOPY` can copy entire target container + - Results don't differ when executed inside legacy or EOF contract + +## Security Considerations + +With the anticipated EOF extensions, the validation is expected to have linear computational and space complexity. +We think that the validation cost is sufficiently covered by: + +- [EIP-3860](./eip-3860.md) for *initcode*, +- high per-byte cost of deploying *code*. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3541.md b/EIPS/eip-3541.md new file mode 100644 index 0000000..fc36498 --- /dev/null +++ b/EIPS/eip-3541.md @@ -0,0 +1,69 @@ +--- +eip: 3541 +title: Reject new contract code starting with the 0xEF byte +author: Alex Beregszaszi (@axic), Paweł Bylica (@chfast), Andrei Maiboroda (@gumb0), Alexey Akhunov (@AlexeyAkhunov), Christian Reitwiessner (@chriseth), Martin Swende (@holiman) +discussions-to: https://ethereum-magicians.org/t/evm-object-format-eof/5727 +status: Final +type: Standards Track +category: Core +created: 2021-03-16 +--- + +## Abstract + +Disallow new code starting with the `0xEF` byte to be deployed. Code already existing in the account trie starting with `0xEF` byte is not affected semantically by this change. + +## Motivation + +Contracts conforming to the EVM Object Format (EOF) are going to be validated at deploy time. In order to guarantee that every EOF-formatted contract in the state is valid, we need to prevent already deployed (and not validated) contracts from being recognized as such format. This will be achieved by choosing a byte sequence for the *magic* that doesn't exist in any of the already deployed contracts. To prevent the growth of the search space and to limit the analysis to the contracts existing before this fork, we disallow the starting byte of the format (the first byte of the magic). + +Should the EVM Object Format proposal not be deployed in the future, the *magic* can be used by other features depending on versioning. In the case versioning becomes obsolete, it is simple to roll this back by allowing contracts starting with the `0xEF` byte to be deployed again. + +## Specification + +After `block.number == HF_BLOCK` new contract creation (via create transaction, `CREATE` or `CREATE2` instructions) results in an exceptional abort if the _code_'s first byte is `0xEF`. + +### Remarks + +The *initcode* is the code executed in the context of the *create* transaction, `CREATE`, or `CREATE2` instructions. The *initcode* returns *code* (via the `RETURN` instruction), which is inserted into the account. See section 7 ("Contract Creation") in the Yellow Paper for more information. + +The opcode `0xEF` is currently an undefined instruction, therefore: *It pops no stack items and pushes no stack items, and it causes an exceptional abort when executed.* This means *initcode* or already deployed *code* starting with this instruction will continue to abort execution. + +The exceptional abort due to *code* starting with `0xEF` behaves exactly the same as any other exceptional abort that can occur during *initcode* execution, i.e. in case of abort all gas provided to a `CREATE*` or create transaction is consumed. + +## Rationale + +The `0xEF` byte was chosen because it resembles **E**xecutable **F**ormat. + +Contracts using unassigned opcodes are generally understood to be at risk of changing semantics. Hence using the unassigned `0xEF` should have lesser effects, than choosing an assigned opcode, such as `0xFD` (`REVERT`), `0xFE` (`INVALID)`, or `0xFF` (`SELFDESTRUCT`). Arguably while such contracts may not be very useful, they are still using valid opcodes. + +Analysis in May 2021, on `18084433` contracts in state, showed that there are 0 existing contracts starting with the `0xEF` byte, as opposed to 1, 4, and 12 starting with `0xFD`, `0xFE`, and `0xFF`, respectively. + +## Test Cases + +Each test case below may be executed in 3 different contexts: +- create transaction (no account code) +- `CREATE`, with account code: `0x6000356000523660006000f0151560165760006000fd5b` (Yul code: `mstore(0, calldataload(0)) if iszero(create(0, 0, calldatasize())) { revert(0, 0) }`), +- `CREATE2`, with account code: `0x60003560005260003660006000f5151560185760006000fd5b` (Yul code: `mstore(0, calldataload(0)) if iszero(create2(0, 0, calldatasize(), 0)) { revert(0, 0) }`) + +| Case | Calldata | Expected result | +| -------- | -------- | -------- | +| deploy one byte `ef` | `0x60ef60005360016000f3` | new contract not deployed, transaction fails | +| deploy two bytes `ef00` | `0x60ef60005360026000f3` | new contract not deployed, transaction fails | +| deploy three bytes `ef0000` | `0x60ef60005360036000f3` | new contract not deployed, transaction fails | +| deploy 32 bytes `ef00...00` | `0x60ef60005360206000f3` | new contract not deployed, transaction fails | +| deploy one byte `fe` | `0x60fe60005360016000f3` | new contract deployed, transaction succeeds | + +## Backwards Compatibility + +This is a breaking change given new code starting with the `0xEF` byte will not be deployable, and contract creation will result in a failure. However, given bytecode is executed starting at its first byte, code deployed with `0xEF` as the first byte is not executable anyway. + +While this means no currently executable contract is affected, it does rejects deployment of new data contracts starting with the `0xEF` byte. + +## Security Considerations + +The authors are not aware of any security or DoS risks posed by this change. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3554.md b/EIPS/eip-3554.md new file mode 100644 index 0000000..0463170 --- /dev/null +++ b/EIPS/eip-3554.md @@ -0,0 +1,58 @@ +--- +eip: 3554 +title: Difficulty Bomb Delay to December 2021 +author: James Hancock (@madeoftin) +discussions-to: https://ethereum-magicians.org/t/eip-3554-ice-age-delay-targeting-december-2021/6188 +status: Final +type: Standards Track +category: Core +created: 2021-05-06 +--- + +## Simple Summary +Delays the difficulty bomb to show effect the first week of December 2021. + +## Abstract +Starting with `FORK_BLOCK_NUMBER` the client will calculate the difficulty based on a fake block number suggesting to the client that the difficulty bomb is adjusting 9,700,000 blocks later than the actual block number. + +## Motivation +Targeting for the Shanghai upgrade and/or the Merge to occur before December 2021. Either the bomb can be readjusted at that time, or removed all together. + +## Specification +#### Relax Difficulty with Fake Block Number +For the purposes of `calc_difficulty`, simply replace the use of `block.number`, as used in the exponential ice age component, with the formula: +```py + fake_block_number = max(0, block.number - 9_700_000) if block.number >= FORK_BLOCK_NUMBER else block.number +``` +## Rationale + +The following script predicts a .1 second delay to blocktime the first week of december and a 1 second delay by the end of the month. This gives reason to address because the effect will be seen, but not so much urgency we don't have space to work around if needed. + +```python +def predict_diff_bomb_effect(current_blknum, current_difficulty, block_adjustment, months): + ''' + Predicts the effect on block time (as a ratio) in a specified amount of months in the future. + Vars used in last prediction: + current_blknum = 12382958 + current_difficulty = 7393633000000000 + block adjustment = 9700000 + months = 6 + ''' + blocks_per_month = (86400 * 30) // 13.3 + future_blknum = current_blknum + blocks_per_month * months + diff_adjustment = 2 ** ((future_blknum - block_adjustment) // 100000 - 2) + diff_adjust_coeff = diff_adjustment / current_difficulty * 2048 + return diff_adjust_coeff + + +diff_adjust_coeff = predict_diff_bomb_effect(12382958,7393633000000000,9700000,6) +``` + +## Backwards Compatibility +No known backward compatibility issues. + +## Security Considerations +Misjudging the effects of the difficulty can mean longer blocktimes than anticipated until a hardfork is released. Wild shifts in difficulty can affect this number severely. Also, gradual changes in blocktimes due to longer-term adjustments in difficulty can affect the timing of difficulty bomb epochs. This affects the usability of the network but unlikely to have security ramifications. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3561.md b/EIPS/eip-3561.md new file mode 100644 index 0000000..f3fbee8 --- /dev/null +++ b/EIPS/eip-3561.md @@ -0,0 +1,282 @@ +--- +eip: 3561 +title: Trust Minimized Upgradeability Proxy +description: proxy with a delay before specified upgrade goes live +author: Sam Porter (@SamPorter1984) +discussions-to: https://ethereum-magicians.org/t/trust-minimized-proxy/5742 +status: Review +type: Standards Track +category: ERC +created: 2021-05-09 +--- + +## Abstract + +Removing trust from upgradeability proxy is necessary for anonymous developers. In order to accomplish this, instant and potentially malicious upgrades must be prevented. This EIP introduces additional storage slots for upgradeability proxy which are assumed to decrease trust in interaction with upgradeable smart contracts. Defined by the admin implementation logic can be made an active implementation logic only after Zero Trust Period allows. + +## Motivation + +Anonymous developers who utilize upgradeability proxies typically struggle to earn the trust of the community. + +Fairer, better future for humanity absolutely requires some developers to stay anonymous while still attract vital attention to solutions they propose and at the same time leverage the benefits of possible upgradeability. + +## Specification + +The specification is an addition to the standard [EIP-1967](./eip-1967.md) transparent proxy design. +The specification focuses on the slots it adds. All admin interactions with trust minimized proxy must emit an event to make admin actions trackable, and all admin actions must be guarded with `onlyAdmin()` modifier. + +### Next Logic Contract Address + +Storage slot `0x19e3fabe07b65998b604369d85524946766191ac9434b39e27c424c976493685` (obtained as `bytes32(uint256(keccak256('eip3561.proxy.next.logic')) - 1)`). +Desirable implementation logic address must be first defined as next logic, before it can function as actual logic implementation stored in EIP-1967 `IMPLEMENTATION_SLOT`. +Admin interactions with next logic contract address correspond with these methods and events: + +```solidity +// Sets next logic contract address. Emits NextLogicDefined +// If current implementation is address(0), then upgrades to IMPLEMENTATION_SLOT +// immedeatelly, therefore takes data as an argument +function proposeTo(address implementation, bytes calldata data) external IfAdmin +// As soon UPGRADE_BLOCK_SLOT allows, sets the address stored as next implementation +// as current IMPLEMENTATION_SLOT and initializes it. +function upgrade(bytes calldata data) external IfAdmin +// cancelling is possible for as long as upgrade() for given next logic was not called +// emits NextLogicCanceled +function cancelUpgrade() external onlyAdmin; + +event NextLogicDefined(address indexed nextLogic, uint earliestArrivalBlock); // important to have +event NextLogicCanceled(address indexed oldLogic); +``` + +### Upgrade Block + +Storage slot `0xe3228ec3416340815a9ca41bfee1103c47feb764b4f0f4412f5d92df539fe0ee` (obtained as `bytes32(uint256(keccak256('eip3561.proxy.next.logic.block')) - 1)`). +On/after this block next logic contract address can be set to EIP-1967 `IMPLEMENTATION_SLOT` or, in other words, `upgrade()` can be called. Updated automatically according to Zero Trust Period, shown as `earliestArrivalBlock` in the event `NextLogicDefined`. + +### Propose Block + +Storage slot `0x4b50776e56454fad8a52805daac1d9fd77ef59e4f1a053c342aaae5568af1388` (obtained as `bytes32(uint256(keccak256('eip3561.proxy.propose.block')) - 1)`). +Defines after/on which block *proposing* next logic is possible. Required for convenience, for example can be manually set to a year from given time. Can be set to maximum number to completely seal the code. +Admin interactions with this slot correspond with this method and event: + +```solidity +function prolongLock(uint b) external onlyAdmin; +event ProposingUpgradesRestrictedUntil(uint block, uint nextProposedLogicEarliestArrival); +``` + +### Zero Trust Period + +Storage slot `0x7913203adedf5aca5386654362047f05edbd30729ae4b0351441c46289146720` (obtained as `bytes32(uint256(keccak256('eip3561.proxy.zero.trust.period')) - 1)`). +Zero Trust Period in amount of blocks, can only be set higher than previous value. While it is at default value(0), the proxy operates exactly as standard EIP-1967 transparent proxy. After zero trust period is set, all above specification is enforced. +Admin interactions with this slot should correspond with this method and event: + +```solidity +function setZeroTrustPeriod(uint blocks) external onlyAdmin; +event ZeroTrustPeriodSet(uint blocks); +``` + +### Implementation Example + +```solidity +pragma solidity >=0.8.0; //important + +// EIP-3561 trust minimized proxy implementation https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3561.md +// Based on EIP-1967 upgradeability proxy: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1967.md + +contract TrustMinimizedProxy { + event Upgraded(address indexed toLogic); + event AdminChanged(address indexed previousAdmin, address indexed newAdmin); + event NextLogicDefined(address indexed nextLogic, uint earliestArrivalBlock); + event ProposingUpgradesRestrictedUntil(uint block, uint nextProposedLogicEarliestArrival); + event NextLogicCanceled(); + event ZeroTrustPeriodSet(uint blocks); + + bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; + bytes32 internal constant LOGIC_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; + bytes32 internal constant NEXT_LOGIC_SLOT = 0x19e3fabe07b65998b604369d85524946766191ac9434b39e27c424c976493685; + bytes32 internal constant NEXT_LOGIC_BLOCK_SLOT = 0xe3228ec3416340815a9ca41bfee1103c47feb764b4f0f4412f5d92df539fe0ee; + bytes32 internal constant PROPOSE_BLOCK_SLOT = 0x4b50776e56454fad8a52805daac1d9fd77ef59e4f1a053c342aaae5568af1388; + bytes32 internal constant ZERO_TRUST_PERIOD_SLOT = 0x7913203adedf5aca5386654362047f05edbd30729ae4b0351441c46289146720; + + constructor() payable { + require( + ADMIN_SLOT == bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1) && + LOGIC_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1) && + NEXT_LOGIC_SLOT == bytes32(uint256(keccak256('eip3561.proxy.next.logic')) - 1) && + NEXT_LOGIC_BLOCK_SLOT == bytes32(uint256(keccak256('eip3561.proxy.next.logic.block')) - 1) && + PROPOSE_BLOCK_SLOT == bytes32(uint256(keccak256('eip3561.proxy.propose.block')) - 1) && + ZERO_TRUST_PERIOD_SLOT == bytes32(uint256(keccak256('eip3561.proxy.zero.trust.period')) - 1) + ); + _setAdmin(msg.sender); + } + + modifier IfAdmin() { + if (msg.sender == _admin()) { + _; + } else { + _fallback(); + } + } + + function _logic() internal view returns (address logic) { + assembly { + logic := sload(LOGIC_SLOT) + } + } + + function _nextLogic() internal view returns (address nextLogic) { + assembly { + nextLogic := sload(NEXT_LOGIC_SLOT) + } + } + + function _proposeBlock() internal view returns (uint b) { + assembly { + b := sload(PROPOSE_BLOCK_SLOT) + } + } + + function _nextLogicBlock() internal view returns (uint b) { + assembly { + b := sload(NEXT_LOGIC_BLOCK_SLOT) + } + } + + function _zeroTrustPeriod() internal view returns (uint ztp) { + assembly { + ztp := sload(ZERO_TRUST_PERIOD_SLOT) + } + } + + function _admin() internal view returns (address adm) { + assembly { + adm := sload(ADMIN_SLOT) + } + } + + function _setAdmin(address newAdm) internal { + assembly { + sstore(ADMIN_SLOT, newAdm) + } + } + + function changeAdmin(address newAdm) external IfAdmin { + emit AdminChanged(_admin(), newAdm); + _setAdmin(newAdm); + } + + function upgrade(bytes calldata data) external IfAdmin { + require(block.number >= _nextLogicBlock(), 'too soon'); + address logic; + assembly { + logic := sload(NEXT_LOGIC_SLOT) + sstore(LOGIC_SLOT, logic) + } + (bool success, ) = logic.delegatecall(data); + require(success, 'failed to call'); + emit Upgraded(logic); + } + + fallback() external payable { + _fallback(); + } + + receive() external payable { + _fallback(); + } + + function _fallback() internal { + require(msg.sender != _admin()); + _delegate(_logic()); + } + + function cancelUpgrade() external IfAdmin { + address logic; + assembly { + logic := sload(LOGIC_SLOT) + sstore(NEXT_LOGIC_SLOT, logic) + } + emit NextLogicCanceled(); + } + + function prolongLock(uint b) external IfAdmin { + require(b > _proposeBlock(), 'can be only set higher'); + assembly { + sstore(PROPOSE_BLOCK_SLOT, b) + } + emit ProposingUpgradesRestrictedUntil(b, b + _zeroTrustPeriod()); + } + + function setZeroTrustPeriod(uint blocks) external IfAdmin { + // before this set at least once acts like a normal eip 1967 transparent proxy + uint ztp; + assembly { + ztp := sload(ZERO_TRUST_PERIOD_SLOT) + } + require(blocks > ztp, 'can be only set higher'); + assembly { + sstore(ZERO_TRUST_PERIOD_SLOT, blocks) + } + _updateNextBlockSlot(); + emit ZeroTrustPeriodSet(blocks); + } + + function _updateNextBlockSlot() internal { + uint nlb = block.number + _zeroTrustPeriod(); + assembly { + sstore(NEXT_LOGIC_BLOCK_SLOT, nlb) + } + } + + function _setNextLogic(address nl) internal { + require(block.number >= _proposeBlock(), 'too soon'); + _updateNextBlockSlot(); + assembly { + sstore(NEXT_LOGIC_SLOT, nl) + } + emit NextLogicDefined(nl, block.number + _zeroTrustPeriod()); + } + + function proposeTo(address newLogic, bytes calldata data) external payable IfAdmin { + if (_zeroTrustPeriod() == 0 || _logic() == address(0)) { + _updateNextBlockSlot(); + assembly { + sstore(LOGIC_SLOT, newLogic) + } + (bool success, ) = newLogic.delegatecall(data); + require(success, 'failed to call'); + emit Upgraded(newLogic); + } else { + _setNextLogic(newLogic); + } + } + + function _delegate(address logic_) internal { + assembly { + calldatacopy(0, 0, calldatasize()) + let result := delegatecall(gas(), logic_, 0, calldatasize(), 0, 0) + returndatacopy(0, 0, returndatasize()) + switch result + case 0 { + revert(0, returndatasize()) + } + default { + return(0, returndatasize()) + } + } + } +} +``` + +## Rationale + +An argument "just don't make such contracts upgadeable at all" fails when it comes to complex systems which do or do not heavily rely on human factor, which might manifest itself in unprecedented ways. It might be impossible to model some systems right on first try. Using decentralized governance for upgrade management coupled with EIP-1967 proxy might become a serious bottleneck for certain protocols before they mature and data is at hand. + +A proxy without a time delay before an actual upgrade is obviously abusable. A time delay is probably unavoidable, even if it means that inexperienced developers might not have confidence using it. Albeit this is a downside of this EIP, it's a critically important option to have in smart contract development today. + +## Security Considerations + +Users must ensure that a trust-minimized proxy they interact with does not allow overflows, ideally represents the exact copy of the code in implementation example above, and also they must ensure that Zero Trust Period length is reasonable(at the very least two weeks if upgrades are usually being revealed beforehand, and in most cases at least a month). + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3569.md b/EIPS/eip-3569.md new file mode 100644 index 0000000..e2419c3 --- /dev/null +++ b/EIPS/eip-3569.md @@ -0,0 +1,139 @@ +--- +eip: 3569 +title: Sealed NFT Metadata Standard +author: Sean Papanikolas (@pizzarob) +discussions-to: https://ethereum-magicians.org/t/eip-3569-sealed-nft-metadata-standard/7130 +status: Stagnant +type: Standards Track +category: ERC +created: 2021-05-07 +--- + +## Simple Summary + +The Sealed NFT Metadata Extension provides a mechanism to immortalize NFT metadata in a cost-effective manner. + +## Abstract + +This standard accomplishes three things; it provides a way for potential collectors to verify that the NFT metadata will not change, allows creators to immortalize metadata for multiple tokens at one time, and allows metadata for many NFTs to be read and cached from one file. A creator can call the `seal` function for a range of one or many sequential NFTs. Included as an argument is a URI which points to a decentralized storage service like IPFS and will be stored in the smart contract. The URI will return a JSON object in which the keys are token IDs and the values are either a string which is a URI pointing to a metadata file stored on a decentralized file system, or raw metadata JSON for each token ID. The token ID(s) will then be marked as sealed in the smart contract and cannot be sealed again. The `seal` function can be called after NFT creation, or during the NFT creation process. + +## Motivation + +In the original ERC-721 standard, the metadata extension specifies a `tokenURI` function which returns a URI for a single token ID. This may be hosted on IPFS or might be hosted on a centralized server. There's no guarantee that the NFT metadata will not change. This is the same for the ERC-1155 metadata extension. In addition to that - if you want to update the metadata for many NFTs you would need to do so in O(n) time, which as we know is not financially feasible at scale. By allowing for a decentralized URI to point to a JSON object of many NFT IDs we can solve this issue by providing metadata for many tokens at one time rather than one at a time. We can also provide methods which give transparency into whether the NFT has be explicitly "sealed" and that the metadata is hosted on a decentralized storage space. + +There is not a way for the smart contract layer to communicate with a storage layer and as such we need a solution which provides a way for potential NFT collectors on Ethereum to verify that their NFT will not be "rug pulled". This standard provides a solution for that. By allowing creators to seal their NFTs during or after creation, they are provided with full flexibility when it comes to creating their NFTs. Decentralized storage means permanence - in the fast-moving world of digital marketing campaigns, or art projects mistakes can happen. As such, it is important for creators to have flexibility when creating their projects. Therefore, this standard allows creators to opt in at a time of their choosing. Mistakes do happen and metadata should be flexible enough so that creators can fix mistakes or create dynamic NFTs (see Beeple's CROSSROAD NFT). If there comes a time when the NFT metadata should be immortalized, then the creator can call the `seal` method. Owners, potential owners, or platforms can verify that the NFT was sealed and can check the returned URI. If the `sealedURI` return value is not hosted on a decentralized storage platform, or the `isSealed` method does not return `true` for the given NFT ID then it can be said that one cannot trust that these NFTs will not change at a future date and can then decide if they want to proceed with collecting the given NFT. + +## Specification + +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. + +``` +interface SealedMetadata { + /** + @notice This function is used to set a sealed URI for the given range of tokens. + @dev + - If the sealed URI is being set for one token then the fromTokenId and toTokenId + values MUST be the same. + + - If any token within the range of tokens specified has already + been sealed then this function MUST throw. + + - This function MAY be called at the time of NFT creation, or after the NFTs have been created. + + - It is RECOMMENDED that this function only be executable by either the creator of the smart contract, + or the creator of the NFTs, but this is OPTIONAL and should be implemented based on use case. + + - This function MUST emit the Sealed event + + - The URI argument SHOULD point to a JSON file hosted within a decentralized file system like IPFS + + @param fromTokenId The first token in a consecutive range of tokens + @param toTokenId The ending token in a consecutive range of tokens + @param uri A URI which points to a JSON file hosted on a decentralized file system. + */ + function seal(uint256 fromTokenId, uint256 toTokenId, string memory uri) external; + + /** + @notice This function returns the URI which the sealed metadata can be found for the given token ID + @dev + - This function MUST throw if the token ID does not exist, or is not sealed + + @param tokenId Token ID to retrieve the sealed URI for + + @return The sealed URI in which the metadata for the given token ID can be found + */ + function sealedURI(uint256 tokenId) external view returns (string); + + /** + @notice This function returns a boolean stating if the token ID is sealed or not + @dev This function should throw if the token ID does not exist + + @param tokenId The token ID that will be checked if sealed or not + + @return Boolean stating if token ID is sealed + */ + function isSealed(uint256 tokenId) external view returns (bool) + + /// @dev This emits when a range of tokens is sealed + event Sealed(uint256 indexed fromTokenId, uint256 indexed toTokenId, string memory uri); + +} +``` + +### Sealed Metadata JSON Format + +The sealed metadata JSON file MAY contain metadata for many different tokens. The top level keys of the JSON object MUST be token IDs. + +``` + +type ERC721Metadata = { + name?: string; + image?: string; + description?: string; +} + +type SealedMetaDataJson = { + [tokenId: string]: string | ERC721Metadata; +} + +const sealedMetadata: SealedMetaDataJson = { + '1': { + name: 'Metadata for token with ID 1' + }, + '2': { + name: 'Metadata for token with ID 2' + }, + // Example pointing to another file + '3': 'ipfs://SOME_HASH_ON_IPFS' +}; +``` + +## Rationale + +**Rationale for rule not explicitly requiring that sealed URI be hosted on decentralized filestorage** + +In order for this standard to remain future proof there is no validation within the smart contract that would verify the sealed URI is hosted on IPFS or another decentralized file storage system. The standard allows potential collectors and platforms to validate the URI on the client. + +**Rationale to include many NFT metadata objects, or URIs in one JSON file** + +By including metadata for many NFTs in one JSON file we can eliminate the need for many transactions to set the metadata for multiple NFTs. Given that this file should not change NFT platforms, or explorers can cache the metadata within the file. + +**Rationale for emitting `Sealed` event** + +Platforms and explorers can use the `Sealed` event to automatically cache metadata, or update information regarding specified NFTs. + +**Rationale for allowing URIs as values in the JSON file** + +If a token's metadata is very large, or there are many tokens you can save file space by referencing another URI rather than storing the metadata JSON within the top level metadata file. + +## Backwards Compatibility + +There is no backwards compatibility with existing standards. This is an extension which could be added to existing NFT standards. + +## Security Considerations + +There are no security considerations related directly to the implementation of this standard. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3584.md b/EIPS/eip-3584.md new file mode 100644 index 0000000..c5261b1 --- /dev/null +++ b/EIPS/eip-3584.md @@ -0,0 +1,125 @@ +--- +eip: 3584 +title: Block Access List +author: Gajinder Singh (@g11in), Piper Merriam (@pipermerriam) +discussions-to: https://ethresear.ch/t/block-access-list-v0-1/9505 +status: Stagnant +type: Standards Track +category: Core +created: 2021-05-22 +requires: 2929, 2930 +--- + +## Simple Summary +A proposal to build a block's `access_list` and include its fingerprint `AccessListRoot` in the block header. + +## Abstract +[EIP-2929](./eip-2929.md)/[EIP-2930](./eip-2930.md) centers around normalizing the (low) gas costs of data/storage accesses made by a transaction as well as providing for (and encouraging) a new transaction type format: +``` +0x01 || rlp([chainId, nonce, gasPrice, gasLimit, to, value, data, access_list, yParity, senderR, senderS]) +``` +that makes upfront `access_list` declarations, where `access_list` is some `[[{20 bytes}, [{32 bytes}...]]...]` map of `AccessedAddress=> AccessedStorageKeys`. + +The first *accesses* of these upfront *declarations* are charged at discounted price (roughly ~`10%`) and first accesses outside this list are charged higher price. Reason is upfront access declaration provides for a way to *preload/optimize/batch* loading these locations while executing the transaction. +This inadvertently leads to generation of transaction `access_list` that has all (first) accesses (declared or not) made by a transaction. +This proposal is to collate these *transaction* `access_list`s for all the transactions in a **block** `access_list` document and include its *fingerprint* in the block header. + +## Motivation +Motivation for collating the *transaction* `access_list`s for all the transactions in a **block**’s `access_list` is to have an *access index* of the block with following benefits: +1. Block execution/validation optimizations/parallelization/cache warm-up by enabling construction of *a partial order* for access and hence execution (hint: *chains* in this *poset* can be parallelized). +2. Enabling partial inspection and fetching/serving of a block data/state by *light sync* or *fast sync* protocols concerned with a subset of addresses. +3. Possible future extension of this list to serve as index for bundling, serving and fetching witness data for *stateless* protocols. + +## Specification +A block `access_list` represents: +``` +Set [ + AccessedAddress, + List [AccessedStorageKeys] , + Set [ AccessedInBlockTransactionNumber, List [ AccessedStorageKeys ]] +] +``` +A **canonical** construction of such an `access_list` is specified as below. + +### Canonical Block Access List +An `access_list` is defined to be comprised of many `access_list_entry` elements: +``` +access_list := [access_list_entry, ...] +``` + +An `access_list_entry` is a 3-tuple of: +* address +* sorted list of storage keys of the address accessed across the entire block +* sorted list of 2-tuples of: + * transaction index in which the address or any of its storage keys were accessed + * sorted list of storage keys which were accessed + +``` +access_list := [access_list_entry, ...] +access_list_entry := [address, storage_keys, accesses_by_txn_index] +address := bytes20 +accesses_by_txn_index := [txn_index_and_keys, ...] +txn_index_and_keys := [txn_index, storage_keys] +txn_index := uint64 # or uint256 or whatever +storage_keys := [storage_key, ...] +storage_key := bytes32 +``` + +Additional sorting rules for the above are that: +* `access_list` is sorted by the `address` +* `storage_keys` is sorted +* `accesses_by_txn_index` is sorted by `txn_index` + +Additional validation rules for the above are that: +* Each unique `address` may only appear at most once in `access_list` +* Each `storage_key` may only appear at most once in `storage_keys` +* Each `txn_index` may only appear at most once in `txn_index_and_keys` + +All sorting is in increasing order. + +### AccessListRoot +An `AccessListRoot` is a URN *like* encoding `Hash/Commitment` of the canonical `access_list` as well as the construction type ( `sha256` ) and serialization type ( `json` ), i.e. +``` +AccessListRoot := "urn:sha256:json:0x${ SHA256( access_list.toJSONString('utf8') ).toHexString() }" +``` +where `0x${ SHA256 (...)...}` is the `SHA256` hashed `32` bytes hex string as indicated by leading `0x`. + +### Additional Block Validation +Validating a new block requires an additional validation check that the block’s `AccessListRoot` matches the one generated by executing the block using the construction as defined by the `AccessListRoot` URN. + +## Rationale +### Sorting of canonical `access_list` +It is specified to be sorted in lexicographic ordering or integer sorting wherever applicable and specified. Sorting with respect to access time was considered but didn't seem to provide any additional benefit at the cost of adding implementation complexity and bookkeeping. + +### `AccessListRoot` +`AccessListRoot` is generated to prevent any *griefing* attacks and hence will need to be included (and validated) in the *block header*. +Even though `AccessListRoot` is currently specified to be a simple `sha256` hash of the canonical `access_list`, it would be beneficial to consider other constructions +* a tree structure (`merkle`/`verkle`). It will be a bit more expensive but will enable partial downloading, inspection and validation of the `access_list`. +* a normal `kate` commitment can also be generated to enable this partial capability and is recommended as validating partial fetch of access list chunks would be very simple. + +Also serialization of the `access_list` is currently specified as a normal `JSON String` dump and these parameters could vary from construction to construction, but for the sake of simplicity, it can always be `sha256` hashed to get a consistent `32` bytes hex string root. + +So this AccessListRoot could evolve to `urn:merkle:ssz:...` or to `urn:kate:...` or to any other scheme as per requirement. And the idea of having the `AccessListRoot` as URN *like* structure is to enable upgradation to these paths without affecting block structure. + + +### Future extensions of `access_list` +We can extend the notion of a block’s `access_list` to include witnesses: +``` +access_list := Set[ + Address, + List [ AddressWitnesses ], + Set [ AccessedStorageKey, List [ StorageKeyWitnesses] ], + Set [ AccessedInBlockTransactionNumber, List [ AccessedStorageKeys ] ] +] +``` +and then get to define the a canonical specification for building the fingerprint. +This will allow an incremental path to partial or full statelessness, where it would be easy to bundle/request **witnesses** using this `access_list`. + +## Backwards Compatibility +The extra block validation will only be mandatory post the block number this EIP comes into effect, but the clients can still provide a way to generate (and possibly store) this access list on request (via the `JSON/RPC` api). However this is optional and client dependent. + +## Security Considerations +There are no known security issues as a result of this change. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3589.md b/EIPS/eip-3589.md new file mode 100644 index 0000000..1bfeb2e --- /dev/null +++ b/EIPS/eip-3589.md @@ -0,0 +1,198 @@ +--- +eip: 3589 +title: Assemble assets into NFTs +author: Zhenyu Sun (@Ungigdu), Xinqi Yang (@xinqiyang) +discussions-to: https://github.com/ethereum/EIPs/issues/3590 +status: Stagnant +type: Standards Track +category: ERC +created: 2021-05-24 +requires: 721 +--- + +## Simple Summary +This standard defines a ERC-721 token called assembly token which can represent a combination of assets. + +## Abstract +The ERC-1155 multi-token contract defines a way to batch transfer tokens, but those tokens must be minted by the ERC-1155 contract itself. This EIP is an ERC-721 extension with ability to assemble assets such as ether, ERC-20 tokens, ERC-721 tokens and ERC-1155 tokens into one ERC-721 token whose token id is also the asset's signature. As assets get assembled into one, batch transfer or swap can be implemented very easily. + +## Motivation +As NFT arts and collectors rapidly increases, some collectors are not satisfied with traditional trading methods. When two collectors want to swap some of their collections, currently they can list their NFTs on the market and notify the other party to buy, but this is inefficient and gas-intensive. Instead, some collectors turn to social media or chat group looking for a trustworthy third party to swap NFTs for them. The third party takes NFTs from both collector A and B, and transfer A's collections to B and B's to A. This is very risky. + +The safest way to do batch swap, is to transform batch swap into atomic swap, i.e. one to one swap. But first we should "assemble" those ether, ERC-20 tokens, ERC-721 tokens and ERC-1155 tokens together, and this is the main purpose of this EIP. + +## Specification +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. + +ERC-721 compliant contracts MAY implement this ERC to provide a standard method to assemble assets. + +`mint` and `safeMint` assemble assets into one ERC-721 token. `mint` SHOULD be implemented for normal ERC-20 tokens whose `_transfer` is lossless. `safeMint` MUST takes care for lossy token such as PIG token whose `_transfer` function is taxed. + +`_salt` of `hash` function MAY be implemented other way, even provided as user input. But the token id MUST be generated by `hash` function. + +Implementations of the standard MAY supports different set of assets. + +Implementers of this standard MUST have all of the following functions: + +``` +pragma solidity ^0.8.0; + +interface AssemblyNFTInterface { + + event AssemblyAsset(address indexed firstHolder, + uint256 indexed tokenId, + uint256 salt, + address[] addresses, + uint256[] numbers); + + /** + * @dev hash function assigns the combination of assets with salt to bytes32 signature that is also the token id. + * @param `_salt` prevents hash collision, can be chosen by user input or increasing nonce from contract. + * @param `_addresses` concat assets addresses, e.g. [ERC-20_address1, ERC-20_address2, ERC-721_address_1, ERC-1155_address_1, ERC-1155_address_2] + * @param `_numbers` describes how many eth, ERC-20 token addresses length, ERC-721 token addresses length, ERC-1155 token addresses length, + * ERC-20 token amounts, ERC-721 token ids, ERC-1155 token ids and amounts. + */ + function hash(uint256 _salt, address[] memory _addresses, uint256[] memory _numbers) external pure returns (uint256 tokenId); + + /// @dev to assemble lossless assets + /// @param `_to` the receiver of the assembly token + function mint(address _to, address[] memory _addresses, uint256[] memory _numbers) payable external returns(uint256 tokenId); + + /// @dev mint with additional logic that calculates the actual received value for tokens. + function safeMint(address _to, address[] memory _addresses, uint256[] memory _numbers) payable external returns(uint256 tokenId); + + /// @dev burn this token and releases assembled assets + /// @param `_to` to which address the assets is released + function burn(address _to, uint256 _tokenId, uint256 _salt, address[] calldata _addresses, uint256[] calldata _numbers) external; + +} + +``` + +## Rationale +There are many reasons why people want to pack their NFTs together. For example, a collector want to pack a set of football players into a football team; a collector has hundreds of of NFTs with no categories to manage them; a collector wants to buy a full collection of NFTs or none of them. They all need a way a assemble those NFTs together. + +The reason for choosing ERC-721 standard as a wrapper is ERC-721 token is already widely used and well supported by NFT wallets. And assembly token itself can also be assembled again. Assembly token is easier for smart contract to use than a batch of assets, in scenarios like batch trade, batch swap or collections exchange. + +This standard has AssemblyAsset event which records the exact kinds and amounts of assets the assembly token represents. The wallet can easily display those NFTs to user just by the token id. + +## Backwards Compatibility +This proposal combines already available 721 extensions and is backwards compatible with the ERC-721 standard. + +## Implementation +``` +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; +import "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol"; +import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; +import "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol"; +import "./AssemblyNFTInterface.sol"; + +abstract contract AssemblyNFT is ERC721, ERC721Holder, ERC1155Holder, AssemblyNFTInterface{ + using SafeERC20 for IERC20; + + function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721, ERC1155Receiver) returns (bool) { + return ERC721.supportsInterface(interfaceId) || ERC1155Receiver.supportsInterface(interfaceId); + } + + uint256 nonce; + + /** + * layout of _addresses: + * erc20 addresses | erc721 addresses | erc1155 addresses + * layout of _numbers: + * eth | erc20.length | erc721.length | erc1155.length | erc20 amounts | erc721 ids | erc1155 ids | erc1155 amounts + */ + + function hash(uint256 _salt, address[] memory _addresses, uint256[] memory _numbers) public pure override returns (uint256 tokenId){ + bytes32 signature = keccak256(abi.encodePacked(_salt)); + for(uint256 i=0; i< _addresses.length; i++){ + signature = keccak256(abi.encodePacked(signature, _addresses[i])); + } + for(uint256 j=0; j<_numbers.length; j++){ + signature = keccak256(abi.encodePacked(signature, _numbers[j])); + } + assembly { + tokenId := signature + } + } + + function mint(address _to, address[] memory _addresses, uint256[] memory _numbers) payable external override returns(uint256 tokenId){ + require(_to != address(0), "can't mint to address(0)"); + require(msg.value == _numbers[0], "value not match"); + require(_addresses.length == _numbers[1] + _numbers[2] + _numbers[3], "2 array length not match"); + require(_addresses.length == _numbers.length -4 - _numbers[3], "numbers length not match"); + uint256 pointerA; //points to first erc20 address, if there is any + uint256 pointerB =4; //points to first erc20 amount, if there is any + for(uint256 i = 0; i< _numbers[1]; i++){ + require(_numbers[pointerB] > 0, "transfer erc20 0 amount"); + IERC20(_addresses[pointerA++]).safeTransferFrom(_msgSender(), address(this), _numbers[pointerB++]); + } + for(uint256 j = 0; j< _numbers[2]; j++){ + IERC721(_addresses[pointerA++]).safeTransferFrom(_msgSender(), address(this), _numbers[pointerB++]); + } + for(uint256 k =0; k< _numbers[3]; k++){ + IERC1155(_addresses[pointerA++]).safeTransferFrom(_msgSender(), address(this), _numbers[pointerB], _numbers[_numbers[3] + pointerB++], ""); + } + tokenId = hash(nonce, _addresses, _numbers); + super._mint(_to, tokenId); + emit AssemblyAsset(_to, tokenId, nonce, _addresses, _numbers); + nonce ++; + } + + function safeMint(address _to, address[] memory _addresses, uint256[] memory _numbers) payable external override returns(uint256 tokenId){ + require(_to != address(0), "can't mint to address(0)"); + require(msg.value == _numbers[0], "value not match"); + require(_addresses.length == _numbers[1] + _numbers[2] + _numbers[3], "2 array length not match"); + require(_addresses.length == _numbers.length -4 - _numbers[3], "numbers length not match"); + uint256 pointerA; //points to first erc20 address, if there is any + uint256 pointerB =4; //points to first erc20 amount, if there is any + for(uint256 i = 0; i< _numbers[1]; i++){ + require(_numbers[pointerB] > 0, "transfer erc20 0 amount"); + IERC20 token = IERC20(_addresses[pointerA++]); + uint256 orgBalance = token.balanceOf(address(this)); + token.safeTransferFrom(_msgSender(), address(this), _numbers[pointerB]); + _numbers[pointerB++] = token.balanceOf(address(this)) - orgBalance; + } + for(uint256 j = 0; j< _numbers[2]; j++){ + IERC721(_addresses[pointerA++]).safeTransferFrom(_msgSender(), address(this), _numbers[pointerB++]); + } + for(uint256 k =0; k< _numbers[3]; k++){ + IERC1155(_addresses[pointerA++]).safeTransferFrom(_msgSender(), address(this), _numbers[pointerB], _numbers[_numbers[3] + pointerB++], ""); + } + tokenId = hash(nonce, _addresses, _numbers); + super._mint(_to, tokenId); + emit AssemblyAsset(_to, tokenId, nonce, _addresses, _numbers); + nonce ++; + } + + function burn(address _to, uint256 _tokenId, uint256 _salt, address[] calldata _addresses, uint256[] calldata _numbers) override external { + require(_msgSender() == ownerOf(_tokenId), "not owned"); + require(_tokenId == hash(_salt, _addresses, _numbers)); + super._burn(_tokenId); + payable(_to).transfer(_numbers[0]); + uint256 pointerA; //points to first erc20 address, if there is any + uint256 pointerB =4; //points to first erc20 amount, if there is any + for(uint256 i = 0; i< _numbers[1]; i++){ + require(_numbers[pointerB] > 0, "transfer erc20 0 amount"); + IERC20(_addresses[pointerA++]).safeTransfer(_to, _numbers[pointerB++]); + } + for(uint256 j = 0; j< _numbers[2]; j++){ + IERC721(_addresses[pointerA++]).safeTransferFrom(address(this), _to, _numbers[pointerB++]); + } + for(uint256 k =0; k< _numbers[3]; k++){ + IERC1155(_addresses[pointerA++]).safeTransferFrom(address(this), _to, _numbers[pointerB], _numbers[_numbers[3] + pointerB++], ""); + } + } + +} +``` + +## Security Considerations +Before using `mint` or `safeMint` functions, user should be aware that some implementations of tokens are pausable. If one of the assets get paused after assembled into one NFT, the `burn` function may not be executed successfully. Platforms using this standard should make support lists or block lists to avoid this situation. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3607.md b/EIPS/eip-3607.md new file mode 100644 index 0000000..c4479a5 --- /dev/null +++ b/EIPS/eip-3607.md @@ -0,0 +1,96 @@ +--- +eip: 3607 +title: Reject transactions from senders with deployed code +description: Do not allow transactions for which `tx.sender` has any code deployed. +author: Dankrad Feist (@dankrad), Dmitry Khovratovich (@khovratovich), Marius van der Wijden (@MariusVanDerWijden) +discussions-to: https://github.com/ethereum/EIPs/issues/3608 +status: Final +type: Standards Track +category: Core +created: 2021-06-10 +--- + +## Abstract + +Ethereum addresses are currently only 160 bits long. This means it is possible to create a collision between a contract account and an Externally Owned Account (EOA) using an estimated `2**80` computing operations, which is feasible now given a large budget (ca. 10 billion USD). The fix in this EIP prevents the worst possible attack, where a safe looking contract (e.g. a token wrapper or an AMM-type contract) is deployed to attract user funds, which can then be spent using the EOA key for the same address. The fix is to never allow to use an address that already has code deployed as an EOA address. + +## Motivation + +### Generating address collisions + +By creating keys for `2**80` EOAs and simulating the deployment of `2**80` contracts from these EOAs (one each), one expects to find about one collision where an EOA has the same address as one contract. + +This very simple form of the attack requires the storage of `2**80` addresses, which is a practical barrier: It would require `2.4*10**25` bytes of memory (24 Yottabyte). However, there are cycle finding algorithms that can perform the collision search without requiring large amounts of storage. An estimate for the complexity has been made [here](https://hackmd.io/Vzhp5YJyTT-LhWm_s0JQpA). We estimate that a collision between a contract and an EOA could be found in about one year with an investment of ca. US$10 billion in hardware and electricity. + +### Background + +There is currently a discussion to move to 256-bit addresses on Ethereum, which would increase collision resistance to a complexity of `2**128` which is currently thought infeasible for the foreseeable future. However, with 160 bit addresses, the collision problem can be effectively solved now, as demonstrated above. + +Most attacks that can occur via address collisions are quite impractical: They involve users sending funds to an address before a contract is deployed. This is a very rare application in practice and users can easily circumvent the attack by never sending funds to a contract until it has been safely deployed with enough confirmations. + +However, the yellow paper does not explicitly specify how a client should handle the case where a transaction is sent from an account that already has contract code deployed; presumably because this was considered infeasible at the time. The assumption is that most client would allow this transaction in their current state. + +This EIP is to specify this behaviour to always forbid such transactions. This fixes most realistic or serious attacks due to address collisions. + + +## Specification + +Any transaction where `tx.sender` has a `CODEHASH != EMPTYCODEHASH` MUST be rejected as invalid, where `EMPTYCODEHASH = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470`. +The invalid transaction MUST be rejected by the client and not be included in a block. +A block containing such a transaction MUST be considered invalid. + +## Rationale + +We note that it was always the expected that a contract account's behaviour is constrained by the code in that contract -- which means that the account's funds should not suddenly be spendable by some private key. It was just implicitly assumed in the past that a 160 bit address length is enough to provide collision resistance, and thus that this case could never occur. In that sense, this EIP should be seen as a clarification of protocol behaviour in a previously undefined case rather than an explicit upgrade of consensus rules. + +This does not exclude all possible attack vectors, only the most serious one. Further possible attack vectors via address collisions between contracts and EOAs are: +1. An attacker can convince a user to send funds to an account before it is deployed. Some applications require this behaviour (e.g. state channels). +2. A chain reorg can happen after a contract is deployed. If the reorg removes the contract deployment transaction the funds can still be accessed using the private key. +3. A contract can self desctruct, with the stated intention that ERC20s (or other tokens) in the contract would be burned. However, they can now be accessed by a key for that address. + +All these scenarios are much harder to exploit for an attacker, and likely have much lower yield making the attacks unlikely to be economically viable. + +## Backwards Compatibility + +It is unlikely that an attack like this has already occurred on the Ethereum mainnet, or we would very likely have heard of it. It is inconceivable that someone would use this as a "feature" to make a contract an EOA at the same time, when they could simply do this by adding some methods to the contract instead of spending billions on building hardware to find hash collisions. + +Private networks may have deployed contracts which also work as EOAs at genesis and should check that this upgrade does not impact their workflows. + +Clients might choose to disable this rule for RPC calls like `eth_call` and `eth_estimateGas` as some Multi-Sig contracts use these calls to create transactions as if they originated from the multisig contract itself. + +## Test Cases + +Given a genesis allocation of +``` +Address: 0x71562b71999873DB5b286dF957af199Ec94617F7 +Balance: 1000000000000000000 // 1 ether +Nonce: 0, +Code: 0xB0B0FACE", +``` +Every transaction sent by the private key corresponding to `0x715656...` ( +`b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291`) should be rejected. +These transaction must be rejected and not included in a block. + +## Reference Implementation + +The following check must be added to the state transition checks after checking that the nonce of the sender is correct. +The sender is the address recovered from the signature of the transaction. +``` +// Make sure the sender is an EOA +Set ch to the CodeHash of the sender account +if ch is not equal to EmptyCodeHash then + return ErrSenderNoEOA +end if +``` + +A diff to implement EIP-3607 in go-ethereum can be found [here](../assets/eip-3607/geth.diff) + +## Security Considerations + +This EIP is a strict security upgrade: It simply makes some transactions that were formerly valid now invalid. There is no legitimate use for such transactions, so there should be no security downsides. + +This EIP can be implemented as a soft fork because the new validity rules are a strict superset of the previous validity rules. + + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3643.md b/EIPS/eip-3643.md new file mode 100644 index 0000000..9a41f2e --- /dev/null +++ b/EIPS/eip-3643.md @@ -0,0 +1,382 @@ +--- +eip: 3643 +title: T-REX - Token for Regulated EXchanges +description: An institutional grade security token standard that provides interfaces for the management and compliant transfer of security tokens. +author: Joachim Lebrun (@Joachim-Lebrun), Tony Malghem (@TonyMalghem), Kevin Thizy (@Nakasar), Luc Falempin (@lfalempin), Adam Boudjemaa (@Aboudjem) +type: Standards Track +category: ERC +status: Stagnant +requires: 20, 1822 +discussions-to: https://ethereum-magicians.org/t/eip-3643-proposition-of-the-t-rex-token-standard-for-securities/6844 +created: 2021-07-09 +--- + +## Simple Summary + +The T-REX token is +This standard + + +## Abstract + +Standards should be backwards compatible with [ERC-20](./eip-20.md) and should be able to interact with [ERC-735](https://github.com/ethereum/EIPs/issues/735) to validate the claims linked to an [`ONCHAINID`](https://github.com/onchain-id/solidity), based on [ERC-734](https://github.com/ethereum/EIPs/issues/734) and ERC-735. +The standard defines several interfaces that are described hereunder: +- Token +- Identity Registry +- Identity Registry Storage +- Compliance +- Trusted Issuers Registry +- Claim Topics Registry + +## Motivation + +Give standard interfaces for security tokens issued on Ethereum, through which any third party could interact with the security token. +The functions described by these interfaces vary and allow the appropriate users to call a range of different actions, such as forced transfers, freeze tokens (partially or totally on a wallet or even freeze the entire token), minting, burning, recover lost tokens (if an investor loses access to his wallet), etc. + +The following requirements have been compiled following discussions with parties across financial institutions that are looking to issue securities on a DLT infrastructure such as ethereum. + +- **MUST** be [ERC-20](./eip-20.md) compatible. +- **MUST** be used in combination with an Identification system onchain ([ONCHAINID](https://github.com/onchain-id/solidity)) +- **MUST** be able to apply any rule of compliance that is required by the regulator or by the token issuer (about the factors of eligibility of an identity or about the rules of the token itself) +- **MUST** have a standard interface to pre-check if a transfer is going to pass or fail before sending it to the blockchain +- **MUST** have a recovery system in case an investor loses access to his private key +- **MUST** be able to freeze tokens on the wallet of investors if needed, partially or totally +- **MUST** have the possibility to pause the token +- **MUST** be able to mint and burn tokens +- **MUST** define an Agent role and an Owner (token issuer) role +- **MUST** be able to force transfers from an Agent wallet +- **MUST** be able to issue transactions in batch (to save gas and to have all the transactions performed in the same block) +- **MUST** be upgradeable (code of the smart contract should be upgradeable without changing the token smart contract address) + +## Rationale + +### Transfer Restrictions + +Transfers of securities can fail for a variety of reasons. This is in direct contrast to utility tokens, of which generally only require the sender to have a sufficient balance. +These conditions can be related to the status of an investor’s wallet, the identity of the sender and receiver of the securities (i.e. whether they +have been through a KYC process, whether they are accredited or an affiliate of the issuer) or for reasons unrelated to the specific transfer but instead set at +the token level (i.e. the token contract enforces a maximum number of investors or a cap on the percentage held by any single investor). +For [ERC-20](./eip-20.md) tokens, the `balanceOf` and `allowance` functions provide a way to check that a transfer is likely to succeed before executing the transfer, which can be +executed both on-chain and off-chain. +For tokens representing securities, the T-REX standard introduces a function `canTransfer` which provides a more general purpose way to achieve this. I.e. when the reasons for +failure are related to the compliance rules of the token and a function `isVerified` which allows to check the eligibility status of the identity of the investor. + +### Upgradeability + +The token contract should be upgradeable without changing its address on the blockchain, therefore, we decided to make it `proxiable` through [ERC-1822](./eip-1822.md) (Universal Upgradeable Proxy Standard) + +### Identity Management + +Security and compliance of transfers is issued through the management of onchain identities. +- ONCHAINID +- Claim +- Identity Storage/registry +Transfers of securities can fail for a variety of reasons in contrast to utility tokens which generally only require the sender to have a sufficient balance. + + +## Specification + +This standard is backwards compatible with [ERC-20](./eip-20.md), therefore, all ERC-20 functions can be called on an ERC-3643 token, the interfaces being compatible. +But the functions are not implemented in the same way as a classic ERC-20 as ERC-3643 is a permissioned token, which implies a check to be performed on each single +token transfer to validate the compliance of the transfer and the eligibility of the stakeholder’s identities. + +### Main functions + +#### Transfer + +To be able to perform a transfer on T-REX you need to fulfill several conditions : + +- The sender needs to hold enough free balance (total balance - frozen tokens, if any) +- The receiver needs to be whitelisted on the Identity Registry and verified (hold the necessary claims on his [ONCHAINID](https://github.com/onchain-id/solidity)) +- The sender's wallet cannot be frozen +- The receiver's wallet cannot be frozen +- The transfer has to respect all the rules of compliance defined in the `Compliance` smart contract (`canTransfer` needs to return `TRUE`) + +Here is an example of `transfer` function implementation : +```solidity +function transfer(address _to, uint256 _amount) public override whenNotPaused returns (bool) { + require(!frozen[_to] && !frozen[msg.sender], 'wallet is frozen'); + require(_amount <= balanceOf(msg.sender).sub(frozenTokens[msg.sender]), 'Insufficient Balance'); + if (tokenIdentityRegistry.isVerified(_to) && tokenCompliance.canTransfer(msg.sender, _to, _amount)) { + tokenCompliance.transferred(msg.sender, _to, _amount); + _transfer(msg.sender, _to, _amount); + return true; + } + revert('Transfer not possible'); + } + ``` + + The `transferFrom` function works the same way while the `mint` function and the `forcedTransfer` function only require the receiver to be whitelisted and verified on the Identity Registry (they bypass the compliance rules). The `burn` function bypasses all checks on eligibility. + +#### isVerified + +The `isVerified` function is called from within the transfer functions `transfer`, `transferFrom`, `mint` and `forcedTransfer` to instruct the `Identity Registry` to check if the receiver is a valid investor, i.e. if his wallet address is in the `Identity Registry` of the token, and if the `ONCHAINID`contract linked to his wallet contains the claims (see ERC-735) required in the `Claim Topics Registry` and if these claims are signed by an authorized Claim Issuer as required in the `Trusted Issuers Registry`. +If all the requirements are fulfilled, the `isVerified` function returns `TRUE`, otherwise it returns `FALSE`. An implementation of this function can be found on the [T-REX repository](https://github.com/TokenySolutions/T-REX). + +#### canTransfer + +The `canTransfer` function is also called from within transfer functions. This function checks if the transfer is compliant with global compliance rules applied to the token, in opposition with `isVerified` that only checks the eligibility of an investor to hold and receive tokens, the `canTransfer` function is looking at global compliance rules, e.g. check if the transfer is compliant in the case there is a fixed maximum number of token holders to respect (can be a limited number of holders per country as well), check if the transfer respects rules setting a maximum amount of tokens per investor, ... +If all the requirements are fulfilled, the `canTransfer` function will return `TRUE` otherwise it will return `FALSE` and the transfer will not be allowed to happen. An implementation of this function can be found on the [T-REX repository](https://github.com/TokenySolutions/T-REX). + +#### Other functions + +Description of other functions of the ERC-3643 can be found in the `interfaces` folder. An implementation of the ERC-3643 suite of smart contracts can be found on the [T-REX repository](https://github.com/TokenySolutions/T-REX). + +### Token interface + +ERC-3643 permissioned tokens are based on a standard ERC-20 structure but with some functions being added in order to ensure compliance in the transactions of the security tokens. The functions `transfer` and `transferFrom` are implemented in a conditional way, allowing them to proceed with a transfer only IF the transaction is valid. The permissioned tokens are allowed to be transferred only to validated counterparties, in order to avoid tokens being held in wallets/ONCHAINIDs of ineligible/unauthorized investors. The ERC-3643 standard also supports the recovery of security tokens in case an investor loses his/her wallet private key. A history of recovered tokens is maintained on the blockchain for transparency reasons. ERC-3643 tokens are implementing a lot of additional functions to give the owner or his agent the possibility to manage supply, transfer rules, lockups and everything that could be required in the management of a security. +A detailed description of the functions can be found in the [interfaces folder](https://github.com/TokenySolutions/EIP3643/tree/main/interfaces). + +```solidity +interface IERC3643 is IERC20 { + + // events + event UpdatedTokenInformation(string _newName, string _newSymbol, uint8 _newDecimals, string _newVersion, address _newOnchainID); + event IdentityRegistryAdded(address indexed _identityRegistry); + event ComplianceAdded(address indexed _compliance); + event RecoverySuccess(address _lostWallet, address _newWallet, address _investorOnchainID); + event AddressFrozen(address indexed _userAddress, bool indexed _isFrozen, address indexed _owner); + event TokensFrozen(address indexed _userAddress, uint256 _amount); + event TokensUnfrozen(address indexed _userAddress, uint256 _amount); + event Paused(address _userAddress); + event Unpaused(address _userAddress); + + + // functions + // getters + function decimals() external view returns (uint8); + function name() external view returns (string memory); + function onchainID() external view returns (address); + function symbol() external view returns (string memory); + function version() external view returns (string memory); + function identityRegistry() external view returns (IIdentityRegistry); + function compliance() external view returns (ICompliance); + function paused() external view returns (bool); + function isFrozen(address _userAddress) external view returns (bool); + function getFrozenTokens(address _userAddress) external view returns (uint256); + + // setters + function setName(string calldata _name) external; + function setSymbol(string calldata _symbol) external; + function setOnchainID(address _onchainID) external; + function pause() external; + function unpause() external; + function setAddressFrozen(address _userAddress, bool _freeze) external; + function freezePartialTokens(address _userAddress, uint256 _amount) external; + function unfreezePartialTokens(address _userAddress, uint256 _amount) external; + function setIdentityRegistry(address _identityRegistry) external; + function setCompliance(address _compliance) external; + + // transfer actions + function forcedTransfer(address _from, address _to, uint256 _amount) external returns (bool); + function mint(address _to, uint256 _amount) external; + function burn(address _userAddress, uint256 _amount) external; + function recoveryAddress(address _lostWallet, address _newWallet, address _investorOnchainID) external returns (bool); + + // batch functions + function batchTransfer(address[] calldata _toList, uint256[] calldata _amounts) external; + function batchForcedTransfer(address[] calldata _fromList, address[] calldata _toList, uint256[] calldata _amounts) external; + function batchMint(address[] calldata _toList, uint256[] calldata _amounts) external; + function batchBurn(address[] calldata _userAddresses, uint256[] calldata _amounts) external; + function batchSetAddressFrozen(address[] calldata _userAddresses, bool[] calldata _freeze) external; + function batchFreezePartialTokens(address[] calldata _userAddresses, uint256[] calldata _amounts) external; + function batchUnfreezePartialTokens(address[] calldata _userAddresses, uint256[] calldata _amounts) external; + + // roles setting + function transferOwnershipOnTokenContract(address _newOwner) external; + function addAgentOnTokenContract(address _agent) external; + function removeAgentOnTokenContract(address _agent) external; +} + +``` + +### Identity Registry Interface + +This Identity Registry is linked to storage that contains a dynamic whitelist of identities. The Identity Registry makes the link between a wallet address, an [ONCHAINID](https://tokeny.com/onchainid/) and a country code corresponding to the country of residence of the investor, this country code is set in accordance with the [ISO-3166 standard](https://www.iso.org/iso-3166-country-codes.html). It also contains a function called `isVerified()`, which returns a status based on the validity of claims (as per the security token requirements) in the user’s ONCHAINID. The Identity Registry is managed by the agent wallet(s) i.e. only the agent(s) can add or remove identities in the registry (note: the agent role on the Identity Registry is set by the owner, therefore the owner could set himself as the agent if he wants to keep everything under his own control). There is a specific identity registry for each security token. +A detailed description of the functions can be found in the [interfaces folder](https://github.com/TokenySolutions/EIP3643/tree/main/interfaces). + +Note that [`IClaimIssuer`](https://github.com/onchain-id/solidity/blob/master/contracts/interface/IClaimIssuer.sol) and [`IIdentity`](https://github.com/onchain-id/solidity/blob/master/contracts/interface/IIdentity.sol) are needed in this interface and are coming from [ONCHAINID](https://github.com/onchain-id/solidity) + +```solidity +interface IIdentityRegistry { + + + // events + event ClaimTopicsRegistrySet(address indexed claimTopicsRegistry); + event IdentityStorageSet(address indexed identityStorage); + event TrustedIssuersRegistrySet(address indexed trustedIssuersRegistry); + event IdentityRegistered(address indexed investorAddress, IIdentity indexed identity); + event IdentityRemoved(address indexed investorAddress, IIdentity indexed identity); + event IdentityUpdated(IIdentity indexed oldIdentity, IIdentity indexed newIdentity); + event CountryUpdated(address indexed investorAddress, uint16 indexed country); + + + // functions + // identity registry getters + function identityStorage() external view returns (IIdentityRegistryStorage); + function issuersRegistry() external view returns (ITrustedIssuersRegistry); + function topicsRegistry() external view returns (IClaimTopicsRegistry); + + //identity registry setters + function setIdentityRegistryStorage(address _identityRegistryStorage) external; + function setClaimTopicsRegistry(address _claimTopicsRegistry) external; + function setTrustedIssuersRegistry(address _trustedIssuersRegistry) external; + + // registry actions + function registerIdentity(address _userAddress, IIdentity _identity, uint16 _country) external; + function deleteIdentity(address _userAddress) external; + function updateCountry(address _userAddress, uint16 _country) external; + function updateIdentity(address _userAddress, IIdentity _identity) external; + function batchRegisterIdentity(address[] calldata _userAddresses, IIdentity[] calldata _identities, uint16[] calldata _countries) external; + + // registry consultation + function contains(address _userAddress) external view returns (bool); + function isVerified(address _userAddress) external view returns (bool); + function identity(address _userAddress) external view returns (IIdentity); + function investorCountry(address _userAddress) external view returns (uint16); + + // roles setters + function transferOwnershipOnIdentityRegistryContract(address _newOwner) external; + function addAgentOnIdentityRegistryContract(address _agent) external; + function removeAgentOnIdentityRegistryContract(address _agent) external; +} +``` + +### Identity Registry Storage Interface + +The Identity Registry Storage stores the identity addresses of all the authorized investors in the security token(s) linked to the storage contract i.e. all identities of investors who have been authorized to hold the token(s) after having gone through the appropriate KYC and eligibility checks. The Identity Registry Storage can be bound to one or several Identity Registry contract(s). The goal of the Identity Registry storage is to separate the Identity Registry functions and specifications from its storage, this way it is possible to keep one single Identity Registry contract per token, with its own Trusted Issuers Registry and Claim Topics Registry but with a shared whitelist of investors used by the `isVerifed()` function implemented in the Identity Registries to check the eligibility of the receiver in a transfer transaction. +A detailed description of the functions can be found in the [interfaces folder](https://github.com/TokenySolutions/EIP3643/tree/main/interfaces). + +```solidity +interface IIdentityRegistryStorage { + + //events + event IdentityStored(address indexed investorAddress, IIdentity indexed identity); + event IdentityUnstored(address indexed investorAddress, IIdentity indexed identity); + event IdentityModified(IIdentity indexed oldIdentity, IIdentity indexed newIdentity); + event CountryModified(address indexed investorAddress, uint16 indexed country); + event IdentityRegistryBound(address indexed identityRegistry); + event IdentityRegistryUnbound(address indexed identityRegistry); + + //functions + // storage related functions + function storedIdentity(address _userAddress) external view returns (IIdentity); + function storedInvestorCountry(address _userAddress) external view returns (uint16); + function addIdentityToStorage(address _userAddress, IIdentity _identity, uint16 _country) external; + function removeIdentityFromStorage(address _userAddress) external; + function modifyStoredInvestorCountry(address _userAddress, uint16 _country) external; + function modifyStoredIdentity(address _userAddress, IIdentity _identity) external; + + // role setter + function transferOwnershipOnIdentityRegistryStorage(address _newOwner) external; + function bindIdentityRegistry(address _identityRegistry) external; + function unbindIdentityRegistry(address _identityRegistry) external; + + // getter for bound IdentityRegistry role + function linkedIdentityRegistries() external view returns (address[] memory); +} + +``` + +### Compliance Interface + +The Compliance is used to set the rules of the offering itself and ensures these rules are respected during the whole lifecycle of the token, e.g. the compliance contract will define the maximum amount of investors per country, the maximum amount of tokens per investor, the accepted countries for the circulation of the token (using the country code corresponding to each investor in the Identity Registry). The compliance smart contract is a “tailor-made” contract that is implemented in accordance with the legal requirements and following the desires of the token issuer. This contract is triggered at every transaction by the Token and returns `TRUE` if the transaction is compliant with the rules of the offering and `FALSE` otherwise. +A detailed description of the functions can be found in the [interfaces folder](https://github.com/TokenySolutions/EIP3643/tree/main/interfaces). + +```solidity +interface ICompliance { + + // events + event TokenAgentAdded(address _agentAddress); + event TokenAgentRemoved(address _agentAddress); + event TokenBound(address _token); + event TokenUnbound(address _token); + + // functions + // initialization of the compliance contract + function addTokenAgent(address _agentAddress) external; + function removeTokenAgent(address _agentAddress) external; + function bindToken(address _token) external; + function unbindToken(address _token) external; + + // check the parameters of the compliance contract + function isTokenAgent(address _agentAddress) external view returns (bool); + function isTokenBound(address _token) external view returns (bool); + + // compliance check and state update + function canTransfer(address _from, address _to, uint256 _amount) external view returns (bool); + function transferred(address _from, address _to, uint256 _amount) external; + function created(address _to, uint256 _amount) external; + function destroyed(address _from, uint256 _amount) external; + + // setting owner role + function transferOwnershipOnComplianceContract(address newOwner) external; +} +``` + +### Trusted Issuer's Registry Interface + +The Trusted Issuer's Registry stores the contract addresses ([ONCHAINID](https://tokeny.com/onchainid/)) of all the trusted claim issuers for a specific security token. The [ONCHAINID](https://tokeny.com/onchainid/) of token owners (the investors) must have claims signed by the claim issuers stored in this smart contract in order to be able to hold the token. The ownership of this contract is given to the token issuer allowing them to manage this registry as per their requirements. +A detailed description of the functions can be found in the [interfaces folder](https://github.com/TokenySolutions/EIP3643/tree/main/interfaces) + +```solidity +interface ITrustedIssuersRegistry { + + // events + event TrustedIssuerAdded(IClaimIssuer indexed trustedIssuer, uint[] claimTopics); + event TrustedIssuerRemoved(IClaimIssuer indexed trustedIssuer); + event ClaimTopicsUpdated(IClaimIssuer indexed trustedIssuer, uint[] claimTopics); + + // functions + // setters + function addTrustedIssuer(IClaimIssuer _trustedIssuer, uint[] calldata _claimTopics) external; + function removeTrustedIssuer(IClaimIssuer _trustedIssuer) external; + function updateIssuerClaimTopics(IClaimIssuer _trustedIssuer, uint[] calldata _claimTopics) external; + + // getters + function getTrustedIssuers() external view returns (IClaimIssuer[] memory); + function isTrustedIssuer(address _issuer) external view returns(bool); + function getTrustedIssuerClaimTopics(IClaimIssuer _trustedIssuer) external view returns(uint[] memory); + function hasClaimTopic(address _issuer, uint _claimTopic) external view returns(bool); + + // role setter + function transferOwnershipOnIssuersRegistryContract(address _newOwner) external; +} +``` + +### Claim Topics Registry Interface + +The Claim Topics Registry stores all the trusted claim topics for the security token. The [ONCHAINID](https://tokeny.com/onchainid/) of token owners must contain claims of the claim topics stored in this smart contract. The ownership of this contract is given to the token issuer allowing them to manage this registry as per their requirements. +A detailed description of the functions can be found in the [interfaces folder](https://github.com/TokenySolutions/EIP3643/tree/main/interfaces) + +```solidity +interface IClaimTopicsRegistry { + + // events + event ClaimTopicAdded(uint256 indexed claimTopic); + event ClaimTopicRemoved(uint256 indexed claimTopic); + + // functions + // setters + function addClaimTopic(uint256 _claimTopic) external; + function removeClaimTopic(uint256 _claimTopic) external; + + // getter + function getClaimTopics() external view returns (uint256[] memory); + + // role setter + function transferOwnershipOnClaimTopicsRegistryContract(address _newOwner) external; +} +``` + +## Test Cases + +The standard is implemented and tested with full coverage on Tokeny's [T-REX repository](https://github.com/TokenySolutions/T-REX) + +## Security Considerations + +The suite of Smart Contracts has been audited by an external and independent company. The results can be found in [this document](https://tokeny.com/wp-content/uploads/2020/05/Tokeny-Solutions_T-REX-v3_Smart-Contract-Audit-Report_Kapersky.pdf). + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3651.md b/EIPS/eip-3651.md new file mode 100644 index 0000000..cd7d6a4 --- /dev/null +++ b/EIPS/eip-3651.md @@ -0,0 +1,46 @@ +--- +eip: 3651 +title: Warm COINBASE +description: Starts the `COINBASE` address warm +author: William Morriss (@wjmelements) +discussions-to: https://ethereum-magicians.org/t/eip-3651-warm-coinbase/6640 +status: Last Call +last-call-deadline: 2023-03-28 +type: Standards Track +category: Core +created: 2021-07-12 +requires: 2929 +--- + +## Abstract + +The `COINBASE` address shall be warm at the start of transaction execution, in accordance with the actual cost of reading that account. + +## Motivation + +Direct `COINBASE` payments are becoming increasingly popular because they allow conditional payments, which provide benefits such as implicit cancellation of transactions that would revert. +But accessing `COINBASE` is overpriced; the address is initially cold under the access list framework introduced in [EIP-2929](./eip-2929.md). +This gas cost mismatch can incentivize alternative payments besides ETH, such as [ERC-20](./eip-20.md), but ETH should be the primary means of paying for transactions on Ethereum. + +## Specification + +At the start of transaction execution, `accessed_addresses` shall be initialized to also include the address returned by `COINBASE` (`0x41`). + +## Rationale + +The addresses currently initialized warm are the addresses that should already be loaded at the start of transaction validation. +The `ORIGIN` address is always loaded to check its balance against the gas limit and the gas price. +The `tx.to` address is always loaded to begin execution. +The `COINBASE` address should also be always be loaded because it receives the block reward and the transaction fees. + +## Backwards Compatibility + +There are no known backward compatibility issues presented by this change. + +## Security Considerations + +There are no known security considerations introduced by this change. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3668.md b/EIPS/eip-3668.md new file mode 100644 index 0000000..b04d3dc --- /dev/null +++ b/EIPS/eip-3668.md @@ -0,0 +1,410 @@ +--- +eip: 3668 +title: "CCIP Read: Secure offchain data retrieval" +description: CCIP Read provides a mechanism to allow a contract to fetch external data. +author: Nick Johnson (@arachnid) +discussions-to: https://ethereum-magicians.org/t/durin-secure-offchain-data-retrieval/6728 +status: Final +type: Standards Track +category: ERC +created: 2020-07-19 +--- + +## Abstract +Contracts wishing to support lookup of data from external sources may, instead of returning the data directly, revert using `OffchainLookup(address sender, string[] urls, bytes callData, bytes4 callbackFunction, bytes extraData)`. Clients supporting this specification then make an RPC call to a URL from `urls`, supplying `callData`, and getting back an opaque byte string `response`. Finally, clients call the function specified by `callbackFunction` on the contract, providing `response` and `extraData`. The contract can then decode and verify the returned data using an implementation-specific method. + +This mechanism allows for offchain lookups of data in a way that is transparent to clients, and allows contract authors to implement whatever validation is necessary; in many cases this can be provided without any additional trust assumptions over and above those required if data is stored onchain. + +## Motivation +Minimising storage and transaction costs on Ethereum has driven contract authors to adopt a variety of techniques for moving data offchain, including hashing, recursive hashing (eg Merkle Trees/Tries) and L2 solutions. While each solution has unique constraints and parameters, they all share in common the fact that enough information is stored onchain to validate the externally stored data when required. + +Thus far, applications have tended to devise bespoke solutions rather than trying to define a universal standard. This is practical - although inefficient - when a single offchain data storage solution suffices, but rapidly becomes impractical in a system where multiple end-users may wish to make use of different data storage and availability solutions based on what suits their needs. + +By defining a common specification allowing smart contract to fetch data from offchain, we facilitate writing clients that are entirely agnostic to the storage solution being used, which enables new applications that can operate without knowing about the underlying storage details of the contracts they interact with. + +Examples of this include: + - Interacting with 'airdrop' contracts that store a list of recipients offchain in a merkle trie. + - Viewing token information for tokens stored on an L2 solution as if they were native L1 tokens. + - Allowing delegation of data such as ENS domains to various L2 solutions, without requiring clients to support each solution individually. + - Allowing contracts to proactively request external data to complete a call, without requiring the caller to be aware of the details of that data. + +## Specification +### Overview +Answering a query via CCIP read takes place in three steps: + + 1. Querying the contract. + 2. Querying the gateway using the URL provided in (1). + 3. Querying or sending a transaction to the contract using the data from (1) and (2). + +In step 1, a standard blockchain call operation is made to the contract. The contract reverts with an error that specifies the data to complete the call can be found offchain, and provides the url to a service that can provide the answer, along with additional contextual information required for the call in step (3). + +In step 2, the client calls the gateway service with the `callData` from the revert message in step (1). The gateway responds with an answer `response`, whose content is opaque to the client. + +In step 3, the client calls the original contract, supplying the `response` from step (2) and the `extraData` returned by the contract in step (1). The contract decodes the provided data and uses it to validate the response and act on it - by returning information to the client or by making changes in a transaction. The contract could also revert with a new error to initiate another lookup, in which case the protocol starts again at step 1. + +``` +┌──────┐ ┌────────┐ ┌─────────────┐ +│Client│ │Contract│ │Gateway @ url│ +└──┬───┘ └───┬────┘ └──────┬──────┘ + │ │ │ + │ somefunc(...) │ │ + ├─────────────────────────────────────────────────►│ │ + │ │ │ + │ revert OffchainData(sender, urls, callData, │ │ + │ callbackFunction, extraData) │ │ + │◄─────────────────────────────────────────────────┤ │ + │ │ │ + │ HTTP request (sender, callData) │ │ + ├──────────────────────────────────────────────────┼────────────►│ + │ │ │ + │ Response (result) │ │ + │◄─────────────────────────────────────────────────┼─────────────┤ + │ │ │ + │ callbackFunction(result, extraData) │ │ + ├─────────────────────────────────────────────────►│ │ + │ │ │ + │ answer │ │ + │◄─────────────────────────────────────────────────┤ │ + │ │ │ +``` + +### Contract interface + +A CCIP read enabled contract MUST revert with the following error whenever a function that requires offchain data is called: + +```solidity +error OffchainLookup(address sender, string[] urls, bytes callData, bytes4 callbackFunction, bytes extraData) +``` + +`sender` is the address of the contract that raised the error, and is used to determine if the error was thrown by the contract the client called, or 'bubbled up' from a nested call. + +`urls` specifies a list of URL templates to services (known as gateways) that implement the CCIP read protocol and can formulate an answer to the query. `urls` can be the empty list `[]`, in which case the client MUST specify the URL template. The order in which URLs are tried is up to the client, but contracts SHOULD return them in order of priority, with the most important entry first. + +Each URL may include two substitution parameters, `{sender}` and `{data}`. Before a call is made to the URL, `sender` is replaced with the lowercase 0x-prefixed hexadecimal formatted `sender` parameter, and `data` is replaced by the the 0x-prefixed hexadecimal formatted `callData` parameter. + +`callData` specifies the data to call the gateway with. This value is opaque to the client. Typically this will be ABI-encoded, but this is an implementation detail that contracts and gateways can standardise on as desired. + +`callbackFunction` is the 4-byte function selector for a function on the original contract to which a callback should be sent. + +`extraData` is additional data that is required by the callback, and MUST be retained by the client and provided unmodified to the callback function. This value is opaque to the client. + +The contract MUST also implement a callback method for decoding and validating the data returned by the gateway. The name of this method is implementation-specific, but it MUST have the signature `(bytes response, bytes extraData)`, and MUST have the same return type as the function that reverted with `OffchainLookup`. + +If the client successfully calls the gateway, the callback function specified in the `OffchainLookup` error will be invoked by the client, with `response` set to the value returned by the gateway, and `extraData` set to the value returned in the contract's `OffchainLookup` error. The contract MAY initiate another CCIP read lookup in this callback, though authors should bear in mind that the limits on number of recursive invocations will vary from client to client. + +In a call context (as opposed to a transaction), the return data from this call will be returned to the user as if it was returned by the function that was originally invoked. + +#### Example + +Suppose a contract has the following method: + +```solidity +function balanceOf(address addr) public view returns(uint balance); +``` + +Data for these queries is stored offchain in some kind of hashed data structure, the details of which are not important for this example. The contract author wants the gateway to fetch the proof information for this query and call the following function with it: + +```solidity +function balanceOfWithProof(bytes calldata response, bytes calldata extraData) public view returns(uint balance); +``` + +One example of a valid implementation of `balanceOf` would thus be: + +```solidity +function balanceOf(address addr) public view returns(uint balance) { + revert OffchainLookup( + address(this), + [url], + abi.encodeWithSelector(Gateway.getSignedBalance.selector, addr), + ContractName.balanceOfWithProof.selector, + abi.encode(addr) + ); +} +``` + +Note that in this example the contract is returning `addr` in both `callData` and `extraData`, because it is required both by the gateway (in order to look up the data) and the callback function (in order to verify it). The contract cannot simply pass it to the gateway and rely on it being returned in the response, as this would give the gateway an opportunity to respond with an answer to a different query than the one that was initially issued. + +#### Recursive calls in CCIP-aware contracts + +When a CCIP-aware contract wishes to make a call to another contract, and the possibility exists that the callee may implement CCIP read, the calling contract MUST catch all `OffchainLookup` errors thrown by the callee, and revert with a different error if the `sender` field of the error does not match the callee address. + +The contract MAY choose to replace all `OffchainLookup` errors with a different error. Doing so avoids the complexity of implementing support for nested CCIP read calls, but renders them impossible. + +Where the possibility exists that a callee implements CCIP read, a CCIP-aware contract MUST NOT allow the default solidity behaviour of bubbling up reverts from nested calls. This is to prevent the following situation: + + 1. Contract A calls non-CCIP-aware contract B. + 2. Contract B calls back to A. + 3. In the nested call, A reverts with `OffchainLookup`. + 4. Contract B does not understand CCIP read and propagates the `OffchainLookup` to its caller. + 5. Contract A also propagates the `OffchainLookup` to its caller. + +The result of this sequence of operations would be an `OffchainLookup` that looks valid to the client, as the `sender` field matches the address of the contract that was called, but does not execute correctly, as it only completes a nested invocation. + +#### Example + +The code below demonstrates one way that a contract may support nested CCIP read invocations. For simplicity this is shown using Solidity's try/catch syntax, although as of this writing it does not yet support catching custom errors. + +```solidity +contract NestedLookup { + error InvalidOperation(); + error OffchainLookup(address sender, string[] urls, bytes callData, bytes4 callbackFunction, bytes extraData); + + function a(bytes calldata data) external view returns(bytes memory) { + try target.b(data) returns (bytes memory ret) { + return ret; + } catch OffchainLookup(address sender, string[] urls, bytes callData, bytes4 callbackFunction, bytes extraData) { + if(sender != address(target)) { + revert InvalidOperation(); + } + revert OffchainLookup( + address(this), + urls, + callData, + NestedLookup.aCallback.selector, + abi.encode(address(target), callbackFunction, extraData) + ); + } + } + + function aCallback(bytes calldata response, bytes calldata extraData) external view returns(bytes memory) { + (address inner, bytes4 innerCallbackFunction, bytes memory innerExtraData) = abi.decode(extraData, (address, bytes4, bytes)); + return abi.decode(inner.call(abi.encodeWithSelector(innerCallbackFunction, response, innerExtraData)), (bytes)); + } +} +``` + +### Gateway Interface +The URLs returned by a contract may be of any schema, but this specification only defines how clients should handle HTTPS URLs. + +Given a URL template returned in an `OffchainLookup`, the URL to query is composed by replacing `sender` with the lowercase 0x-prefixed hexadecimal formatted `sender` parameter, and replacing `data` with the the 0x-prefixed hexadecimal formatted `callData` parameter. + +For example, if a contract returns the following data in an `OffchainLookup`: + +``` +urls = ["https://example.com/gateway/{sender}/{data}.json"] +sender = "0xaabbccddeeaabbccddeeaabbccddeeaabbccddee" +callData = "0x00112233" +``` + +The request URL to query is `https://example.com/gateway/0xaabbccddeeaabbccddeeaabbccddeeaabbccddee/0x00112233.json`. + +If the URL template contains the `{data}` substitution parameter, the client MUST send a GET request after replacing the substitution parameters as described above. + +If the URL template does not contain the `{data}` substitution parameter, the client MUST send a POST request after replacing the substitution parameters as described above. The POST request MUST be sent with a Content-Type of `application/json`, and a payload matching the following schema: + +``` +{ + "type": "object", + "properties": { + "data": { + "type": "string", + "description": "0x-prefixed hex string containing the `callData` from the contract" + }, + "sender": { + "type": "string", + "description": "0x-prefixed hex string containing the `sender` parameter from the contract" + } + } +} +``` + +Compliant gateways MUST respond with a Content-Type of `application/json`, with the body adhering to the following JSON schema: +``` +{ + "type": "object", + "properties": { + "data": { + "type": "string", + "description: "0x-prefixed hex string containing the result data." + } + } +} +``` + +Unsuccessful requests MUST return the appropriate HTTP status code - for example, 404 if the `sender` address is not supported by this gateway, 400 if the `callData` is in an invalid format, 500 if the server encountered an internal error, and so forth. If the Content-Type of a 4xx or 5xx response is `application/json`, it MUST adhere to the following JSON schema: +``` +{ + "type": "object", + "properties": { + "message": { + "type": "string", + "description: "A human-readable error message." + } + } +} +``` + +#### Examples + +***GET request*** + +``` +# Client returned a URL template `https://example.com/gateway/{sender}/{data}.json` +# Request +curl -D - https://example.com/gateway/0x226159d592E2b063810a10Ebf6dcbADA94Ed68b8/0xd5fa2b00.json + +# Successful result + HTTP/2 200 + content-type: application/json; charset=UTF-8 + ... + + {"data": "0xdeadbeefdecafbad"} + +# Error result + HTTP/2 404 + content-type: application/json; charset=UTF-8 + ... + + {"message": "Gateway address not supported."} +} +``` + +***POST request*** + +``` +# Client returned a URL template `https://example.com/gateway/{sender}.json` +# Request +curl -D - -X POST -H "Content-Type: application/json" --data '{"data":"0xd5fa2b00","sender":"0x226159d592E2b063810a10Ebf6dcbADA94Ed68b8"}' https://example.com/gateway/0x226159d592E2b063810a10Ebf6dcbADA94Ed68b8.json + +# Successful result + HTTP/2 200 + content-type: application/json; charset=UTF-8 + ... + + {"data": "0xdeadbeefdecafbad"} + +# Error result + HTTP/2 404 + content-type: application/json; charset=UTF-8 + ... + + {"message": "Gateway address not supported."} +} +``` + +Clients MUST support both GET and POST requests. Gateways may implement either or both as needed. + +### Client Lookup Protocol + +A client that supports CCIP read MUST make contract calls using the following process: + + 1. Set `data` to the call data to supply to the contract, and `to` to the address of the contract to call. + 2. Call the contract at address `to` function normally, supplying `data` as the input data. If the function returns a successful result, return it to the caller and stop. + 3. If the function returns an error other than `OffchainLookup`, return it to the caller in the usual fashion. + 4. Otherwise, decode the `sender`, `urls`, `callData`, `callbackFunction` and `extraData` arguments from the `OffchainLookup` error. + 5. If the `sender` field does not match the address of the contract that was called, return an error to the caller and stop. + 6. Construct a request URL by replacing `sender` with the lowercase 0x-prefixed hexadecimal formatted `sender` parameter, and replacing `data` with the the 0x-prefixed hexadecimal formatted `callData` parameter. The client may choose which URLs to try in which order, but SHOULD prioritise URLs earlier in the list over those later in the list. + 7. Make an HTTP GET request to the request URL. + 8. If the response code from step (5) is in the range 400-499, return an error to the caller and stop. + 9. If the response code from step (5) is in the range 500-599, go back to step (5) and pick a different URL, or stop if there are no further URLs to try. + 10. Otherwise, replace `data` with an ABI-encoded call to the contract function specified by the 4-byte selector `callbackFunction`, supplying the data returned from step (7) and `extraData` from step (4), and return to step (1). + +Clients MUST handle HTTP status codes appropriately, employing best practices for error reporting and retries. + +Clients MUST handle HTTP 4xx and 5xx error responses that have a content type other than application/json appropriately; they MUST NOT attempt to parse the response body as JSON. + +This protocol can result in multiple lookups being requested by the same contract. Clients MUST implement a limit on the number of lookups they permit for a single contract call, and this limit SHOULD be at least 4. + +The lookup protocol for a client is described with the following pseudocode: + +```javascript +async function httpcall(urls, to, callData) { + const args = {sender: to.toLowerCase(), data: callData.toLowerCase()}; + for(const url of urls) { + const queryUrl = url.replace(/\{([^}]*)\}/g, (match, p1) => args[p1]); + // First argument is URL to fetch, second is optional data for a POST request. + const response = await fetch(queryUrl, url.includes('{data}') ? undefined : args); + const result = await response.text(); + if(result.statusCode >= 400 && result.statusCode <= 499) { + throw new Error(data.error.message); + } + if(result.statusCode >= 200 && result.statusCode <= 299) { + return result; + } + } +} +async function durin_call(provider, to, data) { + for(let i = 0; i < 4; i++) { + try { + return await provider.call(to, data); + } catch(error) { + if(error.code !== "CALL_EXCEPTION") { + throw(error); + } + const {sender, urls, callData, callbackFunction, extraData} = error.data; + if(sender !== to) { + throw new Error("Cannot handle OffchainLookup raised inside nested call"); + } + const result = httpcall(urls, to, callData); + data = abi.encodeWithSelector(callbackFunction, result, extraData); + } + } + throw new Error("Too many CCIP read redirects"); +} +``` + +Where: + - `provider` is a provider object that facilitates Ethereum blockchain function calls. + - `to` is the address of the contract to call. + - `data` is the call data for the contract. + +If the function being called is a standard contract function, the process terminates after the original call, returning the same result as for a regular call. Otherwise, a gateway from `urls` is called with the `callData` returned by the `OffchainLookup` error, and is expected to return a valid response. The response and the `extraData` are then passed to the specified callback function. This process can be repeated if the callback function returns another `OffchainLookup` error. + +### Use of CCIP read for transactions +While the specification above is for read-only contract calls (eg, `eth_call`), it is simple to use this method for sending transactions (eg, `eth_sendTransaction` or `eth_sendRawTransaction`) that require offchain data. While 'preflighting' a transaction using `eth_estimateGas` or `eth_call`, a client that receives an `OffchainLookup` revert can follow the procedure described above in [Client lookup protocol](#client-lookup-protocol), substituting a transaction for the call in the last step. This functionality is ideal for applications such as making onchain claims supported by offchain proof data. + +### Glossary + - Client: A process, such as JavaScript executing in a web browser, or a backend service, that wishes to query a blockchain for data. The client understands how to fetch data using CCIP read. + - Contract: A smart contract existing on Ethereum or another blockchain. + - Gateway: A service that answers application-specific CCIP read queries, usually over HTTPS. + +## Rationale +### Use of `revert` to convey call information +For offchain data lookup to function as desired, clients must either have some way to know that a function depends on this specification for functionality - such as a specifier in the ABI for the function - or else there must be a way for the contract to signal to the client that data needs to be fetched from elsewhere. + +While specifying the call type in the ABI is a possible solution, this makes retrofitting existing interfaces to support offchain data awkward, and either results in contracts with the same name and arguments as the original specification, but with different return data - which will cause decoding errors for clients that do not expect this - or duplicating every function that needs support for offchain data with a different name (eg, `balanceOf -> offchainBalanceOf`). Neither solutions is particularly satisfactory. + +Using a revert, and conveying the required information in the revert data, allows any function to be retrofitted to support lookups via CCIP read so long as the client understands the specification, and so facilitates translation of existing specifications to use offchain data. + +### Passing contract address to the gateway service +`address` is passed to the gateway in order to facilitate the writing of generic gateways, thus reducing the burden on contract authors to provide their own gateway implementations. Supplying `address` allows the gateway to perform lookups to the original contract for information needed to assist with resolution, making it possible to operate one gateway for any number of contracts implementing the same interface. + +### Existence of `extraData` argument +`extraData` allows the original contract function to pass information to a subsequent invocation. Since contracts are not persistent, without this data a contract has no state from the previous invocation. Aside from allowing arbitrary contextual information to be propagated between the two calls, this also allows the contract to verify that the query the gateway answered is in fact the one the contract originally requested. + +### Use of GET and POST requests for the gateway interface +Using a GET request, with query data encoded in the URL, minimises complexity and enables entirely static implementations of gateways - in some applications a gateway can simply be an HTTP server or IPFS instance with a static set of responses in text files. + +However, URLs are limited to 2 kilobytes in size, which will impose issues for more complex uses of CCIP read. Thus, we provide for an option to use POST data. This is made at the contract's discretion (via the choice of URL template) in order to preserve the ability to have a static gateway operating exclusively using GET when desired. + +## Backwards Compatibility +Existing contracts that do not wish to use this specification are unaffected. Clients can add support for CCIP read to all contract calls without introducing any new overhead or incompatibilities. + +Contracts that require CCIP read will not function in conjunction with clients that do not implement this specification. Attempts to call these contracts from non-compliant clients will result in the contract throwing an exception that is propagaged to the user. + +## Security Considerations + +### Gateway Response Data Validation +In order to prevent a malicious gateway from causing unintended side-effects or faulty results, contracts MUST include sufficient information in the `extraData` argument to allow them to verify the relevance and validity of the gateway's response. For example, if the contract is requesting information based on an `address` supplied to the original call, it MUST include that address in the `extraData` so that the callback can verify the gateway is not providing the answer to a different query. + +Contracts must also implement sufficient validation of the data returned by the gateway to ensure it is valid. The validation required is application-specific and cannot be specified on a global basis. Examples would include verifying a Merkle proof of inclusion for an L2 or other Merkleized state, or verifying a signature by a trusted signer over the response data. + +### Client Extra Data Validation +In order to prevent a malicious client from causing unintended effects when making transactions using CCIP read, contracts MUST implement appropriate checks on the `extraData` returned to them in the callback. Any sanity/permission checks performed on input data for the initial call MUST be repeated on the data passed through the `extraData` field in the callback. For example, if a transaction should only be executable by an authorised account, that authorisation check MUST be done in the callback; it is not sufficient to perform it with the initial call and embed the authorised address in the `extraData`. + +### HTTP requests and fingerprinting attacks +Because CCIP read can cause a user's browser to make HTTP requests to an address controlled by the contract, there is the potential for this to be used to identify users - for example, to associate their wallet address with their IP address. + +The impact of this is application-specific; fingerprinting a user when they resolve an ENS domain may have little privacy impact, as the attacker will not learn the user's wallet address, only the fact that the user is resolving a given ENS name from a given IP address - information they can also learn from running a DNS server. On the other hand, fingerprinting a user when they attempt a transaction to transfer an NFT may give an attacker everything they need to identify the IP address of a user's wallet. + +To minimise the security impact of this, we make the following recommendations: + + 1. Client libraries should provide clients with a hook to override CCIP read calls - either by rewriting them to use a proxy service, or by denying them entirely. This mechanism or another should be written so as to easily facilitate adding domains to allowlists or blocklists. + 2. Client libraries should disable CCIP read for transactions (but not for calls) by default, and require the caller to explicitly enable this functionality. Enablement should be possible both on a per-contract, per-domain, or global basis. + 3. App authors should not supply a 'from' address for contract calls ('view' operations) where the call could execute untrusted code (that is, code not authored or trusted by the application author). As a precuationary principle it is safest to not supply this parameter at all unless the author is certain that no attacker-determined smart contract code will be executed. + 4. Wallet authors that are responsible for fetching user information - for example, by querying token contracts - should either ensure CCIP read is disabled for transactions, and that no contract calls are made with a 'from' address supplied, or operate a proxy on their users' behalf, rewriting all CCIP read calls to take place via the proxy, or both. + +We encourage client library authors and wallet authors not to disable CCIP read by default, as many applications can be transparently enhanced with this functionality, which is quite safe if the above precautions are observed. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3670.md b/EIPS/eip-3670.md new file mode 100644 index 0000000..469450d --- /dev/null +++ b/EIPS/eip-3670.md @@ -0,0 +1,140 @@ +--- +eip: 3670 +title: EOF - Code Validation +description: Validate EOF bytecode for correctness at the time of deployment. +author: Alex Beregszaszi (@axic), Andrei Maiboroda (@gumb0), Paweł Bylica (@chfast) +discussions-to: https://ethereum-magicians.org/t/eip-3670-eof-code-validation/6693 +status: Review +type: Standards Track +category: Core +created: 2021-06-23 +requires: 3540 +--- + +## Abstract + +Introduce code validation at contract creation time for EOF formatted ([EIP-3540](./eip-3540.md)) +contracts. Reject contracts which contain truncated `PUSH`-data or undefined instructions. +Legacy bytecode (code which is not EOF formatted) is unaffected by this change. + +## Motivation + +Currently existing contracts require no validation of correctness and EVM implementations can decide +how they handle truncated bytecode or undefined instructions. This change aims to bring code +validity into consensus, so that it becomes easier to reason about bytecode. +Moreover, EVM implementations may require fewer paths to decide which instruction is valid in +the current execution context. + +If it will be desired to introduce new instructions without bumping EOF version, having undefined +instructions already deployed would mean such contracts potentially can be broken (since some +instructions are changing their behaviour). Rejecting to deploy undefined instructions allows +introducing new instructions with or without bumping the EOF version. + +### EOF1 forward compatibility + +The EOF1 format provides following forward compatibility properties: + +1. New instructions can be defined for previously unassigned opcodes. These instructions may have immediate values. +2. Mandatory EOF sections may be made optional. +3. New optional EOF sections may be introduced. They can be placed in any order in relation to previously defined sections. + +## Specification + +*Remark:* We rely on the notation of *initcode*, *code* and *creation* as defined by [EIP-3540](./eip-3540.md). + +This feature is introduced on the very same block EIP-3540 is enabled, therefore every EOF1-compatible bytecode MUST be validated according to these rules. + +1. Previously deprecated instructions `CALLCODE` (0xf2) and `SELFDESTRUCT` (0xff) are invalid and their opcodes are undefined. +2. At contract creation time *instructions validation* is performed on both *initcode* and *code*. The code is invalid if any of the checks below fails. For each instruction: + 1. Check if the opcode is defined. The `INVALID` (0xfe) is considered defined. + 2. Check if all instructions' immediate bytes are present in the code (code does not end in the middle of instruction). + +## Rationale + +### Immediate data + +Allowing implicit zero immediate data for `PUSH` instructions introduces inefficiencies to EVM implementations without any practical use-case (the value of a `PUSH` instruction at the code end cannot be observed by EVM). This EIP requires all immediate bytes to be explicitly present in the code. + +### Rejection of deprecated instructions + +The deprecated instructions `CALLCODE` (0xf2) and `SELFDESTRUCT` (0xff) are removed from the `valid_opcodes` list to prevent their use in the future. + +## Backwards Compatibility + +This change poses no risk to backwards compatibility, as it is introduced at the same time EIP-3540 is. The validation does not cover legacy bytecode (code which is not EOF formatted). + +## Test Cases + +### Contract creation + +Each case should be tested for creation transaction, `CREATE` and `CREATE2`. + +- Invalid initcode +- Valid initcode returning invalid code +- Valid initcode returning valid code + +### Valid codes + +- EOF code containing `INVALID` +- EOF code with data section containing bytes that are undefined instructions +- Legacy code containing undefined instruction +- Legacy code ending with incomplete PUSH instruction + +### Invalid codes + +- EOF code containing undefined instruction +- EOF code ending with incomplete `PUSH` instruction + - This can include `PUSH` instruction unreachable by execution, e.g. after `STOP` + +## Reference Implementation + +```python +# The ranges below are as specified in the Yellow Paper. +# Note: range(s, e) excludes e, hence the +1 +valid_opcodes = [ + *range(0x00, 0x0b + 1), + *range(0x10, 0x1d + 1), + 0x20, + *range(0x30, 0x3f + 1), + *range(0x40, 0x48 + 1), + *range(0x50, 0x5b + 1), + *range(0x60, 0x6f + 1), + *range(0x70, 0x7f + 1), + *range(0x80, 0x8f + 1), + *range(0x90, 0x9f + 1), + *range(0xa0, 0xa4 + 1), + # Note: 0xfe is considered assigned. + 0xf0, 0xf1, 0xf3, 0xf4, 0xf5, 0xfa, 0xfd, 0xfe +] + +immediate_sizes = 256 * [0] +immediate_sizes[0x60:0x7f + 1] = range(1, 32 + 1) # PUSH1..PUSH32 + + +# Raises ValidationException on invalid code +def validate_instructions(code: bytes): + # Note that EOF1 already asserts this with the code section requirements + assert len(code) > 0 + + pos = 0 + while pos < len(code): + # Ensure the opcode is valid + opcode = code[pos] + if opcode not in valid_opcodes: + raise ValidationException("undefined opcode") + + # Skip immediate data + pos += 1 + immediate_sizes[opcode] + + # Ensure last instruction's immediate doesn't go over code end + if pos != len(code): + raise ValidationException("truncated immediate") +``` + +## Security Considerations + +See [Security Considerations of EIP-3540](./eip-3540.md#security-considerations). + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3675.md b/EIPS/eip-3675.md new file mode 100644 index 0000000..efa51f5 --- /dev/null +++ b/EIPS/eip-3675.md @@ -0,0 +1,341 @@ +--- +eip: 3675 +title: Upgrade consensus to Proof-of-Stake +description: Specification of the consensus mechanism upgrade on Ethereum Mainnet that introduces Proof-of-Stake +author: Mikhail Kalinin (@mkalinin), Danny Ryan (@djrtwo), Vitalik Buterin (@vbuterin) +discussions-to: https://ethereum-magicians.org/t/eip-3675-upgrade-consensus-to-proof-of-stake/6706 +status: Final +type: Standards Track +category: Core +created: 2021-07-22 +requires: 2124 +--- + + +## Abstract + +This EIP deprecates Proof-of-Work (PoW) and supersedes it with the new Proof-of-Stake consensus mechanism (PoS) driven by the beacon chain. Information on the bootstrapping of the new consensus mechanism is documented in [EIP-2982](./eip-2982.md). Full specification of the beacon chain can be found in the `ethereum/consensus-specs` repository. + +This document specifies the set of changes to the block structure, block processing, fork choice rule and network interface introduced by the consensus upgrade. + + +## Motivation + +The beacon chain network has been up and running since December 2020. Neither safety nor liveness failures were detected during this period of time. This long period of running without failure demonstrates the sustainability of the beacon chain system and its readiness to become a security provider for the Ethereum Mainnet. + +To understand the motivation of introducing the Proof-of-Stake consensus see the Motivation section of [EIP-2982](./eip-2982.md#motivation). + + +## Specification + +### Definitions + +* **PoW block**: Block that is built and verified by the existing proof-of-work mechanism. In other words, a block of the Ethereum network before the consensus upgrade. +* **PoS block**: Block that is built and verified by the new proof-of-stake mechanism. +* **Terminal PoW block**: A PoW block that satisfies the following conditions -- +`pow_block.total_difficulty >= TERMINAL_TOTAL_DIFFICULTY` *and* `pow_block.parent_block.total_difficulty < TERMINAL_TOTAL_DIFFICULTY`. +There can be more than one terminal PoW block in the network, e.g. multiple children of the same pre-terminal block. +* **`TERMINAL_TOTAL_DIFFICULTY`** The amount of total difficulty reached by the network that triggers the consensus upgrade. Ethereum Mainnet configuration **MUST** have this parameter set to the value `58750000000000000000000`. +* **`TRANSITION_BLOCK`** The earliest PoS block of the canonical chain, i.e. the PoS block with the lowest block height. +* **`POS_FORKCHOICE_UPDATED`** An event occurring when the state of the proof-of-stake fork choice is updated. +* **`FORK_NEXT_VALUE`** A block number set to the `FORK_NEXT` parameter for the upcoming consensus upgrade. +* **`TERMINAL_BLOCK_HASH`** Designates the hash of the terminal PoW block if set, i.e. if not stubbed with `0x0000000000000000000000000000000000000000000000000000000000000000`. +* **`TERMINAL_BLOCK_NUMBER`** Designates the number of the terminal PoW block if `TERMINAL_BLOCK_HASH` is set. + +#### PoS events + +Events having the `POS_` prefix in the name (PoS events) are emitted by the new proof-of-stake consensus mechanism. They signify the corresponding assertion that has been made regarding a block specified by the event. The underlying logic of PoS events can be found in the beacon chain specification. On the occurrence of each PoS event the corresponding action that is specified by this EIP **MUST** be taken. + +The details provided below must be taken into account when reading those parts of the specification that refer to the PoS events: +* Reference to a block that is contained by PoS events is provided in a form of a block hash unless another is explicitly specified. +* A `POS_FORKCHOICE_UPDATED` event contains references to the head of the canonical chain and to the most recent finalized block. Before the first finalized block occurs in the system the finalized block hash provided by this event is stubbed with `0x0000000000000000000000000000000000000000000000000000000000000000`. +* **`FIRST_FINALIZED_BLOCK`** The first finalized block that is designated by `POS_FORKCHOICE_UPDATED` event and has the hash that differs from the stub. + + +### Client software configuration + +The following set of parameters is a part of client software configuration and **MUST** be included into its binary distribution: +* `TERMINAL_TOTAL_DIFFICULTY` +* `FORK_NEXT_VALUE` +* `TERMINAL_BLOCK_HASH` +* `TERMINAL_BLOCK_NUMBER` + +*Note*: If `TERMINAL_BLOCK_HASH` is stubbed with `0x0000000000000000000000000000000000000000000000000000000000000000` then `TERMINAL_BLOCK_HASH` and `TERMINAL_BLOCK_NUMBER` parameters **MUST NOT** take an effect. + + +### PoW block processing + +PoW blocks that are descendants of any terminal PoW block **MUST NOT** be imported. This implies that a terminal PoW block will be the last PoW block in the canonical chain. + + +### Constants + +| Name | Value | +|-|-| +| **`MAX_EXTRA_DATA_BYTES`** | `32` | + +### Block structure + +Beginning with `TRANSITION_BLOCK`, a number of previously dynamic block fields are deprecated by enforcing these values to instead be constants. Each block field listed in the table below **MUST** be replaced with the corresponding constant value. + +| Field | Constant value | Comment | +|-|-|-| +| **`ommersHash`** | `0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347` | `= Keccak256(RLP([]))` | +| **`difficulty`** | `0` | | +| **`mixHash`** | `0x0000000000000000000000000000000000000000000000000000000000000000` | | +| **`nonce`** | `0x0000000000000000` | | +| **`ommers`** | `[]` | `RLP([]) = 0xc0` | + +Beginning with `TRANSITION_BLOCK`, the validation of the block's **`extraData`** field changes: The length of the block's **`extraData`** **MUST** be less than or equal to **`MAX_EXTRA_DATA_BYTES`** bytes. + +*Note*: Logic and validity conditions of block fields that are *not* specified here **MUST** remain unchanged. Additionally, the overall block format **MUST** remain unchanged. + +*Note*: Subsequent EIPs may override the constant values specified above to provide additional functionality. For an example, see [EIP-4399](./eip-4399.md). + + +### Block validity + +Beginning with `TRANSITION_BLOCK`, the block validity conditions **MUST** be altered by the following: +* Remove verification of the block's **`difficulty`** value with respect to the difficulty formula. +* Remove verification of the block's **`nonce`** and **`mixHash`** values with respect to the Ethash function. +* Remove all validation rules that are evaluated over the list of ommers and each member of this list. +* Add verification of the fields noted in the [block structure](#block-structure) section. + +*Note*: If one of the new rules fails then the block **MUST** be invalidated. + +*Note*: Validity rules that are not specified in the list above **MUST** remain unchanged. + +#### Transition block validity + +In addition to satisfying the above conditions, `TRANSITION_BLOCK` **MUST** be a child of a terminal PoW block. That is, a parent of `TRANSITION_BLOCK` **MUST** satisfy terminal PoW block conditions. + + +### Block and ommer rewards + +Beginning with `TRANSITION_BLOCK`, block and ommer rewards are deprecated. Specifically, the following actions **MUST** be taken: +* Remove increasing the balance of the block's **`beneficiary`** account by the block reward. +* Remove increasing the balance of the block's **`beneficiary`** account by the ommer inclusion reward per each ommer. +* Remove increasing the balance of the ommer's **`beneficiary`** account by the ommer block reward per each ommer. + +*Note*: Transaction fee mechanics affecting the block's `beneficiary` account **MUST** remain unchanged. + + +### Fork choice rule + +If set, `TERMINAL_BLOCK_HASH` parameter affects the PoW heaviest chain rule in the following way: +* Canonical blockchain **MUST** contain a block with the hash defined by `TERMINAL_BLOCK_HASH` parameter at the height defined by `TERMINAL_BLOCK_NUMBER` parameter. + +*Note*: This rule is akin to block hash whitelisting functionality already present in client software implementations. + +As of the first `POS_FORKCHOICE_UPDATED` event, the fork choice rule **MUST** be altered in the following way: +* Remove the existing PoW heaviest chain rule. +* Adhere to the new PoS LMD-GHOST rule. + +The new PoS LMD-GHOST fork choice rule is specified as follows. On each occurrence of a `POS_FORKCHOICE_UPDATED` event including the first one, the following actions **MUST** be taken: +* Consider the chain starting at genesis and ending with the head block nominated by the event as the canonical blockchain. +* Set the head of the canonical blockchain to the corresponding block nominated by the event. +* Beginning with the `FIRST_FINALIZED_BLOCK`, set the most recent finalized block to the corresponding block nominated by the event. + +Changes to the block tree store that are related to the above actions **MUST** be applied atomically. + +*Note*: This rule **MUST** be strictly enforced. "Optimistic" updates to the head **MUST NOT** be made. That is -- if a new block is processed on top of the current head block, this new block becomes the new head if and only if an accompanying `POS_FORKCHOICE_UPDATED` event occurs. + +### Network + +#### Fork identifier + +For the purposes of the [EIP-2124](./eip-2124.md) fork identifier, nodes implementing this EIP **MUST** set the `FORK_NEXT` parameter to the `FORK_NEXT_VALUE`. + +#### devp2p + +The networking stack **SHOULD NOT** send the following messages if they advertise the descendant of any terminal PoW block: +* `NewBlockHashes (0x01)` +* `NewBlock (0x07)` + +Beginning with receiving the `FIRST_FINALIZED_BLOCK`, the networking stack **MUST** discard the following ingress messages: +* `NewBlockHashes (0x01)` +* `NewBlock (0x07)` + +Beginning with receiving the finalized block next to the `FIRST_FINALIZED_BLOCK`, the networking stack **MUST** remove the handlers corresponding to the following messages: +* `NewBlockHashes (0x01)` +* `NewBlock (0x07)` + +Peers that keep sending these messages after the handlers have been removed **SHOULD** be disconnected. + +*Note:* The logic of message handlers that are not affected by this section **MUST** remain unchanged. + + +## Rationale + +The changes specified in this EIP target a minimal requisite set of consensus and client software modifications to safely replace the existing proof-of-work consensus algorithm with the new proof-of-stake consensus represented by the already in-production beacon chain. + +This EIP was designed to minimize the complexity of hot-swapping the live consensus of the Ethereum network. Both the safety of the operation and time to production were taken into consideration. Additionally, a minimal changeset helps ensure that *most* smart contracts and services will continue to function as intended during and after the transition with little to no required intervention. + +### Total difficulty triggering the upgrade + +See [Security considerations](#terminal-total-difficulty-vs-block-number). + +### Parameterizing terminal block hash + +See [Security considerations](#terminal-pow-block-overriding). + +### Halting the import of PoW blocks + +See [Security considerations](#halt-the-importing-of-pow-blocks). + +### Replacing block fields with constants + +Deprecated block fields are replaced with constant values to ensure the block format remains backwards compatible. Preserving the block format aids existing smart contracts and services in providing uninterrupted service during and after this transition. + +Particularly, this is important for those smart contracts that verify Merkle proofs of transaction/receipt inclusion and state by validating the hash of externally provided block header against the corresponding value returned by the `BLOCKHASH` operation. + +This change introduces an additional validity rule that enforces the replacement of deprecated block fields. + +### Replacing `difficulty` with `0` + +After deprecating the proof-of-work the notion of difficulty no longer exists and replacing the block header **`difficulty`** field with `0` constant is semantically sound. + +### Changing block validity rules + +The rule set enforcing the PoW seal validity is replaced with the corresponding PoS rules along with the consensus upgrade as the rationale behind this change. + +An additional rule validating a set of deprecated block fields is required by the block format changes introduced by this specification. + +### Removing block rewards + +Existing rewards for producing and sealing blocks are deprecated along with the PoW mechanism. The new PoS consensus becomes both responsible for sealing blocks and for issuing block rewards once this specification enters into effect. + +### Supplanting fork choice rule + +The fork choice rule of the PoW mechanism becomes completely irrelevant after the upgrade and is replaced with the corresponding rule of the new PoS consensus mechanism. + +### Remove of `POS_CONSENSUS_VALIDATED` + +In prior draft versions of this EIP, an additional POS event -- `POS_CONSENSUS_VALIDATED` -- was required as a validation condition for blocks. This event gave the signal to either fully incorporate or prune the block from the block tree. + +This event was removed for two reasons: +1. This event was an unnecessary optimization to allow for pruning of "bad" blocks from the block tree. This optimization was unnecessary because the PoS consensus would never send `POS_FORKCHOICE_UPDATED` for any such bad blocks or their descendants, and eventually any such blocks would be able to be pruned after a PoS finality event of an alternative branch in the block tree. +2. This event was dangerous in some scenarios because a block could be referenced by two *different* and conflicting PoS branches. Thus for the same block in some scenarios, both a `POS_CONSENSUS_VALIDATED == TRUE` and `POS_CONSENSUS_VALIDATED == FALSE` event could sent, entirely negating the ability to safely perform the optimization in (1). + +### EIP-2124 fork identifier + +The value of `FORK_NEXT` in EIP-2124 refers to the block number of the next fork a given node knows about and `0` otherwise. + +The number of `TRANSITION_BLOCK` cannot be known ahead of time given the dynamic nature of the transition trigger condition. As the block will not be known a priori, nodes can't use its number for `FORK_NEXT` and in light of this fact an explicitly set `FORK_NEXT_VALUE` is used instead. + +### Removing block gossip + +After the upgrade of the consensus mechanism only the beacon chain network will have enough information to validate a block. Thus, block gossip provided by the `eth` network protocol will become unsafe and is deprecated in favour of the block gossip existing in the beacon chain network. + +It is recommended for the client software to not propagate descendants of any terminal PoW block to reduce the load on processing the P2P component and stop operating in the environment with unknown security properties. + +### Restricting the length of `extraData` + +The `extraData` field is defined as a maximum of `32` bytes in the yellow paper. Thus mainnet and most PoW testnets cap the value at `32` bytes. `extraData` fields of greater length are used by clique testnets and other networks to carry special signature/consensus schemes. This EIP restricts the length of `extraData` to `32` bytes because any network that is transitioning from another consensus mechanism to a beacon chain PoS consensus mechanism no longer needs extended or unbounded `extraData`. + +## Backwards Compatibility + +This EIP introduces backward incompatibilities in block validity, block rewards and fork choice rule. + +The design of the consensus upgrade specified by this document does not introduce backward incompatibilities for existing applications and services built on top of Ethereum except for those that are described in the [EVM](#evm) section below or heavily depends on the PoW consensus in any other way. + + +### EVM + +Although this EIP does not introduce any explicit changes to the EVM there are a couple of places where it may affect the logic of existing smart contracts. + +#### DIFFICULTY + +`DIFFICULTY` operation will always return `0` after this EIP takes effect and deprecates the **`difficulty`** field by replacing it with `0` constant. + +*Note:* Altering the `DIFFICULTY` semantics to return randomness accumulated by the beacon chain is under consideration but will be introduced in a separate EIP. + +#### BLOCKHASH + +Pseudo-random numbers obtained as the output of `BLOCKHASH` operation become more insecure after this EIP takes effect and the PoW mechanism (which decreases the malleability of block hashes) gets supplanted by PoS. + + +## Test Cases + +* Block validity + * Beginning with `TRANSITION_BLOCK`, block is invalidated if any of the following is true: + * `ommersHash != Keccak256(RLP([]))` + * `difficulty != 0` + * `nonce != 0x0000000000000000` + * `len(extraData) > MAX_EXTRA_DATA_BYTES` + * Beginning with `TRANSITION_BLOCK`, block rewards aren't added to `beneficiary` account +* Client software adheres to PoS LMD-GHOST rule + * Head and finalized blocks are set according to the recent `POS_FORKCHOICE_UPDATED` event + * No fork choice state is updated unless `POS_FORKCHOICE_UPDATED` event is received +* Transition process + * Client software doesn't process any PoW block beyond a terminal PoW block + * Beginning with `TRANSITION_BLOCK`, client software applies new block validity rules + * Beginning with the first `POS_FORKCHOICE_UPDATED`, client software switches its fork choice rule to PoS LMD-GHOST + * `TRANSITION_BLOCK` must be a child of a terminal PoW block + * `NewBlockHashes (0x01)` and `NewBlock (0x07)` network messages are discarded after receiving the `FIRST_FINALIZED_BLOCK` + + +## Security Considerations + +### Beacon chain + +See Security Considerations section of [EIP-2982](./eip-2982.md#security-considerations). + +### Transition process + +The transition process used to take this specification into effect is a more sophisticated version of a hardfork -- the regular procedure of applying backwards incompatible changes in the Ethereum network. This process has multiple successive steps instead of the normal block-height point condition of simpler hardforks. + +The complexity of this upgrade process stems from this fork targeting the underlying consensus mechanism rather than the execution layer within the consensus mechanism. Although the design seeks simplicity where possible, safety and liveness considerations during this transition have been prioritized. + +#### Terminal total difficulty vs block number + +Using a pre-defined block number for the hardfork is unsafe in this context due to the PoS fork choice taking priority during the transition. + +An attacker may use a minority of hash power to build a malicious chain fork that would satisfy the block height requirement. Then the first PoS block may be maliciously proposed on top of the PoW block from this adversarial fork, becoming the head and subverting the security of the transition. + +To protect the network from this attack scenario, difficulty accumulated by the chain (total difficulty) is used to trigger the upgrade. + +#### Ability to jump between terminal PoW blocks + +There could be the case when a terminal PoW block is not observed by the majority of network participants due to (temporal) network partitioning. In such a case, this minority would switch their fork choice to the new rule provided by the PoS rooted on the minority terminal PoW block that they observed. + +The transition process allows the network to re-org between forks with different terminal PoW blocks as long as (a) these blocks satisfy the terminal PoW block conditions and (b) the `FIRST_FINALIZED_BLOCK` has not yet been received. This provides resilience against adverse network conditions during the transition process and prevents irreparable forks/partitions. + +#### Halt the importing of PoW blocks + +Suppose the part of the client software that is connected to the beacon chain network goes offline before the Ethereum network reaches the `TERMINAL_TOTAL_DIFFICULTY` and stays offline while the network meets this threshold. Such an event makes the client software unable to switch to PoS and allows it to keep following the PoW chain if this chain is being built beyond the terminal PoW block. Depending on how long the beacon chain part was offline, it could result in different adverse effects such as: +* The client has no post-state for the terminal PoW block (the state has been pruned) which prevents it from doing the re-org to the PoS chain and leaving syncing from scratch as the only option to recover. +* An application, a user or a service uses the data from the wrong fork (PoW chain that is kept being built) which can cause security issues on their side. + +Not importing PoW blocks that are beyond the terminal PoW block prevents these adverse effects on safety/re-orgs in the event of software or configuration failures *in favor* of a liveness failure. + +#### Terminal PoW block overriding + +There is a mechanism allowing for accelerating the consensus upgrade in emergency cases. +This EIP considers the following emergency case scenarios for the acceleration to come into effect: +* A drop of the network hashing rate which delays the upgrade significantly. +* Attacks on the PoW network before the upgrade. + +The first case can be safely accelerated by updating the following parameters: +* `TERMINAL_TOTAL_DIFFICULTY` -- reset to a value that is closer in time than the original one. +* `FORK_NEXT_VALUE` -- adjust accordingly. + +The second, more dire attack scenario requires a more invasive override: +* `TERMINAL_BLOCK_HASH` -- set to the hash of a certain block to become the terminal PoW block. +* `TERMINAL_BLOCK_NUMBER` -- set to the number of a block designated by `TERMINAL_BLOCK_HASH`. +* `TERMINAL_TOTAL_DIFFICULTY` -- set to the total difficulty value of a block designated by `TERMINAL_BLOCK_HASH`. +* `FORK_NEXT_VALUE` -- adjust accordingly. + +*Note*: Acceleration in the second case is considered for the most extreme of scenarios because it will result in a non-trivial liveness failure on Ethereum Mainnet. + +### Ancient blocks are no longer a requisite for a network security + +Keeping historical blocks starting from genesis is essential in the PoW network. A header of every block that belongs to a particular chain is required to justify the validity of this chain with respect to the PoW seal. + +Validating the entire history of the chain is not required by the new PoS mechanism. Instead, the sync process in the PoS network relies on weak subjectivity checkpoints, which are historical snapshots shared by peers on the network. This means historical blocks beyond weak subjectivity checkpoint are no longer a requisite for determining the canonical blockchain. + +Specification of weak subjectivity checkpoints can be found in the `ethereum/consensus-specs` repository. + + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3690.md b/EIPS/eip-3690.md new file mode 100644 index 0000000..99aee94 --- /dev/null +++ b/EIPS/eip-3690.md @@ -0,0 +1,267 @@ +--- +eip: 3690 +title: EOF - JUMPDEST Table +description: A special EOF section for storing the list of JUMPDESTs, which simplifies execution time analysis. +author: Alex Beregszaszi (@axic), Paweł Bylica (@chfast), Andrei Maiboroda (@gumb0) +discussions-to: https://ethereum-magicians.org/t/eip-3690-eof-jumpdest-table/6806 +status: Stagnant +type: Standards Track +category: Core +created: 2021-06-23 +requires: 3540, 3670 +--- + +## Abstract + +Introduce a section in the EOF format ([EIP-3540](./eip-3540.md)) for storing the list of `JUMPDEST`s, validate the correctness of this list at the time of contract creation, and remove the need for `JUMPDEST`-analysis at execution time. In EOF contracts, the `JUMPDEST` instruction is not needed anymore and becomes invalid. Legacy contracts are entirely unaffected by this change. + +## Motivation + +Currently existing contracts require no validation of correctness, but every time they are executed, a list must be built containing all the valid jump-destinations. This is an overhead which can be avoided, albeit the effect of the overhead depends on the client implementation. + +With the structure provided by EIP-3540 it is easy to store and transmit a table of valid jump-destinations instead of using designated `JUMPDEST` (0x5b) opcodes in the code. + +The goal of this change is that we trade less complexity (and processing time) at execution time for more complexity at contract creation time. Through benchmarks we have identified that the mandatory execution preparation time is the same as before for extreme cases (i.e. deliberate edge cases), while it is ~10x faster for the average case. + +Finally, this change puts an implicit bound on "initcode analysis" which is now limited to jumpdests section loading of max size of 0xffff. The legacy code remains vulnerable. + +## Specification + +This feature is introduced on the very same block [EIP-3540](./eip-3540.md) is enabled, therefore every EOF1-compatible bytecode MUST have a JUMPDEST-table if it uses jumps. + +*Remark:* We rely on the notation of *initcode*, *code* and *creation* as defined by [EIP-3540](./eip-3540.md), and extend validation rules of [EIP-3670](./eip-3670.md). + +### EOF container changes + +1. A new EOF section called `jumpdests` (`section_kind = 3`) is introduced. It contains a sequence of *n* unsigned integers *jumploci*. +2. The *jumploci* values are encoded with [unsigned LEB128](https://en.wikipedia.org/wiki/LEB128#Unsigned_LEB128). + + | description | encoding | + |---------------------|-----------------| + | jumploc0 | unsigned LEB128 | + | jumploc1 | unsigned LEB128 | + | ... | | + | jumplocn | unsigned LEB128 | + +3. The jump destinations represent the set of valid code positions as arguments to jump instructions. They are delta-encoded therefore partial sum must be performed to retrieve the absolute offsets. + ```python + def jumpdest(n: int, jumpdests_table: list[int]) -> int: + return sum(jumpdests_table[:n+1]) + ``` + +### Validation rules + +> This section extends contact creation validation rules (as defined in EIP-3540). + +4. The `jumpdests` section MUST be present if and only if the `code` section contains `JUMP` or `JUMPI` opcodes. +5. If the `jumpdests` section is present it MUST directly precede the `code` section. In this case a valid EOF bytecode will have the form of `format, magic, version, [jumpdests_section_header], code_section_header, [data_section_header], 0, [jumpdests_section_contents], code_section_contents, [data_section_contents]`. +6. The LEB128 encoding of a `jumploc` must be valid: the encoding must be complete and not read out of `jumpdests` section. As an additional constraint, the shortest possible encoding must be used. +7. With an exception of the first entry, the value of `jumploc` MUST NOT be 0. +8. Every `jumploc` MUST point to a valid opcode. They MUST NOT point into PUSH-data or outside of the code section. +9. The `JUMPDEST` (0x5b) instruction becomes undefined (Note: According to rules of EIP-3670, deploying the code will fail if it contains `JUMPDEST`) + +### Execution + +10. When executing `JUMP` or `JUMPI` instructions, the jump destination MUST be in the `jumpdests` table. Otherwise, the execution aborts with *bad jump destination*. In case of `JUMPI`, the check is done only when the jump is to be taken (no change to the previous behaviour). + +## Rationale + +### Jumpdests section is bounded + +The length of the `jumpdests` section is bounded by the EOF maximum section size value 0xffff. Moreover, for deployed code this additionally limited by the max bytecode size 0x6000. Then any valid `jumpdests` section may not be more larger than 0x3000. + +### Delta encoding + +Delta-encoding is very efficient for this job. From a quick analysis of a small set of contracts `JUMPDEST` opcodes are relatively close to each other. In the delta-encoding the values almost never exceed 128. Combined with any form of variable-length quantity (VLQ) where values < 128 occupy one byte, encoding of single jumpdest takes ~1 byte. We also remove `JUMPDEST` opcodes from the code section therefore the total bytecode length remains the same if extreme examples are ignored. + +By extreme examples we mean contracts having a distance between two subsequent JUMPDESTs larger than 128. Then the LEB128 encoding of such distance requires more than one byte and the total bytecode size will increase by the additional number of bytes used. + +### LEB128 for offsets + +The LEB128 encoding is the most popular VLQ used in DWARF and WebAssembly. + +LEB128 allows encoding a fixed value with arbitrary number of bytes by having zero payloads for most significant bits of the value. To ensure there exists only single encoding of a given value, we additionally require the shortest possible LEB128 encoding to be used. This constraint is also required by WebAssembly. + +### Size-prefix for offsets + +This is another option for encoding inspired by UTF-8. The benefit is that the number of following bytes is encoded in the first byte (the top two bits), so the expected length is known upfront. + +A simple decoder is the following: +```python +def decode(input: bytes) -> int: + size_prefix = input[0] >> 6 + if size_prefix == 0: + return input[0] & 0x3f + elif size_prefix == 1: + return (input[0] & 0x3f) << 8 | input[1] + elif size_prefix == 2: + return (input[0] & 0x3f) << 16 | (input[1] << 8) | input[2] + # Do not support case 3 + assert(False) +``` + +### Empty table + +In case code does not use jumps, an empty JUMPDEST table is represented by omitting `jumpdests` section as opposed to a section that is always present, but allowed to be empty. This is consistent with the requirement of EIP-3540 for section size to be non-zero. Additionally, omitting the section saves 3 bytes of code storage. + +### Why jumpdests before code? + +The contents of `jumpdests` section are always needed to start EVM execution. For chunked and/or merkleized bytecode it is more efficient to have `jumpdests` just after the EOF header so they can share the same first chunk(s). + +### Code chunking / merkleization + +In code chunking the contract code is split into (fixed size) chunks. Due to the requirement of jumpdest-analysis, it must be known where the first instruction starts in a given chunk, in case the split happened within a PUSH-data. This is commonly accomplished with reserving the first byte of the chunk as the "first instruction offset" (FIO) field. + +With this EIP, code chunking does not need to have such a field. However, the jumpdest table must be provided instead (for all the chunks up until the last jump location used during execution). + +### Benchmarks / performance analysis + +We compared the performance of `jumpdests` section loading to JUMPDEST analysis in evmone/Baseline interpreter. In both cases a bitset of valid jumpdest positions is built. + +We used the worst case for `jumpdests` section as the benchmark baseline. This is the case where every position in the code section is valid jumpdest. I.e. the bytecode has as many jumpdests as possible making the jumpdests section as large as possible. The encoded representation is `0x00, 0x01, 0x01, 0x01, ...`. + +This also happen to be the worst case for the JUMPDEST analysis. + +Further, we picked 5 popular contracts from the Ethereum mainnet. + +| case | size | num JUMPDESTs | JUMPDEST analysis (cycles/byte) | jumpdests load (cycles/byte) | performance change | +| ----------------- | ----- | ----- | ---- | ---- | ------- | +| worst | 65535 | 65535 | 9.11 | 9.36 | 2.75% | +| RoninBridge | 1760 | 71 | 3.57 | | -89.41% | +| UniswapV2ERC20 | 2319 | 61 | 2.10 | | -88.28% | +| DepositContract | 6358 | 123 | 1.86 | | -90.24% | +| TetherToken | 11075 | 236 | 1.91 | | -89.58% | +| UniswapV2Router02 | 21943 | 468 | 2.26 | | -91.17% | + +For the worst case the performance difference between JUMPDEST analysis and jumpdests section loading is very small. The performance very slow compared to memory copy (0.15 cycles/byte). + +However, the maximum length for the worst cases is different. For JUMPDEST analysis this is 24576 (0x6000) for deployed contracts and only limited by EVM memory cost in case of _initcode_ (can be over 1MB). For jumpdests sections, the limit is 12288 for deployed contracts (the deployed bytecode length limit must be split equally between jumpdests and code sections). For _initcode_ case, the limit is 65535 because this is the maximum section size allowed by EOF. + +For "popular" contracts the gained efficiency is ~10x because the jumpdests section is relatively small compared to the code section and therefore there is much less bytes to loop over than in JUMPDEST analysis. + +## Reference Implementation + +We extend the `validate_code()` function of [EIP-3670](./eip-3670.md): +```python +# The same table as in EIP-3670 +valid_opcodes = ... + +# Remove JUMPDEST from the list of valid opcodes +valid_opcodes.remove(0x5b) + +# This helper decodes a single unsigned LEB128 encoded value +# This will abort on truncated (short) input +def leb128u_decode(input: bytes) -> (int, int): + ret = 0 + shift = 0 + consumed_bytes = 0 + while True: + # Check for truncated input + assert(consumed_bytes < len(input)) + # Only allow up to 4-byte long leb128 encodings + assert(consumed_bytes <= 3) + input_byte = input[consumed_bytes] + consumed_bytes += 1 + ret |= (input_byte & 0x7f) << shift + if (input_byte & 0x80) == 0: + # Do not allow additional leading zero bits. + assert(input_byte != 0 || consumed_bytes == 0) + break + shift += 7 + return (ret, consumed_bytes) + +# This helper parses the jumpdest table into a list of relative offsets +# This will abort on truncated (short) input +def parse_table(input: bytes) -> list[int]: + jumpdests = [] + pos = 0 + while pos < len(input): + value, consumed_bytes = leb128u_decode(input[pos:]) + jumpdests.append(value) + pos += consumed_bytes + return jumpdests + +# This helper translates the delta offsets into absolute ones +# This will abort on invalid 0-value entries +def process_jumpdests(delta: list[int]) -> list[int]: + jumpdests = [] + partial_sum = 0 + first = True + for d in delta: + if first: + first = False + else: + assert(d != 0) + partial_sum += d + jumpdests.append(partial_sum) + return jumpdests + +# Fails with assertion on invalid code +# Expects list of absolute jumpdest offsets +def validate_code(code: bytes, jumpdests: list[int]): + pos = 0 + while pos < len(code): + # Ensure the opcode is valid + opcode = code[pos] + pos += 1 + assert(opcode in valid_opcodes) + + # Remove touched offset + try: + jumpdests.remove(pos) + except ValueError: + pass + + # Skip pushdata + if opcode >= 0x60 and opcode <= 0x7f: + pos += opcode - 0x60 + 1 + + # Ensure last PUSH doesn't go over code end + assert(pos == len(code)) + + # The table is invalid if there are untouched locations + assert(len(jumpdests) == 0) +``` + +## Test Cases + +#### Valid bytecodes + +- No jumpdests +- Every byte is a jumpdest +- Distant jumpdests (0x7f and 0x3f01 bytes apart) +- Max number of jumpdests + - 1-byte offset encoding: initcode of max size (64K) with jumpdest at each byte - table contains 65536 1-byte offsets, first one is 0x00, all others equal 0x01 + - 2-byte offset encoding: inicode of max size with jumpdests 0x80 (128) bytes apart - table contains 512 offsets, first one is 0x7f (127), all others equal 0x8001 + - 3-byte offset encoding: inicode of max size with jumpdests 0x4000 (16384) bytes apart - table contains 4 offsets: 0xFF7F (16383), 0x808001, 0x808001, 0x808001 + +#### Invalid bytecodes + +- Empty jumpdest section +- Multiple jumpdest sections +- jumpdest section after the code section +- jumpdest section after the data section +- Final jumploc in the table is truncated (not a valid LEB128) +- LEB128 encoding with extra 0s (non-minimal encoding) +- Jumpdest location pointing to PUSH data +- Jumpdest location out of code section bounds + - pointing into data section + - pointing into jumpdest section + - pointing outside container bounds +- Duplicate jumpdest locations (0 deltas in table other than 1st offset) +- Code containing `JUMP` but no jumpdest table +- Code containing `JUMPI` but no jumpdest table +- Code containing jumpdest table but not `JUMP`/`JUMPI` +- Code containing `JUMPDEST` + +## Backwards Compatibility + +This change poses no risk to backwards compatibility, as it is introduced at the same time EIP-3540 is. The requirement of a JUMPDEST table does not cover legacy bytecode. + +## Security Considerations + +The authors are not aware of any security or DoS risks posed by this change. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3709.md b/EIPS/eip-3709.md new file mode 100644 index 0000000..3d8e771 --- /dev/null +++ b/EIPS/eip-3709.md @@ -0,0 +1,45 @@ +--- +eip: 3709 +title: Remove Support for Type 1 Transactions +author: Gregory Markou (@GregTheGreek) +discussions-to: https://ethereum-magicians.org/t/eip-3709-deprecate-type-1-transactions/6810 +status: Stagnant +type: Standards Track +category: Interface +created: 2021-08-07 +requires: 1559 +--- + +## Simple Summary + +Deprecates usage of [EIP-2718](./eip-2718.md) `TransactionType` 1 in wallets and providers, upgrading all type 1 transactions to a type 2 transaction. + +## Abstract + +Since both `TransactionType` 1 and 2 contain `access_list`, we propose the removal of offering `TransactionType` 1 from wallets and providers, instead the transaction will be converted to `TransactionType` 2 to make use of the new gas properties introduced by [EIP-1559](./eip-1559.md). + +## Motivation + +[EIP-2930](./eip-2930.md) was introduced as the first `TransactionType`, type 1, with the intention of adding `access_list` to the `TransactionPayload`. [EIP-1559](./eip-1559.md) introduced the second `TransactionType` 2, which is represented as `rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination, amount, data, access_list, signature_y_parity, signature_r, signature_s])`. The intention behind EIP-1559 was to enhance the user experience surrounding gas fees, and as we move forward we expect that the majority of the network will begin to using `TransactionType` 2 instead of the legacy style transactions. `TransactionType` 1 is a legacy transaction with the addition of `access_list` meaning that users will not benefit from enhancements made by EIP-1559. `TransactionType` 2 contains `access_list`, thus there is no reason to further support `TransactionType` 1 if the end goal is to push users towards using `TransactionType` 2 anyway. + + +## Specification + +For wallets and providers, if a user submits a transaction for signing with where `TransactionType == 0x1`, the developer should upgrade the transaction to meet the criteria of transaction of type 2. + +The following fields need to be changed, or amended: +- `access_list`: Nothing changes and it should remain in the transaction. +- `type`: Should change from `0x1` to `0x2`. +- `gas_price`: Should be removed in favour of `max_fee_per_gas` & `max_priority_fee_per_gas` (see [EIP-1559](./eip-1559.md) for proper usage). + +## Rationale + +Improve the user experience for submitting transactions, and move away from legacy style transactions. + +## Security Considerations + +There are no known security considerations at this time. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3722.md b/EIPS/eip-3722.md new file mode 100644 index 0000000..1b17795 --- /dev/null +++ b/EIPS/eip-3722.md @@ -0,0 +1,197 @@ +--- +eip: 3722 +title: Poster +description: A ridiculously simple general purpose social media smart contract. +author: Auryn Macmillan (@auryn-macmillan) +discussions-to: https://ethereum-magicians.org/t/eip-poster-a-ridiculously-simple-general-purpose-social-media-smart-contract/6751 +status: Stagnant +type: Standards Track +category: ERC +created: 2021-07-31 +--- + +# Poster + +## Abstract +A ridiculously simple general purpose social media smart contract. +It takes two strings (`content` and `tag`) as parameters and emits those strings, along with msg.sender, as an event. That's it. +The EIP also includes a proposed standard json format for a Twitter-like application, where each `post()` call can include multiple posts and/or operations. The assumption being that application state will be constructed off-chain via some indexer. + +## Motivation +Poster is intended to be used as a base layer for decentralized social media. It can be deployed to the same address (via the singleton factory) on just about any EVM compatible network. Any Ethereum account can make posts to the deployment of Poster on its local network. + +## Specification + +### Contract + +```solidity +contract Poster { + + event NewPost(address indexed user, string content, string indexed tag); + + function post(string calldata content, string calldata tag) public { + emit NewPost(msg.sender, content, tag); + } +} +``` + +### ABI +```json +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "string", + "name": "content", + "type": "string" + }, + { + "indexed": true, + "internalType": "string", + "name": "tag", + "type": "string" + } + ], + "name": "NewPost", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "content", + "type": "string" + }, + { + "internalType": "string", + "name": "tag", + "type": "string" + } + ], + "name": "post", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] +``` + +### Standard json format for Twitter-like posts + +```json +{ + "content": [ + { + "type": "microblog", + "text": "this is the first post in a thread" + }, + { + "type": "microblog", + "text": "this is the second post in a thread", + "replyTo": "this[0]" + }, + { + "type": "microblog", + "text": "this is a reply to some other post", + "replyTo": "some_post_id" + }, + { + "type": "microblog", + "text": "this is a post with an image", + "image": "ipfs://ipfs_hash" + }, + { + "type": "microblog", + "text": "this post replaces a previously posted post", + "edit": "some_post_id" + }, + { + "type": "delete", + "target": "some_post_id" + }, + { + "type": "like", + "target": "some_post_id" + }, + { + "type": "repost", + "target": "some_post_id" + }, + { + "type": "follow", + "target": "some_account" + }, + { + "type": "unfollow", + "target": "some_account" + }, + { + "type": "block", + "target": "some_account" + }, + { + "type": "report", + "target": "some_account or some_post_id" + }, + { + "type": "permissions", + "account": "", + "permissions": { + "post": true, + "delete": true, + "like": true, + "follow": true, + "block": true, + "report": true, + "permissions": true + } + }, + { + "type": "microblog", + "text": "This is a post from an account with permissions to post on behalf of another account.", + "from": "" + } + ] +} + +``` + +## Rationale +There was some discussion around whether or not an post ID should also be emitted, whether the content should be a string or bytes, and whether or not anything at all should actually be emitted. + +We decided not to emit an ID, since it meant adding state or complexity to the contract and there is a fairly common pattern of assigning IDs on the indexer layer based on transactionHash + logIndex. + +We decided to emit a string, rather than bytes, simply because that would make content human readable on many existing interfaces, like Etherscan for example. This did, unfortunately, eliminate some of the benefit that we might have gotten from a more compact encoding scheme like CBOR, rather than JSON. But this also would not have satisfied the human readable criteria. + +While there would have been some gas savings if we decided against emitting anything at all, it would have redically increased the node requirements to index posts. As such, we decided it was worth the extra gas to actually emit the content. + +## Reference Implementation + +Poster has been deployed at `0x000000000000cd17345801aa8147b8D3950260FF` on multiple networks using the [Singleton Factory](https://eips.ethereum.org/EIPS/eip-2470). If it is not yet deployed on your chosen network, you can use the Singleton Factory to deploy an instance of Poster at the same address on just about any EVM compatible network using these parameters: + +> **initCode:** `0x608060405234801561001057600080fd5b506101f6806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80630ae1b13d14610030575b600080fd5b61004361003e3660046100fa565b610045565b005b8181604051610055929190610163565b60405180910390203373ffffffffffffffffffffffffffffffffffffffff167f6c7f3182d7e4cb876251f9ae1489975fdbbf15d9f35d393f2ac9b1ff57cec69f86866040516100a5929190610173565b60405180910390a350505050565b60008083601f8401126100c4578182fd5b50813567ffffffffffffffff8111156100db578182fd5b6020830191508360208285010111156100f357600080fd5b9250929050565b6000806000806040858703121561010f578384fd5b843567ffffffffffffffff80821115610126578586fd5b610132888389016100b3565b9096509450602087013591508082111561014a578384fd5b50610157878288016100b3565b95989497509550505050565b6000828483379101908152919050565b60006020825282602083015282846040840137818301604090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016010191905056fea2646970667358221220ee0377bd266748c5dbaf0a3f15ebd97be153932f2d14d460d9dd4271fee541b564736f6c63430008000033` +> +> **salt:** `0x9245db59943806d06245bc7847b3efb2c899d11b621a0f01bb02fd730e33aed2` + +When verifying on the source code on a block explorer, make sure to set the optimizer to `yes` and the runs to `10000000`. + +The source code is available in the [Poster contract repo](https://github.com/ETHPoster/contract/blob/master/contracts/Poster.sol). + + +## Security Considerations +Given the ridiculously simple implementation of Poster, there does not appear to be any real security concerns at the contract level. + +At the application level, clients should confirm that posts including a `"from"` field that differs from `msg.sender` have been authorized by the `"from"` address via a `"permissions"` post, otherwise they should be considerred invalid or a post from `msg.sender`. + +Clients should also be sure to sanitize post data. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3754.md b/EIPS/eip-3754.md new file mode 100644 index 0000000..bc4dcdc --- /dev/null +++ b/EIPS/eip-3754.md @@ -0,0 +1,74 @@ +--- +eip: 3754 +title: A Vanilla Non-Fungible Token Standard +description: NFTs for representing abstract ownership +author: Simon Tian (@simontianx) +discussions-to: https://github.com/ethereum/EIPs/issues/3753 +status: Stagnant +type: Standards Track +category: ERC +created: 2021-08-21 +--- + +## Abstract +In this standard, a non-fungible token stands as atomic existence and encourages +layers of abstraction built on top of it. Ideal for representing concepts like +rights, a form of abstract ownership. Such right can take the form of NFT options, +oracle membership, virtual coupons, etc., and can then be made liquid because of +this tokenization. + +## Motivation +Non-fungible tokens are popularized by the [ERC-721](./eip-721.md) NFT standard +for representing "ownership over digital or physical assets". Over the course of +development, reputable NFT projects are about crypto-assets, digital collectibles, +etc. The proposed standard aims to single out a special type of NFTs that are +ideal for representing abstract ownership such as rights. Examples include the +right of making a function call to a smart contract, an NFT option that gives +the owner the right, but not obligation, to purchase an ERC-721 NFT, and the prepaid +membership (time-dependent right) of accessing to data feeds provided by oracles +without having to pay the required token fees. An on-chain subscription business +model can then be made available by this standard. The conceptual clarity of an +NFT is hence improved by this standard. + +## Specification +``` +interface IERC3754 { + event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); + event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); + event ApprovalForAll(address indexed owner, address indexed operator, bool approved); + + function balanceOf(address owner) external view returns (uint256); + function ownerOf(uint256 tokenId) external view returns (address); + function approve(address to, uint256 tokenId) external; + function getApproved(uint256 tokenId) external view returns (address); + function setApprovalForAll(address operator, bool approved) external; + function isApprovedForAll(address owner, address operator) external view returns (bool); + function transferFrom(address from, address to, uint256 tokenId) external; + function safeTransferFrom(address from, address to, uint256 tokenId) external; + function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) external; +} +``` + +## Rationale +The NFTs defined in the [ERC-721](./eip-721.md) standard are already largely +accepted and known as representing ownership of digital assets, and the NFTs by +this standard aim to be accepted and known as representing abstract ownership. +This is achieved by allowing and encouraging layers of abstract utilities built +on top of them. Ownership of such NFTs is equivalent with having the rights to +perform functions assigned to such tokens. Transfer of such rights is also made +easier because of this tokenization. To further distinguish this standard +from [ERC-721](./eip-721.md), data fields and functions related to `URI` are +excluded. + +## Backwards Compatibility +There is no further backwards compatibility required. + +## Reference Implementation +https://github.com/simontianx/ERC3754 + +## Security Considerations +The security is enhanced from ERC721, given tokens are minted without having to +provide `URI`s. Errors in dealing with `URI`s can be avoided. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3756.md b/EIPS/eip-3756.md new file mode 100644 index 0000000..59fe6a4 --- /dev/null +++ b/EIPS/eip-3756.md @@ -0,0 +1,55 @@ +--- +eip: 3756 +title: Gas Limit Cap +description: Set an in-protocol cap for the gas limit +author: lightclient (@lightclient) +discussions-to: https://ethereum-magicians.org/t/eip-3756-gas-limit-cap/6921 +status: Stagnant +type: Standards Track +category: Core +created: 2021-08-21 +--- + +## Abstract + +Set an in-protocol cap for the gas limit of 30,000,000. + +## Motivation + +A high gas limit increases pressure on the network. In the benign case, it +increases the size of the state and history faster than we can sustain. In the +malicious case, it amplifies the devastation of certain denial-of-service +attacks. + +## Specification + +As of the fork block `N`, consider blocks with a `gas_limit` greater than +`30,000,000` invalid. + +## Rationale + +### Why Cap the Gas Limit + +The gas limit is currently under the control of block proposers. They have the +ability to increase the gas limit to whatever value they desire. This allows +them to bypass the EIP and All Core Devs processes in protocol decisions that +may negatively affect the security and/or decentralization of the network. + +### No Fixed Gas Limit + +A valuable property of proposers choosing the gas limit is they can scale it +down quickly if the network becomes unstable or is undergoing certain types of +attacks. For this reason, we maintain their ability to lower the gas limit +_below_ 30,000,000. + +## Backwards Compatibility +No backwards compatibility issues. + +## Test Cases +TBD + +## Security Considerations +No security considerations. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3770.md b/EIPS/eip-3770.md new file mode 100644 index 0000000..07f48f9 --- /dev/null +++ b/EIPS/eip-3770.md @@ -0,0 +1,65 @@ +--- +eip: 3770 +title: Chain-specific addresses +description: Prepending chain-specific addresses with a human-readable chain identifier +author: Lukas Schor (@lukasschor), Richard Meissner (@rmeissner), Pedro Gomes (@pedrouid), ligi +discussions-to: https://ethereum-magicians.org/t/chain-specific-addresses/6449 +status: Review +type: Standards Track +category: ERC +created: 2021-08-26 +--- + +## Abstract + +[ERC-3770](./eip-3770.md) introduces a new address standard to be adapted by wallets and dApps to display chain-specific addresses by using a human-reacable prefix. + +## Motivation + +The need for this proposal emerges from the increasing adoption of non-Ethereum Mainnet chains that use the Ethereum Virtual Machine (EVM). In this context, addresses become ambiguous, as the same address may refer to an EOA on chain X or a smart contract on chain Y. This will eventually lead to Ethereum users losing funds due to human error. For example, users sending funds to a smart contract wallet address which was not deployed on a particular chain. + +Therefore we should prefix addresses with a unique identifier that signals to Dapps and wallets on what chain the target account is. In theory, this prefix could be a [EIP-155](./eip-155.md) chainID. However, these chain IDs are not meant to be displayed to users in dApps or wallets, and they were optimized for developer interoperability, rather than human readability. + +## Specification + +This proposal extends addresses with a human-readable blockchain short name. + +### Syntax + +A chain-specific address is prefixed with a chain shortName, separated with a colon sign (:). + +Chain-specific address = "`shortName`" "`:`" "`address`" + +- `shortName` = STRING + +- `address` = STRING + +### Semantics + +``` + +`shortName` is mandatory and MUST be a valid chain short name from https://github.com/ethereum-lists/chains + +`address` is mandatory and MUST be a [ERC-55](./eip-55.md) compatible hexadecimal address + +``` + +### Examples + +![Chain-specific addresses](../assets/eip-3770/examples.png "Examples of chain-specific addresses") + +## Rationale + +To solve the initial problem of user-facing addresses being ambiguous in a multichain context, we need to map EIP-155 chain IDs with a user-facing format of displaying chain identifiers. + +## Backwards Compatibility + +Ethereum addresses without the chain specifier will continue to require additional context to understand which chain the address refers to. + +## Security Considerations + +The Ethereum List curators must consider how similar looking chain short names can be used to confuse users. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3772.md b/EIPS/eip-3772.md new file mode 100644 index 0000000..566964c --- /dev/null +++ b/EIPS/eip-3772.md @@ -0,0 +1,278 @@ +--- +eip: 3772 +title: Compressed Integers +description: Using lossy compression on uint256 to improve gas costs, ideally by a factor up to 4x. +author: Soham Zemse (@zemse) +discussions-to: https://github.com/ethereum/EIPs/issues/3772 +status: Stagnant +type: Standards Track +category: ERC +created: 2021-08-27 +--- + +## Abstract + +This document specifies compression of `uint256` to smaller data structures like `uint64`, `uint96`, `uint128`, for optimizing costs for storage. The smaller data structure (represented as `cintx`) is divided into two parts, in the first one we store `significant` bits and in the other number of left `shift`s needed on the significant bits to decompress. This document also includes two specifications for decompression due to the nature of compression being lossy, i.e. it causes underflow. + +## Motivation + +- Storage is costly, each storage slot costs almost $0.8 to initialize and $0.2 to update (20 gwei, 2000 ETHUSD). +- Usually, we store money amounts in `uint256` which takes up one entire slot. +- If it's DAI value, the range we work with most is 0.001 DAI to 1T DAI (or 1012). If it's ETH value, the range we work with most is 0.000001 ETH to 1B ETH. Similarly, any token of any scale has a reasonable range of 1015 amounts that we care/work with. +- However, uint256 type allows us to represent $10-18 to $1058, and most of it is a waste. In technical terms, we have the probability distribution for values larger than $1015 and smaller than $10-3 as negligible (i.e. P[val > 1015] ≈ 0 and P[val < 10-3] ≈ 0). +- Number of bits required to represent 1015 values = log2(1015) = 50 bits. So just 50 bits (instead of 256) are reasonably enough to represent a practical range of money, causing a very negligible difference. + +## Specification + +In this specification, the structure for representing a compressed value is represented using `cintx`, where x is the number of bits taken by the entire compressed value. On the implementation level, an `uintx` can be used for storing a `cintx` value. + +### Compression + +#### uint256 into cint64 (up to cint120) + +The rightmost, or least significant, 8 bits in `cintx` are reserved for storing the shift and the rest available bits are used to store the significant bits starting from the first `1` bit in `uintx`. + +```solidity +struct cint64 { uint56 significant; uint8 shift; } + +// ... + +struct cint120 { uint112 significant; uint8 shift; } +``` + +#### uint256 into cint128 (up to cint248) + +The rightmost, or least significant, 7 bits in `cintx` are reserved for storing the shift and the rest available bits are used to store the significant bits starting from the first one bit in `uintx`. + +> In the following code example, `uint7` is used just for representation purposes only, but it should be noted that uints in Solidity are in multiples of 8. + +```solidity +struct cint128 { uint121 significant; uint7 shift; } + +// ... + +struct cint248 { uint241 significant; uint7 shift; } +``` + +Examples: + +``` +Example: +uint256 value: 2**100, binary repr: 1000000...(hundred zeros) +cint64 { significant: 10000000...(55 zeros), shift: 00101101 (45 in decimal)} + +Example: +uint256 value: 2**100-1, binary repr: 111111...(hundred ones) +cint64 { significant: 11111111...(56 ones), shift: 00101100 (44 in decimal) } +``` + +### Decompression + +Two decompression methods are defined: a normal `decompress` and a `decompressRoundingUp`. + +```solidity +library CInt64 { + // packs the uint256 amount into a cint64 + function compress(uint256) internal returns (cint64) {} + + // unpacks cint64, by shifting the significant bits left by shift + function decompress(cint64) internal returns (uint256) {} + + // unpacks cint64, by shifting the significant bits left by shift + // and having 1s in the shift bits + function decompressRoundingUp(cint64) internal returns (uint256) {} +} +``` + +#### Normal Decompression + +The `significant` bits in the `cintx` are moved to a `uint256` space and shifted left by `shift`. + +> NOTE: In the following example, cint16 is used for visual demonstration purposes. But it should be noted that it is definitely not safe for storing money amounts because its significant bits capacity is 8, while at least 50 bits are required for storing money amounts. + +``` +Example: +cint16{significant:11010111, shift:00000011} +decompressed uint256: 11010111000 // shifted left by 3 + +Example: +cint64 { significant: 11111111...(56 ones), shift: 00101100 (44 in decimal) } +decompressed uint256: 1111...(56 ones)0000...(44 zeros) +``` + +#### Decompression along with rounding up + +The `significant` bits in the `cintx` are moved to a `uint256` space and shifted left by `shift` and the least significant `shift` bits are `1`s. + +``` +Example: +cint16{significant:11011110, shift:00000011} +decompressed rounded up value: 11011110111 // shifted left by 3 and 1s instead of 0s + +Example: +cint64 { significant: 11111111...(56 ones), shift: 00101100 (44 in decimal) } +decompressed uint256: 1111...(100 ones) +``` + +This specification is to be used by a new smart contract for managing its internal state so that any state mutating calls to it can be cheaper. These compressed values on a smart contract's state are something that should not be exposed to the external world (other smart contracts or clients). A smart contract should expose a decompressed value if needed. + +## Rationale + +- The `significant` bits are stored in the most significant part of `cintx` while `shift` bits in the least significant part, to help prevent obvious dev mistakes. For e.g. a number smaller than 256-1 its compressed `cint64` value would be itself if the arrangement were to be opposite than specified. If a developer forgets to uncompress a value before using it, this case would still pass if the compressed value is the same as decompressed value. +- It should be noted that using `cint64` doesn't render gas savings automatically. The solidity compiler needs to pack more data into the same storage slot. +- Also the packing and unpacking adds some small cost too. +- Though this design can also be seen as a binary floating point representation, however using floating point numbers on EVM is not in the scope of this ERC. The primary goal of floating point numbers is to be able to represent a wider range in an available number of bits, while the goal of compression in this ERC is to keep as much precision as possible. Hence, it specifies for the use of minimum exponent/shift bits (i.e 8 up to `uint120` and 7 up to `uint248`). + +```solidity +// uses 3 slots +struct UserData1 { + uint64 amountCompressed; + bytes32 hash; + address beneficiary; +} + +// uses 2 slots +struct UserData2 { + uint64 amountCompressed; + address beneficiary; + bytes32 hash; +} +``` + +## Backwards Compatibility + +There are no known backward-incompatible issues. + +## Reference Implementation + +On the implementation level `uint64` may be used directly, or with custom types introduced in 0.8.9. + +```soldity +function compress(uint256 full) public pure returns (uint64 cint) { + uint8 bits = mostSignificantBitPosition(full); + if (bits <= 55) { + cint = uint64(full) << 8; + } else { + bits -= 55; + cint = (uint64(full >> bits) << 8) + bits; + } +} + +function decompress(uint64 cint) public pure returns (uint256 full) { + uint8 bits = uint8(cint % (1 << 9)); + full = uint256(cint >> 8) << bits; +} + +function decompressRoundingUp(uint64 cint) public pure returns (uint256 full) { + uint8 bits = uint8(cint % (1 << 9)); + full = uint256(cint >> 8) << bits + ((1 << bits) - 1); +} +``` + +The above gist has `library CInt64` that contains demonstrative logic for compression, decompression, and arithmetic for `cint64`. The gist also has an example contract that uses the library for demonstration purposes. + +The CInt64 format is intended only for storage, while dev should convert it to uint256 form using suitable logic (decompress or decompressRoundingUp) to perform any arithmetic on it. + +## Security Considerations + +The following security considerations are discussed: + +1. Effects due to lossy compression + - Error estimation for `cint64` + - Handling the error +2. Losing precision due to incorrect use of `cintx` +3. Compressing something other than money `uint256`s. + +### 1. Effects due to lossy compression + +When a value is compressed, it causes underflow, i.e. some less significant bits are sacrificed. This results in a `cintx` value whose decompressed value is less than or equal to the actual `uint256` value. + +```solidity +uint a = 2**100 - 1; // 100 # of 1s in binary format +uint c = a.compress().decompress(); + +a > c; // true +a - (2**(100 - 56) - 1) == c; // true + +// Visual example: +// before: 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +// after: 1111111111111111111111111111111111111111111111111111111100000000000000000000000000000000000000000000 +``` + +#### Error estimation for cint64 + +Let's consider we have a `value` of the order 2m (less than 2m and greater than or equal to 2m-1). + +For all values such that 2m - 1 - (2m-56 - 1) <= `value` <= 2m - 1, the compressed value `cvalue` is 2m - 1 - (2m-56 - 1). + +The maximum error is 2m-56 - 1, approximating it to decimal: 10n-17 (log2(56) is 17). Here `n` is number of decimal digits + 1. + +For e.g. compressing a value of the order $1,000,000,000,000 (or 1T or 1012) to `cint64`, the maximum error turns out to be 1012+1-17 = $10-4 = $0.0001. This means the precision after 4 decimal places is lost, or we can say that the uncompressed value is at maximum $0.0001 smaller. Similarly, if someone is storing $1,000,000 into `cint64`, the uncompressed value would be at maximum $0.0000000001 smaller. In comparison, the storage costs are almost $0.8 to initialize and $0.2 to update (20 gwei, 2000 ETHUSD). + +#### Handling the error + +Note that compression makes the value slightly smaller (underflow). But we also have another operation that also does that. In integer math, the division is a lossy operation (causing underflow). For instance, + +```solidity +10000001 / 2 == 5000000 // true +``` + +The result of the division operation is not always exact, but it's smaller than the actual value, in some cases as in the above example. Though, most engineers try to reduce this effect by doing all the divisions at the end. + +``` +1001 / 2 * 301 == 150500 // true +1001 * 301 / 2 == 150650 // true +``` + +The division operation has been in use in the wild, and plenty of lossy integer divisions have taken place, causing DeFi users to get very very slightly less withdrawal amounts, which they don't even notice. If been careful, then the risk is very negligible. Compression is similar, in the sense that it is also a division by 2shift. If been careful with this too, the effects are minimized. + +In general, one should follow the rule: + +1. When a smart contract has to transfer a compressed amount to a user, they should use a rounded down value (by using `amount.decompress()`). +2. When a smart contract has to transferFrom a compressed amount from a user to itself, i.e charging for some bill, they should use a rounded up value (by using `amount.decompressUp()`). + +The above ensures that smart contract does not loose money due to the compression, it is the user who receives less funds or pays more funds. The extent of rounding is something that is negligible enough for the user. Also just to mention, this rounding up and down pattern is observed in many projects including UniswapV3. + +### 2. Losing precision due to incorrect use of `cintx` + +This is an example where dev errors while using compression can be a problem. + +Usual user amounts mostly have an max entropy of 50, i.e. 1015 (or 250) values in use, that is the reason why we find uint56 enough for storing significant bits. However, let's see an example: + +```solidity +uint64 sharesC = // reading compressed value from storage; +uint64 price = // CALL; +uint64 amountC = sharesC.cmuldiv(price, PRICE_UNIT); +user.transfer(amountC.uncompress()); +``` + +The above code results in a serious precision loss. `sharesC` has an entropy of 50, as well as `priceC` also has an entropy of 50. When we multiply them, we get a value that contains entropies of both, and hence, an entropy of 100. After multiplication is done, `cmul` compresses the value, which drops the entropy of `amountC` to 56 (as we have uint56 there to store significant bits). + +To prevent entropy/precision from dropping, we get out from compression. + +```solidity +uint64 sharesC = shares.compress(); +uint64 priceC = price.compress(); +uint256 amount = sharesC.uncompress() * price / PRICE_UNIT; +user.transfer(amount); +``` + +Compression is only useful when writing to storage while doing arithmetic with them should be done very carefully. + +### 3. Compressing something other than money `uint256`s. + +Compressed Integers is intended to only compress money amount. Technically there are about 1077 values that a `uint256` can store but most of those values have a flat distribution i.e. the probability is 0 or extremely negligible. (What is a probability that a user would be depositing 1000T DAI or 1T ETH to a contract? In normal circumstances it doesn't happen, unless someone has full access to the mint function). Only the amounts that people work with have a non-zero distribution ($0.001 DAI to $1T or 1015 to 1030 in uint256). 50 bits are enough to represent this information, just to round it we use 56 bits for precision. + +Using the same method for compressing something else which have a completely different probability distribution will likely result in a problem. It's best to just not compress if you're not sure about the distribution of values your `uint256` is going to take. And also, for things you think you are sure about using compression for, it's better to give more thought if compression can result in edge cases (e.g. in previous multiplication example). + +### 4. Compressing Stable vs Volatile money amounts + +Since we have a dynamic `uint8 shift` value that can move around. So even if you wanted to represent 1 Million SHIBA INU tokens or 0.0002 WBTC (both $10 as of this writing), cint64 will pick its top 56 significant bits which will take care of the value representation. + +It can be a problem for volatile tokens if the coin is extremely volatile wrt user's native currency. Imagine a very unlikely case where a coin goes 256x up (price went up by 1016 lol). In such cases `uint56` might not be enough as even its least significant bit is very valuable. If such insanely volatile tokens are to be stored, you should store more significant bits, i.e. using `cint96` or `cint128`. + +`cint64` has 56 bits for storing significant, when only 50 were required. Hence there are 6 extra bits, which means that it is fine if the $ value of the cryptocurrency stored in cint64 increases by 26 or 64x. If the value goes down it's not a problem. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3779.md b/EIPS/eip-3779.md new file mode 100644 index 0000000..f8a4e1d --- /dev/null +++ b/EIPS/eip-3779.md @@ -0,0 +1,413 @@ +--- +eip: 3779 +title: Safer Control Flow for the EVM +description: Ensure an essential level of safety for EVM code. +author: Greg Colvin (@gcolvin), Greg Colvin , Brooklyn Zelenka (@expede) +discussions-to: https://ethereum-magicians.org/t/eip-3779-safe-control-flow-for-the-evm/6975 +status: Withdrawn +type: Standards Track +category: Core +created: 2021-08-30 +withdrawal-reason: material moved to EIP-2315 +--- + +## Abstract + +We define a safe EVM contract as one that cannot encounter an exceptional halting state. In general, we cannot prove safety for Turing-complete programs. But we can prove a useful subset. + +This EIP specifies validity rules to ensure that: +> Valid contracts will not halt with an exception unless they either +> * throw `out of gas` or +> * recursively overflow stack. + +This EIP does not introduce any new opcodes. Rather, it restricts the use of existing and proposed control-flow instructions. The restrictions must be validated at contract initialization time – not at runtime – by the provided algorithm or its equivalent. This algorithm must take time and space near-linear in the size of the contract, so as not to be a denial of service vulnerability. + +This specification is entirely semantic. It imposes no further syntax on bytecode, as none is required to ensure the specified level of safety. Ethereum Virtual Machine bytecode is just that -- a sequence of bytes that when executed causes a sequence of changes to the machine state. The safety we seek here is simply to not, as it were, jam up the gears. + +## Motivation + +### Safety + +For our purposes we define a safe EVM contract as one that cannot encounter an exceptional halting state. From the standpoint of security it would be best if unsafe contracts were never placed on the blockchain. Unsafe code can attempt to overflow stack, underflow stack, execute invalid instructions, and jump to invalid locations. + +Unsafe contracts are exploits waiting to happen. + +Validating contract safety requires traversing the contract code. So in order to prevent denial of service attacks all jumps, including the existing `JUMP` and `JUMPI`, and also the other proposed jumps -- `RJUMP`, `RJUMPI`, `RJUMPSUB` and `RETURNSUB` -- must be validated at initialization time, and in time and space linear in the size of the code. + +#### Static Jumps and Subroutines + +The relative jumps of [EIP-4200](./eip-4200) and the simple subroutines of [EIP-2315](./eip-2315) provide a complete set of static control flow instructions: +> `RJUMP` _offset_ +* Jumps to _IP+offset_. +> `RJUMPI` _offset_ +* Jumps if the top of stack is non-zero. +> `RJUMPSUB` offset +* Pushes _IP+1_ on the return stack and jumps to _IP+offest_. +> `RETURNSUB` +* Jumps to the address popped off the return stack. + +Note that each jump creates at most two paths of control through the code, such that the complexity of traversing the entire control-flow graph is linear in the size of the code. + +#### *Dynamic Jumps* + +Dynamic jumps, where the destination of a `JUMP` or `JUMPI` is not known until runtime, are an obstacle to proving validity in linear time -- any jump can be to any destination in the code, potentially requiring time quadratic in the size of code. For this reason we have two real choices. + +1. Deprecate dynamic jumps. This is easily done: + +> Define `JUMP` and `JUMPI` as `INVALID` for the purposes of EOF Code Validation + +2. Constrain dynamic jumps. This requires static analysis. + +Consider the simplest and most common case. +``` +PUSH address +JUMP +``` +This is effectively a static jump. + +Another important use of `JUMP` is to implement the return jump from a subroutine. So consider this example of calling and returning from a minimal subroutine: +``` +TEST_SQUARE: + jumpdest + push RTN_SQUARE + 0x02 + push SQUARE + jump +RTN_SQUARE + jumpdest + swap1 + jump + +SQUARE: + jumpdest + dup1 + mul + swap1 + jump +``` +The return address -`RTN_SQUARE` - and the destination address - `SQUARE` - are pushed on the stack as constants and remain unchanged as they move on the stack, such that only those constants are passed to each `JUMP`. They are effectively static. We can track the motion of constants on the `data stack` at validation time, so *we do not need unconstrained dynamic jumps to implement subroutines.* + +*The above is the simplest analysis that suffices. A more powerful analysis that takes in more use cases is possible -- slower, but still linear-time.* + +#### Validation + +We can validate the safety of contracts with a static analysis that takes time and space linear in the size of the *code*, as shown below. And since we can, we should. + +### Performance + +Validating safe control flow at initialization time has potential performance advantages. +* Static jumps do not need to be checked at runtime. +* Stack underflow does not need to be checked for at runtime. + +## Specification + +### Validity + +> In theory, theory and practice are the same. In practice, they're not. -- Albert Einstein + +We define a _safe_ EVM contract as one that cannot encounter an exceptional halting state. We validate _safety_ at initialization time to the extent practical. + +#### *Exceptional Halting States* + +The *execution* of each instruction is defined in the Yellow Paper as a change to the EVM state that preserves the invariants of EVM state. At runtime, if the execution of an instruction would violate an invariant the EVM is in an exceptional halting state. The Yellow Paper defined five such states. +1. Insufficient gas +2. More than 1024 stack items +3. Insufficient stack items +4. Invalid jump destination +5. Invalid instruction + +*A program is safe iff no execution can lead to an exceptional halting state.* + +*We would like to consider EVM programs valid iff they are safe.* + +*In practice*, we must be able to validate *code* in linear time to avoid denial of service attacks. And we must support dynamically-priced instructions, loops, and recursion, which can use arbitrary amounts of gas and stack. + +Thus our validation cannot consider concrete computations -- it only performs a limited symbolic execution of the _code_. This means we will reject programs if we detect any invalid execution paths, even if those paths are not reachable at runtime. And we will count as valid programs that may not always produce correct results. + +We can detect only _non-recursive_ stack overflows at *validation time*, so we must check for the first two states at _runtime_: +* `out of gas` and +* stack overflow. + +The remaining three states we can check at *validation time*: +* stack underflow, +* invalid jump, and +* invalid instruction. + +That is to say: +> Valid contracts will not halt with an exception unless they either +> * throw `out of gas` or +> * recursively overflow stack. + +#### *Constraints on Valid Code* + +* Every instruction is valid. +* Every jump is valid: + * Every`JUMP` and `JUMPI` is *static*. + * No `JUMP`, `JUMPI`, `RJUMP`, `RJUMPI`, or `RJUMPSUB` addresses immediate data. +* The stacks are always valid: + * The _number_ of items on the `data stack` is always positive, and at most 1024. + * The _number_ of items on the `return stack` is always positive, and at most 1024. +* The data stack is consistently aligned: + * The _number_ of items on the `data stack` between the current `stack pointer` and the `stack pointer` on entry to the most recent basic block is the same for each _execution_ of a _byte_code_. + +We define a `JUMP` or `JUMPI` instruction to be *static* if its `jumpsrc` argument was first placed on the stack via a `PUSH…` and that value has not changed since, though it may have been copied via a `DUP…` or `SWAP…`. + +The `RJUMP`, `RJUMPI` and `RJUMPSUB`instructions take their destination as an immediate argument, so they are *static*. + +Taken together, these rules allow for code to be validated by traversing the control-flow graph, in time and space linear in the size of the code, following each edge only once. + +_Note: The definition of 'static' for `JUMP` and `JUMPI` is the bare minimum needed to implement subroutines. Deeper analyses could be proposed that would validate a larger and probably more useful set of jumps, at the cost of more expensive (but still linear) validation._ + + +## Rationale + +Demanding *static* destinations for all jumps means that all jump destinations can be validated at initialization time, not runtime. + +Bounding the stack pointers catches all `data stack` and non-recursive`return stack` overflows. + +Requiring a consistently aligned`data stack` prevents stack underflow. It can also catch such errors as misaligned stacks due to irreducible control flows and calls to subroutines with the wrong number of arguments. + +## Backwards Compatibility + +These changes affect the semantics of EVM code – the use of `JUMP`, `JUMPI`, and the stack are restricted, such that some *code* that would otherwise run correctly will nonetheless be invalid EVM *code*. + +## Reference Implementation + +The following is a pseudo-Go implementation of an algorithm for predicating code validity. An equivalent algorithm must be run at initialization time. + +This algorithm performs a symbolic execution of the program that recursively traverses the _code_, emulating its control flow and stack use and checking for violations of the rules above. + +It runs in time equal to `O(vertices + edges)` in the program's control-flow graph, where edges represent control flow and the vertices represent _basic blocks_ -- thus the algorithm takes time proportional to the size of the _code_. + +_Note: All valid code has a control-flow graph that can be traversed in time and space linear in the length of the code. That means that some other static analyses and code transformations that might otherwise require quadratic time can also be written to run in near-linear time, including one-pass and streaming compilers._ + +### Validation Function + +***Note:** This function is a work in progress, and the version below is known to be incorrect.* + +For simplicity's sake we assume that _jumpdest analysis_ has been done and that we have some helper functions. +* `isValidInstruction(pc)` returns true if `pc` points at a valid instruction +* `isValidJumpdest(dest)` returns true if `dest` is a valid jumpdest +* `immediateData(pc)` returns the immediate data for the instruction at `pc`. +* `advancePC(pc)` returns next `pc`, skipping any immediate data. +* `removed_items(pc)` returns the number of items removed from the `dataStack` by the instruction at `pc`. +* `added_items(pc)` returns the number of items added to the `dataStack` by the instruction at `pc`. + +``` +var bytecode [codeLen]byte +var subMin [codeLen]int +var subMax [codeLen]int +var subDelta [codeLen]int +var visited [codeLen]bool +var dataStack [1024]int + +// validate a path through the control flow of the bytecode at pc +// and return the maximum number of stack items used down that path +// or else the PC and an error +// +// by starting at pc:=0 the entire program is recursively evaluated +// +func validate(pc := 0, sp := 0, rp := 0) int, error { + minStack := 0 + maxStack := 0 + deltaStack := 0 + for pc < codeLen { + if !isValidInstruction(pc) { + return 0,0,0,invalid_instruction + } + + // if we have jumped here before return to break cycle + if visited[pc] { + + // stack is not aligned if deltas not the same + if ??? { + return 0,0,0,invalid_stack + } + return minStack, maxStack, sp + } + visited[pc] = true + switch bytecode[pc] { + + // successful termination + case STOP: + return minStack, maxStack, sp + case RETURN: + return minStack, maxStack, sp + + case SELFDESTRUCT: + return minStack, maxStack, sp + case REVERT: + return minStack, maxStack, sp + case INVALID: + return 0,0,0,invalid_instruction + + case RJUMP: + + // check for valid jump destination + if !isValidJumpdest(jumpdest) { + return 0,0,0,invalid_destination + } + + // reset pc to destination of jump + pc += immediateData(pc) + + case RJUMPI: + + // recurse to validate true side of conditional + jumpdest = pc + immediateData(pc) + if !isValidJumpdest(pc + jumpdest) { + return 0,0,0,invalid_destination + } + minRight, maxLeft, deltaRight, err = + validate(jumpdest, sp, rp) + + err { + return 0,0,0,err + } + + // recurse to validate false side of conditional + pc = advancePC(pc) + minRight, maxRight, deltaRight, err = + validate(pc, sp, rp) + if err { + return 0,0,0,err + } + + // both paths valid, so return max + minStack = min(minStack, min(minLeft, minRight)) + maxStack += max(maxLeft, maxRight) + deltaStack += max(deltaLeft, deltaRight) + return minStack, maxStack, deltaStack + + case RJUMPSUB: + + // check for valid jump destination + jumpdest = immediateData(pc) + if !isValidJumpdest(pc + jumpdest) { + return 0,0,0,invalid_destination + } + + pc += jumpdest + + // recurse to validate subroutine call + minSub, maxSub, deltaSub, err = validate(jumpdest, sp, rp) + if err { + return 0,0,0,err + } + subMin[pc] = minSub + subMax[pc] = maxSub + subDelta[pc] = deltaSub + minStack = min(minStack, sp) + maxStack = max(maxStack, sp) + pc = advancePC(pc) + + case RETURNSUB: + + maxStack = max(maxStack, sp) + return minStack, maxStack, sp, nil + + ///////////////////////////////////////////////////// + // + // The following are to be included only if we take + // + // Option 2 + // + // and do not deprecate JUMP and JUMPI + // + case JUMP: + // pop jump destination + jumpdest = dataStack[--sp] + if !valid_jumpdest(jumpdest) { + return 0,0,0,invalid_destination + } + pc = jumpdest + case JUMPI: + // pop jump destination and conditional + jumpdest = dataStack[--sp] + jumpif = dataStack[--sp] + if sp < 0 {} + return 0,0,0,stack_underflow + } + if !valid_jumpdest(jumpdest) { + return 0,0,0,invalid_destination + } + + // recurse to validate true side of conditional + if !isValidJumpdest(jumpdest) { + return 0,0,0,invalid_destination + } + maxLeft, err = validate(jumpdest, sp, rp) + if err { + return 0,0,0,err + } + + // recurse to validate false side of conditional + pc = advance_pc(pc) + maxRight, err = validate(pc, sp, rp) + if err { + return 0,0,0,err + } + + // both sides valid, return max + maxStack += max(maxLeft, maxRight) + return minStack, maxStack, sp + case PUSH1 <= bytecode[pc] && bytecode[pc] <= PUSH32 { + sp++ + if (sp > 1023) { + return 0,0,0,stack_overflow + } + maxStack = max(maxStack, sp) + dataStack[sp] = immediateData(pc) + pc = advancePC(pc) + case DUP1 <= bytecode[pc] && bytecode[pc] <= DUP32 { + dup = sp - (bytecode[pc] - DUP1) + if dup < 0 { + return 0,0,0,stack_underflow + } + sp++ + if (sp > 1023) { + return 0,0,0,stack_overflow + } + maxStack = max(maxStack, sp) + dataStack[sp] = dataStack[dup] + pc = advancePC(pc) + case SWAP1 <= bytecode[pc] && bytecode[pc] <= SWAP32 { + swap = sp - (bytecode[pc] - SWAP1) + if swap < 0 { + return 0,0,0,stack_underflow + } + temp := dataStack[swap] + dataStack[swap] = dataStack[0] + dataStack[0] = temp + pc = advancePC(pc) + // + ///////////////////////////////////////////////////// + + default: + + // apply other instructions to stack pointer + sp -= removed_items(pc) + if (sp < 0) { + return 0,0,0,stack_underflow + } + minStack = min(minStack, sp) + sp += added_items(pc) + if (sp > 1023) { + return 0,0,0,stack_overflow + } + maxStack = max(maxStack, sp) + pc = advancePC(pc) + } + } + + // successful termination + return minStack, maxStack, sp +} +``` +## Security Considerations + +This EIP is intended to ensure an essential level of safety for EVM code deployed on the blockchain. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). + diff --git a/EIPS/eip-3788.md b/EIPS/eip-3788.md new file mode 100644 index 0000000..6d27c0d --- /dev/null +++ b/EIPS/eip-3788.md @@ -0,0 +1,60 @@ +--- +eip: 3788 +title: Strict enforcement of chainId +description: Reject transactions that do not explicitly have the same chainId as the node's configuration. +author: Gregory Markou (@GregTheGreek) +discussions-to: https://ethereum-magicians.org/t/discussion-to-eip-3788-strict-enforcement-of-chainid/7001 +status: Stagnant +type: Standards Track +category: Core +created: 2021-09-2 +requires: 155 +--- + +## Abstract + +Reject transactions that do not explicitly have the same chainId as the node's configuration. + +## Motivation + +Per [EIP-155](./eip-155.md) a transaction with a `chainId = 0` is considered to be a valid +transaction. This was a feature to offer developers the ability to sumbit replayable transactions +across different chains. With the rise of evm compatible chains, many of which use forks, or packages +from popular Ethereum clients, we are putting user funds at risk. This is because most wallet +interfaces do not expose the chainId to the user, meaning they typically do not have insight +into what chainId they are signing. Should a malicious actor (or accidental) choose to, they +can easily have users submit transactions with a `chainId = 0` on a non-mainnet network, allowing +the malicious actor to replay the transaction on ethereum mainnet (or other networks for that matter) +as a grief or sophisticated attack. + +## Specification + +As of the fork block `N`, consider transactions with a `chaindId = 0` to be invalid. Such that +transactions are verified based on the nodes configuration. Eg: +``` +if (node.cfg.chainId != tx.chainId) { + // Reject transaction +} +``` + +## Rationale + +The configuration set by the node is the main source of truth, and thus should be explicitly used +when deciding how to filter out a transaction. This check should exist in two places, as a filter +on the JSON-RPC (eg: `eth_sendTransaction`), and strictly enforced on the EVM during transaction +validation. + +This ensures that users will not have transactions pending that will be guaranteed to fail, and +prevents the transaction from being included in a block. + +## Backwards Compatibility +This breaks all applications or tooling that submit transactions with a `chainId == 0` after block number `N`. + +## Test Cases +TBD + +## Security Considerations +It should be noted this will not prevent a malicious actor from deploying a network with `chainId = 1`, or copying any other networks chainId. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3855.md b/EIPS/eip-3855.md new file mode 100644 index 0000000..0820a03 --- /dev/null +++ b/EIPS/eip-3855.md @@ -0,0 +1,64 @@ +--- +eip: 3855 +title: PUSH0 instruction +description: Introduce a new instruction which pushes the constant value 0 onto the stack +author: Alex Beregszaszi (@axic), Hugo De la cruz (@hugo-dc), Paweł Bylica (@chfast) +discussions-to: https://ethereum-magicians.org/t/eip-3855-push0-instruction/7014 +status: Stagnant +type: Standards Track +category: Core +created: 2021-02-19 +--- + +## Abstract + +Introduce the `PUSH0` (`0x5f`) instruction, which pushes the constant value 0 onto the stack. + +## Motivation + +Many instructions expect offsets as inputs, which in a number of cases are zero. A good example is the return data parameters of `CALLs`, which are set to zeroes in case the contract prefers using `RETURNDATA*`. This is only one example, but there are many other reasons why a contract would need to push a zero value. They can achieve that today by `PUSH1 0`, which costs 3 gas at runtime, and is encoded as two bytes which means `2 * 200` gas deployment cost. + +Because of the overall cost many try to use various other instructions to achieve the same effect. Common examples include `PC`, `MSIZE`, `CALLDATASIZE`, `RETURNDATASIZE`, `CODESIZE`, `CALLVALUE`, and `SELFBALANCE`. Some of these cost only 2 gas and are a single byte long, but their value can depend on the context. + +We have conducted an analysis on Mainnet (block ranges 8,567,259…8,582,058 and 12,205,970…12,817,405), and ~11.5% of all the `PUSH*` instructions executed push a value of zero. + +The main motivations for this change include: +1. Reducing contract code size. +2. Reducing the risk of contracts (mis)using various instructions as an optimisation measure. Repricing/changing those instructions can be more risky. +3. Reduce the need to use `DUP` instructions for duplicating zeroes. + +To put the "waste" into perspective, across existing accounts 340,557,331 bytes are wasted on `PUSH1 00` instructions, which means 68,111,466,200 gas was spent to deploy them. In practice a lot of these accounts share identical bytecode with others, so their total stored size in clients is lower, however the deploy time cost must have been paid nevertheless. + +An example for 2) is changing the behaviour of `RETURNDATASIZE` such that it may not be guaranteed to be zero at the beginning of the call frame. This was proposed as a way to chain transactions (i.e. EIP-2733). + +## Specification + +The instruction `PUSH0` is introduced at `0x5f`. It has no immediate data, pops no items from the stack, and places a single item with the value 0 onto the stack. The cost of this instruction is 2 gas (aka `base`). + +## Rationale + +### Gas cost + +The `base` gas cost is used for instructions which place constant values onto the stack, such as `ADDRESS`, `ORIGIN`, and so forth. + +### Opcode + +`0x5f` means it is in a "contiguous" space with the rest of the `PUSH` implementations and potentially could share the implementation. + +## Backwards Compatibility + +This EIP introduces a new opcode which did not exists previously. Already deployed contracts using this opcode could change their behaviour after this EIP. + +## Test Cases + +- `5F` -- successful execution, stack consist of a single item, set to zero +- `5F5F..5F` (1024 times) -- successful execution, stack consists of 1024 items, all set to zero +- `5F5F..5F` (1025 times) -- execution aborts due to out of stack + +## Security Considerations + +The authors are not aware of any impact on security. Note that jumpdest-analysis is unaffected, as `PUSH0` has no immediate data bytes. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3860.md b/EIPS/eip-3860.md new file mode 100644 index 0000000..51fb600 --- /dev/null +++ b/EIPS/eip-3860.md @@ -0,0 +1,124 @@ +--- +eip: 3860 +title: Limit and meter initcode +description: Limit the maximum size of initcode to 49152 and apply extra gas cost of 2 for every 32-byte chunk of initcode +author: Martin Holst Swende (@holiman), Paweł Bylica (@chfast), Alex Beregszaszi (@axic), Andrei Maiboroda (@gumb0) +discussions-to: https://ethereum-magicians.org/t/eip-3860-limit-and-meter-initcode/7018 +status: Review +type: Standards Track +category: Core +created: 2021-07-16 +requires: 170 +--- + +## Abstract + +We extend [EIP-170](./eip-170.md) by introducing a maximum size limit for `initcode` (`MAX_INITCODE_SIZE = 2 * MAX_CODE_SIZE = 49152`). + +Furthermore, we introduce a charge of `2` gas for every 32-byte chunk of `initcode` to represent the cost of jumpdest-analysis. + +Lastly, the size limit results in the nice-to-have property that EVM code size, code offset (`PC`), and jump offset fits a 16-bit value. + +## Motivation + +During contract creation the client has to perform jumpdest-analysis on the `initcode` prior to execution. The work performed scales linearly with the size of the `initcode`. This work currently is not metered, nor is there a protocol enforced upper bound for the size. + +There are three costs charged today: + +1. Cost for calldata aka `initcode`: 4 gas for a byte with the value of zero, and 16 gas otherwise. +2. Cost for the resulting deployed code: 200 gas per byte. +3. Cost of address calculation (hashing of code) in case of `CREATE2` only: 6 gas per word. + +Only the first cost applies to `initcode`, but only in the case of contract creation transactions. For the case of `CREATE`/`CREATE2` there is no such cost, and it is possible to programmatically generate variations of `initcode` in a relatively cheap manner. In the past it was possible to craft malicious `initcode` due to a vulnerability fixed in 2017 by geth 1.6.5. + +Furthermore, the lack of a limit has caused lengthy discussions for some EVM proposals, influencing the design, or even causing a delay or cancellation of a feature. + +We are motivated by three reasons: + +1. Ensuring `initcode` is fairly charged (most importantly cost is proportional to `initcode`'s length) to minimize the risks for the future. +2. To have a cost system which is extendable in the future (i.e. for proposals like [EIP-3670](./eip-3670.md)). +3. To simplify EVM engines by the explicit limits (code size, code offsets (`PC`), and jump offsets fit 16-bits). + +## Specification + +### Parameters + +| Constant | Value | +| -------------------- | ------------------- | +| `INITCODE_WORD_COST` | `2` | +| `MAX_INITCODE_SIZE` | `2 * MAX_CODE_SIZE` | + +Where `MAX_CODE_SIZE` is defined by [EIP-170](./eip-170.md) as `24576`. + +We define `initcode_cost(initcode)` to equal `INITCODE_WORD_COST * ceil(len(initcode) / 32)`. + +### Rules + +1. If length of transaction data (`initcode`) in a create transaction exceeds `MAX_INITCODE_SIZE`, transaction is invalid. (*Note that this is similar to transactions considered invalid for not meeting the intrinsic gas cost requirement.*) +2. For a create transaction, extend the transaction data cost formula to include `initcode_cost(initcode)`. (*Note that this is included in transaction intrinsic cost, i.e. transaction with not enough gas to cover initcode cost is invalid.*) +3. If length of `initcode` to `CREATE` or `CREATE2` instructions exceeds `MAX_INITCODE_SIZE`, instruction execution exceptionally aborts (as if it runs out of gas). +4. For the `CREATE` and `CREATE2` instructions charge an extra gas cost equaling to `initcode_cost(initcode)`. This cost is deducted before the calculation of the resulting contract address and the execution of `initcode`. (*Note that this means before or at the same time as the hashing cost is applied in `CREATE2`.*) + +## Rationale + +### Gas cost constant + +The value of `INITCODE_WORD_COST` is selected based on performance benchmarks of differing worst-cases per implementation. The baseline for the benchmarks is the performance of `KECCAK256` hashing in geth 1.10.9, which matches the 70 Mgas/s gas limit target on a 4.0 GHz x86_64 CPU. + +| EVM | version | MB/s | B/CPUcycle | CPUcycle/B | cost of 1 B | cost of 32 B | +| --------------- | ------- | ---- | ---- | ---- | ---- | ---- | +| geth/KECCAK256 | 1.10.9 | 357 | 1.8 | 0.6 | 0.2 | 6.0 | +| geth | 1.10.9 | 1091 | 5.5 | 0.2 | 0.1 | 2.0 | +| evmone/Baseline | 0.8.2 | 727 | 3.7 | 0.3 | 0.1 | 2.9 | +| evmone/Advanced | 0.8.2 | 155 | 0.8 | 1.3 | 0.4 | 13.8 | + +### Gas cost per word (32-byte chunk) + +We have chosen the cost of 2 gas per word based on Geth's implementation and comparing with `KECCAK256` performance. This means the per byte cost is `0.0625`. While fractional gas costs are not permitted in the EVM, we can approximate it by charging per-word. + +Moreover, calculating gas per word is compatible with the calculation of `CREATE2`'s *hashcost* of [EIP-1014](./eip-1014.md). Therefore, the same implementation may be used for `CREATE` and `CREATE2` with different cost constants: before activation `0` for `CREATE` and `6` for `CREATE2`, after activation `2` for `CREATE` and `6 + 2` for `CREATE2`. + +### Reason for size limit of initcode + +Estimating and creating worst case scenarios is easier with an upper bound in place, given one parameter for the search is greatly reduced. This allows for selecting a much more optimistic gas per byte. + +Should there be no upper bound, the cost would need to be higher accounting for unknown unknowns. Given most *initcode* (*TODO: state maximum initcode size resulting in deployment seen on mainnet here*) does not exceed the proposed limit, penalising contracts by overly conservative costs seems unnecessary. + +### Effect of size limit of initcode + +In most, if not all cases when a new contract is being created, the resulting runtime code is copied from the initcode itself. For the basic case the `2 * MAX_CODE_SIZE` limit allows `MAX_CODE_SIZE` for runtime code and another `MAX_CODE_SIZE` for contract constructor code. However, the limit may have practical implications for cases where multiple contracts are deployed in a single create transaction. + +### Initcode cost for create transaction + +The initcode cost for create transaction data (0.0625 gas per byte) is negligible compared to the transaction data cost (4 or 16 gas per byte). Despite that, we decided to include it in the specification for consistency, and more importantly for forward compatibility. + +### How to report initcode limit violation? + +We specified that initcode size limit violation for `CREATE`/`CREATE2` results in exceptional abort of the execution. This places it in the group of early out-of-gas checks, including: stack underflow, memory expansion, static call violation, initcode hashing cost, and initcode cost introduced by this EIP. They precede the later "light" checks: call depth and balance. The choice gives consistency to the order of checks and lowers implementation complexity (out-of-gas checks can be performed in any order). + +## Backwards Compatibility + +This EIP requires a "network upgrade", since it modifies consensus rules. + +Already deployed contracts should not be effected, but certain transactions (with `initcode` beyond the proposed limit) would still be includable in a block, but result in an exceptional abort. + +## Test Cases + +Tests should include the following cases: + +- Creation transaction with gas limit enough to cover initcode cost +- Creation transaction with gas limit enough to cover intrinsic cost except initcode cost +- `CREATE`/`CREATE2`/creation transaction with `len(initcode)` at `MAX_INITCODE_SIZE` +- `CREATE`/`CREATE2`/creation transaction with `len(initcode)` at `MAX_INITCODE_SIZE+1` + +## Security Considerations + +For client implementations, this EIP makes attacks based on jumpdest-analysis less problematic, so should increase the robustness of clients. + +For layer 2, this EIP introduces failure-modes where there previously were none. There *could* exist factory-contracts which deploy multi-level contract hierarchies, such that the code for multiple contracts are included in the initcode of the first contract. The author(s) of this EIP are not aware of any such contracts. + +Currently, on London, with `30M` gas limit, it would be possible to trigger jumpdest-analysis of a total `~1.3GB` of initcode. With this EIP, the cost for such an attack would increase by roughly `80M` gas. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-3978.md b/EIPS/eip-3978.md new file mode 100644 index 0000000..18d1671 --- /dev/null +++ b/EIPS/eip-3978.md @@ -0,0 +1,86 @@ +--- +eip: 3978 +title: Gas refunds on reverts +description: Reprice reverted SSTORE/CREATE/SELFDESTRUCT/LOGX operations gas via gas refund mechanism +author: Anton Bukov (@k06a), Mikhail Melnik (@ZumZoom) +discussions-to: https://ethereum-magicians.org/t/eip-3978-gas-refunds-on-reverts/7071/ +status: Stagnant +type: Standards Track +category: Core +created: 2021-09-16 +updated: 2022-02-14 +requires: 2929 +--- + +## Abstract + +For reverted state modification operations, keep access cost, but refund modification cost. + +## Motivation + +Reverting a transaction, or any of its sub-calls, drops any state modifications that happened inside. +But now, users are being charged for the dropped modifications as if they persisted. + +Since [EIP-3298](./eip-3298.md), the gas refund mechanism works for storage restores only inside the same transaction. But on revert, the gas refund is not increased; it is completely erased. +It can even be cheaper to transfer tokens back at the end of a transaction instead of reverting, to keep the existing gas refund. +This should be changed. + +- Reverted SSTORE deserves to be repriced to SLOAD gas (100 gas) +- Reverted LOG0, LOG1, LOG2, LOG3 and LOG4 deserve to be repriced to 100 gas +- Reverted CALL with value (`positive_value_cost` = 9,000 gas) deserves to be repriced to 100 gas +- Reverted CALL with value and account creation (`value_to_empty_account_cost` = 25,000 gas) deserves to be repriced to 100 gas +- Reverted CREATE and CREATE2 (32,000 gas) deserve to be repriced to 100 gas +- Reverted SELFDESTRUCT (5,000 or 25,000 gas) deserves to be repriced to 100 gas + +Moreover, it seems fair to charge CREATE and CREATE2 operations 32,000 fix price conditionally only if returned bytecode is not empty. + + +## Specification +For each callframe, track `revert_gas_refund`, initially 0. + +The set of operations that modify `revert_gas_refund` are: +- SSTORE +- LOG0, LOG1, LOG2, LOG3, LOG4 +- CALL +- CREATE, CREATE2 +- SELFDESTRUCT + +They increase `revert_gas_refund` as follows: +```javascript +call.revert_gas_refund += operation.gas - WARM_STORAGE_READ_COST +``` + +And in case of revert let's use this value instead of just erasing `gas_refund`: +```javascript +if (call.reverted) { + // existing behavior + tx.gas_refund -= call.gas_refund; + + // New behavior added to existing according to the EIP-3978 + tx.gas_refund += call.revert_gas_refund; +} +``` + +## Rationale + +Gas should reflect the cost of use. +The revert cost reflects the cost of access during execution, but not the cost of modification. + +## Backwards Compatibility + +No known backward incompatibilities. + +## Test Cases + +TBD + +## Reference Implementation + +TBD + +## Security Considerations + +TBD + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4.md b/EIPS/eip-4.md new file mode 100644 index 0000000..2973e03 --- /dev/null +++ b/EIPS/eip-4.md @@ -0,0 +1,66 @@ +--- +eip: 4 +title: EIP Classification +author: Joseph Chow (@ethers) +status: Final +type: Meta +created: 2015-11-17 +--- + +# Abstract + +This document describes a classification scheme for EIPs, adapted from [BIP 123](https://github.com/bitcoin/bips/blob/master/bip-0123.mediawiki). + +EIPs are classified by system layers with lower numbered layers involving more intricate interoperability requirements. + +The specification defines the layers and sets forth specific criteria for deciding to which layer a particular standards EIP belongs. + +# Motivation + +Ethereum is a system involving a number of different standards. Some standards are absolute requirements for interoperability while others can be considered optional, giving implementors a choice of whether to support them. + +In order to have a EIP process which more closely reflects the interoperability requirements, it is necessary to categorize EIPs accordingly. Lower layers present considerably greater challenges in getting standards accepted and deployed. + +# Specification + +Standards EIPs are placed in one of four layers: + +1. Consensus +2. Networking +3. API/RPC +4. Applications + +# 1. Consensus Layer + +The consensus layer defines cryptographic commitment structures. Its purpose is ensuring that anyone can locally evaluate whether a particular state and history is valid, providing settlement guarantees, and assuring eventual convergence. + +The consensus layer is not concerned with how messages are propagated on a network. + +Disagreements over the consensus layer can result in network partitioning, or forks, where different nodes might end up accepting different incompatible histories. We further subdivide consensus layer changes into soft forks and hard forks. + +## Soft Forks + +In a soft fork, some structures that were valid under the old rules are no longer valid under the new rules. Structures that were invalid under the old rules continue to be invalid under the new rules. + +## Hard Forks + +In a hard fork, structures that were invalid under the old rules become valid under the new rules. + +# 2. Networking Layer + +The networking layer specifies the Ethereum wire protocol (eth) and the Light Ethereum Subprotocol (les). RLPx is excluded and tracked in the [https://github.com/ethereum/devp2p devp2p repository]. + +Only a subset of subprotocols are required for basic node interoperability. Nodes can support further optional extensions. + +It is always possible to add new subprotocols without breaking compatibility with existing protocols, then gradually deprecate older protocols. In this manner, the entire network can be upgraded without serious risks of service disruption. + + +# 3. API/RPC Layer + +The API/RPC layer specifies higher level calls accessible to applications. Support for these EIPs is not required for basic network interoperability but might be expected by some client applications. + +There's room at this layer to allow for competing standards without breaking basic network interoperability. + +# 4. Applications Layer + +The applications layer specifies high level structures, abstractions, and conventions that allow different applications to support similar features and share data. diff --git a/EIPS/eip-4200.md b/EIPS/eip-4200.md new file mode 100644 index 0000000..19497a6 --- /dev/null +++ b/EIPS/eip-4200.md @@ -0,0 +1,260 @@ +--- +eip: 4200 +title: EOF - Static relative jumps +description: RJUMP, RJUMPI and RJUMPV instructions with a signed immediate encoding the jump destination +author: Alex Beregszaszi (@axic), Andrei Maiboroda (@gumb0), Paweł Bylica (@chfast) +discussions-to: https://ethereum-magicians.org/t/eip-3920-static-relative-jumps/7108 +status: Review +type: Standards Track +category: Core +created: 2021-07-16 +requires: 3540, 3670 +--- + +## Abstract + +Three new EVM jump instructions are introduced (`RJUMP`, `RJUMPI` and `RJUMPV`) which encode destinations as signed immediate values. These can be useful in the majority of (but not all) use cases and offer a cost reduction. + +## Motivation + +A recurring discussion topic is that EVM only has a mechanism for dynamic jumps. They provide a very flexible architecture with only 2 (!) instructions. This flexibility comes at a cost however: it makes analysis of code more complicated and it also (partially) resulted in the need to have the `JUMPDEST` marker. + +In a great many cases control flow is actually static and there is no need for any dynamic behaviour, though not every use case can be solved by static jumps. + +There are various ways to reduce the need for dynamic jumps, some examples: + +1. With native support for functions / subroutines +2. A "return to caller" instruction +3. A "switch-case" table with dynamic indexing + +This change does not attempt to solve these, but instead introduces a minimal feature set to allow compilers to decide which is the most adequate option for a given use case. It is expected that compilers will use `RJUMP`/`RJUMPI` almost exclusively, with the exception of returning to the caller continuing to use `JUMP`. + +This functionality does not preclude the EVM from introducing other forms of control flow later on. `RJUMP`/`RJUMPI` can efficiently co-exists with a higher-level declaration of functions, where static relative jumps should be used for intra-function control flow. + +The main benefit of these instruction is reduced gas cost (both at deploy and execution time) and better analysis properties. + +## Specification + +We introduce three new instructions on the same block number [EIP-3540](./eip-3540.md) is activated on: + +1. `RJUMP` (0x5c) - relative jump +2. `RJUMPI` (0x5d) - conditional relative jump +3. `RJUMPV` (0x5e) - relative jump via jump table + +If the code is legacy bytecode, all of these instructions result in an *exceptional halt*. (*Note: This means no change to behaviour.*) + +If the code is valid EOF1: + +1. `RJUMP relative_offset` sets the `PC` to `PC_post_instruction + relative_offset`. +2. `RJUMPI relative_offset` pops a value (`condition`) from the stack, and sets the `PC` to `PC_post_instruction + ((condition == 0) ? 0 : relative_offset)`. +3. `RJUMPV count relative_offset+` pops a value (`case`) from the stack, and sets the `PC` to `PC_post_instruction + ((case >= count) ? 0 : relative_offset[case])`. + +The immediate argument `relative_offset` is encoded as a 16-bit **signed** (two's-complement) big-endian value. Under `PC_post_instruction` we mean the `PC` position after the entire immediate value. + +The immediate encoding of `RJUMPV` is more special: the 8-bit `count` value determines the number of `relative_offset` values following. Validation algorithm of [EIP-3670](./eip-3670.md) is extended to verify that `count >= 1`. The encoding of `RJUMPV` must have at least one `relative_offset` and thus it will take at minimum 4 bytes. Furthermore, the `case >= count` condition falling through means that in many use cases one would place the *default* path following the `RJUMPV` instruction. An interesting feature is that `RJUMPV 1 relative_offset` is an inverted-`RJUMPI`, which can be used in many cases instead of `ISZERO RJUMPI relative_offset`. + +We also extend the validation algorithm of [EIP-3670](./eip-3670.md) to verify that each `RJUMP`/`RJUMPI`/`RJUMPV` has a `relative_offset` pointing to an instruction. This means it cannot point to an immediate data of `PUSHn`/`RJUMP`/`RJUMPI`/`RJUMPV`. It cannot point outside of code bounds. It is allowed to point to a `JUMPDEST`, but is not required to. + +Because the destinations are validated upfront, the cost of these instructions are less than their dynamic counterparts: `RJUMP` should cost 2, and `RJUMPI` and `RJUMPV` should cost 4. + +## Rationale + +### Relative addressing + +We chose relative addressing in order to support code which is relocatable. This also means a code snippet can be injected. A technique seen used prior to this EIP to achieve the same goal was to inject code like `PUSHn PC ADD JUMPI`. + +We do not see any significant downside to relative addressing and it allows us to also deprecate the `PC` instruction. + +### Immediate size + +The signed 16-bit immediate means that the largest jump distance possible is 32767. In the case the bytecode at `PC=0` starts with an `RJUMP`, it will be possible to jump as far as `PC=32770`. + +Given `MAX_CODE_SIZE = 24576` (in [EIP-170](./eip-170.md)) and `MAX_INITCODE_SIZE = 49152` (in [EIP-3860](./eip-3860.md)), we think the 16-bit immediate is large enough. + +A version with an 8-bit immediate would only allow moving `PC` backward by 125 or forward by 127 bytes. While that seems to be a good enough distance for many for-loops, it is likely not good enough for cross-function jumps, and since the 16-bit immediate is the same size as what a dynamic jump would take in such cases (3 bytes: `JUMP PUSH1 n`), we think having less instructions is better. + +Should there be a need to have immediate encodings of other size (such as 8-bits, 24-bits or 32-bits), it would be possible to introduce new opcodes, similarly to how multiple `PUSH` instructions exist. + +### `PUSHn JUMP` sequences + +If we chose absolute addressing, then `RJUMP` could be viewed similar to the sequence `PUSHn JUMP` (and `RJUMPI` similar to `PUSHn JUMPI`). In that case one could argue that instead of introducing a new instruction, such sequences should get a discount, because EVMs could optimise them. + +We think this is a bad direction to go: + +1. It further complicates the already complex rules of gas calculation. +2. And it either requires a consensus defined internal representation for EVM code, or forces EVM implementations to do optimisations on their own. + +Both of these are risky. Furthermore we think that EVM implementations should be free to chose what optimisations they apply, and the savings do not need to be passed down at all cost. + +Additionally it requires a potentially significant change to the current implementations which depend on a streaming one-by-one execution without a lookahead. + +### Relation to dynamic jumps + +The goal was not to completely replace the current control flow system of the EVM, but to augment it. There are many cases where dynamic jumps are useful, such as returning to the caller. + +It is possible to introduce a new mechanism for having a pre-defined table of valid jump destinations, and dynamically supplying the index within this table to accomplish some form of dynamic jumps. This is very useful for efficiently encoding a form of "switch-cases" statements. It could also be used for "return to caller" cases, however it is likely inefficient or awkward. + +### Lack of `JUMPDEST` + +`JUMPDEST` serves two purposes: + +1. To efficiently partition code -- this can be useful for pre-calculating total gas usage for a given *block* (i.e. instructions between `JUMPDEST`s), and for JIT/AOT translation. +2. To explicitly show valid locations (otherwise any non-data location would be valid). + +This functionality is not needed for static jumps, as the analysers can easily tell destinations from the static jump immediates during jumpdest-analysis. + +There are two benefits here: + +1. Not wasting a byte for a `JUMPDEST` also means a saving of 200 gas during deployment, for each jump destination. +2. Saving an extra 1 gas per jump during execution, given `JUMPDEST` itself cost 1 gas and is "executed" during jumping. + +### `RJUMPV` fallback case + +If no match is found (i.e. the *default* case) in the `RJUMPV` instruction execution will continue without branching. This allows for gaps in the arguments to be filled with `0`s, and a choice of implementation by the programmer. Alternate options would include exceptional aborts in case of no match. + +## Backwards Compatibility + +This change poses no risk to backwards compatibility, as it is introduced at the same time EIP-3540 is. The new instructions are not introduced for legacy bytecode (code which is not EOF formatted). + +## Test Cases + +### Validation + +#### Valid cases + +- `RJUMP`/`RJUMPI`/`RJUMPV` with `JUMPDEST` as target + - `relative_offset` is positive/negative/`0` +- `RJUMP`/`RJUMPI`/`RJUMPV` with instruction other than `JUMPDEST` as target + - `relative_offset` is positive/negative/`0` +- `RJUMPV` with various valid table sizes from 1 to 255 + +#### Invalid cases + +- `RJUMP`/`RJUMPI`/`RJUMPV` with truncated immediate +- `RJUMP`/`RJUMPI`/`RJUMPV` as a final instruction in code section +- `RJUMP`/`RJUMPI`/`RJUMPV` target outside of code section bounds +- `RJUMP`/`RJUMPI`/`RJUMPV` target push data +- `RJUMP`/`RJUMPI`/`RJUMPV` target another `RJUMP`/`RJUMPI`/`RJUMPV` immediate argument +- `RJUMPV` with table size 0 + +### Execution + +- `RJUMP`/`RJUMPI`/`RJUMPV` in legacy code aborts execution +- `RJUMP` + - `relative_offset` is positive/negative/`0` +- `RJUMPI` + - `relative_offset` is positive/negative/`0` + - `condition` equals `0` + - `condition` does not equal `0` +- `RJUMPV 1 relative_offset` + - `case` equals `0` + - `case` does not equal `0` +- `RJUMPV` with table containing positive, negative, `0` offsets + - `case` equals `0` + - `case` does not equal `0` + - `case` outside of table bounds (`case >= count`, fallback case) + - `case` > 255 + +## Reference Implementation + +```python +# The ranges below are as specified in the Yellow Paper. +# Note: range(s, e) excludes e, hence the +1 +valid_opcodes = [ + *range(0x00, 0x0b + 1), + *range(0x10, 0x1d + 1), + 0x20, + *range(0x30, 0x3f + 1), + *range(0x40, 0x48 + 1), + *range(0x50, 0x5e + 1), + *range(0x60, 0x6f + 1), + *range(0x70, 0x7f + 1), + *range(0x80, 0x8f + 1), + *range(0x90, 0x9f + 1), + *range(0xa0, 0xa4 + 1), + # Note: 0xfe is considered assigned. + *range(0xf0, 0xf5 + 1), 0xfa, 0xfd, 0xfe, 0xff +] + +# STOP, RETURN, REVERT, INVALID, SELFDESTRUCT +terminating_opcodes = [ 0x00, 0xf3, 0xfd, 0xfe, 0xff ] + +immediate_sizes = 256 * [0] +immediate_sizes[0x5c] = 2 # RJUMP +immediate_sizes[0x5d] = 2 # RJUMPI +for opcode in range(0x60, 0x7f + 1): # PUSH1..PUSH32 + immediate_sizes[opcode] = opcode - 0x60 + 1 + +# Raises ValidationException on invalid code +def validate_code(code: bytes): + # Note that EOF1 already asserts this with the code section requirements + assert len(code) > 0 + + opcode = 0 + pos = 0 + rjumpdests = set() + immediates = set() + while pos < len(code): + # Ensure the opcode is valid + opcode = code[pos] + pos += 1 + if not opcode in valid_opcodes: + raise ValidationException("undefined instruction") + + pc_post_instruction = pos + immediate_sizes[opcode] + + if opcode == 0x5c or opcode == 0x5d: + if pos + 2 > len(code): + raise ValidationException("truncated relative jump offset") + offset = int.from_bytes(code[pos:pos+2], byteorder = "big", signed = True) + + rjumpdest = pc_post_instruction + offset + if rjumpdest < 0 or rjumpdest >= len(code): + raise ValidationException("relative jump destination out of bounds") + + rjumpdests.add(rjumpdest) + elif opcode == 0x5e: + if pos + 1 > len(code): + raise ValidationException("truncated jump table") + jump_table_size = code[pos] + if jump_table_size == 0: + raise ValidationException("empty jump table") + + pc_post_instruction = pos + 1 + 2 * jump_table_size + if pc_post_instruction > len(code): + raise ValidationException("truncated jump table") + + for offset_pos in range(pos + 1, pc_post_instruction, 2): + offset = int.from_bytes(code[offset_pos:offset_pos+2], byteorder = "big", signed = True) + + rjumpdest = pc_post_instruction + offset + if rjumpdest < 0 or rjumpdest >= len(code): + raise ValidationException("relative jump destination out of bounds") + rjumpdests.add(rjumpdest) + + + # Save immediate value positions + immediates.update(range(pos, pc_post_instruction)) + # Skip immediates + pos = pc_post_instruction + + # Ensure last opcode's immediate doesn't go over code end + if pos != len(code): + raise ValidationException("truncated immediate") + + # opcode is the *last opcode* + if not opcode in terminating_opcodes: + raise ValidationException("no terminating instruction") + + # Ensure relative jump destinations don't target immediates + if not rjumpdests.isdisjoint(immediates): + raise ValidationException("relative jump destination targets immediate") +``` + +## Security Considerations + +TBA + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4337.md b/EIPS/eip-4337.md new file mode 100644 index 0000000..aed3954 --- /dev/null +++ b/EIPS/eip-4337.md @@ -0,0 +1,919 @@ +--- +eip: 4337 +title: Account Abstraction Using Alt Mempool +description: An account abstraction proposal which completely avoids consensus-layer protocol changes, instead relying on higher-layer infrastructure. +author: Vitalik Buterin (@vbuterin), Yoav Weiss (@yoavw), Kristof Gazso (@kristofgazso), Namra Patel (@namrapatel), Dror Tirosh (@drortirosh), Shahaf Nacson (@shahafn), Tjaden Hess (@tjade273) +discussions-to: https://ethereum-magicians.org/t/erc-4337-account-abstraction-via-entry-point-contract-specification/7160 +status: Draft +type: Standards Track +category: ERC +created: 2021-09-29 +--- + +## Abstract + +An account abstraction proposal which completely avoids the need for consensus-layer protocol changes. Instead of adding new protocol features and changing the bottom-layer transaction type, this proposal instead introduces a higher-layer pseudo-transaction object called a `UserOperation`. Users send `UserOperation` objects into a separate mempool. A special class of actor called bundlers (either block builders, or users that can send transactions to block builders through a bundle marketplace) package up a set of these objects into a transaction making a `handleOps` call to a special contract, and that transaction then gets included in a block. + +## Motivation + +See also `https://ethereum-magicians.org/t/implementing-account-abstraction-as-part-of-eth1-x/4020` and the links therein for historical work and motivation, and [EIP-2938](./eip-2938.md) for a consensus layer proposal for implementing the same goal. + +This proposal takes a different approach, avoiding any adjustments to the consensus layer. It seeks to achieve the following goals: + +* **Achieve the key goal of account abstraction**: allow users to use smart contract wallets containing arbitrary verification logic instead of EOAs as their primary account. Completely remove any need at all for users to also have EOAs (as status quo SC wallets and [EIP-3074](./eip-3074.md) both require) +* **Decentralization** + * Allow any bundler (think: block builder) to participate in the process of including account-abstracted user operations + * Work with all activity happening over a public mempool; users do not need to know the direct communication addresses (eg. IP, onion) of any specific actors + * Avoid trust assumptions on bundlers +* **Do not require any Ethereum consensus changes**: Ethereum consensus layer development is focusing on the merge and later on scalability-oriented features, and there may not be any opportunity for further protocol changes for a long time. Hence, to increase the chance of faster adoption, this proposal avoids Ethereum consensus changes. +* **Try to support other use cases** + * Privacy-preserving applications + * Atomic multi-operations (similar goal to [EIP-3074](./eip-3074.md)) + * Pay tx fees with [ERC-20](./eip-20.md) tokens, allow developers to pay fees for their users, and [EIP-3074](./eip-3074.md)-like **sponsored transaction** use cases more generally + * Support aggregated signature (e.g. BLS) + +## Specification + +### Definitions + +* **UserOperation** - a structure that describes a transaction to be sent on behalf of a user. To avoid confusion, it is not named "transaction". + * Like a transaction, it contains "sender", "to", "calldata", "maxFeePerGas", "maxPriorityFee", "signature", "nonce" + * unlike a transaction, it contains several other fields, described below + * also, the "nonce" and "signature" fields usage is not defined by the protocol, but by each account implementation +* **Sender** - the account contract sending a user operation. +* **EntryPoint** - a singleton contract to execute bundles of UserOperations. Bundlers/Clients whitelist the supported entrypoint. +* **Bundler** - a node (block builder) that bundles multiple UserOperations and create an EntryPoint.handleOps() transaction. Note that not all block-builders on the network are required to be bundlers +* **Aggregator** - a helper contract trusted by accounts to validate an aggregated signature. Bundlers/Clients whitelist the supported aggregators. + + +To avoid Ethereum consensus changes, we do not attempt to create new transaction types for account-abstracted transactions. Instead, users package up the action they want their account to take in an ABI-encoded struct called a `UserOperation`: + +| Field | Type | Description +| - | - | - | +| `sender` | `address` | The account making the operation | +| `nonce` | `uint256` | Anti-replay parameter; also used as the salt for first-time account creation | +| `initCode` | `bytes` | The initCode of the account (needed if and only if the account is not yet on-chain and needs to be created) | +| `callData` | `bytes` | The data to pass to the `sender` during the main execution call | +| `callGasLimit` | `uint256` | The amount of gas to allocate the main execution call | +| `verificationGasLimit` | `uint256` | The amount of gas to allocate for the verification step | +| `preVerificationGas` | `uint256` | The amount of gas to pay for to compensate the bundler for pre-verification execution and calldata | +| `maxFeePerGas` | `uint256` | Maximum fee per gas (similar to [EIP-1559](./eip-1559.md) `max_fee_per_gas`) | +| `maxPriorityFeePerGas` | `uint256` | Maximum priority fee per gas (similar to EIP-1559 `max_priority_fee_per_gas`) | +| `paymasterAndData` | `bytes` | Address of paymaster sponsoring the transaction, followed by extra data to send to the paymaster (empty for self-sponsored transaction) | +| `signature` | `bytes` | Data passed into the account along with the nonce during the verification step | + +Users send `UserOperation` objects to a dedicated user operation mempool. A specialized class of actors called **bundlers** (either block builders running special-purpose code, or users that can relay transactions to block builders eg. through a bundle marketplace such as Flashbots that can guarantee next-block-or-never inclusion) listen in on the user operation mempool, and create **bundle transactions**. A bundle transaction packages up multiple `UserOperation` objects into a single `handleOps` call to a pre-published global **entry point contract**. + +To prevent replay attacks (both cross-chain and multiple `EntryPoint` implementations), the `signature` should depend on `chainid` and the `EntryPoint` address. + +The core interface of the entry point contract is as follows: + +```solidity +function handleOps(UserOperation[] calldata ops, address payable beneficiary); + +function handleAggregatedOps( + UserOpsPerAggregator[] calldata opsPerAggregator, + address payable beneficiary +); + + +struct UserOpsPerAggregator { + UserOperation[] userOps; + IAggregator aggregator; + bytes signature; +} +function simulateValidation(UserOperation calldata userOp); + +error ValidationResult(ReturnInfo returnInfo, + StakeInfo senderInfo, StakeInfo factoryInfo, StakeInfo paymasterInfo); + +error ValidationResultWithAggregation(ReturnInfo returnInfo, + StakeInfo senderInfo, StakeInfo factoryInfo, StakeInfo paymasterInfo, + AggregatorStakeInfo aggregatorInfo); + +struct ReturnInfo { + uint256 preOpGas; + uint256 prefund; + bool sigFailed; + uint48 validAfter; + uint48 validUntil; + bytes paymasterContext; +} + +struct StakeInfo { + uint256 stake; + uint256 unstakeDelaySec; +} + +struct AggregatorStakeInfo { + address actualAggregator; + StakeInfo stakeInfo; +} +``` + +The core interface required for an account to have is: + +```solidity +interface IAccount { + function validateUserOp + (UserOperation calldata userOp, bytes32 userOpHash, uint256 missingAccountFunds) + external returns (uint256 validationData); +} +``` + +The `userOpHash` is a hash over the userOp (except signature), entryPoint and chainId. + +The account: + +* MUST validate the caller is a trusted EntryPoint +* If the account does not support signature aggregation, it MUST validate the signature is a valid signature of the `userOpHash`, and + SHOULD return SIG_VALIDATION_FAILED (and not revert) on signature mismatch. Any other error should revert. +* MUST pay the entryPoint (caller) at least the "missingAccountFunds" (which might be zero, in case current account's deposit is high enough) +* The account MAY pay more than this minimum, to cover future transactions (it can always issue `withdrawTo` to retrieve it) +* The return value MUST be packed of `authorizer`, `validUntil` and `validAfter` timestamps. + * authorizer - 0 for valid signature, 1 to mark signature failure. Otherwise, an address of an authorizer contract. This ERC defines "signature aggregator" as authorizer. + * `validUntil` is 6-byte timestamp value, or zero for "infinite". The UserOp is valid only up to this time. + * `validAfter` is 6-byte timestamp. The UserOp is valid only after this time. + +An account that works with aggregated signature, should return its signature aggregator address in the "sigAuthorizer" return value of validateUserOp. +It MAY ignore the signature field + +The core interface required by an aggregator is: + +```solidity +interface IAggregator { + + function validateUserOpSignature(UserOperation calldata userOp) + external view returns (bytes memory sigForUserOp); + + function aggregateSignatures(UserOperation[] calldata userOps) external view returns (bytes memory aggregatesSignature); + + function validateSignatures(UserOperation[] calldata userOps, bytes calldata signature) view external; +} +``` + +* If an account uses an aggregator (returns it from validateUserOp), then its address is returned by `simulateValidation()` reverting with `ValidationResultWithAggregator` instead of `ValidationResult` +* To accept the UserOp, the bundler must call **validateUserOpSignature()** to validate the userOp's signature. +* **aggregateSignatures()** must aggregate all UserOp signature into a single value. +* Note that the above methods are helper method for the bundler. The bundler MAY use a native library to perform the same validation and aggregation logic. +* **validateSignatures()** MUST validate the aggregated signature matches for all UserOperations in the array, and revert otherwise. + This method is called on-chain by `handleOps()` + +#### Using signature aggregators + +An account signifies it uses signature aggregation returning its address from `validateUserOp`. +During `simulateValidation`, this aggregator is returned (in the `ValidationResultWithAggregator`) + +The bundler should first accept the aggregator (validate its stake info and that it is not throttled/banned) +Then it MUST verify the userOp using `aggregator.validateUserOpSignature()` + +Signature aggregator SHOULD stake just like a paymaster, unless it is exempt due to not accessing global storage - see [reputation, throttling and banning section](#reputation-scoring-and-throttlingbanning-for-global-entities) for details. Bundlers MAY throttle down and ban aggregators in case they take too much +resources (or revert) when the above methods are called in view mode, or if the signature aggregation fails. + +### Required entry point contract functionality + +There are 2 separate entry point methods: `handleOps` and `handleAggregatedOps` + +* `handleOps` handle userOps of accounts that don't require any signature aggregator. +* `handleAggregatedOps` can handle a batch that contains userOps of multiple aggregators (and also requests without any aggregator) +* `handleAggregatedOps` performs the same logic below as `handleOps`, but it must transfer the correct aggregator to each userOp, and also must call `validateSignatures` on each aggregator after doing all the per-account validation. +The entry point's `handleOps` function must perform the following steps (we first describe the simpler non-paymaster case). It must make two loops, the **verification loop** and the **execution loop**. In the verification loop, the `handleOps` call must perform the following steps for each `UserOperation`: + +* **Create the account if it does not yet exist**, using the initcode provided in the `UserOperation`. If the account does not exist, _and_ the initcode is empty, or does not deploy a contract at the "sender" address, the call must fail. +* **Call `validateUserOp` on the account**, passing in the `UserOperation`, the required fee and aggregator (if there is one). The account should verify the operation's signature, and pay the fee if the account considers the operation valid. If any `validateUserOp` call fails, `handleOps` must skip execution of at least that operation, and may revert entirely. +* Validate the account's deposit in the entryPoint is high enough to cover the max possible cost (cover the already-done verification and max execution gas) + +In the execution loop, the `handleOps` call must perform the following steps for each `UserOperation`: + +* **Call the account with the `UserOperation`'s calldata**. It's up to the account to choose how to parse the calldata; an expected workflow is for the account to have an `execute` function that parses the remaining calldata as a series of one or more calls that the account should make. + +![](../assets/eip-4337/image1.png) + +Before accepting a `UserOperation`, bundlers should use an RPC method to locally call the `simulateValidation` function of the entry point, to verify that the signature is correct and the operation actually pays fees; see the [Simulation section below](#simulation) for details. +A node/bundler SHOULD drop (not add to the mempool) a `UserOperation` that fails the validation + +### Extension: paymasters + +We extend the entry point logic to support **paymasters** that can sponsor transactions for other users. This feature can be used to allow application developers to subsidize fees for their users, allow users to pay fees with [ERC-20](./eip-20.md) tokens and many other use cases. When the paymaster is not equal to the zero address, the entry point implements a different flow: + +![](../assets/eip-4337/image2.png) + +During the verification loop, in addition to calling `validateUserOp`, the `handleOps` execution also must check that the paymaster has enough ETH deposited with the entry point to pay for the operation, and then call `validatePaymasterUserOp` on the paymaster to verify that the paymaster is willing to pay for the operation. Note that in this case, the `validateUserOp` is called with a `missingAccountFunds` of 0 to reflect that the account's deposit is not used for payment for this userOp. + +If the paymaster's validatePaymasterUserOp returns a "context", then `handleOps` must call `postOp` on the paymaster after making the main execution call. It must guarantee the execution of `postOp`, by making the main execution inside an inner call context, and if the inner call context reverts attempting to call `postOp` again in an outer call context. + +Maliciously crafted paymasters _can_ DoS the system. To prevent this, we use a reputation system. paymaster must either limit its storage usage, or have a stake. see the [reputation, throttling and banning section](#reputation-scoring-and-throttlingbanning-for-global-entities) for details. + +The paymaster interface is as follows: + +```c++ + function validatePaymasterUserOp + (UserOperation calldata userOp, bytes32 userOpHash, uint256 maxCost) + external returns (bytes memory context, uint256 validationData); + +function postOp + (PostOpMode mode, bytes calldata context, uint256 actualGasCost) + external; + +enum PostOpMode { + opSucceeded, // user op succeeded + opReverted, // user op reverted. still has to pay for gas. + postOpReverted // user op succeeded, but caused postOp to revert +} +``` + + +```c++ +// add a paymaster stake (must be called by the paymaster) +function addStake(uint32 _unstakeDelaySec) external payable + +// unlock the stake (must wait unstakeDelay before can withdraw) +function unlockStake() external + +// withdraw the unlocked stake +function withdrawStake(address payable withdrawAddress) external +``` + +The paymaster must also have a deposit, which the entry point will charge UserOperation costs from. +The deposit (for paying gas fees) is separate from the stake (which is locked). + +The entry point must implement the following interface to allow paymasters (and optionally accounts) manage their deposit: + +```c++ +// return the deposit of an account +function balanceOf(address account) public view returns (uint256) + +// add to the deposit of the given account +function depositTo(address account) public payable + +// withdraw from the deposit +function withdrawTo(address payable withdrawAddress, uint256 withdrawAmount) external +``` + +### Client behavior upon receiving a UserOperation + +When a client receives a `UserOperation`, it must first run some basic sanity checks, namely that: + +* Either the `sender` is an existing contract, or the `initCode` is not empty (but not both) +* If `initCode` is not empty, parse its first 20 bytes as a factory address. Record whether the factory is staked, in case the later simulation indicates that it needs to be. If the factory accesses global state, it must be staked - see [reputation, throttling and banning section](#reputation-scoring-and-throttlingbanning-for-global-entities) for details. +* The `verificationGasLimit` is sufficiently low (`<= MAX_VERIFICATION_GAS`) and the `preVerificationGas` is sufficiently high (enough to pay for the calldata gas cost of serializing the `UserOperation` plus `PRE_VERIFICATION_OVERHEAD_GAS`) +* The `paymasterAndData` is either empty, or start with the **paymaster** address, which is a contract that (i) currently has nonempty code on chain, (ii) has a sufficient deposit to pay for the UserOperation, and (iii) is not currently banned. During simulation, the paymaster's stake is also checked, depending on its storage usage - see [reputation, throttling and banning section](#reputation-scoring-and-throttlingbanning-for-global-entities) for details. +* The callgas is at least the cost of a `CALL` with non-zero value. +* The `maxFeePerGas` and `maxPriorityFeePerGas` are above a configurable minimum value that the client is willing to accept. At the minimum, they are sufficiently high to be included with the current `block.basefee`. +* The sender doesn't have another `UserOperation` already present in the pool (or it replaces an existing entry with the same sender and nonce, with a higher `maxPriorityFeePerGas` and an equally increased `maxFeePerGas`). Only one `UserOperation` per sender may be included in a single batch. A sender is exempt from this rule and may have multiple `UserOperations` in the pool and in a batch if it is staked (see [reputation, throttling and banning section](#reputation-scoring-and-throttlingbanning-for-global-entities) below), but this exception is of limited use to normal accounts. + +If the `UserOperation` object passes these sanity checks, the client must next run the first op simulation, and if the simulation succeeds, the client must add the op to the pool. A second simulation must also happen during bundling to make sure the UserOperation is still valid. + +### Simulation + +#### Simulation Rationale + +In order to add a UserOperation into the mempool (and later to add it into a bundle) we need to "simulate" its validation to make sure it is valid, and that it is capable of paying for its own execution. +In addition, we need to verify that the same will hold true when executed on-chain. +For this purpose, a UserOperation is not allowed to access any information that might change between simulation and execution, such as current block time, number, hash etc. +In addition, a UserOperation is only allowed to access data related to this sender address: Multiple UserOperations should not access the same storage, so that it is impossible to invalidate a large number of UserOperations with a single state change. +There are 3 special contracts that interact with the account: the factory (initCode) that deploys the contract, the paymaster that can pay for the gas, and signature aggregator (described later) +Each of these contracts is also restricted in its storage access, to make sure UserOperation validations are isolated. + +#### Specification: + +To simulate a `UserOperation` validation, the client makes a view call to `simulateValidation(userop)` + +This method always revert with `ValidationResult` as successful response. +If the call reverts with other error, the client rejects this `userOp`. + +The simulated call performs the full validation, by calling: + +1. If `initCode` is present, create the account. +2. `account.validateUserOp`. +3. if specified a paymaster: `paymaster.validatePaymasterUserOp`. + +Either `validateUserOp` or `validatePaymasterUserOp` may return a "validAfter" and "validUntil" timestamps, which is the time-range that this UserOperation is valid on-chain. +The simulateValidation call returns this range. +A node MAY drop a UserOperation if it expires too soon (e.g. wouldn't make it to the next block) +If the `ValidationResult` includes `sigFail`, the client SHOULD drop the `UserOperation`. + +The operations differ in their opcode banning policy. +In order to distinguish between them, there is a call to the NUMBER opcode (`block.number`), used as a delimiter between the 3 functions. +While simulating `userOp` validation, the client should make sure that: + +1. May not invokes any **forbidden opcodes** +2. Must not use GAS opcode (unless followed immediately by one of { `CALL`, `DELEGATECALL`, `CALLCODE`, `STATICCALL` }.) +3. Storage access is limited as follows: + 1. self storage (of factory/paymaster, respectively) is allowed, but only if self entity is staked + 2. account storage access is allowed (see Storage access by Slots, below), + 3. in any case, may not use storage used by another UserOp `sender` in the same bundle (that is, paymaster and factory are not allowed as senders) +4. Limitation on "CALL" opcodes (`CALL`, `DELEGATECALL`, `CALLCODE`, `STATICCALL`): + 1. must not use value (except from account to the entrypoint) + 2. must not revert with out-of-gas + 3. destination address must have code (EXTCODESIZE>0) + 4. cannot call EntryPoint's methods, except `depositFor` (to avoid recursion) +5. `EXTCODEHASH` of every address accessed (by any opcode) does not change between first and second simulations of the op. +6. `EXTCODEHASH`, `EXTCODELENGTH`, `EXTCODECOPY` may not access address with no code. +7. If `op.initcode.length != 0` , allow only one `CREATE2` opcode call (in the first (deployment) block), otherwise forbid `CREATE2`. + +#### Storage associated with an address + +We define storage slots as "associated with an address" as all the slots that uniquely related on this address, and cannot be related with any other address. +In solidity, this includes all storage of the contract itself, and any storage of other contracts that use this contract address as a mapping key. + +An address `A` is associated with: + +1. Slots of contract `A` address itself. +2. Slot `A` on any other address. +3. Slots of type `keccak256(A || X) + n` on any other address. (to cover `mapping(address => value)`, which is usually used for balance in ERC-20 tokens). + `n` is an offset value up to 128, to allow accessing fields in the format `mapping(address => struct)` + + +#### Alternative Mempools + +The simulation rules above are strict and prevent the ability of paymasters and signature aggregators to grief the system. +However, there might be use-cases where specific paymasters (and signature aggregators) can be validated +(through manual auditing) and verified that they cannot cause any problem, while still require relaxing of the opcode rules. +A bundler cannot simply "whitelist" request from a specific paymaster: if that paymaster is not accepted by all +bundlers, then its support will be sporadic at best. +Instead, we introduce the term "alternate mempool". +UserOperations that use whitelisted paymasters (or signature aggregators) are put into a separate mempool. +Only bundlers that support this whitelist will use UserOperations from this mempool. +These UserOperations can be bundled together with UserOperations from the main mempool + +### Bundling + +During bundling, the client should: + +* Exclude UserOps that access any sender address of another UserOp in the same batch. +* Exclude UserOps that access any address created by another UserOp validation in the same batch (via a factory). +* For each paymaster used in the batch, keep track of the balance while adding UserOps. Ensure that it has sufficient deposit to pay for all the UserOps that use it. +* Sort UserOps by aggregator, to create the lists of UserOps-per-aggregator. +* For each aggregator, run the aggregator-specific code to create aggregated signature, and update the UserOps + +After creating the batch, before including the transaction in a block, the client should: + +* Run `eth_estimateGas` with maximum possible gas, to verify the entire `handleOps` batch transaction, and use the estimated gas for the actual transaction execution. +* If the call reverted, check the `FailedOp` event. A `FailedOp` during `handleOps` simulation is an unexpected event since it was supposed to be caught by the single-UserOperation simulation. Remove the failed op that caused the revert from the batch and drop from the mempool. + If the error is caused by a factory (error code is "AA1.") or paymaster (error code is "AA3."), then also drop from mempool all other UserOps of this entity. + Repeat until `eth_estimateGas` succeeds. + +In practice, restrictions (2) and (3) basically mean that the only external accesses that the account and the paymaster can make are reading code of other contracts if their code is guaranteed to be immutable (eg. this is useful for calling or delegatecalling to libraries). + +If any of the three conditions is violated, the client should reject the `op`. If both calls succeed (or, if `op.paymaster == ZERO_ADDRESS` and the first call succeeds)without violating the three conditions, the client should accept the op. On a bundler node, the storage keys accessed by both calls must be saved as the `accessList` of the `UserOperation` + +When a bundler includes a bundle in a block it must ensure that earlier transactions in the block don't make any UserOperation fail. It should either use access lists to prevent conflicts, or place the bundle as the first transaction in the block. + +#### Forbidden opcodes + +The forbidden opcodes are to be forbidden when `depth > 2` (i.e. when it is the factory, account, paymaster, or other contracts called by them that are being executed). They are: `GASPRICE`, `GASLIMIT`, `DIFFICULTY`, `TIMESTAMP`, `BASEFEE`, `BLOCKHASH`, `NUMBER`, `SELFBALANCE`, `BALANCE`, `ORIGIN`, `GAS`, `CREATE`, `COINBASE`, `SELFDESTRUCT`. They should only be forbidden during verification, not execution. These opcodes are forbidden because their outputs may differ between simulation and execution, so simulation of calls using these opcodes does not reliably tell what would happen if these calls are later done on-chain. + +Exceptions to the forbidden opcodes: + +1. A single `CREATE2` is allowed if `op.initcode.length != 0` and must result in the deployment of a previously-undeployed `UserOperation.sender`. +2. `GAS` is allowed if followed immediately by one of { `CALL`, `DELEGATECALL`, `CALLCODE`, `STATICCALL` }. + (that is, making calls is allowed, using `gasleft()` or `gas` opcode directly is forbidden) + +### Reputation scoring and throttling/banning for global entities + +#### Reputation Rationale. + +UserOperation's storage access rules prevent them from interfere with each other. +But "global" entities - paymasters, factories and aggregators are accessed by multiple UserOperations, and thus might invalidate multiple previously-valid UserOperations. + +To prevent abuse, we throttle down (or completely ban for a period of time) an entity that causes invalidation of large number of UserOperations in the mempool. +To prevent such entities from "sybil-attack", we require them to stake with the system, and thus make such DoS attack very expensive. +Note that this stake is never slashed, and can be withdrawn any time (after unstake delay) + +Unstaked entities are allowed, under the rules below. + +When staked, an entity is also allowed to use its own associated storage, in addition to sender's associated storage. + +The stake value is not enforced on-chain, but specifically by each node while simulating a transaction. +The stake is expected to be above MIN_STAKE_VALUE, and unstake delay above MIN_UNSTAKE_DELAY +The value of MIN_UNSTAKE_DELAY is 84600 (one day) +The value of MIN_STAKE_VALUE is determined per chain, and specified in the "bundler specification test suite" + +#### Un-staked entities + +Under the following special conditions, unstaked entities still can be used: + +* An entity that doesn't use any storage at all, or only the senders's storage (not the entity's storage - that does require a stake) +* If the UserOp doesn't create a new account (that is initCode is empty), then the entity may also use [storage associated with the sender](#storage-associated-with-an-address)) +* A paymaster that has a “postOp()” method (that is, validatePaymasterUserOp returns “context”) must be staked + +#### Specification. + +In the following specification, "entity" is either address that is explicitly referenced by the UserOperation: sender, factory, paymaster and aggregator. +Clients maintain two mappings with a value for staked entities: + +* `opsSeen: Map[Address, int]` +* `opsIncluded: Map[Address, int]` + +If an entity doesn't use storage at all, or only reference storage associated with the "sender" (see [Storage associated with an address](#storage-associated-with-an-address)), then it is considered "OK", without using the rules below. + +When the client learns of a new staked entity, it sets `opsSeen[paymaster] = 0` and `opsIncluded[paymaster] = 0` . + +The client sets `opsSeen[entity] +=1` each time it adds an op with that `entity` to the `UserOperationPool`, and the client sets `opsIncluded[entity] += 1` each time an op that was in the `UserOperationPool` is included on-chain. + +Every hour, the client sets `opsSeen[entity] -= opsSeen[entity] // 24` and `opsIncluded[entity] -= opsIncluded[entity] // 24` for all entities (so both values are 24-hour exponential moving averages). + +We define the **status** of an entity as follows: + +```python +OK, THROTTLED, BANNED = 0, 1, 2 + +def status(paymaster: Address, + opsSeen: Map[Address, int], + opsIncluded: Map[Address, int]): + if paymaster not in opsSeen: + return OK + min_expected_included = opsSeen[paymaster] // MIN_INCLUSION_RATE_DENOMINATOR + if min_expected_included <= opsIncluded[paymaster] + THROTTLING_SLACK: + return OK + elif min_expected_included <= opsIncluded[paymaster] + BAN_SLACK: + return THROTTLED + else: + return BANNED +``` + +Stated in simpler terms, we expect at least `1 / MIN_INCLUSION_RATE_DENOMINATOR` of all ops seen on the network to get included. If an entity falls too far behind this minimum, it gets **throttled** (meaning, the client does not accept ops from that paymaster if there is already an op with that entity, and an op only stays in the pool for 10 blocks), If the entity falls even further behind, it gets **banned**. Throttling and banning naturally decay over time because of the exponential-moving-average rule. + +**Non-bundling clients and bundlers should use different settings for the above params**: + +| Param | Client setting | Bundler setting | +| - | - | - | +| `MIN_INCLUSION_RATE_DENOMINATOR` | 100 | 10 | +| `THROTTLING_SLACK` | 10 | 10 | +| `BAN_SLACK` | 50 | 50 | + +To help make sense of these params, note that a malicious paymaster can at most cause the network (only the p2p network, not the blockchain) to process `BAN_SLACK * MIN_INCLUSION_RATE_DENOMINATOR / 24` non-paying ops per hour. + +## Rationale + +The main challenge with a purely smart contract wallet based account abstraction system is DoS safety: how can a block builder including an operation make sure that it will actually pay fees, without having to first execute the entire operation? Requiring the block builder to execute the entire operation opens a DoS attack vector, as an attacker could easily send many operations that pretend to pay a fee but then revert at the last moment after a long execution. Similarly, to prevent attackers from cheaply clogging the mempool, nodes in the P2P network need to check if an operation will pay a fee before they are willing to forward it. + +In this proposal, we expect accounts to have a `validateUserOp` method that takes as input a `UserOperation`, and verify the signature and pay the fee. This method is required to be almost-pure: it is only allowed to access the storage of the account itself, cannot use environment opcodes (eg. `TIMESTAMP`), and can only edit the storage of the account, and can also send out ETH (needed to pay the entry point). The method is gas-limited by the `verificationGasLimit` of the `UserOperation`; nodes can choose to reject operations whose `verificationGasLimit` is too high. These restrictions allow block builders and network nodes to simulate the verification step locally, and be confident that the result will match the result when the operation actually gets included into a block. + +The entry point-based approach allows for a clean separation between verification and execution, and keeps accounts' logic simple. The alternative would be to require accounts to follow a template where they first self-call to verify and then self-call to execute (so that the execution is sandboxed and cannot cause the fee payment to revert); template-based approaches were rejected due to being harder to implement, as existing code compilation and verification tooling is not designed around template verification. + +### Paymasters + +Paymasters facilitate transaction sponsorship, allowing third-party-designed mechanisms to pay for transactions. Many of these mechanisms _could_ be done by having the paymaster wrap a `UserOperation` with their own, but there are some important fundamental limitations to that approach: + +* No possibility for "passive" paymasters (eg. that accept fees in some ERC-20 token at an exchange rate pulled from an on-chain DEX) +* Paymasters run the risk of getting griefed, as users could send ops that appear to pay the paymaster but then change their behavior after a block + +The paymaster scheme allows a contract to passively pay on users' behalf under arbitrary conditions. It even allows ERC-20 token paymasters to secure a guarantee that they would only need to pay if the user pays them: the paymaster contract can check that there is sufficient approved ERC-20 balance in the `validatePaymasterUserOp` method, and then extract it with `transferFrom` in the `postOp` call; if the op itself transfers out or de-approves too much of the ERC-20s, the inner `postOp` will fail and revert the execution and the outer `postOp` can extract payment (note that because of storage access restrictions the ERC-20 would need to be a wrapper defined within the paymaster itself). + +### First-time account creation + +It is an important design goal of this proposal to replicate the key property of EOAs that users do not need to perform some custom action or rely on an existing user to create their wallet; they can simply generate an address locally and immediately start accepting funds. + +The wallet creation itself is done by a "factory" contract, with wallet-specific data. +The factory is expected to use CREATE2 (not CREATE) to create the wallet, so that the order of creation of wallets doesn't interfere with the generated addresses. +The `initCode` field (if non-zero length) is parsed as a 20-byte address, followed by "calldata" to pass to this address. +This method call is expected to create a wallet and return its address. +If the factory does use CREATE2 or some other deterministic method to create the wallet, it's expected to return the wallet address even if the wallet has already been created. This is to make it easier for clients to query the address without knowing if the wallet has already been deployed, by simulating a call to `entryPoint.getSenderAddress()`, which calls the factory under the hood. +When `initCode` is specified, if either the `sender` address points to an existing contract, or (after calling the initCode) the `sender` address still does not exist, +then the operation is aborted. +The `initCode` MUST NOT be called directly from the entryPoint, but from another address. +The contract created by this factory method should accept a call to `validateUserOp` to validate the UserOp's signature. +For security reasons, it is important that the generated contract address will depend on the initial signature. +This way, even if someone can create a wallet at that address, he can't set different credentials to control it. +The factory has to be staked if it accesses global storage - see [reputation, throttling and banning section](#reputation-scoring-and-throttlingbanning-for-global-entities) for details. + +NOTE: In order for the wallet to determine the "counterfactual" address of the wallet (prior its creation), +it should make a static call to the `entryPoint.getSenderAddress()` + +### Entry point upgrading + +Accounts are encouraged to be DELEGATECALL forwarding contracts for gas efficiency and to allow account upgradability. The account code is expected to hard-code the entry point into their code for gas efficiency. If a new entry point is introduced, whether to add new functionality, improve gas efficiency, or fix a critical security bug, users can self-call to replace their account's code address with a new code address containing code that points to a new entry point. During an upgrade process, it's expected that two mempools will run in parallel. + +### RPC methods (eth namespace) + +#### * eth_sendUserOperation + +eth_sendUserOperation submits a User Operation object to the User Operation pool of the client. The client MUST validate the UserOperation, and return a result accordingly. + +The result `SHOULD` be set to the **userOpHash** if and only if the request passed simulation and was accepted in the client's User Operation pool. If the validation, simulation, or User Operation pool inclusion fails, `result` `SHOULD NOT` be returned. Rather, the client `SHOULD` return the failure reason. + +##### Parameters: + +1. **UserOperation** a full user-operation struct. All fields MUST be set as hex values. empty `bytes` block (e.g. empty `initCode`) MUST be set to `"0x"` +2. **EntryPoint** the entrypoint address the request should be sent through. this MUST be one of the entry points returned by the `supportedEntryPoints` rpc call. + +##### Return value: + +* If the UserOperation is valid, the client MUST return the calculated **userOpHash** for it +* in case of failure, MUST return an `error` result object, with `code` and `message`. The error code and message SHOULD be set as follows: + * **code: -32602** - invalid UserOperation struct/fields + * **code: -32500** - transaction rejected by entryPoint's simulateValidation, during wallet creation or validation + * The `message` field MUST be set to the FailedOp's "`AAxx`" error message from the EntryPoint + * **code: -32501** - transaction rejected by paymaster's validatePaymasterUserOp + * The `message` field SHOULD be set to the revert message from the paymaster + * The `data` field MUST contain a `paymaster` value + * **code: -32502** - transaction rejected because of opcode validation + * **code: -32503** - UserOperation out of time-range: either wallet or paymaster returned a time-range, and it is already expired (or will expire soon) + * The `data` field SHOULD contain the `validUntil` and `validAfter` values + * The `data` field SHOULD contain a `paymaster` value, if this error was triggered by the paymaster + * **code: -32504** - transaction rejected because paymaster (or signature aggregator) is throttled/banned + * The `data` field SHOULD contain a `paymaster` or `aggregator` value, depending on the failed entity + * **code: -32505** - transaction rejected because paymaster (or signature aggregator) stake or unstake-delay is too low + * The `data` field SHOULD contain a `paymaster` or `aggregator` value, depending on the failed entity + * The `data` field SHOULD contain a `minimumStake` and `minimumUnstakeDelay` + * **code: -32506** - transaction rejected because wallet specified unsupported signature aggregator + * The `data` field SHOULD contain an `aggregator` value + * **code: -32507** - transaction rejected because of wallet signature check failed (or paymaster signature, if the paymaster uses its data as signature) + +##### Example: + +Request: + +```json= +{ + "jsonrpc": "2.0", + "id": 1, + "method": "eth_sendUserOperation", + "params": [ + { + sender, // address + nonce, // uint256 + initCode, // bytes + callData, // bytes + callGasLimit, // uint256 + verificationGasLimit, // uint256 + preVerificationGas, // uint256 + maxFeePerGas, // uint256 + maxPriorityFeePerGas, // uint256 + paymasterAndData, // bytes + signature // bytes + }, + entryPoint // address + ] +} + +``` + +Response: + +``` +{ + "jsonrpc": "2.0", + "id": 1, + "result": "0x1234...5678" +} +``` + +##### Example failure responses: + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "error": { + "message": "AA21 didn't pay prefund", + "code": -32500 + } +} +``` + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "error": { + "message": "paymaster stake too low", + "data": { + "paymaster": "0x123456789012345678901234567890123456790", + "minimumStake": "0xde0b6b3a7640000", + "minimumUnstakeDelay": "0x15180" + }, + "code": -32504 + } +} +``` + + +#### * eth_estimateUserOperationGas + +Estimate the gas values for a UserOperation. +Given UserOperation optionally without gas limits and gas prices, return the needed gas limits. +The signature field is ignored by the wallet, so that the operation will not require user's approval. +Still, it might require putting a "semi-valid" signature (e.g. a signature in the right length) + +**Parameters**: same as `eth_sendUserOperation` + gas limits (and prices) parameters are optional, but are used if specified. + `maxFeePerGas` and `maxPriorityFeePerGas` default to zero, so no payment is required by neither account nor paymaster. + +**Return Values:** + +* **preVerificationGas** gas overhead of this UserOperation +* **verificationGasLimit** actual gas used by the validation of this UserOperation +* **callGasLimit** value used by inner account execution + +##### Error Codes: + +Same as `eth_sendUserOperation` +This operation may also return an error if the inner call to the account contract reverts. + +#### * eth_getUserOperationByHash + +Return a UserOperation based on a hash (userOpHash) returned by `eth_sendUserOperation` + +**Parameters** + +* **hash** a userOpHash value returned by `eth_sendUserOperation` + +**Return value**: + +`null` in case the UserOperation is not yet included in a block, or a full UserOperation, with the addition of `entryPoint`, `blockNumber`, `blockHash` and `transactionHash` + +#### * eth_getUserOperationReceipt + +Return a UserOperation receipt based on a hash (userOpHash) returned by `eth_sendUserOperation` + +**Parameters** + +* **hash** a userOpHash value returned by `eth_sendUserOperation` + +**Return value**: + +`null` in case the UserOperation is not yet included in a block, or: + +* **userOpHash** the request hash +* **entryPoint** +* **sender** +* **nonce** +* **paymaster** the paymaster used for this userOp (or empty) +* **actualGasCost** - actual amount paid (by account or paymaster) for this UserOperation +* **actualGasUsed** - total gas used by this UserOperation (including preVerification, creation, validation and execution) +* **success** boolean - did this execution completed without revert +* **reason** in case of revert, this is the revert reason +* **logs** the logs generated by this UserOperation (not including logs of other UserOperations in the same bundle) +* **receipt** the TransactionReceipt object. + Note that the returned TransactionReceipt is for the entire bundle, not only for this UserOperation. + +#### * eth_supportedEntryPoints + +Returns an array of the entryPoint addresses supported by the client. The first element of the array `SHOULD` be the entryPoint addressed preferred by the client. + +```json= +# Request +{ + "jsonrpc": "2.0", + "id": 1, + "method": "eth_supportedEntryPoints", + "params": [] +} + +# Response +{ + "jsonrpc": "2.0", + "id": 1, + "result": [ + "0xcd01C8aa8995A59eB7B2627E69b40e0524B5ecf8", + "0x7A0A0d159218E6a2f407B99173A2b12A6DDfC2a6" + ] +} +``` + +#### * eth_chainId + +Returns [EIP-155](./eip-155.md) Chain ID. + +```json= +# Request +{ + "jsonrpc": "2.0", + "id": 1, + "method": "eth_chainId", + "params": [] +} + +# Response +{ + "jsonrpc": "2.0", + "id": 1, + "result": "0x1" +} +``` + +### RPC methods (debug Namespace) + +This api must only be available on testing mode and is required by the compatibility test suite. In production, any `debug_*` rpc calls should be blocked. + +#### * debug_bundler_clearState + +Clears the bundler mempool and reputation data of paymasters/accounts/factories/aggregators. + +```json= +# Request +{ + "jsonrpc": "2.0", + "id": 1, + "method": "debug_bundler_clearState", + "params": [] +} + +# Response +{ + "jsonrpc": "2.0", + "id": 1, + "result": "ok" +} +``` + +#### * debug_bundler_dumpMempool + +Dumps the current UserOperations mempool + +**Parameters:** + +* **EntryPoint** the entrypoint used by eth_sendUserOperation + +**Returns:** + +`array` - Array of UserOperations currently in the mempool. + +```json= +# Request +{ + "jsonrpc": "2.0", + "id": 1, + "method": "debug_bundler_dumpMempool", + "params": ["0x1306b01bC3e4AD202612D3843387e94737673F53"] +} + +# Response +{ + "jsonrpc": "2.0", + "id": 1, + "result": [ + { + sender, // address + nonce, // uint256 + initCode, // bytes + callData, // bytes + callGasLimit, // uint256 + verificationGasLimit, // uint256 + preVerificationGas, // uint256 + maxFeePerGas, // uint256 + maxPriorityFeePerGas, // uint256 + paymasterAndData, // bytes + signature // bytes + } + ] +} +``` + +#### * debug_bundler_sendBundleNow + +Forces the bundler to build and execute a bundle from the mempool as `handleOps()` transaction. + +Returns: `transactionHash` + +```json= +# Request +{ + "jsonrpc": "2.0", + "id": 1, + "method": "debug_bundler_sendBundleNow", + "params": [] +} + +# Response +{ + "jsonrpc": "2.0", + "id": 1, + "result": "0xdead9e43632ac70c46b4003434058b18db0ad809617bd29f3448d46ca9085576" +} +``` + +#### * debug_bundler_setBundlingMode + +Sets bundling mode. + +After setting mode to "manual", an explicit call to debug_bundler_sendBundleNow is required to send a bundle. + +##### parameters: + +`mode` - 'manual' | 'auto' + +```json= +# Request +{ + "jsonrpc": "2.0", + "id": 1, + "method": "debug_bundler_setBundlingMode", + "params": ["manual"] +} + +# Response +{ + "jsonrpc": "2.0", + "id": 1, + "result": "ok" +} +``` + +#### * debug_bundler_setReputation + +Sets reputation of given addresses. parameters: + +**Parameters:** + +* An array of reputation entries to add/replace, with the fields: + + * `address` - The address to set the reputation for. + * `opsSeen` - number of times a user operations with that entity was seen and added to the mempool + * `opsIncluded` - number of times a user operations that uses this entity was included on-chain + * `status` - (string) The status of the address in the bundler 'ok' | 'throttled' | 'banned'. + +* **EntryPoint** the entrypoint used by eth_sendUserOperation + +```json= +# Request +{ + "jsonrpc": "2.0", + "id": 1, + "method": "debug_bundler_setReputation", + "params": [ + [ + { + "address": "0x7A0A0d159218E6a2f407B99173A2b12A6DDfC2a6", + "opsSeen": 20, + "opsIncluded": 13 + } + ], + "0x1306b01bC3e4AD202612D3843387e94737673F53" + ] +} + +# Response +{ + "jsonrpc": "2.0", + "id": 1, + "result": "ok" +} +``` + + +#### * debug_bundler_dumpReputation + +Returns the reputation data of all observed addresses. +Returns an array of reputation objects, each with the fields described above in `debug_bundler_setReputation` with the + + +**Parameters:** + +* **EntryPoint** the entrypoint used by eth_sendUserOperation + +**Return value:** + +An array of reputation entries with the fields: + +* `address` - The address to set the reputation for. +* `opsSeen` - number of times a user operations with that entity was seen and added to the mempool +* `opsIncluded` - number of times a user operations that uses this entity was included on-chain +* `status` - (string) The status of the address in the bundler 'ok' | 'throttled' | 'banned'. + +```json= +# Request +{ + "jsonrpc": "2.0", + "id": 1, + "method": "debug_bundler_dumpReputation", + "params": ["0x1306b01bC3e4AD202612D3843387e94737673F53"] +} + +# Response +{ + "jsonrpc": "2.0", + "id": 1, + "result": [ + { "address": "0x7A0A0d159218E6a2f407B99173A2b12A6DDfC2a6", + "opsSeen": 20, + "opsIncluded": 19, + "status": "ok" + } + ] +} +``` + +## Backwards Compatibility + +This EIP does not change the consensus layer, so there are no backwards compatibility issues for Ethereum as a whole. Unfortunately it is not easily compatible with pre-[ERC-4337](./eip-4337.md) accounts, because those accounts do not have a `validateUserOp` function. If the account has a function for authorizing a trusted op submitter, then this could be fixed by creating an [ERC-4337](./eip-4337.md) compatible account that re-implements the verification logic as a wrapper and setting it to be the original account's trusted op submitter. + +## Reference Implementation + +See `https://github.com/eth-infinitism/account-abstraction/tree/main/contracts` + +## Security Considerations + +The entry point contract will need to be very heavily audited and formally verified, because it will serve as a central trust point for _all_ [ERC-4337](./eip-4337.md). In total, this architecture reduces auditing and formal verification load for the ecosystem, because the amount of work that individual _accounts_ have to do becomes much smaller (they need only verify the `validateUserOp` function and its "check signature, increment nonce and pay fees" logic) and check that other functions are `msg.sender == ENTRY_POINT` gated (perhaps also allowing `msg.sender == self`), but it is nevertheless the case that this is done precisely by concentrating security risk in the entry point contract that needs to be verified to be very robust. + +Verification would need to cover two primary claims (not including claims needed to protect paymasters, and claims needed to establish p2p-level DoS resistance): + +* **Safety against arbitrary hijacking**: The entry point only calls an account generically if `validateUserOp` to that specific account has passed (and with `op.calldata` equal to the generic call's calldata) +* **Safety against fee draining**: If the entry point calls `validateUserOp` and passes, it also must make the generic call with calldata equal to `op.calldata` + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4341.md b/EIPS/eip-4341.md new file mode 100644 index 0000000..6bc8c21 --- /dev/null +++ b/EIPS/eip-4341.md @@ -0,0 +1,124 @@ +--- +eip: 4341 +title: Ordered NFT Batch Standard +description: The ordering information of multiple NFTs is retained and managed +author: Simon Tian (@simontianx) +discussions-to: https://github.com/ethereum/EIPs/issues/3782 +status: Stagnant +type: Standards Track +category: ERC +created: 2021-10-01 +--- + +## Abstract +This standard introduces a smart contract interface that can represent a batch +of non-fungible tokens of which the ordering information shall be retained and +managed. Such information is particularly useful if `tokenId`s are encoded with +the sets of `unicodes` for logographic characters and emojis. As a result, NFTs +can be utilized as carriers of meanings. + +## Motivation +Non-fungible tokens are widely accepted as carriers of crypto-assets, hence in both +[ERC-721](./eip-721.md) and [ERC-1155](./eip-1155.md), the ordering information of +multiple NFTs is discarded. However, as proposed in [EIP-3754](./eip-3754.md), +non-fungible tokens are thought of as basic units on a blockchain and can carry +abstract meanings with unicoded `tokenId`s. Transferring such tokens is transmitting +an ordered sequence of unicodes, thus effectively transmitting phrases or meanings +on a blockchain. + +A **[logograph](https://en.wikipedia.org/wiki/Logogram)** is a written character +that represents a word or morpheme, examples include _hanzi_ in Mandarin, _kanji_ +in Japanese, _hanja_ in Korean, and etc. A [unicode](https://en.wikipedia.org/wiki/Unicode) +is an information technology standard for the consistent encoding, representation, and +handling of texts. + +It is natural to combine the two to create unicoded NFTs to represent logographic +characters. Since a rich amount of meanings can be transmitted in just a few +characters in such languages, it is technically practical and valuable to create +a standard for it. Emojis are similar with logographs and can be included as well. +For non-logographic languages such as English, although the same standard can be +applied, it is tedious to represent each letter with an NFT, hence the gain is +hardly justifiable. + +A motivating example is instead of sending the two Chinese characters of the +Great Wall `长城`, two NFTs with IDs `#38271` and `#22478` respectively can be +transferred in a batch. The two IDs are corresponding to the decimal unicode of +the two characters. The receiving end decodes the IDs and retrieves the original +characters. A key point is the ordering information matters in this scenario +since the tuples `(38271, 22478)` and `(22478, 38271)` can be decoded as +`长城` and `城长`, respectively, and both are legitimate words in the Chinese +language. This illustrates the key difference between this standard and [ERC-1155](./eip-1155.md). + +Besides, in the eastern Asian culture, characters are sometimes considered or +practically used as gifts in holidays such as Spring Feastival, etc. +`(24685, 21916, 21457, 36001)` `恭喜发财` can be used literally as a gift to +express the best wishes for financial prosperity. It is therefore cuturally +natural to transfer tokens to express meanings with this standard. + +Also in logographic language systems, ancient teachings are usually written in +concise ways such that a handful of characters can unfold a rich amount of +meanings. Modern people now get a reliable technical means to pass down their +words, poems and proverbs to the future generations by sending tokens. + +Other practical and interesting applications include Chinese chess, wedding +vows, family generation quotes and sayings, funeral commendation words, prayers, +anecdotes and etc. + +## Specification +``` +pragma solidity ^0.8.0; + +/** + @title EIP-4341 Multi Ordered NFT Standard + @dev See https://eips.ethereum.org/EIPS/eip-4341 + */ +interface ERC4341 /* is ERC165 */ { + event Transfer(address indexed from, address indexed to, uint256 id, uint256 amount); + + event TransferBatch(address indexed from, address indexed to, uint256[] ids, uint256[] amounts); + + event ApprovalForAll(address indexed owner, address indexed operator, bool approved); + + function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external; + + function safeBatchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data) external; + + function safePhraseTransferFrom(address from, address to, uint256[] calldata phrase, bytes calldata data) external; + + function balanceOf(address owner, uint256 id) external view returns (uint256); + + function balanceOfPhrase(address owner) external view returns (uint256); + + function balanceOfBatch(address[] calldata owners, uint256[] calldata ids) external view returns (uint256[] memory); + + function retrievePhrase(address owner, uint256 phraseId) external view returns (uint256[] memory); + + function setApprovalForAll(address operator, bool approved) external; + + function isApprovedForAll(address owner, address operator) external view returns (bool); +} +``` + +## Rationale +In [ERC-1155](./eip-1155.md) and [ERC-721](./eip-721.md), NFTs are used to represent +crypto-assets, and in this standard together with [EIP-3754](./eip-3754.md), NFTs +are equipped with utilities. In this standard, the ordering information of a batch +of NFTs is retained and managed through a construct `phrase`. + +### Phrase +A `phrase` is usually made of a handful of basic characters or an orderred sequence +of unicodes and is able to keep the ordering information in a batch of tokens. +Technically, it is stored in an array of unsigned integers, and is not supposed +to be disseminated. A phrase does not increase or decrease the amount of any NFT +in anyway. A phrase cannot be transferred, however, it can be retrieved and +decoded to restore the original sequence of unicodes. The phrase information +is kept in storage and hence additional storage than [ERC-1155](./eip-1155.md) is required. + +## Backwards Compatibility +[EIP-3754](./eip-3754.md) is the pre-requisite to this standard. + +## Reference Implementation +https://github.com/simontianx/ERC4341 + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4345.md b/EIPS/eip-4345.md new file mode 100644 index 0000000..34cb5bb --- /dev/null +++ b/EIPS/eip-4345.md @@ -0,0 +1,60 @@ +--- +eip: 4345 +title: Difficulty Bomb Delay to June 2022 +description: Delays the difficulty bomb to be noticeable in June 2022. +author: Tim Beiko (@timbeiko), James Hancock (@MadeOfTin), Thomas Jay Rush (@tjayrush) +discussions-to: https://ethereum-magicians.org/t/eip-4345-difficulty-bomb-delay-to-may-2022/7209 +status: Final +type: Standards Track +category: Core +created: 2021-10-05 +--- + +## Abstract +Starting with `FORK_BLOCK_NUMBER` the client will calculate the difficulty based on a fake block number suggesting to the client that the difficulty bomb is adjusting 10,700,000 blocks later than the actual block number. + +## Motivation +Targeting for The Merge to occur before June 2022. If it is not ready by then, the bomb can be delayed further. + +## Specification +#### Relax Difficulty with Fake Block Number +For the purposes of `calc_difficulty`, simply replace the use of `block.number`, as used in the exponential ice age component, with the formula: +```py + fake_block_number = max(0, block.number - 10_700_000) if block.number >= FORK_BLOCK_NUMBER else block.number +``` +## Rationale + +The following script predicts a ~0.1 second delay to block time by June 2022 and a ~0.5 second delay by July 2022. This gives reason to address because the effect will be seen, but not so much urgency we don't have space to work around if needed. + +```python +def predict_diff_bomb_effect(current_blknum, current_difficulty, block_adjustment, months): + ''' + Predicts the effect on block time (as a ratio) in a specified amount of months in the future. + Vars used for predictions: + current_blknum = 13423376 # Oct 15, 2021 + current_difficulty = 9545154427582720 + block adjustment = 10700000 + months = 7.5 # June 2022 + months = 8.5 # July 2022 + ''' + blocks_per_month = (86400 * 30) // 13.3 + future_blknum = current_blknum + blocks_per_month * months + diff_adjustment = 2 ** ((future_blknum - block_adjustment) // 100000 - 2) + diff_adjust_coeff = diff_adjustment / current_difficulty * 2048 + return diff_adjust_coeff + + +diff_adjust_coeff = predict_diff_bomb_effect(13423376,9545154427582720,10700000,7.5) +diff_adjust_coeff = predict_diff_bomb_effect(13423376,9545154427582720,10700000,8.5) +``` + +## Backwards Compatibility +No known backward compatibility issues. + +## Security Considerations +Misjudging the effects of the difficulty can mean longer blocktimes than anticipated until a hardfork is released. Wild shifts in difficulty can affect this number severely. Also, gradual changes in blocktimes due to longer-term adjustments in difficulty can affect the timing of difficulty bomb epochs. This affects the usability of the network but unlikely to have security ramifications. + +In this specific instance, it is possible that the network hashrate drops considerably before The Merge, which could accelerate the timeline by which the bomb is felt in block times. The offset value chosen aimed to take this into account. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4353.md b/EIPS/eip-4353.md new file mode 100644 index 0000000..b0b4a3e --- /dev/null +++ b/EIPS/eip-4353.md @@ -0,0 +1,230 @@ +--- +eip: 4353 +title: Interface for Staked Tokens in NFTs +description: This interface enables access to publicly viewable staking data of an NFT. +author: Rex Creed (@aug2uag), Dane Scarborough +discussions-to: https://ethereum-magicians.org/t/eip-4353-viewing-staked-tokens-in-nft/7234 +status: Stagnant +type: Standards Track +category: ERC +created: 2021-10-08 +requires: 165 +--- + +## Abstract +[EIP-721](./eip-721.md) tokens can be deposited or staked in NFTs for a variety of reasons including escrow, rewards, benefits, and others. There is currently no means of retrieving the number of tokens staked and/or bound to an NFT. This proposal outlines a standard that may be implemented by all wallets and marketplaces easily to correctly retrieve the staked token amount of an NFT. + +## Motivation +Without staked token data, the actual amount of staked tokens cannot be conveyed from token owners to other users, and cannot be displayed in wallets, marketplaces, or block explorers. The ability to identify and verify an exogenous value derived from the staking process may be critical to the aims of an NFT holder. + +## Specification +```solidity +// SPDX-License-Identifier: CC0-1.0 + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC4353 standard, as defined in the + * https://eips.ethereum.org/EIPS/eip-4353. + * + * Implementers can declare support of contract interfaces, which can then be + * queried by others. + * + * Note: The ERC-165 identifier for this interface is 0x3a3d855f. + * + */ +interface IERC721Staked { + + /** + * @dev Returns uint256 amount of on-chain tokens staked to the NFT. + * + * @dev Wallets and marketplaces would need to call this for displaying + * the amount of tokens staked and/or bound to the NFT. + */ + function stakedAmount(uint256 tokenId) external view returns (uint256); + +} +``` + +### Suggested flow: + +#### Constructor/deployment +* Creator - the owner of an NFT with its own rules for depositing tokens at and/or after the minting of a token. +* Token Amount - the current amount of on-chain [EIP-20](./eip-20.md) or derived tokens bound to an NFT from one or more deposits. +* Withdraw Mechanism - rules based approach for withdrawing staked tokens and making sure to update the balance of the staked tokens. + +### Staking at mint and locking tokens in NFT +The suggested and intended implementation of this standard is to stake tokens at the time of minting an NFT, and not implementing any outbound transfer of tokens outside of `burn`. Therefore, only to stake at minting and withdraw only at burning. + +#### NFT displayed in wallet or marketplace +A wallet or marketplace checks if an NFT has publicly staked tokens available for display - if so, call `stakedAmount(tokenId)` to get the current amount of tokens staked and/or bound to the NFT. + +The logical code looks something like this and inspired by William Entriken: + +```solidity +// contracts/Token.sol +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; + +/** + * @title Token + * @dev Very simple ERC721 example with stake interface example. + * Note this implementation enforces recommended procedure: + * 1) stake at mint + * 2) withdraw at burn + */ +contract ERC721Staked is ERC721URIStorage, Ownable { + /// @dev track original minter of tokenId + mapping (uint256 => address payable) private payees; + /// @dev map tokens to stored staked token value + mapping (uint256 => uint256) private tokenValue; + + /// @dev metadata + constructor() ERC721 ( + "Staked NFT", + "SNFT" + ){} + + /// @dev mints a new NFT + /// @param _to address that will own the minted NFT + /// @param _tokenId id the NFT + /// @param _uri metadata + function mint( + address payable _to, + uint256 _tokenId, + string calldata _uri + ) + external + payable + onlyOwner + { + _mint(_to, _tokenId); + _setTokenURI(_tokenId, _uri); + payees[_tokenId] = _to; + tokenValue[_tokenId] = msg.value; + } + + /// @dev staked interface + /// @param _tokenId id of the NFT + /// @return _value staked value + function stakedAmount( + uint256 _tokenId + ) external view returns (uint256 _value) { + _value = tokenValue[_tokenId]; + return _value; + } + + /// @dev removes NFT & transfers crypto to minter + /// @param _tokenId the NFT we want to remove + function burn( + uint256 _tokenId + ) + external + onlyOwner + { + super._burn(_tokenId); + payees[_tokenId].transfer(tokenValue[_tokenId]); + tokenValue[_tokenId] = 0; + } + +} +``` + +## Rationale +This standard is completely agnostic to how tokens are deposited or handled by the NFT. It is, therefore, the choice and responsibility of the author to encode and communicate the encoding of their tokenomics to purchasees of their token and/or to make their contracts viewable by purchasees. + +Although the intention of this standard is for tokens staked at mint and withdrawable only upon burn, the interface may be modified for dynamic withdrawing and depositing of tokens especially under DeFi application settings. In its current form, the contract logic may be the determining factor whether a deviation from the standard exists. + +## Backward Compatibility +TBD + +## Test Cases +```js +const { expect } = require("chai"); +const { ethers, waffle } = require("hardhat"); +const provider = waffle.provider; + +describe("StakedNFT", function () { + let _id = 1234567890; + let value = '1.5'; + let Token; + let Interface; + let owner; + let addr1; + let addr2; + + beforeEach(async function () { + Token = await ethers.getContractFactory("ERC721Staked"); + [owner, addr1, ...addr2] = await ethers.getSigners(); + Interface = await Token.deploy(); + }); + + describe("Staked NFT", function () { + it("Should set the right owner", async function () { + let mint = await Interface.mint( + addr1.address, _id, 'http://foobar') + expect(await Interface.ownerOf(_id)).to.equal(addr1.address); + }); + + it("Should not have staked balance without value", async function () { + let mint = await Interface.mint( + addr1.address, _id, 'http://foobar') + expect(await Interface.stakedAmount(_id)).to.equal( + ethers.utils.parseEther('0')); + }); + + it("Should set and return the staked amount", async function () { + let mint = await Interface.mint( + addr1.address, _id, 'http://foobar', + {value: ethers.utils.parseEther(value)}) + expect(await Interface.stakedAmount(_id)).to.equal( + ethers.utils.parseEther(value)); + }); + + it("Should decrease owner eth balance on mint (deposit)", async function () { + let balance1 = await provider.getBalance(owner.address); + let mint = await Interface.mint( + addr1.address, _id, 'http://foobar', + {value: ethers.utils.parseEther(value)}) + let balance2 = await provider.getBalance(owner.address); + let diff = parseFloat(ethers.utils.formatEther( + balance1.sub(balance2))).toFixed(1); + expect(diff === value); + }); + + it("Should add to payee's eth balance on burn (withdraw)", async function () { + let balance1 = await provider.getBalance(addr1.address); + let mint = await Interface.mint( + addr1.address, _id, 'http://foobar', + {value: ethers.utils.parseEther(value)}) + await Interface.burn(_id); + let balance2 = await provider.getBalance(addr1.address); + let diff = parseFloat(ethers.utils.formatEther( + balance2.sub(balance1))).toFixed(1); + expect(diff === value); + }); + + it("Should update balance after transfer", async function () { + let mint = await Interface.mint( + addr1.address, _id, 'http://foobar', + {value: ethers.utils.parseEther(value)}) + await Interface.burn(_id); + expect(await Interface.stakedAmount(_id)).to.equal( + ethers.utils.parseEther('0')); + }); + }); +}); +``` + +## Security Considerations +The purpose of this standard is to simply and publicly identify whether an NFT claims to have staked tokens. + +Staked claims will be unreliable without a locking mechanism enforced, for example, if staked tokens can only be transferred at burn. Otherwise, tokens may be deposited and/or withdrawn at any time via arbitrary methods. Also, contracts that may allow arbitrary transfers without updating the correct balance will result in potential issues. A strict rules-based approach should be taken with these edge cases in mind. + +A dedicated service may exist to verify the claims of a token by analyzing transactions on the explorer. In this manner, verification may be automated to ensure a token's claims are valid. The logical extension of this method may be to extend the interface and support flagging erroneous claims, all the while maintaining a simple goal of validating and verifying a staked amount exists to benefit the operator experience. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4361.md b/EIPS/eip-4361.md new file mode 100644 index 0000000..5b96c43 --- /dev/null +++ b/EIPS/eip-4361.md @@ -0,0 +1,326 @@ +--- +eip: 4361 +title: Sign-In with Ethereum +description: Off-chain authentication for Ethereum accounts to establish sessions. +author: Wayne Chang (@wyc), Gregory Rocco (@obstropolos), Brantly Millegan (@brantlymillegan), Nick Johnson (@Arachnid) +discussions-to: https://ethereum-magicians.org/t/eip-4361-sign-in-with-ethereum/7263 +status: Review +type: Standards Track +category: ERC +created: 2021-10-11 +requires: 55, 137, 155, 191, 1271, 1328 +--- + +## Abstract + +Sign-In with Ethereum describes how Ethereum accounts authenticate with off-chain services by signing a standard message format parameterized by scope, session details, and security mechanisms (e.g., a nonce). The goals of this specification are to provide a self-custodied alternative to centralized identity providers, improve interoperability across off-chain services for Ethereum-based authentication, and provide wallet vendors a consistent machine-readable message format to achieve improved user experiences and consent management. + +## Motivation + +When signing in to popular non-blockchain services today, users will typically use identity providers (IdPs) that are centralized entities with ultimate control over users' identifiers, for example, large internet companies and email providers. Incentives are often misaligned between these parties. Sign-In with Ethereum offers a new self-custodial option for users who wish to assume more control and responsibility over their own digital identity. + +Already, many services support workflows to authenticate Ethereum accounts using message signing, such as to establish a cookie-based web session which can manage privileged metadata about the authenticating address. This is an opportunity to standardize the sign-in workflow and improve interoperability across existing services, while also providing wallet vendors a reliable method to identify signing requests as Sign-In with Ethereum requests for improved UX. + +## Specification + +Sign-In with Ethereum works as follows: + +1. The wallet presents the user with a structured plaintext message or equivalent interface for signing with the [ERC-191](./eip-191.md) signed data format. Before signing, the `message` is prefixed with `\x19Ethereum Signed Message:\n` as defined in [ERC-191](./eip-191.md). +The `message` MUST incorporate an Ethereum `address`, `domain` requesting the signing, `version` of the message, a chain identifier `chain-id`, `uri` for scoping, `nonce` acceptable to the relying party, and `issued-at` timestamp. +2. The signature is then presented to the relying party, which checks the signature's validity and message content. +3. Additional fields, including `expiration-time`, `not-before`, `request-id`, `statement`, and `resources` MAY be incorporated as part of the sign-in process. +4. The relying party MAY further fetch data associated with the Ethereum address, such as from the Ethereum blockchain (e.g., ENS, account balances, [ERC-20](./eip-20.md)/[ERC-721](./eip-721.md)/[ERC-1155](./eip-1155.md) asset ownership), or other data sources that might or might not be permissioned. + +### Example message + +``` +service.invalid wants you to sign in with your Ethereum account: +0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 + +I accept the ServiceOrg Terms of Service: https://service.invalid/tos + +URI: https://service.invalid/login +Version: 1 +Chain ID: 1 +Nonce: 32891756 +Issued At: 2021-09-30T16:25:24Z +Resources: +- ipfs://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/ +- https://example.com/my-web2-claim.json +``` + +### Informal Message Template + +A Bash-like informal template of the full message is presented below for readability and ease of understanding. Field descriptions are provided in the following section. A full ABNF description is provided in the section thereafter. + +``` +${domain} wants you to sign in with your Ethereum account: +${address} + +${statement} + +URI: ${uri} +Version: ${version} +Chain ID: ${chain-id} +Nonce: ${nonce} +Issued At: ${issued-at} +Expiration Time: ${expiration-time} +Not Before: ${not-before} +Request ID: ${request-id} +Resources: +- ${resources[0]} +- ${resources[1]} +... +- ${resources[n]} +``` + +### Message Field Descriptions + +- `domain` is the RFC 3986 authority that is requesting the signing. +- `address` is the Ethereum address performing the signing conformant to capitalization encoded checksum specified in [ERC-55](./eip-55.md) where applicable. +- `statement` (optional) is a human-readable ASCII assertion that the user will sign, and it must not contain `'\n'` (the byte `0x0a`). +- `uri` is an RFC 3986 URI referring to the resource that is the subject of the signing (as in the _subject of a claim_). +- `version` is the current version of the `message`, which MUST be `1` for this specification. +- `chain-id` is the [EIP-155](./eip-155.md) Chain ID to which the session is bound, and the network where Contract Accounts MUST be resolved. +- `nonce` is a randomized token typically chosen by the relying party and used to prevent replay attacks, at least 8 alphanumeric characters. +- `issued-at` is the ISO 8601 datetime string of the current time. +- `expiration-time` (optional) is the ISO 8601 datetime string that, if present, indicates when the signed authentication message is no longer valid. +- `not-before` (optional) is the ISO 8601 datetime string that, if present, indicates when the signed authentication message will become valid. +- `request-id` (optional) is an system-specific identifier that may be used to uniquely refer to the sign-in request. +- `resources` (optional) is a list of information or references to information the user wishes to have resolved as part of authentication by the relying party. They are expressed as RFC 3986 URIs separated by `"\n- "` where `\n` is the byte `0x0a`. + +### ABNF Message Format + +The `message` MUST conform with the following Augmented Backus–Naur Form (ABNF, RFC 5234) expression (note that `%s` denotes case sensitivity for a string term, as per RFC 7405). + +```abnf +sign-in-with-ethereum = + domain %s" wants you to sign in with your Ethereum account:" LF + address LF + LF + [ statement LF ] + LF + %s"URI: " uri LF + %s"Version: " version LF + %s"Chain ID: " chain-id LF + %s"Nonce: " nonce LF + %s"Issued At: " issued-at + [ LF %s"Expiration Time: " expiration-time ] + [ LF %s"Not Before: " not-before ] + [ LF %s"Request ID: " request-id ] + [ LF %s"Resources:" + resources ] + +domain = authority + ; From RFC 3986: + ; authority = [ userinfo "@" ] host [ ":" port ] + ; See RFC 3986 for the fully contextualized + ; definition of "authority". + +address = "0x" 40*40HEXDIG + ; Must also conform to captilization + ; checksum encoding specified in EIP-55 + ; where applicable (EOAs). + +statement = *( reserved / unreserved / " " ) + ; See RFC 3986 for the definition + ; of "reserved" and "unreserved". + ; The purpose is to exclude LF (line break). + +uri = URI + ; See RFC 3986 for the definition of "URI". + +version = "1" + +chain-id = 1*DIGIT + ; See EIP-155 for valid CHAIN_IDs. + +nonce = 8*( ALPHA / DIGIT ) + ; See RFC 5234 for the definition + ; of "ALPHA" and "DIGIT". + +issued-at = date-time +expiration-time = date-time +not-before = date-time + ; See RFC 3339 (ISO 8601) for the + ; definition of "date-time". + +request-id = *pchar + ; See RFC 3986 for the definition of "pchar". + +resources = *( LF resource ) + +resource = "- " URI +``` + +#### Signing and Verifying with Ethereum Accounts + +- For Externally Owned Accounts (EOAs), the verification method specified in [ERC-191](./eip-191.md) MUST be used. + +- For Contract Accounts, + - The verification method specified in [ERC-1271](./eip-1271.md) SHOULD be used, and if it is not, the implementer MUST clearly define the verification method to attain security and interoperability for both wallets and relying parties. + - When performing [ERC-1271](./eip-1271.md) signature verification, the contract performing the verification MUST be resolved from the specified `chain-id`. + - Implementers SHOULD take into consideration that [ERC-1271](./eip-1271.md) implementations are not required to be pure functions, and can return different results for the same inputs depending on blockchain state. This can affect the security model and session validation rules. For example, a service with [ERC-1271](./eip-1271.md) signing enabled could rely on webhooks to receive notifications when state affecting the results is changed. When it receives a notification, it invalidates any matching sessions. + +### Resolving Ethereum Name Service (ENS) Data + +- The relying party or wallet MAY additionally perform resolution of ENS data, as this can improve the user experience by displaying human-friendly information that is related to the `address`. Resolvable ENS data include: + - The [primary ENS name](./eip-181.md). + - The ENS avatar. + - Any other resolvable resources specified in the ENS documentation. +- If resolution of ENS data is performed, implementers SHOULD take precautions to preserve user privacy and consent, as their `address` could be forwarded to third party services as part of the resolution process. + +### Relying Party Implementer Steps + +#### Verifying a signed `message` + +- The message MUST be checked for conformance to the ABNF above, checked against expected term values after parsing, and its signature MUST be verified. + +#### Creating sessions + +- Sessions MUST be bound to the `address` and not to further resolved resources that can change. + +#### Interpreting and resolving `resources` + +- The listed `resources` MUST be RFC 3986 URIs, but their interpretation is out of scope of this specification. +- Implementers SHOULD ensure that that URIs are human-friendly when expressed in plaintext form. + +### Wallet Implementer Steps + +#### Verifying `message` + +- The full `message` MUST be checked for conformance to the ABNF above. +- Wallet implementers SHOULD warn users if the substring `"wants you to sign in + with your Ethereum account"` appears anywhere in an [ERC-191](./eip-191.md) message signing + request unless the message fully conforms to the format defined in this specification. + +#### Verifying `domain` binding + +- Wallet implementers MUST prevent phishing attacks by matching on the `domain` term when processing a signing request. For example, when processing the message beginning with `"service.invalid wants you to sign in..."`, the wallet checks that the request actually originated from `service.invalid`. +- The domain SHOULD be read from a trusted data source such as the browser window or over WalletConnect ([ERC-1328](./eip-1328.md)) sessions for comparison against the signing message contents. + +#### Creating Sign-In with Ethereum interfaces + +- Wallet implementers MUST display to the user the following terms from the Sign-In with Ethereum signing request by default and prior to signing, if they are present: `domain`, `address`, `statement`, and `resources`. Other present terms MUST also be made available to the user prior to signing either by default or through an extended interface. +- Wallet implementers displaying a plaintext `message` to the user SHOULD require the user to scroll to the bottom of the text area prior to signing. +- Wallet implementers MAY construct a custom Sign-In With Ethereum user interface by parsing the ABNF terms into data elements for use in the interface. The display rules above still apply to custom interfaces. + +#### Supporting internationalization (i18n) + +- After successfully parsing the message into ABNF terms, translation MAY happen at the UX level per human language. + +## Rationale + +### Requirements + +Write a specification for how Sign-In with Ethereum should work. The specification should be simple and generally follow existing practices. Avoid feature bloat, particularly the inclusion of lesser-used projects who see getting into the specification as a means of gaining adoption. The core specification should be decentralized, open, non-proprietary, and have long-term viability. It should have no dependence on a centralized server except for the servers already being run by the application that the user is signing in to. The basic specification should include: Ethereum accounts used for authentication, ENS names for usernames (via reverse resolution), and data from the ENS name’s text records for additional profile information (e.g. avatar, social media handles, etc). + +Additional functional requirements: + +1. The user must be presented a human-understandable interface prior to signing, mostly free of machine-targeted artifacts such as JSON blobs, hex codes (aside from the Ethereum address), and baseXX-encoded strings. +2. The application server must be able to implement fully usable support for its end without forcing a change in the wallets. +3. There must be a simple and straightforward upgrade path for both applications and wallets already using Ethereum account-based signing for authentication. +4. There must be facilities and guidelines for adequate mitigation of Man-in-the-Middle (MITM) attacks, replay attacks, and malicious signing requests. + +### Design Goals + +1. Human-Friendly +2. Simple to Implement +3. Secure +4. Machine Readable +5. Extensible + +### Technical Decisions + +- Why [ERC-191](./eip-191.md) (Signed Data Standard) over [EIP-712](./eip-712.md) (Ethereum typed structured data hashing and signing) + - [ERC-191](./eip-191.md) is already broadly supported across wallets UX, while [EIP-712](./eip-712.md) support for friendly user display is pending. **(1, 2, 3, 4)** + - [ERC-191](./eip-191.md) is simple to implement using a pre-set prefix prior to signing, while [EIP-712](./eip-712.md) is more complex to implement requiring the further implementations of a bespoke Solidity-inspired type system, RLP-based encoding format, and custom keccak-based hashing scheme. **(2)** + - [ERC-191](./eip-191.md) produces more human-readable messages, while [EIP-712](./eip-712.md) creates signing outputs for machine consumption, with most wallets not displaying the payload to be signed in a manner friendly to humans. **(1)**![](../assets/eip-4361/signing.png) + + - [EIP-712](./eip-712.md) has the advantage of on-chain representation and on-chain verifiability, such as for their use in metatransactions, but this feature is not relevant for the specification's scope. **(2)** +- Why not use JWTs? Wallets don't support JWTs. The keccak hash function is not assigned by IANA for use as a JOSE algorithm. **(2, 3)** +- Why not use YAML or YAML with exceptions? YAML is loose compared to ABNF, which can readily express character set limiting, required ordering, and strict whitespacing. **(2, 3)** + +### Out of Scope + +The following concerns are out of scope for this version of the specification to define: + +- Additional authentication not based on Ethereum addresses. +- Authorization to server resources. +- Interpretation of the URIs in the `resources` term as claims or other resources. +- The specific mechanisms to ensure domain-binding. +- The specific mechanisms to generate nonces and evaluation of their appropriateness. +- Protocols for use without TLS connections. + +### Considerations for Forwards Compatibility + +The following items are considered for future support in either through an iteration of this specification or new work items using this specification as a dependency. + +- Possible support for Decentralized Identifiers and Verifiable Credentials. +- Possible cross-chain support. +- Possible SIOPv2 support. +- Possible future support for [EIP-712](./eip-712.md). +- Version interpretation rules, e.g., sign with minor revision greater than understood, but not greater major version. + +## Backwards Compatibility + +- Most wallet implementations already support [ERC-191](./eip-191.md), so this is used as a base pattern with additional features. +- Requirements were gathered from existing implementations of similar sign-in workflows, including statements to allow the user to accept a Terms of Service, nonces for replay protection, and inclusion of the Ethereum address itself in the message. + +## Reference Implementation + +A reference implementation is available [here](../assets/eip-4361/example.js). + +## Security Considerations + +### Identifier reuse + +- Towards perfect privacy, it would be ideal to use a new uncorrelated identifier (e.g., Ethereum address) per digital interaction, selectively disclosing the information required and no more. +- This concern is less relevant to certain user demographics who are likely to be early adopters of this specification, such as those who manage an Ethereum address and/or ENS names intentionally associated with their public presence. These users often prefer identifier reuse to maintain a single correlated identity across many services. +- This consideration will become increasingly important with mainstream adoption. There are several ways to move towards this model, such as using HD wallets, signed delegations, and zero-knowledge proofs. However, these approaches are out of scope for this specification and better suited for follow-on specifications. + +### Key management + +- Sign-In with Ethereum gives users control through their keys. This is additional responsibility that mainstream users may not be accustomed to accepting, and key management is a hard problem especially for individuals. For example, there is no "forgot password" button as centralized identity providers commonly implement. +- Early adopters of this specification are likely to be already adept at key management, so this consideration becomes more relevant with mainstream adoption. +- Certain wallets can use smart contracts and multisigs to provide an enhanced user experiences with respect to key usage and key recovery, and these can be supported via [ERC-1271](./eip-1271.md) signing. + +### Wallet and relying party combined security + +- Both the wallet and relying party have to implement this specification for improved security to the end user. Specifically, the wallet MUST confirm that the message is for the correct `domain` or provide the user means to do so manually (such as instructions to visually confirming the correct domain in a TLS-protected website prior to connecting via QR code or deeplink), otherwise the user is subject to phishing attacks. + +### Minimizing wallet and server interaction + +- In some implementions of wallet sign-in workflows, the server first sends parameters of the `message` to the wallet. Others generate the message for signing entirely in the client side (e.g., dapps). The latter approach without initial server interaction SHOULD be preferred when there is a user privacy advantage by minimizing wallet-server interaction. Often, the backend server first produces a `nonce` to prevent replay attacks, which it verifies after signing. Privacy-preserving alternatives are suggested in the next section on preventing replay attacks. +- Before the wallet presents the message signing request to the user, it MAY consult the server for the proper contents of the message to be signed, such as an acceptable `nonce` or requested set of `resources`. When communicating to the server, the wallet SHOULD take precautions to protect user privacy by mitigating user information revealed as much as possible. +- Prior to signing, the wallet MAY consult the user for preferences, such as the selection of one `address` out of many, or a preferred ENS name out of many. + +### Preventing replay attacks + +- A `nonce` SHOULD be selected per session initiation with enough entropy to prevent replay attacks, a man-in-the-middle attack in which an attacker is able to capture the user's signature and resend it to establish a new session for themselves. +- Implementers MAY consider using privacy-preserving yet widely-available `nonce` values, such as one derived from a recent Ethereum block hash or a recent Unix timestamp. + +### Verification of domain binding + +- Wallets MUST check that the `domain` matches the actual signing request source. +- This value SHOULD be checked against a trusted data source such as the browser window or over another protocol. + +### Channel security + +- For web-based applications, all communications SHOULD use HTTPS to prevent man-in-the-middle attacks on the message signing. +- When using protocols other than HTTPS, all communications SHOULD be protected with proper techniques to maintain confidentiality, data integrity, and sender/receiver authenticity. + +### Session invalidation + +There are several cases where an implementer SHOULD check for state changes as they relate to sessions. + +- If an [ERC-1271](./eip-1271.md) implementation or dependent data changes the signature computation, the server SHOULD invalidate sessions appropriately. +- If any resources specified in `resources` change, the server SHOULD invalidate sessions appropriately. However, the interpretation of `resources` is out of scope of this specification. + +### Maximum lengths for ABNF terms + +- While this specification does not contain normative requirements around maximum string lengths, implementers SHOULD choose maximum lengths for terms that strike a balance across the prevention of denial of service attacks, support for arbitrary use cases, and user readability. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4393.md b/EIPS/eip-4393.md new file mode 100644 index 0000000..5ecd4f1 --- /dev/null +++ b/EIPS/eip-4393.md @@ -0,0 +1,363 @@ +--- +eip: 4393 +title: Micropayments for NFTs and Multi Tokens +description: An interface for tip tokens that allows tipping to holders of NFTs and multi tokens +author: Jules Lai (@julesl23) +discussions-to: https://ethereum-magicians.org/t/eip-proposal-micropayments-standard-for-nfts-and-multi-tokens/7366 +status: Draft +type: Standards Track +category: ERC +created: 2021-10-24 +requires: 165, 721, 1155 +--- + +## Abstract + +This standard outlines a smart contract interface for tipping to non-fungible and multi tokens. Holders of the tokens are able to withdraw the tips as [EIP-20](./eip-20.md) rewards. + +For the purpose of this EIP, a micropayment is termed as a financial transaction that involves usually a small sum of money called "tips" that are sent to specific [EIP-721](./eip-721.md) NFTs and [EIP-1155](./eip-1155.md) multi tokens, as rewards to their holders. A holder (also referred to as controller) is used as a more generic term for owner, as NFTs may represent non-digital assets such as services. + +## Motivation + +A cheap way to send tips to any type of NFT or multi token. This can be achieved by gas optimising the tip token contract and sending the tips in batches using the `tipBatch` function from the interface. + +To make it easy to implement into dapps a tipping service to reward the NFT and multi token holders. Allows for fairer distribution of revenue back to NFT holders from the user community. + +To make the interface as minimal as possible in order to allow adoption into many different use cases. + +Some use cases include: + +- In game purchases and other virtual goods + +- Tipping messages, posts, music and video content + +- Donations/crowdfunding + +- Distribution of royalties + +- Pay per click advertising + +- Incentivising use of services + +- Reward cards and coupons + +These can all leverage the security, immediacy and transparency of blockchain. + +## Specification + +This standard proposal outlines a generalised way to allow tipping via implementation of an `ITipToken` interface. The interface is intentionally kept to a minimum in order to allow for maximum use cases. + +Smart contracts implementing this EIP standard MUST implement all of the functions in this EIP interface. MUST also emit the events specified in the interface so that a complete state of the tip token contract can be derived from the events emitted alone. + +Smart contracts implementing this EIP standard MUST implement the [EIP-165](./eip-165.md) supportsInterface function and MUST return the constant value true if 0xE47A7022 is passed through the interfaceID argument. Note that revert in this document MAY mean a require, throw (not recommended as depreciated) or revert solidity statement with or without error messages. + +Note that, nft (or NFT in caps) in the code and as mentioned in this document, MAY also refer to an EIP-1155 fungible token. + +```solidity +interface ITipToken { + /** + @dev This emits when the tip token implementation approves the address + of an NFT for tipping. + The holders of the 'nft' are approved to receive rewards. + When an NFT Transfer event emits, this also indicates that the approved + addresses for that NFT (if any) is reset to none. + Note: the ERC-165 identifier for this interface is 0x985A3267. + */ + event ApprovalForNFT( + address[] holders, + address indexed nft, + uint256 indexed id, + bool approved + ); + + /** + @dev This emits when a user has deposited an ERC-20 compatible token to + the tip token's contract address or to an external address. + This also indicates that the deposit has been exchanged for an + amount of tip tokens + */ + event Deposit( + address indexed user, + address indexed rewardToken, + uint256 amount, + uint256 tipTokenAmount + ); + + /** + @dev This emits when a holder withdraws an amount of ERC-20 compatible + reward. This reward comes from the tip token's contract address or from + an external address, depending on the tip token implementation + */ + event WithdrawReward( + address indexed holder, + address indexed rewardToken, + uint256 amount + ); + + /** + @dev This emits when the tip token constructor or initialize method is + executed. + Importantly the ERC-20 compatible token 'rewardToken_' to use as reward + to NFT holders is set at this time and remains the same throughout the + lifetime of the tip token contract. + The 'rewardToken_' and 'tipToken_' MAY be the same. + */ + event InitializeTipToken( + address indexed tipToken_, + address indexed rewardToken_, + address owner_ + ); + + /** + @dev This emits every time a user tips an NFT holder. + Also includes the reward token address and the reward token amount that + will be held pending until the holder withdraws the reward tokens. + */ + event Tip( + address indexed user, + address[] holder, + address indexed nft, + uint256 id, + uint256 amount, + address rewardToken, + uint256[] rewardTokenAmount + ); + + /** + @notice Enable or disable approval for tipping for a single NFT held + by a holder or a multi token shared by holders + @dev MUST revert if calling nft's supportsInterface does not return + true for either IERC721 or IERC1155. + MUST revert if any of the 'holders' is the zero address. + MUST revert if 'nft' has not approved the tip token contract address as operator. + MUST emit the 'ApprovalForNFT' event to reflect approval or not approval. + @param holders The holders of the NFT (NFT controllers) + @param nft The NFT contract address + @param id The NFT token id + @param approved True if the 'holder' is approved, false to revoke approval + */ + function setApprovalForNFT( + address[] calldata holders, + address nft, + uint256 id, + bool approved + ) external; + + /** + @notice Checks if 'holder' and 'nft' with token 'id' have been approved + by setApprovalForNFT + @dev This does not check that the holder of the NFT has changed. That is + left to the implementer to detect events for change of ownership and to + take appropriate action + @param holder The holder of the NFT (NFT controller) + @param nft The NFT contract address + @param id The NFT token id + @return True if 'holder' and 'nft' with token 'id' have previously been + approved by the tip token contract + */ + function isApprovalForNFT( + address holder, + address nft, + uint256 id + ) external returns (bool); + + /** + @notice Sends tip from msg.sender to holder of a single NFT or + to shared holders of a multi token + @dev If 'nft' has not been approved for tipping, MUST revert + MUST revert if 'nft' is zero address. + MUST burn the tip 'amount' to the 'holder' and send the reward to + an account pending for the holder(s). + If 'nft' is a multi token that has multiple holders then each holder + MUST receive tip amount in proportion of their balance of multi tokens + MUST emit the 'Tip' event to reflect the amounts that msg.sender tipped + to holder(s) of 'nft'. + @param nft The NFT contract address + @param id The NFT token id + @param amount Amount of tip tokens to send to the holder of the NFT + */ + function tip( + address nft, + uint256 id, + uint256 amount + ) external; + + /** + @notice Sends a batch of tips to holders of 'nfts' for gas efficiency + @dev If NFT has not been approved for tipping, revert + MUST revert if the input arguments lengths are not all the same + MUST revert if any of the user addresses are zero + MUST revert the whole batch if there are any errors + MUST emit the 'Tip' events so that the state of the amounts sent to + each holder and for which nft and from whom, can be reconstructed. + @param users User accounts to tip from + @param nfts The NFT contract addresses whose holders to tip to + @param ids The NFT token ids that uniquely identifies the 'nfts' + @param amounts Amount of tip tokens to send to the holders of the NFTs + */ + function tipBatch( + address[] calldata users, + address[] calldata nfts, + uint256[] calldata ids, + uint256[] calldata amounts + ) external; + + /** + @notice Deposit an ERC-20 compatible token in exchange for tip tokens + @dev The price of tip tokens can be different for each deposit as + the amount of reward token sent ultimately is a ratio of the + amount of tip tokens to tip over the user's tip tokens balance available + multiplied by the user's deposit balance. + The deposited tokens can be held in the tip tokens contract account or + in an external escrow. This will depend on the tip token implementation. + Each tip token contract MUST handle only one type of ERC-20 compatible + reward for deposits. + This token address SHOULD be passed in to the tip token constructor or + initialize method. SHOULD revert if ERC-20 reward for deposits is + zero address. + MUST emit the 'Deposit' event that shows the user, deposited token details + and amount of tip tokens minted in exchange + @param user The user account + @param amount Amount of ERC-20 token to deposit in exchange for tip tokens. + This deposit is to be used later as the reward token + */ + function deposit(address user, uint256 amount) external payable; + + /** + @notice An NFT holder can withdraw their tips as an ERC-20 compatible + reward at a time of their choosing + @dev MUST revert if not enough balance pending available to withdraw. + MUST send 'amount' to msg.sender account (the holder) + MUST reduce the balance of reward tokens pending by the 'amount' withdrawn. + MUST emit the 'WithdrawReward' event to show the holder who withdrew, the reward + token address and 'amount' + @param amount Amount of ERC-20 token to withdraw as a reward + */ + function withdrawReward(uint256 amount) external payable; + + /** + @notice MUST have identical behaviour to ERC-20 balanceOf and is the amount + of tip tokens held by 'user' + @param user The user account + @return The balance of tip tokens held by user + */ + function balanceOf(address user) external view returns (uint256); + + /** + @notice The balance of deposit available to become rewards when + user sends the tips + @param user The user account + @return The remaining balance of the ERC-20 compatible token deposited + */ + function balanceDepositOf(address user) external view returns (uint256); + + /** + @notice The amount of reward token owed to 'holder' + @dev The pending tokens can come from the tip token contract account + or from an external escrow, depending on tip token implementation + @param holder The holder of NFT(s) (NFT controller) + @return The amount of reward tokens owed to the holder from tipping + */ + function rewardPendingOf(address holder) external view returns (uint256); +} +``` + +### Tipping and rewards to holders + +A user first deposits a compatible EIP-20 to the tip token contract that is then held (less any agreed fee) in escrow, in exchange for tip tokens. These tip tokens can then be sent by the user to NFTs and multi tokens (that have been approved by the tip token contract for tipping) to be redeemed for the original EIP-20 deposits on withdrawal by the holders as rewards. + +### Tip Token transfer and value calculations + +Tip token values are exchanged with EIP-20 deposits and vice-versa. It is left to the tip token implementer to decide on the price of a tip token and hence how much tip to mint in exchange for the EIP-20 deposited. One possibility is to have fixed conversion rates per geographical region so that users from poorer countries are able to send the same number of tips as those from richer nations for the same level of appreciation for content/assets etc. Hence, not skewed by average wealth when it comes to analytics to discover what NFTs are actually popular, allowing creators to have a level playing field. + +Whenever a user sends a tip, an equivalent value of deposited EIP-20 MUST be transferred to a pending account for the NFT or multi token holder, and the tip tokens sent MUST be burnt. This equivalent value is calculated using a simple formula: + +_total user balance of EIP-20 deposit _ tip amount / total user balance of tip tokens\* + +Thus adding \*free\* tips to a user's balance of tips for example, simply dilutes the overall value of each tip for that user, as collectively they still refer to the same amount of EIP-20 deposited. + +Note if the tip token contract inherits from an EIP-20, tips can be transferred from one user to another directly. The deposit amount would be already in the tip token contract (or an external escrow account) so only tip token contract's internal mapping of user account to deposit balances needs to be updated. It is RECOMMENDED that the tip amount be burnt from user A and then minted back to user B in the amount that keeps user B's average EIP-20 deposited value per tip the same, so that the value of the tip does not fluctuate in the process of tipping. + +If not inheriting from EIP-20, then minting the tip tokens MUST emit `event Transfer(address indexed from, address indexed to, uint256 value)` where sender is the zero address for a mint and to is the zero address for a burn. The Transfer event MUST be the same signature as the Transfer function in the `IERC20` interface. + +### Royalty distribution + +EIP-1155 allows for shared holders of a token id. Imagine a scenario where an article represented by an NFT was written by multiple contributors. Here, each contributor is a holder and the fractional sharing percentage between them can be represented by the balance that each holds in the EIP-1155 token id. So for two holders A and B of EIP-1155 token 1, if holder A's balance is 25 and holder B's is 75 then any tip sent to token 1 would distribute 25% of the reward pending to holder A and the remaining 75% pending to holder B. + +Here is an example implementation of ITipToken contract data structures: + +```solidity + /// Mapping from NFT/multi token to token id to holder(s) + mapping(address => mapping(uint256 => address[])) private _tokenIdToHolders; + + /// Mapping from user to user's deposit balance + mapping(address => uint256) private _depositBalances; + + /// Mapping from holder to holder's reward pending amount + mapping(address => uint256) private _rewardsPending; +``` + +This copes with EIP-721 contracts that must have unique token ids and single holders (to be compliant with the standard), and EIP-1155 contracts that can have multiple token ids and multiple holders per instance. The `tip` function implementation would then access `_tokenIdToHolders` via indices NFT/multi token address and token id to distribute to holder's or holders' `_rewardsPending`. + +For scenarios where royalties are to be distributed to holders directly, then implementation of the `tip` method of `ITipToken` contract MAY send the royalty amount straight from the user's account to the holder of a single NFT or to the shared holders of a multi token, less an optional agreed fee. In this case, the tip token type is the reward token. + +### Caveats + +To keep the `ITipToken` interface simple and general purpose, each tip token contract MUST use one EIP-20 compatible deposit type at a time. If tipping is required to support many EIP-20 deposits then each tip token contract MUST be deployed separately per EIP-20 compatible type required. Thus, if tipping is required from both ETH and BTC wrapper EIP-20 deposits then the tip token contract is deployed twice. The tip token contract's constructor is REQUIRED to pass in the address of the EIP-20 token supported for the deposits for the particular tip token contract. Or in the case for upgradeable tip token contracts, an initialize method is REQUIRED to pass in the EIP-20 token address. + +This EIP does not provide details for where the EIP-20 reward deposits are held. It MUST be available at the time a holder withdraws the rewards that they are owed. A RECOMMENDED implementation would be to keep the deposits locked in the tip token contract address. By keeping a mapping structure that records the balances pending to holders then the +deposits can remain where they are when a user tips, and only transferred out to a holder's address when a holder withdraws it as their reward. + +This standard does not specify the type of EIP-20 compatible deposits allowed. Indeed, could be tip tokens themselves. But it is RECOMMENDED that balances of the deposits be checked after transfer to find out the exact amount deposited to keep internal accounting consistent. In case, for example, the EIP-20 contract takes fees and hence reduces the actual amount deposited. + +This standard does not specify any functionality for refunds for deposits nor for tip tokens sent, it is left to the implementor to add this to their smart contract(s). The reasoning for this is to keep the interface light and not to enforce upon implementors the need for refunds but to leave that as a choice. + +### Minimising Gas Costs + +By caching tips off-chain and then batching them up to call the `tipBatch` method of the ITipToken interface then essentially the cost of initialising transactions is paid once rather than once per tip. Plus, further gas savings can be made off-chain if multiple tips sent by the same user to the same NFT token are accumulated together and sent as one entry in the batch. + +Further savings can be made by grouping users together sending to the same NFT, so that checking the validity of the NFT and whether it is an EIP-721 or EIP-1155, is performed once for each group. + +Clever ways to minimise on-chain state updating of the deposit balances for each user and the reward balances of each holder, can help further to minimise the gas costs when sending in a batch if the batch is ordered beforehand. For example, can avoid the checks if the next NFT in the batch is the same. This left to the tip token contract implementer. Whatever optimisation is applied, it MUST still allow information of which account tipped which account and for what NFT to be reconstructed from the Tip and the TipBatch events emitted. + +## Rationale + +### Simplicity + +ITipToken interface uses a minimal number of functions, in order to keep its use as general purpose as possible, whilst there being enough to guide implementation that fulfils its purpose for micropayments to NFT holders. + +### Use of NFTs + +Each NFT is a unique non-fungible token digital asset stored on the blockchain that are uniquely identified by its address and token id. It's truth burnt using cryptographic hashing on a secure blockchain means that it serves as an anchor for linking with a unique digital asset, service or other contractual agreement. Such use cases may include (but only really limited by imagination and acceptance): + +- Digital art, collectibles, music, video, licenses and certificates, event tickets, ENS names, gaming items, objects in metaverses, proof of authenticity of physical items, service agreements etc. + +This mechanism allows consumers of the NFT a secure way to easily tip and reward the NFT holder. + +### New Business Models + +To take the music use case for example. Traditionally since the industry transitioned from audio distributed on physical medium such as CDs, to an online digital distribution model via streaming, the music industry has been controlled by oligopolies that served to help in the transition. They operate a fixed subscription model and from that they set the amount of royalty distribution to content creators; such as the singers, musicians etc. Using tip tokens represent an additional way for fans of music to reward the content creators. Each song or track is represented by an NFT and fans are able to tip the song (hence the NFT) that they like, and in turn the content creators of the NFT are able to receive the EIP-20 rewards that the tips were bought for. A fan led music industry with decentralisation and tokenisation is expected to bring new revenue, and bring fans and content creators closer together. + +Across the board in other industries a similar ethos can be applied where third party controllers move to a more facilitating role rather than a monetary controlling role that exists today. + +### Guaranteed audit trail + +As the Ethereum ecosystem continues to grow, many dapps are relying on traditional databases and explorer API services to retrieve and categorize data. This EIP standard guarantees that event logs emitted by the smart contract MUST provide enough data to create an accurate record of all current tip token and EIP-20 reward balances. A database or explorer can provide indexed and categorized searches of every tip token and reward sent to NFT holders from the events emitted by any tip token contract that implements this standard. Thus, the state of the tip token contract can be reconstructed from the events emitted alone. + +## Backwards Compatibility + +A tip token contract can be fully compatible with EIP-20 specification and inherit some functions such as transfer if the tokens are allowed to be sent directly to other users. Note that balanceOf has been adopted and MUST be the number of tips held by a user's address. If inheriting from, for example, OpenZeppelin's implementation of EIP-20 token then their contract is responsible for maintaining the balance of tip token. Therefore, tip token balanceOf function SHOULD simply directly call the parent (super) contract's balanceOf function. + +What hasn't been carried over to tip token standard, is the ability for a spender of other users' tips. For the moment, this standard does not foresee a need for this. + +This EIP does not stress a need for tip token secondary markets or other use cases where identifying the tip token type with names rather than addresses might be useful, so these functions were left out of the ITipToken interface and is the remit for implementers. + +## Security Considerations + +Though it is RECOMMENDED that users' deposits are kept locked in the tip token contract or external escrow account, and SHOULD NOT be used for anything but the rewards for holders, this cannot be enforced. This standard stipulates that the rewards MUST be available for when holders withdraw their rewards from the pool of deposits. + +Before any users can tip an NFT, the holder of the NFT has to give their approval for tipping from the tip token contract. This standard stipulates that holders of the NFTs receive the rewards. It SHOULD be clear in the tip token contract code that it does so, without obfuscation to where the rewards go. Any fee charges SHOULD be made obvious to users before acceptance of their deposit. There is a risk that rogue implementers may attempt to \*hijack\* potential tip income streams for their own purposes. But additionally the number and frequency of transactions of the tipping process should make this type of fraud quicker to be found out. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4396.md b/EIPS/eip-4396.md new file mode 100644 index 0000000..da534dd --- /dev/null +++ b/EIPS/eip-4396.md @@ -0,0 +1,158 @@ +--- +eip: 4396 +title: Time-Aware Base Fee Calculation +description: Accounts for block time in the base fee calculation to target a stable throughput by time instead of by block. +author: Ansgar Dietrichs (@adietrichs) +discussions-to: https://ethereum-magicians.org/t/eip-4396-time-aware-base-fee-calculation/7363 +status: Stagnant +type: Standards Track +category: Core +created: 2021-10-28 +--- + +## Abstract +This EIP proposes accounting for time between blocks in the base fee calculation to target a stable throughput by time, instead of by block. Aiming to minimize changes to the calculation, it only introduces a variable block gas target proportional to the block time. The EIP can, in principle, be applied to either a Proof-of-Work or a Proof-of-Stake chain, however the security implications for the Proof-of-Work case remain unexplored. + +## Motivation + +The current base fee calculation chooses the gas usage of a block as the signal to determine whether demand for block space is too low (indicating that the base fee should be lowered) or too high (indicating that the base fee should be increased). While simple, this choice of signal has drawbacks: it does not take the block time into account. Assuming a relatively constant demand, a proposer constructing a block after 20 seconds will have transactions available with twice the gas of a proposer constructing a block after 10 seconds. Using the same gas target for both is accordingly sub-optimal. In practice, there are several undesirable consequences of this flawed signal: + +### Base Fee Volatility Under Proof-of-Work + +Under Proof-of-Work (PoW), block times are stochastic, and for that reason there exists large block time variability. This variability contributes to the base fee volatility, where the base fee can be expected to oscillate around the equilibrium value even under perfectly stable demand. + +### Missed Slots + +Under Proof-of-Stake (PoS), block times are ideally uniform (always 12s), but missed slots lead to individual blocks with increased block time (24s, 36s, ...). Such missed slots will result in the next block being overfull, and with the current update rule, signal a fake demand spike and thus cause a small unwarranted base fee spike. + +More importantly, these missed slots directly reduce the overall throughput of the execution chain by the gas target of one block. While the next block can be expected to include the "delayed" transactions of the missed slot, the resulting base fee spike then results in some number of under-full blocks. In the end the block space of the missed slot is lost for the chain. + +This is particularly problematic because a Denial-of-Service (DoS) attack on block proposers can cause them to miss slots, and compromises the overall chain performance. + +### Throughput Degradation During Consensus Issues + +A more severe version of individual missed slots can be caused by consensus issues that prevent a significant portion of block proposers from continuing to create blocks. This can be due to block proposers forking off (and creating blocks on their own fork), being unable to keep up with the current chain head for another reason, or simply being unable to create valid blocks. + +In all these situations, average block times go up significantly, causing chain throughput to fall by the same fraction. While this effect is already present under PoW, the self-healing mechanism of difficulty adjustments is relatively quick to kick in and restore normal block times. On the other hand, under PoS the automatic self-healing mechanism can be extremely slow: potentially several months to return to normal with up to a third of slots missed, or several weeks if more than a third of slots are missed. + +For all these reasons, it would be desirable to target a stable throughput per time instead of per block, by taking block time into account during the base fee calculation. + +To maximize the chance of this EIP being included in the merge fork, the adjustments are kept to a minimum, with more involved changes discussed in the rationale section. + +## Specification +Using the pseudocode language of [EIP-1559](./eip-1559.md), the updated base fee calculation becomes: + +```python +... + +BASE_FEE_MAX_CHANGE_DENOMINATOR = 8 +BLOCK_TIME_TARGET = 12 +MAX_GAS_TARGET_PERCENT = 95 + +class World(ABC): + def validate_block(self, block: Block) -> None: + parent_gas_limit = self.parent(block).gas_limit + parent_block_time = self.parent(block).timestamp - self.parent(self.parent(block)).timestamp + parent_base_gas_target = parent_gas_limit // ELASTICITY_MULTIPLIER + parent_adjusted_gas_target = min(parent_base_gas_target * parent_block_time // BLOCK_TIME_TARGET, parent_gas_limit * MAX_GAS_TARGET_PERCENT // 100) + parent_base_fee_per_gas = self.parent(block).base_fee_per_gas + parent_gas_used = self.parent(block).gas_used + + ... + + if parent_gas_used == parent_adjusted_gas_target: + expected_base_fee_per_gas = parent_base_fee_per_gas + elif parent_gas_used > parent_adjusted_gas_target: + gas_used_delta = parent_gas_used - parent_adjusted_gas_target + base_fee_per_gas_delta = max(parent_base_fee_per_gas * gas_used_delta // parent_base_gas_target // BASE_FEE_MAX_CHANGE_DENOMINATOR, 1) + expected_base_fee_per_gas = parent_base_fee_per_gas + base_fee_per_gas_delta + else: + gas_used_delta = parent_adjusted_gas_target - parent_gas_used + base_fee_per_gas_delta = parent_base_fee_per_gas * gas_used_delta // parent_base_gas_target // BASE_FEE_MAX_CHANGE_DENOMINATOR + expected_base_fee_per_gas = parent_base_fee_per_gas - base_fee_per_gas_delta + + ... + + ... +``` + +## Rationale + +### Mechanism + +The proposed new base fee calculation only adjusts the block gas target by scaling it with the block time, capped at a maximum percent of the overall block gas limit: + +#### Current Base Fee Calculation + +![](../assets/eip-4396/old_formula.png) + +#### Proposed Base Fee Calculation + +![](../assets/eip-4396/new_formula.png) + +This new calculation thus targets a stable throughput per time instead of per block. + +### Limitations + +Under PoS, block time increases always come in multiples of full blocks (e.g. a single missed slot = 24s instead of 12s block time). Accounting for this already requires doubling the block gas target, even for a single missed slot. However, with the block elasticity currently set to 2, this target would be equal to the block gas limit. Having the new target equal to the block gas limit is less than ideal, and thus is reduced slightly, according to the `MAX_GAS_TARGET_PERCENT` parameter. The reason for the existence of this parameter is twofold: + +- Ensure that the signal remains meaningful: A target equal to or greater than the gas limit could never be reached, so the base fee would always be reduced after a missed slot. +- Ensure that the base fee can still react to genuine demand increases: During times of many offline block proposers (and thus many missed slots), genuine demand increases still need a way to eventually result in a base fee increase, to avoid a fallback to a first-price priority fee auction. + +However, this means that even a single missed slot cannot be fully compensated. Even worse, any second or further sequential missed slot cannot be compensated for at all, as the gas target is already at its max. This effect becomes more pronounced as the share of offline validators increases: + +![](../assets/eip-4396/degradation.png) + +As can be observed, while this EIP does indeed increase the robustness of the network throughput in cases of offline validators, it does so imperfectly. Furthermore, there is a tradeoff effected by the `MAX_GAS_TARGET_PERCENT` parameter, with a higher value resulting in a higher network robustness, but a more impaired base fee adjustment mechanism during times of frequent missed slots. + +### Possible Extensions + +These limitations directly result from the design goal of a minimal change, to maximize chances of being included in the merge. There are natural ways of extending the EIP design to more effectively handle offline validators, at the expense of somewhat more extensive changes: + +#### Persistent Multi-Slot Buffer + +To be able to compensate multiple consecutive missed slots, a gas buffer could be introduced, that would allow the gas beyond the block elasticity to be carried forward to future blocks. To avoid long-run buffer accumulation that would delay a return to normal operations once block proposers are back online, a cap on the buffer would be added. Even for a relatively small buffer cap, the throughput robustness is significantly improved: + +![](../assets/eip-4396/degradation_buffers.png) + +With an elasticity still at 2, there is no way of avoiding the eventual breakdown for more than 50% offline block proposers. + +The main implementation complexity for this approach comes from the introduction of the buffer as a new persistent field. To retain the ability for calculating base fees only based on headers, it would have to be added to the block header. + +#### Increased Block Elasticity + +In addition to the introduction of a buffer, increasing the block elasticity is another tool for increasing throughput robustness. The following diagram shows the effect of different elasticity levels, both in the presence and absence of a persistent buffer: + +![](../assets/eip-4396/degradation_elasticity.png) + +Again, a clear positive effect can be observed. + +The main additional complexity here would come from the increased peak load (networking, compute & disk access) of multiple sequential overfull blocks. Note though that PoS with its minimum block time of 12s significantly reduces worst case peak stress as compared to PoW. + +## Backwards Compatibility + +The EIP has minimal impact on backwards compatibility, only requiring updates to existing base fee calculation tooling. + +## Test Cases +tbd + +## Reference Implementation +tbd + +## Security Considerations + +### Timestamp Manipulation + +Under PoW, miners are in control over the timestamp field of their blocks. While there are some enforced limits to valid timestamps, implications regarding potential timestamp manipulation are nontrivial and remain unexplored for this EIP. + +Under PoS, each slot has a [fixed assigned timestamp](https://github.com/ethereum/consensus-specs/blob/v1.1.3/specs/merge/beacon-chain.md#process_execution_payload), rendering any timestamp manipulation by block proposers impossible. + +### Suppressing Base Fee Increases +As discussed in the rationale, a high value for `MAX_GAS_TARGET_PERCENT` during times of many offline block proposers results in a small remaining signal space for genuine demand increases that should result in base fee increases. This in turn decreases the cost for block proposers for suppresing these base fee increases, instead forcing the fallback to a first-price priority fee auction. + +While the arguments of incentive incompatibility for base fee suppression of the the base EIP-1559 case still apply here, with a decreasing cost of this individually irrational behavior the risk for overriding psychological factors becomes more significant. + +Even in such a case the system degradation would however be graceful, as it would only temporarily suspend the base fee burn. As soon as the missing block proposers would come back online, the system would return to its ordinary EIP-1559 equilibrium. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4399.md b/EIPS/eip-4399.md new file mode 100644 index 0000000..eb7d731 --- /dev/null +++ b/EIPS/eip-4399.md @@ -0,0 +1,155 @@ +--- +eip: 4399 +title: Supplant DIFFICULTY opcode with PREVRANDAO +description: Expose beacon chain randomness in the EVM by supplanting DIFFICULTY opcode semantics +author: Mikhail Kalinin (@mkalinin), Danny Ryan (@djrtwo) +discussions-to: https://ethereum-magicians.org/t/eip-4399-supplant-difficulty-opcode-with-random/7368 +status: Final +type: Standards Track +category: Core +created: 2021-10-30 +requires: 3675 +--- + +## Abstract + +This EIP supplants the semantics of the return value of existing `DIFFICULTY (0x44)` opcode and renames the opcode to `PREVRANDAO (0x44)`. + +The return value of the `DIFFICULTY (0x44)` instruction after this change is the output of the randomness beacon provided by the beacon chain. + + +## Motivation + +Applications may benefit from using the randomness accumulated by the beacon chain. Thus, randomness outputs produced by the beacon chain should be accessible in the EVM. + +At the point of `TRANSITION_BLOCK` of the Proof-of-Stake (PoS) upgrade described in [EIP-3675](./eip-3675.md), the `difficulty` block field **MUST** be `0` thereafter because there is no longer any Proof-of-Work (PoW) seal on the block. This means that the `DIFFICULTY (0x44)` instruction no longer has it's previous semantic meaning, nor a clear "correct" value to return. + +Given prior analysis on the usage of `DIFFICULTY`, the value returned by the instruction mixed with other values is a common pattern used by smart contracts to obtain randomness. The instruction with the same number as the `DIFFICULTY` opcode returning outputs of the beacon chain RANDAO implementation makes the upgrade to PoS backwards compatible for existing smart contracts obtaining randomness from the `DIFFICULTY` instruction. + +Additionally, changes proposed by this EIP allow for smart contracts to determine whether the upgrade to the PoS has already happened. This can be done by analyzing the return value of the `DIFFICULTY` instruction. A value greater than `2**64` indicates that the transaction is being executed in the PoS block. Decompilers and other similar tooling may also use this trick to discern the new semantics of the instruction if data of the block including the transaction in question is available. + + +## Specification + +### Definitions + +* **`TRANSITION_BLOCK`** The definition of this block can be found in the Definitions section of [EIP-3675](./eip-3675.md#definitions). + +### Block structure + +Beginning with `TRANSITION_BLOCK`, client software **MUST** set the value of the `mixHash`, i.e. the field with the number `13` (0-indexed) in a block header, to the latest RANDAO mix of the post beacon state of the previous block. + +### EVM + +Beginning with `TRANSITION_BLOCK`, the `DIFFICULTY (0x44)` instruction **MUST** return the value of the `mixHash` field. + +*Note*: The gas cost of the `DIFFICULTY (0x44)` opcode remains unchanged. + +### Renaming + +The `mixHash` field **SHOULD** further be renamed to `prevRandao`. + +The `DIFFICULTY (0x44)` opcode **SHOULD** further be renamed to `PREVRANDAO (0x44)`. + + +## Rationale + +### Including RANDAO output in the block header + +Including a RANDAO output in the block header provides a straightforward method of accessing it from inside of the EVM as block header data is already available in the EVM context. + +Additionally, this ensures that the execution layer can be fully executed with the block alone rather than requiring extra inputs from the PoS consensus layer. + +Mixing the randomness into a block header may contribute to uniqueness of the block hash in the case when values of other fields of the block header match the corresponding values of the header of another block. + +### Using `mixHash` field instead of `difficulty` + +The `mixHash` header field is used instead of `difficulty` to avoid a class of hidden forkchoice bugs after the PoS upgrade. + +Client software implementing pre-EIP-3675 logic heavily depends on the `difficulty` value as total difficulty computation is the basis of the PoW fork choice rule. Setting the `difficulty` field to `0` at the PoS upgrade aims to reduce the surface of bugs related to the total difficulty value growing after the upgrade. + +Additionally, any latent total difficulty computation after the PoS upgrade would become overflow prone if the randomness output supplanted the value of the `difficulty` field. + +### Reusing existing field instead of appending a new one + +The `mixHash` field is deprecated at the PoS upgrade and set to zero bytes array thereafter. Reusing an existing field as a place for the randomness output saves 32 bytes per block and effectively removes the deprecation of one of the fields induced by the upgrade. + +### Reusing the `DIFFICULTY` opcode instead of introducing a new one + +See the [Motivation](#motivation). + +### Renaming the field and the opcode + +The renaming should be done to make the field and the opcode names semantically sound. + +### Using `TRANSITION_BLOCK` rather than a block or slot number + +By utilizing `TRANSITION_BLOCK` to trigger the change in logic defined in this EIP rather than a block or slot number, this EIP is tightly coupled to the PoS upgrade defined by [EIP-3675](./eip-3675.md). + +By tightly coupling to the PoS upgrade, we ensure that there is no discontinuity for the usecase of this opcode for randomness -- the primary [motivation](#motivation) for re-using `DIFFICULTY` rather than creating a new opcode. + +### Using `2**64` threshold to determine PoS blocks + +The probability of RANDAO value to fall into the range between `0` and `2**64` and, thus, to be mixed with PoW difficulty values, is drastically low. Though, proposed threshold might seem to have insufficient distance from difficulty values on Ethereum Mainnet (they are currently around `2**54`), it requires a thousand times increase of the hashrate to make this threshold insecure. Such an increase is considered impossible to occur before the upcoming consensus upgrade. + + +## Backwards Compatibility + +This EIP introduces backward incompatible changes to the execution and validation of EVM state transitions. As written, this EIP utilizes `TRANSITION_BLOCK` and is thus tightly coupled with the PoS upgrade introduced in [EIP-3675](./eip-3675.md). If this EIP is to be adopted, it **MUST** be scheduled at the same time as EIP-3675. + +Additionally, the changes proposed might be backward incompatible for the following categories of applications: +* Applications that use the value returned by the `DIFFICULTY` opcode as the PoW `difficulty` parameter +* Applications with logic that depends on the `DIFFICULTY` opcode returning a relatively small number with respect to the full 256-bit size of the field. + +The first category is already affected by switching the consensus mechanism to PoS and no additional breaking changes are introduced by this specification. + +The second category is comprised of applications that use the return value of the `DIFFICULTY` opcode in operations that might cause either overflow or underflow errors. While it is theoretically possible to author an application where a change in the range of possible values this opcode may return could lead to a security vulnerability, the chances of that are negligible. + + +## Test Cases + +* In one of ancestors of `TRANSITION_BLOCK` deploy a contract that stores return value of `DIFFICULTY (0x44)` to the state +* Check that value returned by `DIFFICULTY (0x44)` in transaction executed within the parent of `TRANSITION_BLOCK` equals `difficulty` field value +* Check that value returned by `PREVRANDAO (0x44)` in transaction executed within `TRANSITION_BLOCK` equals `prevRandao` field value + + +## Security Considerations + +The `PREVRANDAO (0x44)` opcode in PoS Ethereum (based on the beacon chain RANDAO implementation) is a source of randomness with different properties to the randomness supplied by `BLOCKHASH (0x40)` or `DIFFICULTY (0x44)` opcodes in the PoW network. + +### Biasability + +The beacon chain RANDAO implementation gives every block proposer 1 bit of influence power per slot. Proposer may deliberately refuse to propose a block on the opportunity cost of proposer and transaction fees to prevent beacon chain randomness (a RANDAO mix) from being updated in a particular slot. + +An effect of proposer's influence power is limited in time and lasts until the first honest RANDAO reveal is made afterwards. This limitation does also exist in the case when proposers of `n` consecutive slots are colluding to get `n` bits of influence power. Simply speaking, one honest block proposal is enough to unbias the RANDAO even if it was biased during several slots in a row. + +Additionally, semantics of the `PREVRANDAO (0x44)` instruction gives proposers another way to gain 1 bit of influence power on applications. Biased proposer may censor a rolling the dice transaction to force it to be included into the next block, thus, force it to use a RANDAO mix that the proposer knows in advance. The opportunity cost in this case would be negligible. + +### Predictability + +Obviously, historical randomness provided by any decentralized oracle is 100% predictable. On the contrary, the randomness that is revealed in the future is predictable up to a limited extent. + +A list of inputs influencing future randomness on the beacon chain consists of but is not limited to the following items: +* **Accumulated randomness.** A RANDAO mix produced by the beacon chain in the last slot of epoch `N` is the main input to the function defining block proposers in each slot of epoch `N + MIN_SEED_LOOKAHEAD + 1`, i.e. it is the main factor defining future RANDAO revealers. +* **Number of active validators.** A number of active validators throughout an epoch is another input to the block proposer function. +* **Effective balance.** All else being equal, the lower the effective balance of a validator the lower the chance this validator has to be designated as a proposer in a slot. +* **Accidentally missed proposals.** Network conditions and other factors that are resulting in accidentally missed proposals is a source of highly qualitative entropy that impacts RANDAO mixes. Usual rate of missed proposals on the Mainnet is about `1%`. + +These inputs may be predictable and malleable on a short range of slots but the longer the attempted lookahead the more entropy is accumulated by the beacon chain. + +### Tips for application developers + +The following tips attempt to reduce predictability and biasability of randomness outputs returned by `PREVRANDAO (0x44)`: + +1. Make your applications rely on the future randomness with a reasonably high lookahead. For example, an application stops accepting bids at the end of epoch `K` and uses a RANDAO mix produced in slot `K + N + ε` to roll the dice, where `N` is a lookahead in epochs and `ε` is a few slots into epoch `N + 1`. +2. At least four epochs of lookahead results in the following outcome: + * A proposer set of epoch `N + 1` isn't known at the end of epoch `K` breaking a direct link between bidders and dice rollers + * A number of active validators is updated at the end of each epoch affecting a set of proposers of next epochs, thus, impacting a RANDAO mix used by the application to roll the dice + * Due to Mainnet statistics, there is about a `100%` chance for the network to accidentally miss a proposal during this period of time which reduces predictability of a RANDAO mix used to roll the dice. +3. Setting `ε` to a small number, e.g. 2 or 4 slots, gives a third party a little time to gain influence power on the future randomness that is being used to roll the dice. This amount of time is defined by `MIN_SEED_LOOKAHEAD` parameter and is about 6 minutes on the Mainnet. + +A reasonably high distance between bidding and rolling the dice attempts to leave low chance for bidders controlling a subset of validators to directly exploit their influence power. Ultimately, this chance depends on the type of the game and on a number of controlled validators. For instance, a chance of a single validator to affect a one-time game is negligible, and becomes bigger for multiple validators in a repeated game scenario. + + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4400.md b/EIPS/eip-4400.md new file mode 100644 index 0000000..500ff8f --- /dev/null +++ b/EIPS/eip-4400.md @@ -0,0 +1,113 @@ +--- +eip: 4400 +title: EIP-721 Consumable Extension +description: Interface extension for EIP-721 consumer role +author: Daniel Ivanov (@Daniel-K-Ivanov), George Spasov (@Perseverance) +discussions-to: https://ethereum-magicians.org/t/EIP-4400-EIP721consumer-extension/7371 +status: Final +type: Standards Track +category: ERC +created: 2021-10-30 +requires: 165, 721 +--- + +## Abstract + +This specification defines standard functions outlining a `consumer` role for instance(s) of [EIP-721](./eip-721.md). An implementation allows reading the current `consumer` for a given NFT (`tokenId`) along with a standardized event for when an `consumer` has changed. The proposal depends on and extends the existing [EIP-721](./eip-721.md). + +## Motivation + +Many [EIP-721](./eip-721.md) contracts introduce their own custom role that grants permissions for utilising/consuming a given NFT instance. The need for that role stems from the fact that other than owning the NFT instance, there are other actions that can be performed on an NFT. For example, various metaverses use `operator` / `contributor` roles for Land (EIP-721), so that owners of the land can authorise other addresses to deploy scenes to them (f.e. commissioning a service company to develop a scene). + +It is common for NFTs to have utility other than ownership. That being said, it requires a separate standardized consumer role, allowing compatibility with user interfaces and contracts, managing those contracts. + +Having a `consumer` role will enable protocols to integrate and build on top of dApps that issue EIP-721 tokens. One example is the creation of generic/universal NFT renting marketplaces. + +Example of kinds of contracts and applications that can benefit from this standard are: +- metaverses that have land and other types of digital assets in those metaverses (scene deployment on land, renting land / characters / clothes / passes to events etc.) +- NFT-based yield-farming. Adopting the standard enables the "staker" (owner of the NFT) to have access to the utility benefits even after transferring his NFT to the staking contract + +## Specification + +The keywords “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. + +Every contract compliant to the `EIP721Consumable` extension MUST implement the `IEIP721Consumable` interface. The **consumer extension** is OPTIONAL for EIP-721 contracts. + +```solidity +/// @title EIP-721 Consumer Role extension +/// Note: the EIP-165 identifier for this interface is 0x953c8dfa +interface IEIP721Consumable /* is EIP721 */ { + + /// @notice Emitted when `owner` changes the `consumer` of an NFT + /// The zero address for consumer indicates that there is no consumer address + /// When a Transfer event emits, this also indicates that the consumer address + /// for that NFT (if any) is set to none + event ConsumerChanged(address indexed owner, address indexed consumer, uint256 indexed tokenId); + + /// @notice Get the consumer address of an NFT + /// @dev The zero address indicates that there is no consumer + /// Throws if `_tokenId` is not a valid NFT + /// @param _tokenId The NFT to get the consumer address for + /// @return The consumer address for this NFT, or the zero address if there is none + function consumerOf(uint256 _tokenId) view external returns (address); + + /// @notice Change or reaffirm the consumer address for an NFT + /// @dev The zero address indicates there is no consumer address + /// Throws unless `msg.sender` is the current NFT owner, an authorised + /// operator of the current owner or approved address + /// Throws if `_tokenId` is not valid NFT + /// @param _consumer The new consumer of the NFT + function changeConsumer(address _consumer, uint256 _tokenId) external; +} +``` + +Every contract implementing the `EIP721Consumable` extension is free to define the permissions of a `consumer` (e.g. what are consumers allowed to do within their system) with only one exception - consumers MUST NOT be considered owners, authorised operators or approved addresses as per the EIP-721 specification. Thus, they MUST NOT be able to execute transfers & approvals. + +The `consumerOf(uint256 _tokenId)` function MAY be implemented as `pure` or `view`. + +The `changeConsumer(address _consumer, uint256 _tokenId)` function MAY be implemented as `public` or `external`. + +The `ConsumerChanged` event MUST be emitted when a consumer is changed. + +On every `transfer`, the consumer MUST be changed to a default address. It is RECOMMENDED for implementors to use `address(0)` as that default address. + +The `supportsInterface` method MUST return `true` when called with `0x953c8dfa`. + +## Rationale + +Key factors influencing the standard: + +- Keeping the number of functions in the interfaces to a minimum to prevent contract bloat +- Simplicity +- Gas Efficiency +- Not reusing or overloading other already existing roles (e.g. owners, operators, approved addresses) + +### Name + +The chosen name resonates with the purpose of its existence. Consumers can be considered entities that utilise the token instances, without necessarily having ownership rights to it. + +The other name for the role that was considered was `operator`, however it is already defined and used within the `EIP-721` standard. + +### Restriction on the Permissions + +There are numerous use-cases where a distinct role for NFTs is required that MUST NOT have owner permissions. A contract that implements the consumer role and grants ownership permissions to the consumer renders this standard pointless. + +## Backwards Compatibility + +This standard is compatible with current EIP-721 standards. There are no other standards that define a similar role for NFTs and the name (`consumer`) is not used by other EIP-721 related standards. + +## Test Cases + +Test cases are available in the reference implementation [here](../assets/eip-4400/test/erc721-consumable.ts). + +## Reference Implementation + +The reference implementation can be found [here](../assets/eip-4400/contracts/ERC721Consumable.sol). + +## Security Considerations + +Implementors of the `EIP721Consumable` standard must consider thoroughly the permissions they give to `consumers`. Even if they implement the standard correctly and do not allow transfer/burning of NFTs, they might still provide permissions to the `consumers` that they might not want to provide otherwise and should be restricted to `owners` only. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4430.md b/EIPS/eip-4430.md new file mode 100644 index 0000000..48dc234 --- /dev/null +++ b/EIPS/eip-4430.md @@ -0,0 +1,145 @@ +--- +eip: 4430 +title: Described Transactions +description: A technique for contracts to provide a human-readable description of a transaction's side-effects. +author: Richard Moore (@ricmoo), Nick Johnson (@arachnid) +discussions-to: https://ethereum-magicians.org/t/discussion-eip-4430-described-transactions/8762 +status: Stagnant +type: Standards Track +category: ERC +created: 2021-11-07 +--- + +## Abstract + +Use a contract method to provide *virtual functions* which can generate +a human-readable description at the same time as the machine-readable +bytecode, allowing the user to agree to the human-readable component +in a UI while the machine can execute the bytecode once accepted. + + +## Motivation + +When using an Ethereum Wallet (e.g. MetaMask, Clef, Hardware Wallets) +users must accept a transaction before it can be submitted (or the user +may decline). + +Due to the complexity of Ethereum transactions, wallets are very limited +in their ability to provide insight into the effects of a transaction +that the user is approving; outside special-cased support for common +transactions such as ERC20 transfers, this often amounts to asking the +user to sign an opaque blob of binary data. + +This EIP presents a method for dapp developers to enable a more comfortable +user experience by providing wallets with a means to generate a better +description about what the contract claims will happen. + +It does not address malicious contracts which wish to lie, it only addresses +honest contracts that want to make their user's life better. We believe +that this is a reasonable security model, as transaction descriptions can be +audited at the same time as contract code, allowing auditors and code +reviewers to check that transaction descriptions are accurate as part of +their review. + + +## Specification + +The **description** (a string) and the matching **execcode** (bytecode) +are generated simultaneously by evaluating the method on a contract: + +```solidity +function eipXXXDescribe(bytes inputs, bytes32 reserved) view returns (string description, bytes execcode) +``` + +The human-readable **description** can be shown in any client which supports +user interaction for approval, while the **execcode** is the data that +should be included in a transaction to the contract to perform that operation. + +The method must be executable in a static context, (i.e. any side effects, +such as logX, sstore, etc.), including through indirect calls may be ignored. + +During evaluation, the `ADDRESS` (i.e. `to`), `CALLER` (i.e. `from`), `VALUE`, +and `GASPRICE` must be the same as the values for the transaction being +described, so that the code generating the description can rely on them. + +When executing the bytecode, best efforts should be made to ensure `BLOCKHASH`, +`NUMBER`, `TIMESTAMP` and `DIFFICULTY` match the `"latest"` block. The +`COINBASE` should be the zero address. + +The method may revert, in which case the signing must be aborted. + + +## Rationale + +### Meta Description + +There have been many attempts to solve this problem, many of which attempt +to examine the encoded transaction data or message data directly. + +In many cases, the information that would be necessary for a meaningful +description is not present in the final encoded transaction data or message +data. + +Instead this EIP uses an indirect description of the data. + +For example, the `commit(bytes32)` method of ENS places a commitment +**hash** on-chain. The hash contains the **blinded** name and address; +since the name is blinded, the encoded data (i.e. the hash) no longer +contains the original values and is insufficient to access the necessary +values to be included in a description. + +By instead describing the commitment indirectly (with the original information +intact: NAME, ADDRESS and SECRET) a meaningful description can be computed +(e.g. "commit to NAME for ADDRESS (with SECRET)") and the matching data can +be computed (i.e. `commit(hash(name, owner, secret))`). + +This technique of blinded data will become much more popular with L2 +solutions, which use blinding not necessarily for privacy, but for +compression. + +### Entangling the Contract Address + +To prevent signed data being used across contracts, the contract address +is entanlged into both the transaction implicitly via the `to` field. + + +### Alternatives + +- NatSpec and company are a class of more complex languages that attempt to describe the encoded data directly. Because of the language complexity they often end up being quite large requiring entire runtime environments with ample processing power and memory, as well as additional sandboxing to reduce security concerns. One goal of this is to reduce the complexity to something that could execute on hardware wallets and other simple wallets. These also describe the data directly, which in many cases (such as blinded data), cannot adequately describe the data at all + +- Custom Languages; due to the complexity of Ethereum transactions, any language used would require a lot of expressiveness and re-inventing the wheel. The EVM already exists (it may not be ideal), but it is there and can handle everything necessary. + +- Format Strings (e.g. Trustless Signing UI Protocol; format strings can only operate on the class of regular languages, which in many cases is insufficient to describe an Ethereum transaction. This was an issue quite often during early attempts at solving this problem. + +- The signTypedData [EIP-712](./eip-712.md) has many parallels to what this EIP aims to solve + + +## Backwards Compatibility + +This does not affect backwards compatibility. + + +## Reference Implementation + +I will add deployed examples by address and chain ID. + + +## Security Considerations + +### Escaping Text + +Wallets must be careful when displaying text provided by contracts and proper +efforts must be taken to sanitize it, for example, be sure to consider: + +- HTML could be embedded to attempt to trick web-based wallets into executing code using the script tag (possibly uploading any private keys to a server) +- In general, extreme care must be used when rendering HTML; consider the ENS names `not-ricmoo.eth` or ` ricmoo.eth`, which if rendered without care would appear as `ricmoo.eth`, which it is not +- Other marks which require escaping could be included, such as quotes (`"`), formatting (`\n` (new line), `\f` (form feed), `\t` (tab), any of many non-standard whitespaces), back-slassh (`\`) +- UTF-8 has had bugs in the past which could allow arbitrary code execution and crashing renderers; consider using the UTF-8 replacement character (or *something*) for code-points outside common planes or common sub-sets within planes +- Homoglyphs attacks +- Right-to-left mark may affect rendering +- Many other things, deplnding on your environment + + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4444.md b/EIPS/eip-4444.md new file mode 100644 index 0000000..df543d4 --- /dev/null +++ b/EIPS/eip-4444.md @@ -0,0 +1,105 @@ +--- +eip: 4444 +title: Bound Historical Data in Execution Clients +description: Prune historical data in clients older than one year +author: George Kadianakis (@asn-d6), lightclient (@lightclient), Alex Stokes (@ralexstokes) +discussions-to: https://ethereum-magicians.org/t/eip-4444-bound-historical-data-in-execution-clients/7450 +status: Stagnant +type: Standards Track +category: Networking +created: 2021-11-02 +--- + +## Abstract + +Clients must stop serving historical headers, bodies, and receipts older than one year on the p2p layer. Clients may locally prune this historical data. + +## Motivation + +Historical blocks and receipts currently occupy more than 400GB of disk space (and growing!). Therefore, to validate the chain, users must typically have a 1TB disk. + +Historical data is not necessary for validating new blocks, so once a client has synced the tip of the chain, historical data is only retrieved when requested explicitly over the JSON-RPC or when a peer attempts to sync the chain. By pruning the history, this proposal reduces the disk requirements for users. Pruning history also allows clients to remove code that processes historical blocks. This means that execution clients don't need to maintain code paths that deal with each upgrade's compounding changes. + +Finally, this change will result in less bandwidth usage on the network as clients adopt more lightweight sync strategies based on the PoS weak subjectivity assumption. + +## Specification + +| Parameter | Value | Description | +| - | - | - | +| `HISTORY_PRUNE_EPOCHS` | 82125 | A year in beacon chain epochs | + +Clients SHOULD NOT serve headers, block bodies, and receipts that are older than `HISTORY_PRUNE_EPOCHS` epochs on the p2p network. + +Clients MAY locally prune headers, block bodies, and receipts that is older than `HISTORY_PRUNE_EPOCHS` epochs. + +#### Bootstrapping and syncing + +This EIP impacts the way clients bootstrap and sync. Clients will not be able to full sync using devp2p since historical data will no longer be served. + +Clients MUST use a valid Weak Subjectivity Checkpoint to bootstrap from a more recent view of the chain. For the purpose of syncing, clients treat weak subjectivity checkpoints as the genesis block. We call this method "checkpoint sync". + +For the purposes of this proposal, we assume clients always start with a configured and valid weak subjectivity checkpoint. The way this is achieved is out-of-scope for this proposal. + +## Rationale + +This proposal forces clients to stop serving old historical data over p2p. We make this explicit to force clients to seek historical data from other sources, instead of relying on the optional behavior of some clients which would result in quality degradation. + +### Why a year? + +This proposal sets `HISTORY_PRUNE_EPOCHS` to 82125 epochs (one earth year). This constant is large enough to provide sufficient room for the Weak Subjectivity Period to grow, and it's also small enough so as to not occupy too much disk space. + +## Backwards Compatibility + +### Preserving historical data + +This proposal impacts nodes that make use of historical data (e.g. web3 applications that display history of blocks, transactions or accounts). Preserving the history of Ethereum is fundamental and we believe there are various out-of-band ways to achieve this. + +Historical data can be packaged and shared via torrent magnet links or over networks like IPFS. Furthermore, systems like the Portal Network or The Graph can be used to acquire historical data. + +Clients should allow importing and exporting of historical data. Clients can provide scripts that fetch/verify data and automatically import them. + +### Full syncing from genesis + +Full syncing will no longer be possible over the p2p network. However, we do want to allow interested parties to do so on their own. + +We suggest that a specialized "full sync" client is built. The client is a shim that pieces together different releases of execution engines and can import historical blocks to validate the entire Ethereum chain from genesis and generate all other historical data. + +It's important to also note that although archive nodes with "state sync" functionality are in development, full sync is currently the only reliable way to bootstrap them. Clients that wish to continue providing archive support would need the ability to import historical blocks retrieved out-of-band and retain support for each historical upgrade of the network. + +### User experience + +This proposal impacts the UX for setting up applications that use historical data. Hence we suggest that clients introduce this change in two phases: + +In the first phase, clients don't prune historical data by default. They introduce a command line option similar to geth's `--txlookuplimit` that users can use if they want to prune historical data. + +In the second phase, history is pruned by default and the command line option is removed. Clients assume that users will find and import data in an out-of-band way. + +### JSON-RPC changes + +After this proposal is implemented, certain JSON-RPC endpoints (e.g. like `getBlockByHash`) won't be able to tell whether a given hash is invalid or just too old. Other endpoints like `getLogs` will simply no longer have the data the user is requesting. The way this regression should be handled by applications or clients is out-of-scope for this proposal. + +## Security Considerations + +### Relying on weak subjectivity + +With the move to PoS, it's essential for security to use valid weak subjectivity checkpoints because of long-range attacks. + +This proposal relies on the weak subjectivity assumption and assumes that clients will not bootstrap with an invalid or old WS checkpoint. + +This proposal also assumes that the weak subjectivity period will never be longer than `HISTORY_PRUNE_EPOCHS`. If that were to happen, clients with an old weak subjectivity checkpoint would never be able to "checkpoint sync" since the p2p network would not be able to provide the required data. + +### Centralization/censorship risk + +There are censorship/availability risks if there is a lack of incentives to keep historical data. + +It's important that Ethereum historical data are preserved and seeded by independent organizations, and their availability should be checked frequently. We consider these mechanisms as out-of-scope for this proposal. + +Furthermore, there is a risk that more dapps will rely on centralized services for acquiring historical data because it will be harder to setup a full node. + +### Confusion with other proposals + +Because there are a number of alternative proposals for reducing the execution client's footprint on disk, we've decided to enforce a specific pronouncination of the EIP. When pronouncing the EIP number, it **MUST** be pronounced EIP "four fours". All other pronounciations are incorrect. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). + diff --git a/EIPS/eip-4488.md b/EIPS/eip-4488.md new file mode 100644 index 0000000..0ac0355 --- /dev/null +++ b/EIPS/eip-4488.md @@ -0,0 +1,71 @@ +--- +eip: 4488 +title: Transaction calldata gas cost reduction with total calldata limit +description: Greatly decreases the gas cost of transaction calldata and simultaneously caps total transaction calldata in a block +author: Vitalik Buterin (@vbuterin), Ansgar Dietrichs (@adietrichs) +discussions-to: https://ethereum-magicians.org/t/eip-4488-transaction-calldata-gas-cost-reduction-with-total-calldata-limit/7555 +type: Standards Track +category: Core +status: Stagnant +created: 2021-11-23 +--- + +## Abstract + +Decrease transaction calldata gas cost, and add a limit of how much total transaction calldata can be in a block. + +## Motivation + +Rollups are in the short and medium term, and possibly the long term, the only trustless scaling solution for Ethereum. Transaction fees on L1 have been very high for months and there is greater urgency in doing anything required to help facilitate an ecosystem-wide move to rollups. Rollups are significantly reducing fees for many Ethereum users: Optimism and Arbitrum frequently provide fees that are ~3-8x lower than the Ethereum base layer itself, and ZK rollups, which have better data compression and can avoid including signatures, have fees ~40-100x lower than the base layer. + +However, even these fees are too expensive for many users. The long-term solution to the long-term inadequacy of rollups by themselves has always been [data sharding](https://github.com/ethereum/consensus-specs#sharding), which would add ~1-2 MB/sec of dedicated data space for rollups to the chain. However, data sharding will still take a considerable amount of time to finish implementing and deploying. Hence, a short-term solution to further cut costs for rollups, and to incentivize an ecosystem-wide transition to a rollup-centric Ethereum, is desired. + +This EIP presents a quick-to-implement short-term solution that also mitigates security risks. + +## Specification + +| Parameter | Value | +| - | - | +| `NEW_CALLDATA_GAS_COST` | `3` | +| `BASE_MAX_CALLDATA_PER_BLOCK` | `1,048,576` | +| `CALLDATA_PER_TX_STIPEND` | `300` | + +Reduce the gas cost of transaction calldata to `NEW_CALLDATA_GAS_COST` per byte, regardless of whether the byte is zero or nonzero. + +Add a rule that a block is only valid if `sum(len(tx.calldata) for tx in block.txs) <= BASE_MAX_CALLDATA_PER_BLOCK + len(block.txs) * CALLDATA_PER_TX_STIPEND`. + +## Rationale + +A natural alternative proposal is to decrease `NEW_CALLDATA_GAS_COST` without adding a limit. However, this presents a security concern: today, the average block size [is 60-90 kB](https://etherscan.io/chart/blocksize), but the _maximum_ block size is `30M / 16 = 1,875,000` bytes (plus about a kilobyte of block and tx overhead). Simply decreasing the calldata gas cost from 16 to 3 would increase the maximum block size to 10M bytes. This would push the Ethereum p2p networking layer to unprecedented levels of strain and risk breaking the network; some previous live tests of ~500 kB blocks a few years ago had already taken down a few bootstrap nodes. + +The decrease-cost-and-cap proposal achieves most of the benefits of the decrease, as rollups are unlikely to _dominate_ Ethereum block space in the short term future and so 1.5 MB will be sufficient, while preventing most of the security risk. + +Historically, the Ethereum protocol community has been suspicious of multi-dimensional resource limit rules (in this case, gas and calldata) because such rules greatly increase the complexity of the block packing problem that proposers (today miners, post-merge validators) need to solve. Today, proposers can generate blocks with near-optimal fee revenue by simply choosing transactions in highest-to-lowest order of priority fee. In a multi-dimensional world, proposers would have to deal with multi-dimensional constraints. Multi-dimensional knapsack problems are much more complicated than the single-dimensional equivalent, and well-optimized proprietary implementations built by pools may well outperform default open source implementations. + +Today, there are two key reasons why this is less of a problem than before: + +1. [EIP-1559](./eip-1559.md) means that, at least most of the time, the problem that block proposers are solving is _not_ a knapsack problem. Rather, block proposers are simply including all the transactions they can find with sufficient base fee and priority fee. Hence, naive algorithms will also frequently generate close-to-optimal results. +2. The existence of sophisticated proprietary strategies for miner extractable value (MEV) extraction means that decentralized optimal block production is already in the medium and long term a lost cause. Research is instead going into solutions that separate away the specialization-friendly task of block body generation from the role of a validator ("proposer/builder separation"). Instead of being a fundamental change, two-dimensional knapsack problems today would be merely "yet another" MEV opportunity. + +Hence, it's worth rethinking the historical opposition to multi-dimensional resource limits and considering them as a pragmatic way to simultaneously achieve moderate scalability gains while retaining security. + +Additionally, the stipend mechanism makes the two-dimensional optimization problem even less of an issue in practice. 90% of all transactions ([sample](../assets/eip-4488/gas_and_calldata_sample.csv) taken from blocks `13500000, 13501000 ... 13529000`) have <300 bytes of calldata. Hence, if a naive transaction selection algorithm overfills the calldata of a block that the proposer is creating, the proposer will still be able to keep adding transactions from 90% of their mempool. + +## Backwards Compatibility + +This is a backwards incompatible gas repricing that requires a scheduled network upgrade. + +Users will be able to continue operating with no changes. + +Miners will be able to continue operating with no changes except for a rule to stop adding new transactions into a block when the total calldata size reaches the maximum. However, there are pragmatic heuristics that they could add to achieve closer-to-optimal returns in such cases: for example, if a block fills up to the size limit, they could repeatedly remove the last data-heavy transaction and replace it with as many data-light transactions as possible, until doing so is no longer profitable. + +## Security Considerations + +The _burst_ data capacity of the chain does not increase as a result of this proposal (in fact, it slightly decreases). However, the _average_ data capacity will increase. This means that the storage requirements of history-storing will go up. A worst-case scenario would be a theoretical long-run maximum of ~1,262,861 bytes per 12 sec slot, or ~3.0 TB per year. + +We recommend [EIP-4444](./eip-4444.md) or some similar history expiry proposal be implemented either at the same time or soon after this EIP to mitigate this risk. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). + diff --git a/EIPS/eip-4494.md b/EIPS/eip-4494.md new file mode 100644 index 0000000..8da81a9 --- /dev/null +++ b/EIPS/eip-4494.md @@ -0,0 +1,207 @@ +--- +eip: 4494 +title: Permit for ERC-721 NFTs +description: ERC-712-singed approvals for ERC-721 NFTs +author: Simon Fremaux (@dievardump), William Schwab (@wschwab) +discussions-to: https://ethereum-magicians.org/t/eip-extending-erc2612-style-permits-to-erc721-nfts/7519/2 +status: Draft +type: Standards Track +category: ERC +created: 2021-11-25 +requires: 165, 712, 721 +--- + +## Abstract +The "Permit" approval flow outlined in [ERC-2612](./eip-2612.md) has proven a very valuable advancement in UX by creating gasless approvals for ERC20 tokens. This EIP extends the pattern to ERC-721 NFTs. This EIP borrows heavily from ERC-2612. + +This requires a separate EIP due to the difference in structure between ERC-20 and ERC-721 tokens. While ERC-20 permits use value (the amount of the ERC-20 token being approved) and a nonce based on the owner's address, ERC-721 permits focus on the `tokenId` of the NFT and increment nonce based on the transfers of the NFT. + +## Motivation +The permit structure outlined in [ERC-2612](./eip-2612.md) allows for a signed message (structured as outlined in [ERC-712](./eip-712.md)) to be used in order to create an approval. Whereas the normal approval-based pull flow generally involves two transactions, one to approve a contract and a second for the contract to pull the asset, which is poor UX and often confuses new users, a permit-style flow only requires signing a message and a transaction. Additional information can be found in [ERC-2612](./eip-2612.md). + +[ERC-2612](./eip-2612.md) only outlines a permit architecture for ERC-20 tokens. This ERC proposes an architecture for ERC-721 NFTs, which also contain an approve architecture that would benefit from a signed message-based approval flow. + +## Specification +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. + +Three new functions MUST be added to [ERC-721](./eip-721.md): +```solidity +pragma solidity 0.8.10; + +import "./IERC165.sol"; + +/// +/// @dev Interface for token permits for ERC-721 +/// +interface IERC4494 is IERC165 { + /// ERC165 bytes to add to interface array - set in parent contract + /// + /// _INTERFACE_ID_ERC4494 = 0x5604e225 + + /// @notice Function to approve by way of owner signature + /// @param spender the address to approve + /// @param tokenId the index of the NFT to approve the spender on + /// @param deadline a timestamp expiry for the permit + /// @param sig a traditional or EIP-2098 signature + function permit(address spender, uint256 tokenId, uint256 deadline, bytes memory sig) external; + /// @notice Returns the nonce of an NFT - useful for creating permits + /// @param tokenId the index of the NFT to get the nonce of + /// @return the uint256 representation of the nonce + function nonces(uint256 tokenId) external view returns(uint256); + /// @notice Returns the domain separator used in the encoding of the signature for permits, as defined by EIP-712 + /// @return the bytes32 domain separator + function DOMAIN_SEPARATOR() external view returns(bytes32); +} +``` +The semantics of which are as follows: + +For all addresses `spender`, `uint256`s `tokenId`, `deadline`, and `nonce`, and `bytes` `sig`, a call to `permit(spender, tokenId, deadline, sig)` MUST set `spender` as approved on `tokenId` as long as the owner of `tokenId` remains in possession of it, and MUST emit a corresponding `Approval` event, if and only if the following conditions are met: + +* the current blocktime is less than or equal to `deadline` +* the owner of the `tokenId` is not the zero address +* `nonces[tokenId]` is equal to `nonce` +* `sig` is a valid `secp256k1` or [EIP-2098](./eip-2098.md) signature from owner of the `tokenId`: +``` +keccak256(abi.encodePacked( + hex"1901", + DOMAIN_SEPARATOR, + keccak256(abi.encode( + keccak256("Permit(address spender,uint256 tokenId,uint256 nonce,uint256 deadline)"), + spender, + tokenId, + nonce, + deadline)) +)); +``` +where `DOMAIN_SEPARATOR` MUST be defined according to [EIP-712](./eip-712.md). The `DOMAIN_SEPARATOR` should be unique to the contract and chain to prevent replay attacks from other domains, and satisfy the requirements of EIP-712, but is otherwise unconstrained. A common choice for `DOMAIN_SEPARATOR` is: +``` +DOMAIN_SEPARATOR = keccak256( + abi.encode( + keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'), + keccak256(bytes(name)), + keccak256(bytes(version)), + chainid, + address(this) +)); +``` +In other words, the message is the following ERC-712 typed structure: +```json +{ + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Permit": [ + { + "name": "spender", + "type": "address" + }, + { + "name": "tokenId", + "type": "uint256" + }, + { + "name": "nonce", + "type": "uint256" + }, + { + "name": "deadline", + "type": "uint256" + } + ], + "primaryType": "Permit", + "domain": { + "name": erc721name, + "version": version, + "chainId": chainid, + "verifyingContract": tokenAddress + }, + "message": { + "spender": spender, + "value": value, + "nonce": nonce, + "deadline": deadline + } +}} +``` +In addition: +* the `nonce` of a particular `tokenId` (`nonces[tokenId]`) MUST be incremented upon any transfer of the `tokenId` +* the `permit` function MUST check that the signer is not the zero address + +Note that nowhere in this definition do we refer to `msg.sender`. The caller of the `permit` function can be any address. + +This EIP requires [EIP-165](./eip-165.md). EIP165 is already required in [ERC-721](./eip-721.md), but is further necessary here in order to register the interface of this EIP. Doing so will allow easy verification if an NFT contract has implemented this EIP or not, enabling them to interact accordingly. The interface of this EIP (as defined in EIP-165) is `0x5604e225`. Contracts implementing this EIP MUST have the `supportsInterface` function return `true` when called with `0x5604e225`. + +## Rationale +The `permit` function is sufficient for enabling a `safeTransferFrom` transaction to be made without the need for an additional transaction. + +The format avoids any calls to unknown code. + +The `nonces` mapping is given for replay protection. + +A common use case of permit has a relayer submit a Permit on behalf of the owner. In this scenario, the relaying party is essentially given a free option to submit or withhold the Permit. If this is a cause of concern, the owner can limit the time a Permit is valid for by setting deadline to a value in the near future. The deadline argument can be set to uint(-1) to create Permits that effectively never expire. + +ERC-712 typed messages are included because of its use in [ERC-2612](./eip-2612.md), which in turn cites widespread adoption in many wallet providers. + +While ERC-2612 focuses on the value being approved, this EIP focuses on the `tokenId` of the NFT being approved via `permit`. This enables a flexibility that cannot be achieved with ERC-20 (or even [ERC-1155](./eip-1155.md)) tokens, enabling a single owner to give multiple permits on the same NFT. This is possible since each ERC-721 token is discrete (oftentimes referred to as non-fungible), which allows assertion that this token is still in the possession of the `owner` simply and conclusively. + +Whereas ERC-2612 splits signatures into their `v,r,s` components, this EIP opts to instead take a `bytes` array of variable length in order to support [EIP-2098](./eip-2098) signatures (64 bytes), which cannot be easily separated or reconstructed from `r,s,v` components (65 bytes). + +## Backwards Compatibility +There are already some ERC-721 contracts implementing a `permit`-style architecture, most notably Uniswap v3. + +Their implementation differs from the specification here in that: + * the `permit` architecture is based on `owner` + * the `nonce` is incremented at the time the `permit` is created + * the `permit` function must be called by the NFT owner, who is set as the `owner` + * the signature is split into `r,s,v` instead of `bytes` + + Rationale for differing on design decisions is detailed above. + +## Test Cases + +Basic test cases for the reference implementation can be found [here](https://github.com/dievardump/erc721-with-permits/tree/main/test). + +In general, test suites should assert at least the following about any implementation of this EIP: +* the nonce is incremented after each transfer +* `permit` approves the `spender` on the correct `tokenId` +* the permit cannot be used after the NFT is transferred +* an expired permit cannot be used + +## Reference Implementation + +A reference implementation has been set up [here](https://github.com/dievardump/erc721-with-permits). + +## Security Considerations + +Extra care should be taken when creating transfer functions in which `permit` and a transfer function can be used in one function to make sure that invalid permits cannot be used in any way. This is especially relevant for automated NFT platforms, in which a careless implementation can result in the compromise of a number of user assets. + +The remaining considerations have been copied from [ERC-2612](./eip-2612.md) with minor adaptation, and are equally relevant here: + +Though the signer of a `Permit` may have a certain party in mind to submit their transaction, another party can always front run this transaction and call `permit` before the intended party. The end result is the same for the `Permit` signer, however. + +Since the ecrecover precompile fails silently and just returns the zero address as `signer` when given malformed messages, it is important to ensure `ownerOf(tokenId) != address(0)` to avoid `permit` from creating an approval to any `tokenId` which does not have an approval set. + +Signed `Permit` messages are censorable. The relaying party can always choose to not submit the `Permit` after having received it, withholding the option to submit it. The `deadline` parameter is one mitigation to this. If the signing party holds ETH they can also just submit the `Permit` themselves, which can render previously signed `Permit`s invalid. + +The standard [ERC-20 race condition for approvals](https://swcregistry.io/docs/SWC-114) applies to `permit` as well. + +If the `DOMAIN_SEPARATOR` contains the `chainId` and is defined at contract deployment instead of reconstructed for every signature, there is a risk of possible replay attacks between chains in the event of a future chain split. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4519.md b/EIPS/eip-4519.md new file mode 100644 index 0000000..b174332 --- /dev/null +++ b/EIPS/eip-4519.md @@ -0,0 +1,225 @@ +--- +eip: 4519 +title: Non-Fungible Tokens Tied to Physical Assets +description: Interface for non-fungible tokens representing physical assets that can generate or recover their own accounts and obey users. +author: Javier Arcenegui (@Hardblock-IMSE-CNM), Rosario Arjona (@RosarioArjona), Roberto Román , Iluminada Baturone (@lumi2018) +discussions-to: https://ethereum-magicians.org/t/new-proposal-of-smart-non-fungible-token/7677 +status: Final +type: Standards Track +category: ERC +created: 2021-12-03 +requires: 165, 721 +--- + +## Abstract + +This EIP standardizes an interface for non-fungible tokens representing physical assets, such as Internet of Things (IoT) devices. These NFTs are tied to physical assets and can verify the authenticity of the tie. They can include an Ethereum address of the physical asset, permitting physical assets to sign messages and transactions. Physical assets can operate with an operating mode defined by its corresponding NFT. + +## Motivation + +This standard was developed because [EIP-721](./eip-721.md) only tracks ownership (not usage rights) and does not track the Ethereum addresses of the asset. The popularity of smart assets, such as IoT devices, is increasing. To permit secure and traceable management, these NFTs can be used to establish secure communication channels between the physical asset, its owner, and its user. + +## Specification + +The attributes `addressAsset` and `addressUser` are, respectively, the Ethereum addresses of the physical asset and the user. They are optional attributes but at least one of them should be used in an NFT. In the case of using only the attribute `addressUser`, two states define if the token is assigned or not to a user. `Figure 1` shows these states in a flow chart. When a token is created, transferred or unassigned, the token state is set to `notAssigned`. If the token is assigned to a valid user, the state is set to `userAssigned`. + +![Figure 1 : Flow chart of the token states with `addressUser` defined (and `addressAsset` undefined)](../assets/eip-4519/images/Figure1.jpg) + +In the case of defining the attribute `addressAsset` but not the attribute `addressUser`, two states define if the token is waiting for authentication with the owner or if the authentication has finished successfully. `Figure 2` shows these states in a flow chart. When a token is created or transferred to a new owner, then the token changes its state to `waitingForOwner`. In this state, the token is waiting for the mutual authentication between the asset and the owner. Once authentication is finished successfully, the token changes its state to `engagedWithOwner`. + +![Figure 2 : Flow chart of the token states with `addressAsset` defined (and `addressUser` undefined)](../assets/eip-4519/images/Figure2.jpg) + +Finally, if both the attributes `addressAsset` and `addressUser` are defined, the states of the NFT define if the asset has been engaged or not with the owner or the user (`waitingForOwner`, `engagedWithOwner`, `waitingForUser` and `engagedWithUser`). The flow chart in `Figure 3` shows all the possible state changes. The states related to the owner are the same as in `Figure 2`. The difference is that, at the state `engagedWithOwner`, the token can be assigned to a user. If a user is assigned (the token being at states `engagedWithOwner`, `waitingForUser` or `engagedWithUser`), then the token changes its state to `waitingForUser`. Once the asset and the user authenticate each other, the state of the token is set to `engagedWithUser`, and the user is able to use the asset. + + ![Figure 3 : Flow chart of the token states with `addressUser` and `addressUser` defined](../assets/eip-4519/images/Figure3.jpg) + +In order to complete the ownership transfer of a token, the new owner must carry out a mutual authentication process with the asset, which is off-chain with the asset and on-chain with the token, by using their Ethereum addresses. Similarly, a new user must carry out a mutual authentication process with the asset to complete a use transfer. NFTs define how the authentication processes start and finish. These authentication processes allow deriving fresh session cryptographic keys for secure communication between assets and owners, and between assets and users. Therefore, the trustworthiness of the assets can be traced even if new owners and users manage them. + +When the NFT is created or when the ownership is transferred, the token state is `waitingForOwner`. The asset sets its operating mode to `waitingForOwner`. The owner generates a pair of keys using the elliptic curve secp256k1 and the primitive element P used on this curve: a secret key SKO_A and a Public Key PKO_A, so that PKO_A = SKO_A * P. To generate the shared key between the owner and the asset, KO, the public key of the asset, PKA, is employed as follows: + +KO = PKA * SKO_A + +Using the function `startOwnerEngagement`, PKO_A is saved as the attribute `dataEngagement` and the hash of KO as the attribute `hashK_OA`. The owner sends request engagement to the asset, and the asset calculates: + +KA = SKA * PKO_A + +If everything is correctly done, KO and KA are the same since: + +KO = PKA * SKO_A = (SKA * P) * SKO_A = SKA * (SKO_A * P) = SKA * PKO_A + +Using the function `ownerEngagement`, the asset sends the hash of KA, and if it is the same as the data in `hashK_OA`, then the state of the token changes to `engagedWithOwner` and the event `OwnerEngaged` are sent. Once the asset receives the event, it changes its operation mode to `engagedWithOwner`. This process is shown in `Figure 4`. From this moment, the asset can be managed by the owner and they can communicate in a secure way using the shared key. + + ![Figure 4: Steps in a successful owner and asset mutual authentication process](../assets/eip-4519/images/Figure4.jpg) + +If the asset consults Ethereum and the state of its NFT is `waitingForUser`, the asset (assuming it is an electronic physical asset) sets its operating mode to `waitingForUser`. Then, a mutual authentication process is carried out with the user, as already done with the owner. The user sends the transaction associated with the function `startUserEngagement`. As in `startOwnerEngagement`, this function saves the public key generated by the user, PKU_A, as the attribute `dataEngagement` and the hash of KU = PKA * SKU_A as the attribute `hashK_UA` in the NFT. + +The user sends request engagement and the asset calculates: + +KA = SKA * PKU_A + +If everything is correctly done, KU and KA are the same since: + +KU = PKA * SKU_A = (SKA * P) * SKU_A = SKA * (SKU_A * P) = SKA * PKU_A + +Using the function `userEngagement`, the asset sends the hash of KA obtained and if it is the same as the data in `hashK_UA`, then the state of the token changes to `engagedWithUser` and the event `UserEngaged` is sent. Once the asset receives the event, it changes its operation mode to `engagedWithUser`. This process is shown in `Figure 5`. From this moment, the asset can be managed by the user and they can communicate in a secure way using the shared key. + + ![Figure 5: Steps in a successful user and asset mutual authentication process](../assets/eip-4519/images/Figure5.jpg) + +Since the establishment of a shared secret key is very important for a secure communication, NFTs include the attributes +`hashK_OA`, `hashK_UA` and `dataEngagement`. The first two attributes define, respectively, the hash of the secret key shared between the asset and its owner and between the asset and its user. Assets, owners and users should check they are using the correct shared secret keys. The attribute `dataEngagement` defines the public data needed for the agreement. + +```solidity +pragma solidity ^0.8.0; + /// @title EIP-4519 NFT: Extension of EIP-721 Non-Fungible Token Standard. +/// Note: the EIP-165 identifier for this interface is 0x8a68abe3 + interface EIP-4519 NFT is EIP721/*,EIP165*/{ + /// @dev This emits when the NFT is assigned as utility of a new user. + /// This event emits when the user of the token changes. + /// (`_addressUser` == 0) when no user is assigned. + event UserAssigned(uint256 indexed tokenId, address indexed _addressUser); + + /// @dev This emits when user and asset finish mutual authentication process successfully. + /// This event emits when both the user and the asset prove they share a secure communication channel. + event UserEngaged(uint256 indexed tokenId); + + /// @dev This emits when owner and asset finish mutual authentication process successfully. + /// This event emits when both the owner and the asset prove they share a secure communication channel. + event OwnerEngaged(uint256 indexed tokenId); + + /// @dev This emits when it is checked that the timeout has expired. + /// This event emits when the timestamp of the EIP-4519 NFT is not updated in timeout. + event TimeoutAlarm(uint256 indexed tokenId); + /// @notice This function defines how the NFT is assigned as utility of a new user (if "addressUser" is defined). + /// @dev Only the owner of the EIP-4519 NFT can assign a user. If "addressAsset" is defined, then the state of the token must be + /// "engagedWithOwner","waitingForUser" or "engagedWithUser" and this function changes the state of the token defined by "_tokenId" to + /// "waitingForUser". If "addressAsset" is not defined, the state is set to "userAssigned". In both cases, this function sets the parameter + /// "addressUser" to "_addressUser". + /// @param _tokenId is the tokenId of the EIP-4519 NFT tied to the asset. + /// @param _addressUser is the address of the new user. + function setUser(uint256 _tokenId, address _addressUser) external payable; + /// @notice This function defines the initialization of the mutual authentication process between the owner and the asset. + /// @dev Only the owner of the token can start this authentication process if "addressAsset" is defined and the state of the token is "waitingForOwner". + /// The function does not change the state of the token and saves "_dataEngagement" + /// and "_hashK_OA" in the parameters of the token. + /// @param _tokenId is the tokenId of the EIP-4519 NFT tied to the asset. + /// @param _dataEngagement is the public data proposed by the owner for the agreement of the shared key. + /// @param _hashK_OA is the hash of the secret proposed by the owner to share with the asset. + function startOwnerEngagement(uint256 _tokenId, uint256 _dataEngagement, uint256 _hashK_OA) external payable; + + /// @notice This function completes the mutual authentication process between the owner and the asset. + /// @dev Only the asset tied to the token can finish this authentication process provided that the state of the token is + /// "waitingForOwner" and dataEngagement is different from 0. This function compares hashK_OA saved in + /// the token with hashK_A. If they are equal then the state of the token changes to "engagedWithOwner", dataEngagement is set to 0, + /// and the event "OwnerEngaged" is emitted. + /// @param _hashK_A is the hash of the secret generated by the asset to share with the owner. + function ownerEngagement(uint256 _hashK_A) external payable; + + /// @notice This function defines the initialization of the mutual authentication process between the user and the asset. + /// @dev Only the user of the token can start this authentication process if "addressAsset" and "addressUser" are defined and + /// the state of the token is "waitingForUser". The function does not change the state of the token and saves "_dataEngagement" + /// and "_hashK_UA" in the parameters of the token. + /// @param _tokenId is the tokenId of the EIP-4519 NFT tied to the asset. + /// @param _dataEngagement is the public data proposed by the user for the agreement of the shared key. + /// @param _hashK_UA is the hash of the secret proposed by the user to share with the asset. + function startUserEngagement(uint256 _tokenId, uint256 _dataEngagement, uint256 _hashK_UA) external payable; + + /// @notice This function completes the mutual authentication process between the user and the asset. + /// @dev Only the asset tied to the token can finish this authentication process provided that the state of the token is + /// "waitingForUser" and dataEngagement is different from 0. This function compares hashK_UA saved in + /// the token with hashK_A. If they are equal then the state of the token changes to "engagedWithUser", dataEngagement is set to 0, + /// and the event "UserEngaged" is emitted. + /// @param _hashK_A is the hash of the secret generated by the asset to share with the user. + function userEngagement(uint256 _hashK_A) external payable; + + /// @notice This function checks if the timeout has expired. + /// @dev Everybody can call this function to check if the timeout has expired. The event "TimeoutAlarm" is emitted + /// if the timeout has expired. + /// @param _tokenId is the tokenId of the EIP-4519 NFT tied to the asset. + /// @return true if timeout has expired and false in other case. + function checkTimeout(uint256 _tokenId) external returns (bool); + + /// @notice This function sets the value of timeout. + /// @dev Only the owner of the token can set this value provided that the state of the token is "engagedWithOwner", + /// "waitingForUser" or "engagedWithUser". + /// @param _tokenId is the tokenId of the EIP-4519 NFT tied to the asset. + /// @param _timeout is the value to assign to timeout. + function setTimeout(uint256 _tokenId, uint256 _timeout) external; + + /// @notice This function updates the timestamp, thus avoiding the timeout alarm. + /// @dev Only the asset tied to the token can update its own timestamp. + function updateTimestamp() external; + + /// @notice This function lets obtain the tokenId from an address. + /// @dev Everybody can call this function. The code executed only reads from Ethereum. + /// @param _addressAsset is the address to obtain the tokenId from it. + /// @return tokenId of the token tied to the asset that generates _addressAsset. + function tokenFromBCA(address _addressAsset) external view returns (uint256); + + /// @notice This function lets know the owner of the token from the address of the asset tied to the token. + /// @dev Everybody can call this function. The code executed only reads from Ethereum. + /// @param _addressAsset is the address to obtain the owner from it. + /// @return owner of the token bound to the asset that generates _addressAsset. + function ownerOfFromBCA(address _addressAsset) external view returns (address); + + /// @notice This function lets know the user of the token from its tokenId. + /// @dev Everybody can call this function. The code executed only reads from Ethereum. + /// @param _tokenId is the tokenId of the EIP-4519 NFT tied to the asset. + /// @return user of the token from its _tokenId. + function userOf(uint256 _tokenId) external view returns (address); + + /// @notice This function lets know the user of the token from the address of the asset tied to the token. + /// @dev Everybody can call this function. The code executed only reads from Ethereum. + /// @param _addressAsset is the address to obtain the user from it. + /// @return user of the token tied to the asset that generates _addressAsset. + function userOfFromBCA(address _addressAsset) external view returns (address); + + /// @notice This function lets know how many tokens are assigned to a user. + /// @dev Everybody can call this function. The code executed only reads from Ethereum. + /// @param _addressUser is the address of the user. + /// @return number of tokens assigned to a user. + function userBalanceOf(address _addressUser) external view returns (uint256); + + /// @notice This function lets know how many tokens of a particular owner are assigned to a user. + /// @dev Everybody can call this function. The code executed only reads from Ethereum. + /// @param _addressUser is the address of the user. + /// @param _addressOwner is the address of the owner. + /// @return number of tokens assigned to a user from an owner. + function userBalanceOfAnOwner(address _addressUser, address _addressOwner) external view returns (uint256); +} +``` + +## Rationale + +### Authentication + +This EIP uses smart contracts to verify the mutual authentication process since smart contracts are trustless. + +### Tie Time + +This EIP proposes including the attribute timestamp (to register in Ethereum the last time that the physical asset checked the tie with its token) and the attribute timeout (to register the maximum delay time established for the physical asset to prove again the tie). These attributes avoid that a malicious owner or user could use the asset endlessly. + +When the asset calls `updateTimestamp`, the smart contract must call `block.timestamp`, which provides current block timestamp as seconds since Unix epoch. For this reason, `timeout` must be provided in seconds. + +### EIP-721-based + +[EIP-721](./eip-721.md) is the most commonly-used standard for generic NFTs. This EIP extends EIP-721 for backwards compatibility. + +## Backwards Compatibility + +This standard is an extension of EIP-721. It is fully compatible with both of the commonly used optional extensions (`IERC721Metadata` and `IERC721Enumerable`) mentioned in the EIP-721 standard. + +## Test Cases + +The test cases presented in the paper shown below are available [here](../assets/eip-4519/PoC_SmartNFT/README.md). + +## Reference Implementation + +A first version was presented in a paper of the Special Issue **Security, Trust and Privacy in New Computing Environments** of **Sensors** journal of **MDPI** editorial. The paper, entitled [Secure Combination of IoT and Blockchain by Physically Binding IoT Devices to Smart Non-Fungible Tokens Using PUFs](../assets/eip-4519/sensors-21-03119.pdf), was written by the same authors of this EIP. + +## Security Considerations + +In this EIP, a generic system has been proposed for the creation of non-fungible tokens tied to physical assets. A generic point of view based on the improvements of the current EIP-721 NFT is provided, such as the implementation of the user management mechanism, which does not affect the token's ownership. The physical asset should have the ability to generate an Ethereum address from itself in a totally random way so that only the asset is able to know the secret from which the Ethereum address is generated. In this way, identity theft is avoided and the asset can be proven to be completely genuine. In order to ensure this, it is recommended that only the manufacturer of the asset has the ability to create its associated token. In the case of an IoT device, the device firmware will be unable to share and modify the secret. Instead of storing the secrets, it is recommended that assets reconstruct their secrets from non-sensitive information such as the helper data associated with Physical Unclonable Functions (PUFs). Although a secure key exchange protocol based on elliptic curves has been proposed, the token is open to coexist with other types of key exchange. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4520.md b/EIPS/eip-4520.md new file mode 100644 index 0000000..1ee2113 --- /dev/null +++ b/EIPS/eip-4520.md @@ -0,0 +1,36 @@ +--- +eip: 4520 +title: Mult-byte opcodes prefixed by EB and EC. +description: Reserve `0xEB` and `0xEC` for usage as extended opcode space. +author: Brayton Goodall (@Spore-Druid-Bray), Mihir Faujdar (@uink45) +discussions-to: https://ethereum-magicians.org/t/multi-byte-opcodes/7681 +status: Stagnant +type: Standards Track +category: Core +created: 2021-12-1 +--- + +## Abstract +Reserve `0xEB` and `0xEC` for usage as extended opcode space. + +## Motivation +It would be convenient to introduce new opcodes that are likely to be infrequently used, whilst also being able to have greater than 256 opcodes in total. As a single byte opcode is half the size of a double byte opcode, the greatest efficiency in code sizes will be one where frequently used opcodes are single bytes. Two prefix bytes are used to accommodate up to 510 double byte opcodes. + +## Specification +For example, a new arithmetic opcode may be allocated to `0xEC 01`(`ADD`), and a novel opcode may be introduced at `0xEB F4`(`DELEGATECALL`). + +Triple byte opcodes may be doubly-prefixed by `0xEB EB`, `0xEC EC`, `0xEB EC` and `0xEC EB`. It is possible to allocate experimental opcodes to this triple-byte space initially, and if they prove safe and useful, they could later be allocated a location in double-byte or single-byte space. + +Only `0xEB EB`, `0xEC EC`, `0xEC EC`, and `0xEB EC` may be interpreted as further extensions of the opcode space. `0xEB` and `0xEC` do not themselves affect the stack or memory, however opcodes specified by further bytes may. If a multi-byte opcode is yet to be defined, it is to be treated as `INVALID` rather than as a `NOP`, as per usual for undefined opcodes. + +## Rationale +It was considered that two prefix bytes rather than one would be adequate for reservation as extension addresses. Both `0xEB` and `0xEC` were chosen to be part of the E-series of opcodes. For example, the `0xEF` byte is reserved for contracts conforming to the Ethereum Object Format. By having unassigned opcodes for extending the opcode space, there will be a lower risk of breaking the functionalities of deployed contracts compared to choosing assigned opcodes. + +## Backwards Compatibility +Previous usage of `0xEB` and `0xEC` may result in unexpected behaviour and broken code. + +## Security Considerations +There are no known security considerations. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4521.md b/EIPS/eip-4521.md new file mode 100644 index 0000000..d12bf99 --- /dev/null +++ b/EIPS/eip-4521.md @@ -0,0 +1,62 @@ +--- +eip: 4521 +title: 721/20-compatible transfer +description: Recommends a simple extension to make NFTs compatible with apps and contracts that handle fungibles. +author: Ross Campbell (@z0r0z) +discussions-to: https://ethereum-magicians.org/t/eip-4521-721-20-compatible-transfer/7903 +status: Stagnant +type: Standards Track +category: ERC +created: 2021-12-13 +requires: 721 +--- + +## Abstract +ERC-721, the popular standard for non-fungible tokens (NFTs), includes send functions, such as `transferFrom()` and `safeTransferFrom()`, but does not include a backwards-compatible `transfer()` found in fungible ERC-20 tokens. This standard provides references to add such a `transfer()`. + +## Motivation +This standard proposes a simple extension to allow NFTs to work with contracts designed to manage ERC-20s and many consumer wallets which expect to be able to execute a token `transfer()`. For example, if an NFT is inadvertently sent to a contract that typically handles ERC-20, that NFT will be locked. It should also simplify the task for contract programmers if they can rely on `transfer()` to both handle ERC-20 and NFTs. + +## Specification +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. + +The interface for ERC-4521 `transfer()` MUST conform to ERC-20 and resulting transfers MUST fire the `Transfer` event as described in ERC-721. + +```sol +function transfer(address to, uint256 tokenId) external returns (bool success); +``` + +## Rationale +Replicating ERC-20 `transfer()` with just a minor change to accurately reflect that a unique `tokenId` rather than fungible sum is being sent is desirable for code simplicity and to make integration easier. + +## Backwards Compatibility +This EIP does not introduce any known backward compatibility issues. + +## Reference Implementation +A reference implementation of an ERC-4521 `transfer()`: + +```sol +function transfer(address to, uint256 tokenId) public virtual returns (bool success) { + require(msg.sender == ownerOf[tokenId], "NOT_OWNER"); + + unchecked { + balanceOf[msg.sender]--; + + balanceOf[to]++; + } + + delete getApproved[tokenId]; + + ownerOf[tokenId] = to; + + emit Transfer(msg.sender, to, tokenId); + + success = true; +} +``` + +## Security Considerations +Implementers must be sure to include the relevant return `bool` value for an ERC-4521 in order to conform with existing contracts that use ERC-20 interfaces, otherwise, NFTs may be locked unless a `safeTransfer` is used in such contracts. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4524.md b/EIPS/eip-4524.md new file mode 100644 index 0000000..4475577 --- /dev/null +++ b/EIPS/eip-4524.md @@ -0,0 +1,82 @@ +--- +eip: 4524 +title: Safer ERC-20 +description: Extending ERC-20 with ERC165 and adding safeTransfer (like ERC-721 and ERC-1155) +author: William Schwab (@wschwab) +discussions-to: https://ethereum-magicians.org/t/why-isnt-there-an-erc-for-safetransfer-for-erc20/7604 +status: Stagnant +type: Standards Track +category: ERC +created: 2021-12-05 +requires: 20, 165 +--- + +## Abstract + +This standard extends [ERC-20](./eip-20.md) tokens with [EIP-165](./eip-165.md), and adds familiar functions from [ERC-721](./eip-721.md) and [ERC-1155](./eip-1155.md) ensuring receiving contracts have implemented proper functionality. + +## Motivation + +[EIP-165](./eip-165.md) adds (among other things) the ability to tell if a target recipient explicitly signals compatibility with an ERC. This is already used in the EIPs for NFTs, [ERC-721](./eip-721.md) and [ERC-1155](./eip-1155.md). In addition, EIP-165 is a valuable building block for extensions on popular standards to signal implementation, a trend we've seen in a number of NFT extensions. This EIP aims to bring these innovations back to ERC-20. + +The importance of [EIP-165](./eip-165.md) is perhaps felt most for app developers looking to integrate with a generic standard such as ERC-20 or ERC-721, while integrating newer innovations built atop these standards. An easy example would be token permits, which allow for a one-transaction approval and transfer. This has already been implemented in many popular ERC-20 tokens using the [ERC-2612](./eip-2612.md) standard or similar. A platform integrating ERC-20 tokens has no easy way of telling if a particular token has implemented token permits or not. (As of this writing, ERC-2612 does not require EIP-165.) With EIP-165, the app (or contracts) could query `supportsInterface` to see if the `interfaceId` of a particular EIP is registered (in this case, EIP-2612), allowing for easier and more modular functions interacting with ERC-20 contracts. It is already common in NFT extensions to include an EIP-165 interface with a standard, we would argue this is at least in part due to the underlying [ERC-721](./eip-721.md) and [ERC-1155](./eip-1155.md) standards integrating EIP-165. Our hope is that this extension to ERC-20 would also help future extensions by making them easier to integrate. + +## Specification +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. + +In order to be compliant with this EIP, and ERC-20-compliant contract MUST also implement the following functions: +```solidity +pragma solidity 0.8.10; + +import './IERC20.sol'; +import './IERC165.sol'; + +// the EIP-165 interfaceId for this interface is 0x534f5876 + +interface SaferERC-20 is IERC20, IERC165 { + function safeTransfer(address to, uint256 amount) external returns(bool); + function safeTransfer(address to, uint256 amount, bytes memory data) external returns(bool); + function safeTransferFrom(address from, address to, uint256 amount) external returns(bool); + function safeTransferFrom(address from, address to, uint256 amount, bytes memory data) external returns(bool); +} +``` +`safeTransfer` and `safeTransferFrom` MUST transfer as expected to EOA addresses, and to contracts implementing `ERC20Receiver` and returning the function selector (`0x4fc35859`) when called, and MUST revert when transferring to a contract which either does not have `ERC20Receiver` implemented, or does not return the function selector when called. + +In addition, a contract accepting safe transfers MUST implement the following if it wishes to accept safe transfers, and MUST return the function selector (`0x4fc35859`): +```solidity +pragma solidity 0.8.10; + +import './IERC165.sol'; + +interface ERC20Receiver is IERC165 { + function onERC20Received( + address _operator, + address _from, + uint256 _amount, + bytes _data + ) external returns(bytes4); +} +``` + +## Rationale + +This EIP is meant to be minimal and straightforward. Adding EIP-165 to ERC-20 is useful for a number of applications, and outside of a minimal amount of code increasing contract size, carries no downside. The `safeTransfer` and `safeTransferFrom` functions are well recognized from ERC-721 and ERC-1155, and therefore keeping identical naming conventions is reasonable, and the benefits of being able to check for implementation before transferring are as useful for ERC-20 tokens as they are for ERC-721 and ERC-1155. + +Another easy backport from EIP721 and EIP1155 might be the inclusion of a metadata URI for tokens, allowing them to easily reference logo and other details. This has not been included, both in order to keep this EIP as minimal as possible, and because it is already sufficiently covered by [EIP-1046](./eip-1046.md). + +## Backwards Compatibility + +There are no issues with backwards compatibility in this EIP, as the full suite of ERC-20 functions is unchanged. + +## Test Cases +Test cases have been provided in the implementation repo [here](https://github.com/wschwab/SaferERC-20/blob/main/src/SaferERC-20.t.sol). + +## Reference Implementation +A sample repo demonstrating an implementation of this EIP has been created [here](https://github.com/wschwab/SaferERC-20). It is (as of this writing) in a Dapptools environment, for details on installing and running Dapptools see the Dapptools repo. + +## Security Considerations + +`onERC20Received` is a callback function. Callback functions have been exploited in the past as a reentrancy vector, and care should be taken to make sure implementations are not vulnerable. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4527.md b/EIPS/eip-4527.md new file mode 100644 index 0000000..76e1e8a --- /dev/null +++ b/EIPS/eip-4527.md @@ -0,0 +1,239 @@ +--- +eip: 4527 +title: QR Code transmission protocol for wallets +description: QR Code data transmission protocol between wallets and offline signers. +author: Aaron Chen (@aaronisme), Sora Lee (@soralit), ligi (@ligi), Dan Miller (@danjm), AndreasGassmann (@andreasgassmann), xardass (@xardass), Lixin Liu (@BitcoinLixin) +discussions-to: https://ethereum-magicians.org/t/add-qr-code-scanning-between-software-wallet-cold-signer-hardware-wallet/6568 +status: Review +type: Standards Track +category: ERC +created: 2021-12-07 +--- + +## Abstract + +The purpose of this EIP is to provide a process and data transmission protocol via QR Code between offline signers and watch-only wallets. + +## Motivation + +There is an increasing number of users whom like to use complete offline signers to manage their private keys, signers like hardware wallets and mobile phones in offline mode. In order to sign transactions or data, these offline signers have to rely on a watch-only wallet since it would prepare the data to be signed. Currently, there are 4 possible data transmission methods between offline signers and watch-only wallets: QR Code, USB, Bluetooth, and file transfer. The QR Code data transmission method have the following advantages when compared to the other three methods mentioned above: + +- Transparency and Security: Compared to USB or Bluetooth, users can easily decode the data via QR Code (with the help of some tools). It can also help users clearly identify what they are going to sign, which improves transparency and thus better security. +- Improved Compatibility: Compared to USB and Bluetooth, QR Code data transmissions has a wider range of compatibility. Normally, it wouldn’t be broken by software changes like browser upgrades, system upgrade, and etc. +- Improved User experience: QR Code data transmissions can provide a better user experience compared to USB, Bluetooth, and file transfer especially when the user is using a mobile device. +- A smaller attack surface: USB and Bluetooth have a bigger attack surface than QR-Codes. + +Due to these advantages, QR Code data transmissions is a better choice. Unfortunately, there is no modern standard for how offline signers should work with watch-only wallets nor how data should be encoded. +This EIP presents a standard process and data transmission protocol for offline signers to work with watch-only wallets. + +## Specification + +**Offline signer**: An offline signer is a device or application which holds the user’s private keys and does not have network access. + +**Watch-only wallet**: A watch-only wallet is a wallet that has network access and can interact with the Ethereum blockchain. + +### Process + +In order to work with offline signers, the watch-only wallet should follow the following process. + +1. The offline signer provides the public key information to the watch-only wallet to generate addresses, sync balances and etc via QR Codes. +2. The watch-only wallet generates the unsigned data and sends it to an offline signer for signing via QR Code, data that can include transactions, typed data, and etc. +3. The offline signer signs the data and provides a signature back to the watch-only wallet via QR Code. +4. The watch-only wallet receives the signature, constructs the signed data (transaction) and performs the following activities like broadcasting the transaction etc. + +### Data Transmission Protocol + +Since a single QR Code can only contain a limited amount of data, animated QR Codes should be utilized for data transmission. The `BlockchainCommons` have published a series of data transmission protocol called Uniform Resources (UR). It provides a basic method to encode data into animated QR Codes. This EIP will use UR and extend its current definition. + +`Concise Binary Object Representation(CBOR)` will be used for binary data encoding. `Concise Data Definition Language(CDDL)` will be used for expressing the CBOR. + +### Setting up the watch-only wallet with the offline signer + +In order to allow a watch-only wallet to collect information from the Ethereum blockchain, the offline signer would need to provide the public keys to the watch-only wallet in which the wallet will use them to query the necessary information from the Ethereum blockchain. + +In such a case, offline signers should provide the extended public keys and derivation path. The UR Type called `crypto-hdkey` will be used to encode this data and the derivation path will be encoded as `crypto-keypath`. + + +#### CDDL for Key Path + +The `crypto-keypath` will be used to specify the key path.The following specification is written in Concise Data Definition Language(CDDL) for `crypto-key-path` + +``` +; Metadata for the derivation path of a key. +; +; `source-fingerprint`, if present, is the fingerprint of the +; ancestor key from which the associated key was derived. +; +; If `components` is empty, then `source-fingerprint` MUST be a fingerprint of +; a master key. +; +; `depth`, if present, represents the number of derivation steps in +; the path of the associated key, even if not present in the `components` element +; of this structure. + crypto-keypath = { + components: [path-component], ; If empty, source-fingerprint MUST be present + ? source-fingerprint: uint32 .ne 0 ; fingerprint of ancestor key, or master key if components is empty + ? depth: uint8 ; 0 if this is a public key derived directly from a master key + } + path-component = ( + child-index / child-index-range / child-index-wildcard-range, + is-hardened + ) + uint32 = uint .size 4 + uint31 = uint32 .lt 2147483648 ;0x80000000 + child-index = uint31 + child-index-range = [child-index, child-index] ; [low, high] where low < high + child-index-wildcard = [] + is-hardened = bool + components = 1 + source-fingerprint = 2 + depth = 3 +``` + +#### CDDL for Extended Public Keys + +Since the purpose is to transfer public key data, the definition of `crypto-hdkey` will be kept only for public key usage purposes. + +The following specification is written in Concise Data Definition Language `CDDL` and includes the crypto-keypath spec above. + +``` +; An hd-key must be a derived key. +hd-key = { + derived-key +} +; A derived key must be public, has an optional chain code, and +; may carry additional metadata about its use and derivation. +; To maintain isomorphism with [BIP32] and allow keys to be derived from +; this key `chain-code`, `origin`, and `parent-fingerprint` must be present. +; If `origin` contains only a single derivation step and also contains `source-fingerprint`, +; then `parent-fingerprint` MUST be identical to `source-fingerprint` or may be omitted. +derived-key = ( + key-data: key-data-bytes, + ? chain-code: chain-code-bytes ; omit if no further keys may be derived from this key + ? origin: #6.304(crypto-keypath), ; How the key was derived + ? name: text, ; A short name for this key. + ? source: text, ; The device info or any other description for this key +) +key-data = 3 +chain-code = 4 +origin = 6 +name = 9 +source = 10 + +uint8 = uint .size 1 +key-data-bytes = bytes .size 33 +chain-code-bytes = bytes .size 32 +``` + +If the chain-code is provided, then it can be used to derive child keys but if it isn’t provided, it is simply a solo key and the origin can be provided to indicate the derivation key path. + +If the signer would like to provide muliple public keys instead of the extended public key for any reason, the signer can use `crypto-account` for that. + +### Sending the unsigned data from the watch-only wallet to the offline signer + +To send the unsigned data from a watch-only wallet to an offline signer, the new UR type `eth-sign-request` will be introduced to encode the signing request. + +#### CDDL for Eth Sign Request. + +The following specification is written in Concise Data Definition Language `CDDL`. +UUIDs in this specification notated UUID are CBOR binary strings tagged with #6.37, per the IANA `CBOR Tags Registry`. + +``` +; Metadata for the signing request for Ethereum. +; +sign-data-type = { + type: int .default 1 transaction data; the unsigned data type +} + +eth-transaction-data = 1; legacy transaction rlp encoding of unsigned transaction data +eth-typed-data = 2; EIP-712 typed signing data +eth-raw-bytes=3; for signing message usage, like EIP-191 personal_sign data +eth-typed-transaction=4; EIP-2718 typed transaction of unsigned transaction data + +; Metadata for the signing request for Ethereum. +; request-id: the identifier for this signing request. +; sign-data: the unsigned data +; data-type: see sign-data-type definition +; chain-id: chain id definition see https://github.com/ethereum-lists/chains for detail +; derivation-path: the key path of the private key to sign the data +; address: Ethereum address of the signing type for verification purposes which is optional + +eth-sign-request = ( + sign-data: sign-data-bytes, ; sign-data is the data to be signed by offline signer, currently it can be unsigned transaction or typed data + data-type: #3.401(sign-data-type), + chain-id: int .default 1, + derivation-path: #5.304(crypto-keypath), ;the key path for signing this request + ?request-id: uuid, ; the uuid for this signing request + ?address: eth-address-bytes, ;verification purpose for the address of the signing key + ?origin: text ;the origin of this sign request, like wallet name +) +request-id = 1 +sign-data = 2 +data-type = 3 +chain-id = 4 ;it will be the chain id of ethereum related blockchain +derivation-path = 5 +address = 6 +origin = 7 +eth-address-bytes = bytes .size 20 +sign-data-bytes = bytes ; for unsigned transactions it will be the rlp encoding for unsigned transaction data and ERC 712 typed data it will be the bytes of json string. +``` + +### The signature provided by offline signers to watch-only wallets + +After the data is signed, the offline signer should send the signature back to the watch-only wallet. The new UR type called `eth-signature` is introduced here to encode this data. + +#### CDDL for Eth Signature. + +The following specification is written in Concise Data Definition Language `CDDL`. + +``` +eth-signature = ( + request-id: uuid, + signature: eth-signature-bytes, + ? origin: text, ; The device info for providing this signature +) + +request-id = 1 +signature = 2 +origin = 3 + +eth-signature-bytes = bytes .size 65; the signature of the signing request (r,s,v) +``` + +## Rationale + +This EIP uses some existing UR types like `crypto-keypath` and `crypto-hdkey` and also introduces some new UR types like `eth-sign-request` and `eth-signature`. Here are the reasons we choose UR for the QR Code data transmission protocol: + +### UR provides a solid foundation for QR Code data transmission + +- Uses the alphanumeric QR code mode for efficiency. +- Includes a CRC32 checksum of the entire message in each part to tie the different parts of the QR code together and ensure the transmitted message has been reconstructed. +- uses `Fountain Code` for the arbitrary amount of data which can be both a minimal, finite sequence of parts and an indefinite sequence of parts. The Fountain Code can ultimately help the receiver to make the data extraction easier. + +### UR provides existing helpful types and scalability for new usages + +Currently, UR has provided some existing types like `crypto-keypath` and `crypto-hdkey` so it is quite easy to add a new type and definitions for new usages. + +### UR has an active air-gapped wallet community. + +Currently, the UR has an active `airgapped wallet community` which continues to improve the UR forward. + +## Backwards Compatibility + +Currently, there is no existing protocol to define data transmissions via QR Codes so there are no backward compatibility issues that needs to be addressed now. + +## Test Cases + +The test cases can be found on the `ur-registry-eth` package released by the Keystone team. + +## Reference Implementation + +The reference implementation can be found on the `ur-registry-eth` package released by the Keystone team. + +## Security Considerations + +The offline signer should decode all the data from `eth-sign-request` and show them to the user for confirmation prior to signing. It is recommended to provide an address field in the `eth-sign-request`. If provided, the offline signer should verify the address being the same one as the address associated with the signing key. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4546.md b/EIPS/eip-4546.md new file mode 100644 index 0000000..c27fcab --- /dev/null +++ b/EIPS/eip-4546.md @@ -0,0 +1,173 @@ +--- +eip: 4546 +title: Wrapped Deposits +description: A singleton contract for managing asset deposits. +author: Justice Hudson (@jchancehud) +discussions-to: https://ethereum-magicians.org/t/wrapped-deposit-contract-eip/7740 +status: Stagnant +type: Standards Track +category: ERC +created: 2021-12-11 +--- + +## Abstract +The wrapped deposit contract handles deposits of assets (Ether, [ERC-20](./eip-20.md), [ERC-721](./eip-721.md)) on behalf of a user. A user must only approve a spend limit once and then an asset may be deposited to any number of different applications that support deposits from the contract. + +## Motivation +The current user flow for depositing assets in dapps is unnecessarily expensive and insecure. To deposit an ERC-20 asset a user must either: + + - send an approve transaction for the exact amount being sent, before making a deposit, and then repeat this process for every subsequent deposit. + - send an approve transaction for an infinite spend amount before making deposits. + +The first option is inconvenient, and expensive. The second option is insecure. Further, explaining approvals to new or non-technical users is confusing. This has to be done in _every_ dapp that supports ERC20 deposits. + +## Specification +The wrapped deposit contract SHOULD be deployed at an identifiable address (e.g. `0x1111119a9e30bceadf9f939390293ffacef93fe9`). The contract MUST be non-upgradable with no ability for state variables to be changed. + +The wrapped deposit contract MUST have the following public functions: + +```js +depositERC20(address to, address token, uint amount) external; +depositERC721(address to, address token, uint tokenId) external; +safeDepositERC721(address to, address token, uint tokenId, bytes memory data) external; +safeDepositERC1155(address to, address token, uint tokenId, uint value, bytes calldata data) external; +batchDepositERC1155(address to, address token, uint[] calldata tokenIds, uint[] calldata values, bytes calldata data) external; +depositEther(address to) external payable; +``` + +Each of these functions MUST revert if `to` is an address with a zero code size. Each function MUST attempt to call a method on the `to` address confirming that it is willing and able to accept the deposit. If this function call does not return a true value execution MUST revert. If the asset transfer is not successful execution MUST revert. + +The following interfaces SHOULD exist for contracts wishing to accept deposits: + +```ts +interface ERC20Receiver { + function acceptERC20Deposit(address depositor, address token, uint amount) external returns (bool); +} + +interface ERC721Receiver { + function acceptERC721Deposit(address depositor, address token, uint tokenId) external returns (bool); +} + +interface ERC1155Receiver { + function acceptERC1155Deposit(address depositor, address token, uint tokenId, uint value, bytes calldata data) external returns (bool); + function acceptERC1155BatchDeposit(address depositor, address token, uint[] calldata tokenIds, uint[] calldata values, bytes calldata data) external returns (bool); +} + +interface EtherReceiver { + function acceptEtherDeposit(address depositor, uint amount) external returns (bool); +} +``` + +A receiving contract MAY implement any of these functions as desired. If a given function is not implemented deposits MUST not be sent for that asset type. + +## Rationale +Having a single contract that processes all token transfers allows users to submit a single approval per token to deposit to any number of contracts. The user does not have to trust receiving contracts with token spend approvals and receiving contracts have their complexity reduced by not having to implement token transfers themselves. + +User experience is improved because a simple global dapp can be implemented with the messaging: "enable token for use in other apps". + +## Backwards Compatibility + +This EIP is not backward compatible. Any contract planning to use this deposit system must implement specific functions to accept deposits. Existing contracts that are upgradeable can add support for this EIP retroactively by implementing one or more accept deposit functions. + +Upgraded contracts can allow deposits using both the old system (approving the contract itself) and the proposed deposit system to preserve existing approvals. New users should be prompted to use the proposed deposit system. + +## Reference Implementation +```ts +pragma solidity ^0.7.0; + +interface ERC20Receiver { + function acceptERC20Deposit(address depositor, address token, uint amount) external returns (bool); +} + +interface ERC721Receiver { + function acceptERC721Deposit(address depositor, address token, uint tokenId) external returns (bool); +} + +interface ERC1155Receiver { + function acceptERC1155Deposit(address depositor, address token, uint tokenId, uint value, bytes calldata data) external returns (bool); + function acceptERC1155BatchDeposit(address depositor, address token, uint[] calldata tokenIds, uint[] calldata values, bytes calldata data) external returns (bool); +} + +interface EtherReceiver { + function acceptEtherDeposit(address depositor, uint amount) external returns (bool); +} + +interface IERC20 { + function transferFrom(address sender, address recipient, uint amount) external returns (bool); +} + +interface IERC721 { + function transferFrom(address _from, address _to, uint256 _tokenId) external payable; + function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes memory data) external payable; +} + +interface IERC1155 { + function safeTransferFrom(address _from, address _to, uint _id, uint _value, bytes calldata _data) external; + function safeBatchTransferFrom(address _from, address _to, uint256[] calldata _ids, uint256[] calldata _values, bytes calldata _data) external; +} + +contract WrappedDeposit { + function depositERC20(address to, address token, uint amount) public { + _assertContract(to); + require(ERC20Receiver(to).acceptERC20Deposit(msg.sender, token, amount)); + bytes memory data = abi.encodeWithSelector( + IERC20(token).transferFrom.selector, + msg.sender, + to, + amount + ); + (bool success, bytes memory returndata) = token.call(data); + require(success); + // backward compat for tokens incorrectly implementing the transfer function + if (returndata.length > 0) { + require(abi.decode(returndata, (bool)), "ERC20 operation did not succeed"); + } + } + + function depositERC721(address to, address token, uint tokenId) public { + _assertContract(to); + require(ERC721Receiver(to).acceptERC721Deposit(msg.sender, token, tokenId)); + IERC721(token).transferFrom(msg.sender, to, tokenId); + } + + function safeDepositERC721(address to, address token, uint tokenId, bytes memory data) public { + _assertContract(to); + require(ERC721Receiver(to).acceptERC721Deposit(msg.sender, token, tokenId)); + IERC721(token).safeTransferFrom(msg.sender, to, tokenId, data); + } + + function safeDepositERC1155(address to, address token, uint tokenId, uint value, bytes calldata data) public { + _assertContract(to); + require(ERC1155Receiver(to).acceptERC1155Deposit(msg.sender, to, tokenId, value, data)); + IERC1155(token).safeTransferFrom(msg.sender, to, tokenId, value, data); + } + + function batchDepositERC1155(address to, address token, uint[] calldata tokenIds, uint[] calldata values, bytes calldata data) public { + _assertContract(to); + require(ERC1155Receiver(to).acceptERC1155BatchDeposit(msg.sender, to, tokenIds, values, data)); + IERC1155(token).safeBatchTransferFrom(msg.sender, to, tokenIds, values, data); + } + + function depositEther(address to) public payable { + _assertContract(to); + require(EtherReceiver(to).acceptEtherDeposit(msg.sender, msg.value)); + (bool success, ) = to.call{value: msg.value}(''); + require(success, "nonpayable"); + } + + function _assertContract(address c) private view { + uint size; + assembly { + size := extcodesize(c) + } + require(size > 0, "noncontract"); + } +} +``` +## Security Considerations +The wrapped deposit implementation should be as small as possible to reduce the risk of bugs. The contract should be small enough that an engineer can read and understand it in a few minutes. + +Receiving contracts MUST verify that `msg.sender` is equal to the wrapped deposit contract. Failing to do so allows anyone to simulate deposits. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4573.md b/EIPS/eip-4573.md new file mode 100644 index 0000000..9015b07 --- /dev/null +++ b/EIPS/eip-4573.md @@ -0,0 +1,195 @@ +--- +eip: 4573 +title: Procedures for the EVM +description: Introduces support for EVM Procedures. +status: Stagnant +type: Standards Track +category: Core +author: Greg Colvin (@gcolvin), Greg Colvin +discussions-to: https://ethereum-magicians.org/t/eip-4573-named-procedures-for-evm-code-sections/7776 +created: 2021-12-16 +requires: 2315, 3540, 3670, 3779, 4200 +--- + +## Abstract + +Five EVM instructions are introduced to define, call, and return from named EVM _procedures_ and access their _call frames_ in memory - `ENTERPROC`, `LEAVEPROC`, `CALLPROC`, `RETURNPROC`, and `FRAMEADDRESS`. + +## Motivation + +Currently, Ethereum bytecode has no syntactic structure, and _subroutines_ have no defined interfaces. + +We propose to add _procedures_ -- delimited blocks of code that can be entered only by calling into them via defined interfaces. + +Also, the EVM currently has no automatic management of memory for _procedures_. So we also propose to automatically reserve call frames on an in-memory stack. + +Constraints on the use of _procedures_ must be validated at contract initialization time to maintain the safety properties of [EIP-3779](./eip-3779.md): Valid programs will not halt with an exception unless they run out of gas or recursively overflow stack. + +### Prior Art + +The terminology is not well-defined, but we will follow Intel in calling the low-level concept _subroutines_ and the higher level concept _procedures_. The distinction is that _subroutines_ are little more than a jump that knows where it came from, whereas procedures have a defined interface and manage memory as a stack. [EIP-2315](./eip-2315.md) introduces _subroutines_, and this EIP introduces _procedures_. + +## Specification + +### Instructions + +#### ENTERPROC (0x??) dest_section: uint8, dest_offset: uint8, n_inputs: uint16, n_outputs: uint16, n_locals: uint16 +``` +frame_stack.push(FP) +FP -= n_locals * 32 +PC +- +``` +Marks the entry point to a procedure +* at offset `dest_offset` from the beginning of the `dest_section`. +* taking `n_inputs` arguments from the data stack, +* returning `n_outputs` values on the `data stack`, and +* reserving `n_locals` words of data in memory on the `frame stack`. + +Procedures can only be entered via a `CALLPROC` to their entry point. + +#### LEAVEPROC (0x??) + +``` + FP = frame_stack.pop() + asm RETURNSUB +``` +> Pop the `frame stack` and return to the calling procedure using `RETURNSUB`. + +Marks the end of a procedure. Each `ENTERPROC` requires a closing `LEAVEPROC`. + +*Note: Attempts to jump into a procedure (including its `LEAVEPROC`) from outside of the procedure or to jump or step to `ENTERPROC` at all must be prevented at validation time. `CALLPROC` is the only valid way to enter a procedure.* + +#### CALLPROC (0x??) dest_section: uint16, dest_proc: uint16 + ``` + FP -= n_locals + asm JUMPSUB + +``` +> Allocate a *stack frame* and transfer control and `JUMPSUB` to the Nth (N=*dest_proc*) _procedure_ in the Mth(M=*dest_section*) _section_ of the code. _Section 0_ is the current code section, any other code sections are indexed starting at _1_. + +*Note: That the procedure is defined and the required `n_inputs` words are available on the `data stack` must be shown at validation time.* + +#### RETURNPROC (0x??) +``` + FP += n_locals + asm RETURNSUB +``` +> Pop the `frame stack` and return control to the calling procedure using `RETURNSUB`. + +*Note: That the promised `n_outputs` words are available on the `data stack` must be shown at validation time.* + +#### FRAMEADDRESS (0x??) offset: int16 +``` +asm PUSH2 FP + offset +``` +> Push the address `FP + offset` onto the data stack. + +Call frame data is addressed at an immediate `offset` relative to `FP`. + +Typical usage includes storing data on a call frame +``` +PUSH 0xdada +FRAMEADDRESS 32 +MSTORE +``` +and loading data from a call frame +``` +FRAMEADDRESS 32 +MLOAD +``` + +### Memory Costs + +Presently,`MSTORE` is defined as +``` + memory[stack[0]...stack[0]+31] = stack[1] + memory_size = max(memory_size,floor((stack[0]+32)÷32) +``` +* where `memory_size` is the number of active words of memory above _0_. + +We propose to treat memory addresses as signed, so the formula needs to be +``` + memory[stack[0]...stack[0]+31] = stack[1] + if (stack[0])+32)÷32) < 0 + negative_memory_size = max(negative_memory_size,floor((stack[0]+32)÷32)) + else + positive_memory_size = max(positive_memory_size,floor((stack[0]+32)÷32)) + memory_size = positive_memory_size + negative_memory_size +``` +* where `negative_memory_size` is the number of active words of memory below _0_ and +* where `positive_memory_size` is the number of active words of memory at or above _0_. + +### Call Frame Stack + +These instructions make use of a `frame stack` to allocate and free frames of local data for _procedures_ in memory. Frame memory begins at address 0 in memory and grows downwards, towards more negative addresses. A frame is allocated for each procedure when it is called, and freed when it returns. + +Memory can be addressed relative to the frame pointer `FP` or by absolute address. `FP` starts at 0, and moves downward towards more negative addresses to point to the frame for each `CALLPROC` and moving upward towards less negative addresses to point to the previous frame for the corresponding `RETURNPROC`. + +Equivalently, in the EVM's twos-complement arithmetic, `FP` moves from the highest address down, as is common in many calling conventions. + +For example, after an initial `CALLPROC` to a procedure needing two words of data the `frame stack` might look like this + +``` + 0-> ........ + ........ + FP-> +``` +Then, after a further `CALLPROC` to a procedure needing three words of data the `frame stack` would like this + +``` + 0-> ........ + ........ + -64-> ........ + ........ + ........ + FP-> +``` +After a `RETURNPROC` from that procedure the `frame stack` would look like this +``` + 0-> ........ + ........ + FP-> ........ + ........ + ........ +``` +and after a final `RETURNPROC`, like this +``` + FP-> ........ + ........ + ........ + ........ + ........ +``` + +## Rationale + +There is actually not much new here. It amounts to [EIP-615](./eip-615.md), refined and refactored into bite-sized pieces, along lines common to other machines. + +This proposal uses the [EIP-2315](./eip-2315.md) return stack to manage calls and returns, and steals ideas from [EIP-615](./eip-615.md), [EIP-3336](./eip-3336.md), and [EIP-4200](./eip-4200.md). `ENTERPROC` corresponds to `BEGINSUB` from EIP-615. Like EIP-615 it uses a frame stack to track call-frame addresses with `FP` as _procedures_ are entered and left, but like EIP-3336 and EIP-3337 it moves call frames from the data stack to memory. + +Aliasing call frames with ordinary memory supports addressing call-frame data with ordinary stores and loads. This is generally useful, especially for languages like C that provide pointers to variables on the stack. + +The design model here is the _subroutines_ and _procedures_ of the Intel x86 architecture. +* `JUMPSUB` and `RETURNSUB` (from [EIP-2315](./eip-2315.md) -- like `CALL` and `RET` -- jump to and return from _subroutines_. +* `ENTERPROC` -- like `ENTER` -- sets up the stack frame for a _procedure_. +* `CALLPROC` amounts to a `JUMPSUB` to an `ENTERPROC`. +* `RETURNPROC` amounts to an early `LEAVEPROC`. +* `LEAVEPROC` -- like `LEAVE` -- takes down the stack frame for a _procedure_. It then executes a `RETURNSUB`. + +## Backwards Compatibility + +This proposal adds new EVM opcodes. It doesn't remove or change the semantics of any existing opcodes, so there should be no backwards compatibility issues. + +## Security + +Safe use of these constructs must be checked completely at validation time -- per EIP-3779 -- so there should be no security issues at runtime. + +`ENTERPROC` and `LEAVEPROC` must follow the same safety rules as for `JUMPSUB` and `RETURNSUB` in EIP-2315. In addition, the following constraints must be validated: + +* Every`ENTERPROC` must be followed by a `LEAVEPROC` to delimit the bodies of _procedures_. +* There can be no nested _procedures_. +* There can be no jump into the body of a procedure (including its `LEAVEPROC`) from outside of that body. +* There can be no jump or step to `BEGINPROC` at all -- only `CALLPROC`. +* The specified `n_inputs` and `n_outputs` must be on the stack. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4626.md b/EIPS/eip-4626.md new file mode 100644 index 0000000..c28df5c --- /dev/null +++ b/EIPS/eip-4626.md @@ -0,0 +1,612 @@ +--- +eip: 4626 +title: Tokenized Vaults +description: Tokenized Vaults with a single underlying EIP-20 token. +author: Joey Santoro (@joeysantoro), t11s (@transmissions11), Jet Jadeja (@JetJadeja), Alberto Cuesta Cañada (@alcueca), Señor Doggo (@fubuloubu) +discussions-to: https://ethereum-magicians.org/t/eip-4626-yield-bearing-vault-standard/7900 +status: Final +type: Standards Track +category: ERC +created: 2021-12-22 +requires: 20, 2612 +--- + +## Abstract + +The following standard allows for the implementation of a standard API for tokenized Vaults +representing shares of a single underlying [EIP-20](./eip-20.md) token. +This standard is an extension on the EIP-20 token that provides basic functionality for depositing +and withdrawing tokens and reading balances. + +## Motivation + +Tokenized Vaults have a lack of standardization leading to diverse implementation details. +Some various examples include lending markets, aggregators, and intrinsically interest bearing tokens. +This makes integration difficult at the aggregator or plugin layer for protocols which need to conform to many standards, and forces each protocol to implement their own adapters which are error prone and waste development resources. + +A standard for tokenized Vaults will lower the integration effort for yield-bearing vaults, while creating more consistent and robust implementation patterns. + +## Specification + +All [EIP-4626](./eip-4626.md) tokenized Vaults MUST implement EIP-20 to represent shares. +If a Vault is to be non-transferrable, it MAY revert on calls to `transfer` or `transferFrom`. +The EIP-20 operations `balanceOf`, `transfer`, `totalSupply`, etc. operate on the Vault "shares" +which represent a claim to ownership on a fraction of the Vault's underlying holdings. + +All EIP-4626 tokenized Vaults MUST implement EIP-20's optional metadata extensions. +The `name` and `symbol` functions SHOULD reflect the underlying token's `name` and `symbol` in some way. + +EIP-4626 tokenized Vaults MAY implement [EIP-2612](./eip-2612.md) to improve the UX of approving shares on various integrations. + +### Definitions: + +- asset: The underlying token managed by the Vault. + Has units defined by the corresponding EIP-20 contract. +- share: The token of the Vault. Has a ratio of underlying assets + exchanged on mint/deposit/withdraw/redeem (as defined by the Vault). +- fee: An amount of assets or shares charged to the user by the Vault. Fees can exists for + deposits, yield, AUM, withdrawals, or anything else prescribed by the Vault. +- slippage: Any difference between advertised share price and economic realities of + deposit to or withdrawal from the Vault, which is not accounted by fees. + +### Methods + +#### asset + +The address of the underlying token used for the Vault for accounting, depositing, and withdrawing. + +MUST be an EIP-20 token contract. + +MUST _NOT_ revert. + +```yaml +- name: asset + type: function + stateMutability: view + + inputs: [] + + outputs: + - name: assetTokenAddress + type: address +``` + +#### totalAssets + +Total amount of the underlying asset that is "managed" by Vault. + +SHOULD include any compounding that occurs from yield. + +MUST be inclusive of any fees that are charged against assets in the Vault. + +MUST _NOT_ revert. + +```yaml +- name: totalAssets + type: function + stateMutability: view + + inputs: [] + + outputs: + - name: totalManagedAssets + type: uint256 +``` + +#### convertToShares + +The amount of shares that the Vault would exchange for the amount of assets provided, in an ideal scenario where all the conditions are met. + +MUST NOT be inclusive of any fees that are charged against assets in the Vault. + +MUST NOT show any variations depending on the caller. + +MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. + +MUST NOT revert unless due to integer overflow caused by an unreasonably large input. + +MUST round down towards 0. + +This calculation MAY NOT reflect the "per-user" price-per-share, and instead should reflect the "average-user's" price-per-share, meaning what the average user should expect to see when exchanging to and from. + +```yaml +- name: convertToShares + type: function + stateMutability: view + + inputs: + - name: assets + type: uint256 + + outputs: + - name: shares + type: uint256 +``` + +#### convertToAssets + +The amount of assets that the Vault would exchange for the amount of shares provided, in an ideal scenario where all the conditions are met. + +MUST NOT be inclusive of any fees that are charged against assets in the Vault. + +MUST NOT show any variations depending on the caller. + +MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. + +MUST NOT revert unless due to integer overflow caused by an unreasonably large input. + +MUST round down towards 0. + +This calculation MAY NOT reflect the "per-user" price-per-share, and instead should reflect the "average-user's" price-per-share, meaning what the average user should expect to see when exchanging to and from. + +```yaml +- name: convertToAssets + type: function + stateMutability: view + + inputs: + - name: shares + type: uint256 + + outputs: + - name: assets + type: uint256 +``` + +#### maxDeposit + +Maximum amount of the underlying asset that can be deposited into the Vault for the `receiver`, through a `deposit` call. + +MUST return the maximum amount of assets `deposit` would allow to be deposited for `receiver` and not cause a revert, which MUST NOT be higher than the actual maximum that would be accepted (it should underestimate if necessary). This assumes that the user has infinite assets, i.e. MUST NOT rely on `balanceOf` of `asset`. + +MUST factor in both global and user-specific limits, like if deposits are entirely disabled (even temporarily) it MUST return 0. + +MUST return `2 ** 256 - 1` if there is no limit on the maximum amount of assets that may be deposited. + +MUST NOT revert. + +```yaml +- name: maxDeposit + type: function + stateMutability: view + + inputs: + - name: receiver + type: address + + outputs: + - name: maxAssets + type: uint256 +``` + +#### previewDeposit + +Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given current on-chain conditions. + +MUST return as close to and no more than the exact amount of Vault shares that would be minted in a `deposit` call in the same transaction. I.e. `deposit` should return the same or more `shares` as `previewDeposit` if called in the same transaction. + +MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the deposit would be accepted, regardless if the user has enough tokens approved, etc. + +MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. + +MUST NOT revert due to vault specific user/global limits. MAY revert due to other conditions that would also cause `deposit` to revert. + +Note that any unfavorable discrepancy between `convertToShares` and `previewDeposit` SHOULD be considered slippage in share price or some other type of condition, meaning the depositor will lose assets by depositing. + +```yaml +- name: previewDeposit + type: function + stateMutability: view + + inputs: + - name: assets + type: uint256 + + outputs: + - name: shares + type: uint256 +``` + +#### deposit + +Mints `shares` Vault shares to `receiver` by depositing exactly `assets` of underlying tokens. + +MUST emit the `Deposit` event. + +MUST support EIP-20 `approve` / `transferFrom` on `asset` as a deposit flow. +MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the `deposit` execution, and are accounted for during `deposit`. + +MUST revert if all of `assets` cannot be deposited (due to deposit limit being reached, slippage, the user not approving enough underlying tokens to the Vault contract, etc). + +Note that most implementations will require pre-approval of the Vault with the Vault's underlying `asset` token. + +```yaml +- name: deposit + type: function + stateMutability: nonpayable + + inputs: + - name: assets + type: uint256 + - name: receiver + type: address + + outputs: + - name: shares + type: uint256 +``` + +#### maxMint + +Maximum amount of shares that can be minted from the Vault for the `receiver`, through a `mint` call. + +MUST return the maximum amount of shares `mint` would allow to be deposited to `receiver` and not cause a revert, which MUST NOT be higher than the actual maximum that would be accepted (it should underestimate if necessary). This assumes that the user has infinite assets, i.e. MUST NOT rely on `balanceOf` of `asset`. + +MUST factor in both global and user-specific limits, like if mints are entirely disabled (even temporarily) it MUST return 0. + +MUST return `2 ** 256 - 1` if there is no limit on the maximum amount of shares that may be minted. + +MUST NOT revert. + +```yaml +- name: maxMint + type: function + stateMutability: view + + inputs: + - name: receiver + type: address + + outputs: + - name: maxShares + type: uint256 +``` + +#### previewMint + +Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given current on-chain conditions. + +MUST return as close to and no fewer than the exact amount of assets that would be deposited in a `mint` call in the same transaction. I.e. `mint` should return the same or fewer `assets` as `previewMint` if called in the same transaction. + +MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint would be accepted, regardless if the user has enough tokens approved, etc. + +MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. + +MUST NOT revert due to vault specific user/global limits. MAY revert due to other conditions that would also cause `mint` to revert. + +Note that any unfavorable discrepancy between `convertToAssets` and `previewMint` SHOULD be considered slippage in share price or some other type of condition, meaning the depositor will lose assets by minting. + +```yaml +- name: previewMint + type: function + stateMutability: view + + inputs: + - name: shares + type: uint256 + + outputs: + - name: assets + type: uint256 +``` + +#### mint + +Mints exactly `shares` Vault shares to `receiver` by depositing `assets` of underlying tokens. + +MUST emit the `Deposit` event. + +MUST support EIP-20 `approve` / `transferFrom` on `asset` as a mint flow. +MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the `mint` execution, and are accounted for during `mint`. + +MUST revert if all of `shares` cannot be minted (due to deposit limit being reached, slippage, the user not approving enough underlying tokens to the Vault contract, etc). + +Note that most implementations will require pre-approval of the Vault with the Vault's underlying `asset` token. + +```yaml +- name: mint + type: function + stateMutability: nonpayable + + inputs: + - name: shares + type: uint256 + - name: receiver + type: address + + outputs: + - name: assets + type: uint256 +``` + +#### maxWithdraw + +Maximum amount of the underlying asset that can be withdrawn from the `owner` balance in the Vault, through a `withdraw` call. + +MUST return the maximum amount of assets that could be transferred from `owner` through `withdraw` and not cause a revert, which MUST NOT be higher than the actual maximum that would be accepted (it should underestimate if necessary). + +MUST factor in both global and user-specific limits, like if withdrawals are entirely disabled (even temporarily) it MUST return 0. + +MUST NOT revert. + +```yaml +- name: maxWithdraw + type: function + stateMutability: view + + inputs: + - name: owner + type: address + + outputs: + - name: maxAssets + type: uint256 +``` + +#### previewWithdraw + +Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block, given current on-chain conditions. + +MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a `withdraw` call in the same transaction. I.e. `withdraw` should return the same or fewer `shares` as `previewWithdraw` if called in the same transaction. + +MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though the withdrawal would be accepted, regardless if the user has enough shares, etc. + +MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. + +MUST NOT revert due to vault specific user/global limits. MAY revert due to other conditions that would also cause `withdraw` to revert. + +Note that any unfavorable discrepancy between `convertToShares` and `previewWithdraw` SHOULD be considered slippage in share price or some other type of condition, meaning the depositor will lose assets by depositing. + +```yaml +- name: previewWithdraw + type: function + stateMutability: view + + inputs: + - name: assets + type: uint256 + + outputs: + - name: shares + type: uint256 +``` + +#### withdraw + +Burns `shares` from `owner` and sends exactly `assets` of underlying tokens to `receiver`. + +MUST emit the `Withdraw` event. + +MUST support a withdraw flow where the shares are burned from `owner` directly where `owner` is `msg.sender`. + +MUST support a withdraw flow where the shares are burned from `owner` directly where `msg.sender` has EIP-20 approval over the shares of `owner`. + +MAY support an additional flow in which the shares are transferred to the Vault contract before the `withdraw` execution, and are accounted for during `withdraw`. + +SHOULD check `msg.sender` can spend owner funds, assets needs to be converted to shares and shares should be checked for allowance. + +MUST revert if all of `assets` cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner not having enough shares, etc). + +Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed. Those methods should be performed separately. + +```yaml +- name: withdraw + type: function + stateMutability: nonpayable + + inputs: + - name: assets + type: uint256 + - name: receiver + type: address + - name: owner + type: address + + outputs: + - name: shares + type: uint256 +``` + +#### maxRedeem + +Maximum amount of Vault shares that can be redeemed from the `owner` balance in the Vault, through a `redeem` call. + +MUST return the maximum amount of shares that could be transferred from `owner` through `redeem` and not cause a revert, which MUST NOT be higher than the actual maximum that would be accepted (it should underestimate if necessary). + +MUST factor in both global and user-specific limits, like if redemption is entirely disabled (even temporarily) it MUST return 0. + +MUST NOT revert. + +```yaml +- name: maxRedeem + type: function + stateMutability: view + + inputs: + - name: owner + type: address + + outputs: + - name: maxShares + type: uint256 +``` + +#### previewRedeem + +Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block, given current on-chain conditions. + +MUST return as close to and no more than the exact amount of assets that would be withdrawn in a `redeem` call in the same transaction. I.e. `redeem` should return the same or more `assets` as `previewRedeem` if called in the same transaction. + +MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the redemption would be accepted, regardless if the user has enough shares, etc. + +MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. + +MUST NOT revert due to vault specific user/global limits. MAY revert due to other conditions that would also cause `redeem` to revert. + +Note that any unfavorable discrepancy between `convertToAssets` and `previewRedeem` SHOULD be considered slippage in share price or some other type of condition, meaning the depositor will lose assets by redeeming. + +```yaml +- name: previewRedeem + type: function + stateMutability: view + + inputs: + - name: shares + type: uint256 + + outputs: + - name: assets + type: uint256 +``` + +#### redeem + +Burns exactly `shares` from `owner` and sends `assets` of underlying tokens to `receiver`. + +MUST emit the `Withdraw` event. + +MUST support a redeem flow where the shares are burned from `owner` directly where `owner` is `msg.sender`. + +MUST support a redeem flow where the shares are burned from `owner` directly where `msg.sender` has EIP-20 approval over the shares of `owner`. + +MAY support an additional flow in which the shares are transferred to the Vault contract before the `redeem` execution, and are accounted for during `redeem`. + +SHOULD check `msg.sender` can spend owner funds using allowance. + +MUST revert if all of `shares` cannot be redeemed (due to withdrawal limit being reached, slippage, the owner not having enough shares, etc). + +Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed. Those methods should be performed separately. + +```yaml +- name: redeem + type: function + stateMutability: nonpayable + + inputs: + - name: shares + type: uint256 + - name: receiver + type: address + - name: owner + type: address + + outputs: + - name: assets + type: uint256 +``` + +### Events + +#### Deposit + +`sender` has exchanged `assets` for `shares`, and transferred those `shares` to `owner`. + +MUST be emitted when tokens are deposited into the Vault via the `mint` and `deposit` methods. + +```yaml +- name: Deposit + type: event + + inputs: + - name: sender + indexed: true + type: address + - name: owner + indexed: true + type: address + - name: assets + indexed: false + type: uint256 + - name: shares + indexed: false + type: uint256 +``` + +#### Withdraw + +`sender` has exchanged `shares`, owned by `owner`, for `assets`, and transferred those `assets` to `receiver`. + +MUST be emitted when shares are withdrawn from the Vault in `EIP-4626.redeem` or `EIP-4626.withdraw` methods. + +```yaml +- name: Withdraw + type: event + + inputs: + - name: sender + indexed: true + type: address + - name: receiver + indexed: true + type: address + - name: owner + indexed: true + type: address + - name: assets + indexed: false + type: uint256 + - name: shares + indexed: false + type: uint256 +``` + +## Rationale + +The Vault interface is designed to be optimized for integrators with a feature complete yet minimal interface. +Details such as accounting and allocation of deposited tokens are intentionally not specified, +as Vaults are expected to be treated as black boxes on-chain and inspected off-chain before use. + +EIP-20 is enforced because implementation details like token approval +and balance calculation directly carry over to the shares accounting. +This standardization makes the Vaults immediately compatible with all EIP-20 use cases in addition to EIP-4626. + +The mint method was included for symmetry and feature completeness. +Most current use cases of share-based Vaults do not ascribe special meaning to the shares such that +a user would optimize for a specific number of shares (`mint`) rather than specific amount of underlying (`deposit`). +However, it is easy to imagine future Vault strategies which would have unique and independently useful share representations. + +The `convertTo` functions serve as rough estimates that do not account for operation specific details like withdrawal fees, etc. +They were included for frontends and applications that need an average value of shares or assets, not an exact value possibly including slippage or other fees. +For applications that need an exact value that attempts to account for fees and slippage we have included a corresponding `preview` function to match each mutable function. These functions must not account for deposit or withdrawal limits, to ensure they are easily composable, the `max` functions are provided for that purpose. + +## Backwards Compatibility + +EIP-4626 is fully backward compatible with the EIP-20 standard and has no known compatibility issues with other standards. +For production implementations of Vaults which do not use EIP-4626, wrapper adapters can be developed and used. + +## Reference Implementation + +See [Solmate EIP-4626](https://github.com/Rari-Capital/solmate/blob/main/src/mixins/ERC4626.sol): +a minimal and opinionated implementation of the standard with hooks for developers to easily insert custom logic into deposits and withdrawals. + +See [Vyper EIP-4626](https://github.com/fubuloubu/ERC4626): +a demo implementation of the standard in Vyper, with hooks for share price manipulation and other testing needs. + +## Security Considerations + +Fully permissionless use cases could fall prey to malicious implementations which only conform to the interface but not the specification. +It is recommended that all integrators review the implementation for potential ways of losing user deposits before integrating. + +If implementors intend to support EOA account access directly, they should consider adding an additional function call for `deposit`/`mint`/`withdraw`/`redeem` with the means to accommodate slippage loss or unexpected deposit/withdrawal limits, since they have no other means to revert the transaction if the exact output amount is not achieved. + +The methods `totalAssets`, `convertToShares` and `convertToAssets` are estimates useful for display purposes, +and do _not_ have to confer the _exact_ amount of underlying assets their context suggests. + +The `preview` methods return values that are as close as possible to exact as possible. For that reason, they are manipulable by altering the on-chain conditions and are not always safe to be used as price oracles. This specification includes `convert` methods that are allowed to be inexact and therefore can be implemented as robust price oracles. For example, it would be correct to implement the `convert` methods as using a time-weighted average price in converting between assets and shares. + +Integrators of EIP-4626 Vaults should be aware of the difference between these view methods when integrating with this standard. Additionally, note that the amount of underlying assets a user may receive from redeeming their Vault shares (`previewRedeem`) can be significantly different than the amount that would be taken from them when minting the same quantity of shares (`previewMint`). The differences may be small (like if due to rounding error), or very significant (like if a Vault implements withdrawal or deposit fees, etc). Therefore integrators should always take care to use the preview function most relevant to their use case, and never assume they are interchangeable. + +Finally, EIP-4626 Vault implementers should be aware of the need for specific, opposing rounding directions across the different mutable and view methods, as it is considered most secure to favor the Vault itself during calculations over its users: + +- If (1) it's calculating how many shares to issue to a user for a certain amount of the underlying tokens they provide or (2) it's determining the amount of the underlying tokens to transfer to them for returning a certain amount of shares, it should round _down_. + +- If (1) it's calculating the amount of shares a user has to supply to receive a given amount of the underlying tokens or (2) it's calculating the amount of underlying tokens a user has to provide to receive a certain amount of shares, it should round _up_. + +The only functions where the preferred rounding direction would be ambiguous are the `convertTo` functions. To ensure consistency across all EIP-4626 Vault implementations it is specified that these functions MUST both always round _down_. Integrators may wish to mimic rounding up versions of these functions themselves, like by adding 1 wei to the result. + +Although the `convertTo` functions should eliminate the need for any use of an EIP-4626 Vault's `decimals` variable, it is still strongly recommended to mirror +the underlying token's `decimals` if at all possible, to eliminate possible sources of confusion and simplify integration across front-ends and for other off-chain users. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4671.md b/EIPS/eip-4671.md new file mode 100644 index 0000000..f7f55b9 --- /dev/null +++ b/EIPS/eip-4671.md @@ -0,0 +1,296 @@ +--- +eip: 4671 +title: Non-Tradable Tokens Standard +description: A standard interface for non-tradable tokens, aka badges or souldbound NFTs. +author: Omar Aflak (@omaraflak), Pol-Malo Le Bris, Marvin Martin (@MarvinMartin24) +discussions-to: https://ethereum-magicians.org/t/eip-4671-non-tradable-token/7976 +status: Stagnant +type: Standards Track +category: ERC +created: 2022-01-13 +requires: 165 +--- + +## Abstract + +A non-tradable token, or NTT, represents inherently personal possessions (material or immaterial), such as university diplomas, online training certificates, government issued documents (national id, driving license, visa, wedding, etc.), labels, and so on. + +As the name implies, non-tradable tokens are made to not be traded or transferred, they are "soulbound". They don't have monetary value, they are personally delivered to **you**, and they only serve as a **proof of possession/achievement**. + +In other words, the possession of a token carries a strong meaning in itself depending on **why** it was delivered. + +## Motivation + +We have seen in the past smart contracts being used to deliver university diplomas or driving licenses, for food labeling or attendance to events, and much more. All of these implementations are different, but they have a common ground: the tokens are **non-tradable**. + +The blockchain has been used for too long as a means of speculation, and non-tradable tokens want to be part of the general effort aiming to provide usefulness through the blockchain. + +By providing a common interface for non-tradable tokens, we allow more applications to be developed and we position blockchain technology as a standard gateway for verification of personal possessions and achievements. + +## Specification + +### Non-Tradable Token + +A NTT contract is seen as representing **one type of certificate** delivered by **one authority**. For instance, one NTT contract for the French National Id, another for Ethereum EIP creators, and so on... + +* An address might possess multiple tokens. Each token has a unique identifier: `tokenId`. +* An authority who delivers a certificate should be in position to revoke it. Think of driving licenses or weddings. However, it cannot delete your token, i.e. the record will show that you once owned a token from that contract. +* The most typical usage for third-parties will be to verify if a user has a valid token in a given contract. + +```solidity +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "./IERC165.sol"; + +interface IERC4671 is IERC165 { + /// Event emitted when a token `tokenId` is minted for `owner` + event Minted(address owner, uint256 tokenId); + + /// Event emitted when token `tokenId` of `owner` is revoked + event Revoked(address owner, uint256 tokenId); + + /// @notice Count all tokens assigned to an owner + /// @param owner Address for whom to query the balance + /// @return Number of tokens owned by `owner` + function balanceOf(address owner) external view returns (uint256); + + /// @notice Get owner of a token + /// @param tokenId Identifier of the token + /// @return Address of the owner of `tokenId` + function ownerOf(uint256 tokenId) external view returns (address); + + /// @notice Check if a token hasn't been revoked + /// @param tokenId Identifier of the token + /// @return True if the token is valid, false otherwise + function isValid(uint256 tokenId) external view returns (bool); + + /// @notice Check if an address owns a valid token in the contract + /// @param owner Address for whom to check the ownership + /// @return True if `owner` has a valid token, false otherwise + function hasValid(address owner) external view returns (bool); +} +``` + +#### Extensions + +##### Metadata + +An interface allowing to add metadata linked to each token. + +```solidity +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "./IERC4671.sol"; + +interface IERC4671Metadata is IERC4671 { + /// @return Descriptive name of the tokens in this contract + function name() external view returns (string memory); + + /// @return An abbreviated name of the tokens in this contract + function symbol() external view returns (string memory); + + /// @notice URI to query to get the token's metadata + /// @param tokenId Identifier of the token + /// @return URI for the token + function tokenURI(uint256 tokenId) external view returns (string memory); +} +``` + +##### Enumerable + +An interface allowing to enumerate the tokens of an owner. + +```solidity +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "./IERC4671.sol"; + +interface IERC4671Enumerable is IERC4671 { + /// @return emittedCount Number of tokens emitted + function emittedCount() external view returns (uint256); + + /// @return holdersCount Number of token holders + function holdersCount() external view returns (uint256); + + /// @notice Get the tokenId of a token using its position in the owner's list + /// @param owner Address for whom to get the token + /// @param index Index of the token + /// @return tokenId of the token + function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256); + + /// @notice Get a tokenId by it's index, where 0 <= index < total() + /// @param index Index of the token + /// @return tokenId of the token + function tokenByIndex(uint256 index) external view returns (uint256); +} +``` + +##### Delegation + +An interface allowing delegation rights of token minting. + +```solidity +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "./IERC4671.sol"; + +interface IERC4671Delegate is IERC4671 { + /// @notice Grant one-time minting right to `operator` for `owner` + /// An allowed operator can call the function to transfer rights. + /// @param operator Address allowed to mint a token + /// @param owner Address for whom `operator` is allowed to mint a token + function delegate(address operator, address owner) external; + + /// @notice Grant one-time minting right to a list of `operators` for a corresponding list of `owners` + /// An allowed operator can call the function to transfer rights. + /// @param operators Addresses allowed to mint + /// @param owners Addresses for whom `operators` are allowed to mint a token + function delegateBatch(address[] memory operators, address[] memory owners) external; + + /// @notice Mint a token. Caller must have the right to mint for the owner. + /// @param owner Address for whom the token is minted + function mint(address owner) external; + + /// @notice Mint tokens to multiple addresses. Caller must have the right to mint for all owners. + /// @param owners Addresses for whom the tokens are minted + function mintBatch(address[] memory owners) external; + + /// @notice Get the issuer of a token + /// @param tokenId Identifier of the token + /// @return Address who minted `tokenId` + function issuerOf(uint256 tokenId) external view returns (address); +} +``` + +##### Consensus + +An interface allowing minting/revocation of tokens based on a consensus of a predefined set of addresses. + +```solidity +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "./IERC4671.sol"; + +interface IERC4671Consensus is IERC4671 { + /// @notice Get voters addresses for this consensus contract + /// @return Addresses of the voters + function voters() external view returns (address[] memory); + + /// @notice Cast a vote to mint a token for a specific address + /// @param owner Address for whom to mint the token + function approveMint(address owner) external; + + /// @notice Cast a vote to revoke a specific token + /// @param tokenId Identifier of the token to revoke + function approveRevoke(uint256 tokenId) external; +} +``` + +##### Pull + +An interface allowing a token owner to pull his token to a another of his wallets (here `recipient`). The caller must provide a signature of the tuple `(tokenId, owner, recipient)` using the `owner` wallet. + +```solidity +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "./IERC4671.sol"; + +interface IERC4671Pull is IERC4671 { + /// @notice Pull a token from the owner wallet to the caller's wallet + /// @param tokenId Identifier of the token to transfer + /// @param owner Address that owns tokenId + /// @param signature Signed data (tokenId, owner, recipient) by the owner of the token + function pull(uint256 tokenId, address owner, bytes memory signature) external; +} +``` + +### NTT Store + +Non-tradable tokens are meant to be fetched by third-parties, which is why there needs to be a convenient way for users to expose some or all of their tokens. We achieve this result using a store which must implement the following interface. + +```solidity +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "./IERC165.sol"; + +interface IERC4671Store is IERC165 { + // Event emitted when a IERC4671Enumerable contract is added to the owner's records + event Added(address owner, address token); + + // Event emitted when a IERC4671Enumerable contract is removed from the owner's records + event Removed(address owner, address token); + + /// @notice Add a IERC4671Enumerable contract address to the caller's record + /// @param token Address of the IERC4671Enumerable contract to add + function add(address token) external; + + /// @notice Remove a IERC4671Enumerable contract from the caller's record + /// @param token Address of the IERC4671Enumerable contract to remove + function remove(address token) external; + + /// @notice Get all the IERC4671Enumerable contracts for a given owner + /// @param owner Address for which to retrieve the IERC4671Enumerable contracts + function get(address owner) external view returns (address[] memory); +} +``` + +## Rationale + +### On-chain vs Off-chain + +A decision was made to keep the data off-chain (via `tokenURI()`) for two main reasons: +* Non-tradable tokens represent personal possessions. Therefore, there might be cases where the data should be encrypted. The standard should not outline decisions about encryption because there are just so many ways this could be done, and every possibility is specific to the use-case. +* Non-tradable tokens must stay generic. There could have been a possibility to make a `MetadataStore` holding the data of tokens in an elegant way, unfortunately we would have needed a support for generics in solidity (or struct inheritance), which is not available today. + +## Reference Implementation + +You can find an implementation of this standard in [../assets/eip-4671](https://github.com/ethereum/EIPs/tree/master/assets/eip-4671). + +Using this implementation, this is how you would create a token: + +```solidity +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "./ERC4671.sol"; + +contract EIPCreatorBadge is ERC4671 { + constructor() ERC4671("EIP Creator Badge", "EIP") {} + + function giveThatManABadge(address owner) external { + require(_isCreator(), "You must be the contract creator"); + _mint(owner); + } + + function _baseURI() internal pure override returns (string memory) { + return "https://eips.ethereum.org/ntt/"; + } +} +``` + +This could be a contract managed by the Ethereum foundation and which allows them to deliver tokens to EIP creators. + +## Security Considerations + +One security aspect is related to the `tokenURI` method which returns the metadata linked to a token. Since the standard represents inherently personal possessions, users might want to encrypt the data in some cases e.g. national id cards. Moreover, it is the responsibility of the contract creator to make sure the URI returned by this method is available at all times. + +The standard does not define any way to transfer a token from one wallet to another. Therefore, users must be very cautious with the wallet they use to receive these tokens. If a wallet is lost, the only way to get the tokens back is for the issuing authorities to deliver the tokens again, akin real life. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4675.md b/EIPS/eip-4675.md new file mode 100644 index 0000000..6e04d3f --- /dev/null +++ b/EIPS/eip-4675.md @@ -0,0 +1,213 @@ +--- +eip: 4675 +title: Multi-Fractional Non-Fungible Tokens +description: Fractionalize multiple NFTs using a single contract +author: David Kim (@powerstream3604) +discussions-to: https://ethereum-magicians.org/t/eip-4675-multi-fractional-non-fungible-token-standard/8008 +status: Draft +type: Standards Track +category: ERC +created: 2022-01-13 +requires: 165, 721 +--- + +## Abstract +This standard outlines a smart contract interface eligible to represent any number of fractionalized non-fungible tokens. Existing projects utilizing standards like [EIP-1633](./eip-1633.md) conventionally deploy separate [EIP-20](./eip-20.md) compatible token contracts to fractionalize the non-fungible token into EIP-20 tokens. In contrast, this ERC allows each token ID to represent a token type representing(fractionalizing) the non-fungible token. + +This standard is approximate in terms of using `_id` for distinguishing token types. However, this ERC has a clear difference with [EIP-1155](./eip-1155.md) as each `_id` represents a distinct NFT. + +## Motivation +The conventional fractionalization process of fractionalizing a NFT to FT requires deployment of a FT token contract representing the ownership of NFT. This leads to inefficient bytecode usage on Ethereum Blockchain and limits functionalities since each token contract is separated into its own permissioned address. +With the rise of multiple NFT projects needing to fractionalize NFT to FT, new type of token standard is needed to back up them. + +## Specification + +```solidity +/** + @title Multi-Fractional Non-Fungible Token Standard + @dev Note : The ERC-165 identifier for this interface is 0x83f5d35f. +*/ +interface IMFNFT { + /** + @dev This emits when ownership of any token changes by any mechanism. + The `_from` argument MUST be the address of an account/contract sending the token. + The `_to` argument MUST be the address of an account/contract receiving the token. + The `_id` argument MUST be the token type being transferred. (represents NFT) + The `_value` argument MUST be the number of tokens the holder balance is decrease by and match the recipient balance is increased by. + */ + event Transfer(address indexed _from, address indexed _to, uint256 indexed _id, uint256 _value); + + /** + @dev This emits when the approved address for token is changed or reaffirmed. + The `_owner` argument MUST be the address of account/contract approving to withdraw. + The `_spender` argument MUST be the address of account/contract approved to withdraw from the `_owner` balance. + The `_id` argument MUST be the token type being transferred. (represents NFT) + The `_value` argument MUST be the number of tokens the `_approved` is able to withdraw from `_owner` balance. + */ + event Approval(address indexed _owner, address indexed _spender, uint256 indexed _id, uint256 _value); + + /** + @dev This emits when new token type is added which represents the share of the Non-Fungible Token. + The `_parentToken` argument MUST be the address of the Non-Fungible Token contract. + The `_parentTokenId` argument MUST be the token ID of the Non-Fungible Token. + The `_id` argument MUST be the token type being added. (represents NFT) + The `_totalSupply` argument MUST be the number of total token supply of the token type. + */ + event TokenAddition(address indexed _parentToken, uint256 indexed _parentTokenId, uint256 _id, uint256 _totalSupply); + + /** + @notice Transfers `_value` amount of an `_id` from the msg.sender address to the `_to` address specified + @dev msg.sender must have sufficient balance to handle the tokens being transferred out of the account. + MUST revert if `_to` is the zero address. + MUST revert if balance of msg.sender for token `_id` is lower than the `_value` being transferred. + MUST revert on any other error. + MUST emit the `Transfer` event to reflect the balance change. + @param _to Source address + @param _id ID of the token type + @param _value Transfer amount + @return True if transfer was successful, false if not + */ + function transfer(address _to, uint256 _id, uint256 _value) external returns (bool); + + /** + @notice Approves `_value` amount of an `_id` from the msg.sender to the `_spender` address specified. + @dev msg.sender must have sufficient balance to handle the tokens when the `_spender` wants to transfer the token on behalf. + MUST revert if `_spender` is the zero address. + MUST revert on any other error. + MUST emit the `Approval` event. + @param _spender Spender address(account/contract which can withdraw token on behalf of msg.sender) + @param _id ID of the token type + @param _value Approval amount + @return True if approval was successful, false if not + */ + function approve(address _spender, uint256 _id, uint256 _value) external returns (bool); + + /** + @notice Transfers `_value` amount of an `_id` from the `_from` address to the `_to` address specified. + @dev Caller must be approved to manage the tokens being transferred out of the `_from` account. + MUST revert if `_to` is the zero address. + MUST revert if balance of holder for token `_id` is lower than the `_value` sent. + MUST revert on any other error. + MUST emit `Transfer` event to reflect the balance change. + @param _from Source address + @param _to Target Address + @param _id ID of the token type + @param _value Transfer amount + @return True if transfer was successful, false if not + + */ + function transferFrom(address _from, address _to, uint256 _id, uint256 _value) external returns (bool); + + /** + @notice Sets the NFT as a new type token + @dev The contract itself should verify if the ownership of NFT is belongs to this contract itself with the `_parentNFTContractAddress` & `_parentNFTTokenId` before adding the token. + MUST revert if the same NFT is already registered. + MUST revert if `_parentNFTContractAddress` is address zero. + MUST revert if `_parentNFTContractAddress` is not ERC-721 compatible. + MUST revert if this contract itself is not the owner of the NFT. + MUST revert on any other error. + MUST emit `TokenAddition` event to reflect the token type addition. + @param _parentNFTContractAddress NFT contract address + @param _parentNFTTokenId NFT tokenID + @param _totalSupply Total token supply + */ + function setParentNFT(address _parentNFTContractAddress, uint256 _parentNFTTokenId, uint256 _totalSupply) external; + + /** + @notice Get the token ID's total token supply. + @param _id ID of the token + @return The total token supply of the specified token type + */ + function totalSupply(uint256 _id) external view returns (uint256); + + /** + @notice Get the balance of an account's tokens. + @param _owner The address of the token holder + @param _id ID of the token + @return The _owner's balance of the token type requested + */ + function balanceOf(address _owner, uint256 _id) external view returns (uint256); + + /** + @notice Get the amount which `_spender` is still allowed to withdraw from `_owner` + @param _owner The address of the token holder + @param _spender The address approved to withdraw token on behalf of `_owner` + @param _id ID of the token + @return The amount which `_spender` is still allowed to withdraw from `_owner` + */ + function allowance(address _owner, address _spender, uint256 _id) external view returns (uint256); + + /** + @notice Get the bool value which represents whether the NFT is already registered and fractionalized by this contract. + @param _parentNFTContractAddress NFT contract address + @param _parentNFTTokenId NFT tokenID + @return The bool value representing the whether the NFT is already registered. + */ + function isRegistered(address _parentNFTContractAddress, uint256 _parentNFTTokenId) external view returns (bool); +} + +interface ERC165 { + /** + @notice Query if a contract implements an interface + @param interfaceID The interface identifier, as specified in ERC-165 + @dev Interface identification is specified in ERC-165. This function + uses less than 30,000 gas. + @return `true` if the contract implements `interfaceID` and + `interfaceID` is not 0xffffffff, `false` otherwise + */ + function supportsInterface(bytes4 interfaceID) external view returns (bool); +} +``` + +To receive Non-Fungible Token on `safe Transfer` the contract should include `onERC721Received()`. +Including `onERC721Received()` is needed to be compatible with Safe Transfer Rules. +```solidity +/** + @notice Handle the receipt of an NFT + @param _operator The address which called `safeTransferFrom` function + @param _from The address which previously owned the token + @param _tokenId The NFT identifier which is being transferred + @param _data Additional data with no specified format + @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` +*/ +function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes calldata _data) external pure returns (bytes4); +``` + +## Rationale + +**Metadata** + +The `symbol()` & `name()` functions were not included since the majority of users can just fetch it from the originating NFT contract. Also, copying the name & symbol every time when token gets added might place a lot of redundant bytecode on the Ethereum blockchain. +However, according to the need and design of the project it could also be added to each token type by fetching the metadata from the NFT contract. + +**Design** + +Most of the decisions made around the design of this ERC were done to keep it as flexible for diverse token design & architecture. +These minimum requirement for this standard allows for each project to determine their own system for minting, governing, burning their MFNFT tokens depending on their programmable architecture. + +## Backwards Compatibility + +To make this standard compatible with existing standards, this standard `event` & `function` names are identical with ERC-20 token standard with some more `events` & `functions` to add token type dynamically. + +Also, the sequence of parameter in use of `_id` for distinguishing token types in `functions` and `events` are very much similar to ERC-1155 Multi-Token Standard. + +Since this standard is intended to interact with the EIP-721 Non-Fungible Token Standard, it is kept purposefully agnostic to extensions beyond the standard in order to allow specific projects to design their own token usage and scenario. + +## Test Cases + +Reference Implementation of MFNFT Token includes test cases written using hardhat. (Test coverage : 100%) + +## Reference Implementation +[MFNFT - Implementation](../assets/eip-4675/README.md) + +## Security Considerations + +To fractionalize an already minted NFT, it is evident that ownership of NFT should be given to token contracts before fractionalization. +In the case of fractionalizing NFT, the token contract should thoroughly verify the ownership of NFT before fractionalizing it to prevent tokens from being a separate tokens with the NFT. + +If an arbitrary account has the right to call `setParentNFT()` there might be a front-running issue. The caller of `setParentNFT()` might be different from the real NFT sender. +To prevent this issue, implementors should just allow **admin** to call, or fractionalize and receive NFT in an atomic transaction similar to flash loan(swap). + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4736.md b/EIPS/eip-4736.md new file mode 100644 index 0000000..3266787 --- /dev/null +++ b/EIPS/eip-4736.md @@ -0,0 +1,157 @@ +--- +eip: 4736 +title: Consensus Layer Withdrawal Protection +description: Additional security for BLSToExecutionChange operation when a consensus layer mnemonic may be compromised, without changing consensus +author: Benjamin Chodroff (@benjaminchodroff), Jim McDonald (@mcdee) +discussions-to: https://ethereum-magicians.org/t/consensus-layer-withdrawal-protection/8161 +status: Final +type: Standards Track +category: Interface +created: 2022-01-30 +--- + +## Abstract + +If a consensus layer mnemonic phrase is compromised, it is impossible for the consensus layer network to differentiate the legitimate holder of the key from an illegitimate holder. However, there are signals that can be considered in a wider sense without changing core Ethereum consensus. This proposal outlines ways in which on chain evidence such as the execution layer deposit address and list of signed messages could create a social consensus that would significantly favor but not guarantee legitimate mnemonic holders would win a race condition against an attacker. + +## Motivation + +The consensus layer `BLSToExecutionChange` message is secure for a single user who has certainty their keys and mnemonic have not been compromised. However, as validator withdrawals on the consensus layer are not possible until the Capella hard fork, no user can have absolute certainty that their keys are not compromised until the `BLSToExecutionChange` is on chain, and by then too late to change. All legitimate mnemonic phrase holders were originally in control of the execution layer deposit address. Beacon node clients and node operators may optionally load a list of verifiable `BLSToExecutionChange` messages to broadcasts that may create a social consensus for legitimate holders to successfully win a race condition against an attacker. If attackers compromise a significant number of consensus layer nodes, it would pose risks to the entire Ethereum community. + +Setting a withdrawal address to an execution layer address was not supported by the eth2.0-deposit-cli until v1.1.1 on March 23, 2021, leaving early adopters wishing they could force set their execution layer address to a deposit address earlier. Forcing this change is not something that can be enforced in-protocol, partly due to lack of information on the beacon chain about the execution layer deposit address and partly due to the fact that this was never listed as a requirement. It is also possible that the execution layer deposit address is no longer under the control of the legitimate holder of the withdrawal private key. + +However, it is possible for individual nodes to locally restrict the changes they wish to include in blocks they propose, and which they propagate around the network, in a way that does not change consensus. It is also possible for client nodes to help broadcast signed `BLSToExecutionChange` requests to ensure as many nodes witness this message as soon as possible in a fair manner. Further, such `BLSToExecutionChange` signed messages can be preloaded into clients in advance to further help nodes filter attacking requests. + +This proposal provides purely optional additional protection. It aims to request nodes set a priority on withdrawal credential claims that favour a verifiable execution layer deposit address in the event of two conflicting `BLSToExecutionChange` messages. It also establishes a list of `BLSToExecutionChange` signed messages to help broadcast "as soon as possible" when the network supports it, and encourage client teams to help use these lists to honour filter and prioritize accepting requests by REST and transmitting them via P2P. This will not change consensus, but may help prevent propagating an attack where a withdrawal key has been knowingly or unknowingly compromised. + +It is critical to understand that this proposal is not a consensus change. Nothing in this proposal restricts the validity of withdrawal credential operations within the protocol. It is a voluntary change by client teams to build this functionality in to their beacon nodes, and a voluntary change by node operators to accept any or all of the restrictions and broadcasting capabilities suggested by end users. + +Because of the above, even if fully implemented, it will be down to chance as to which validators propose blocks, and which voluntary restrictions those validators’ beacon nodes are running. Node operators can do what they will to help the community prevent attacks on any compromised consensus layer keys, but there are no guarantees of success this will prevent a successful attack. + +## Specification + +The Consensus Layer `BLSToExecutionChange` operation has the following fields: + +* Validator index +* Current withdrawal BLS public key +* Proposed execution layer withdrawal address +* Signature by withdrawal private key over the prior fields + +This proposal describes OPTIONAL and RECOMMENDED mechanisms which a client beacon node MAY implement, and end users are RECOMMENDED to use in their beacon node operation. + +### `BLSToExecutionChange` Broadcast File + +Beacon node clients MAY support an OPTIONAL file of lines specifying "validator index" , "current withdrawal BLS public key" , "proposed execution layer withdrawal address", and "signature" which, if implemented and if provided, SHALL instruct nodes to automatically submit a one-time `BLSToExecutionChange` broadcast message for each valid signature at the Capella hard fork. This file SHALL give all node operators an OPTIONAL opportunity to ensure any valid `BLSToExecutionChange` messages are broadcast, heard, and shared by nodes at the Capella hard fork. This OPTIONAL file SHALL also instruct nodes to perpetually prefer accepting and repeating signatures matching the signature in the file, and SHALL reject accepting or rebroadcasting messages which do not match a signature for a given withdrawal credential. + +### `BLSToExecutionChange` Handling + +Beacon node clients are RECOMMENDED to allow accepting "`BLSToExecutionChange` Broadcast" file of verifiable signatures, and then MAY fallback to accept a "first request" via P2P. All of this proposal is OPTIONAL for beacon nodes to implement or use, but all client teams are RECOMMENDED to allow a "`BLSToExecutionChange` Broadcast File" to be loaded locally before the Capella hard fork. This OPTIONAL protection will allow a user to attempt to set a withdrawal address message as soon as the network supports it without any change to consensus. + +## Rationale + +This proposal is intended to protect legitimate validator mnemonic holders where it was knowingly or unknowingly compromised. As there is no safe way to transfer ownership of a validator without exiting, it can safely be assumed that all validator holders intend to set to a withdrawal address they specify. Using the deposit address in the execution layer to determine the legitimate holder is not possible to consider in consensus as it may be far back in history and place an overwhelming burden to maintain such a list. As such, this proposal outlines optional mechanism which protect legitimate original mnemonic holders and does so in a way that does not place any mandatory burden on client node software or operators. + +## Backwards Compatibility + +As there is no existing `BLSToExecutionChange` operation prior to Capella, there is no documented backwards compatibility. As all of the proposal is OPTIONAL in both implementation and operation, it is expected that client beacon nodes that do not implement this functionality would still remain fully backwards compatible with any or all clients that do implement part or all of the functionality described in this proposal. Additionally, while users are RECOMMENDED to enable these OPTIONAL features, if they decide to either disable or ignore some or all of the features, or even purposefully load content contrary to the intended purpose, the beacon node client will continue to execute fully compatible with the rest of the network as none of the proposal will change core Ethereum consensus. + +## Reference Implementation + +### `BLSToExecutionChange` Broadcast File + +A "change-operations.json" file intended to be preloaded with all consensus layer withdrawal credential signatures and verifiable execution layer deposit addresses. This file may be generated by a script and able to be independently verified by community members using the consensus layer node, and intended to be included by all clients, enabled by default. Client nodes are encouraged to enable packaging this independently verifiable list with the client software, and enable it by default to help further protect the community from unsuspected attacks. + +The change-operations.json format is the "`BLSToExecutionChange` File - Claim" combined into a single JSON array. + +### `BLSToExecutionChange` Broadcast File - Claim + +A community collected and independently verifiable list of "`BLSToExecutionChange` Broadcasts" containing verifiable claims will be collected. Node operators may verify these claims independently and are suggested to load claims in compatible beacon node clients. + +To make a verifiable claim, users MAY upload a claim to any public repository in a text file "[chain]/validatorIndex.json" such as "mainnet/123456.json". + +123456.json: + +``` +[{"message":{"validator_index":"123456","from_bls_pubkey":"0x977cc21a067003e848eb3924bcf41bd0e820fbbce026a0ff8e9c3b6b92f1fea968ca2e73b55b3908507b4df89eae6bfb","to_execution_address":"0x08f2e9Ce74d5e787428d261E01b437dC579a5164"},"signature":"0x872935e0724b31b2f0209ac408b673e6fe2f35b640971eb2e3b429a8d46be007c005431ef46e9eb11a3937f920cafe610c797820ca088543c6baa0b33797f0a38f6db3ac68ffc4fd03290e35ffa085f0bfd56b571d7b2f13c03f7b6ce141c283"}] +``` + +#### Claim Acceptance + +In order for a submission to be merged into public repository, the submission must have: + +1. Valid filename in the format validatorIndex.json +2. Valid validator index which is active on the consensus layer +3. Verifiable signature +5. A single change operation for a single validator, with all required fields in the file with no other content present + +All merge requests that fail will be provided a reason from above which must be addressed prior to merge. Any future verifiable amendments to accepted claims must be proposed by the same submitter, or it will be treated as a contention. + +#### `BLSToExecutionChange` Broadcast + +Anyone in the community will be able to independently verify the files from the claims provided using the Capella spec and command line clients such as "ethdo" which support the specification. + +A claim will be considered contested if a claim arrives where the verifiable consensus layer signatures differ between two or more submissions, where neither party has proven ownership of the execution layer deposit address. If a contested but verified "`BLSToExecutionChange` Broadcast" request arrives to a repository, all parties can be notified, and may try to convince the wider community by providing any publicly verifiable on chain evidence or off chain evidence supporting their claim to then include their claim in nodes. Node operators may decide which verifiable claims they wish to include based on social consensus. + +## Security Considerations + +### 1: Attacker lacks EL deposit key, uncontested claim + +* User A: Controls the CL keys and the EL key used for the deposit +* User B: Controls the CL keys, but does not control the EL key for the deposit + +User A signs and submits a claim to the CLWP repository, clients load User A message into the "`BLSToExecutionChange` Broadcast" file. At the time of the first epoch support `BLSToExecutionChange`, many (not all) nodes begin to broadcast the message. User B also tries to submit a different but valid `BLSToExecutionChange` to an address that does not match the signature in the claim. This message is successfully received via REST API, but some (not all) nodes begin to silently drop this message as the signature does not match the signature in the "`BLSToExecutionChange` Broadcast" file. As such, these nodes do not replicate this message via P2P. + +### 2: Attacker has both EL deposit key and CL keys, uncontested claim + +* User A: Controls the CL key/mnemonic and the EL key used for the deposit, and submits a claim to move to a new address +* User B: Controls the CL and EL key/mnemonic used for the EL deposit, but fails to submit a claim + +It is possible/likely that User A would notice that all their funds in the EL deposit address had been stolen. This may signal that their CL key is compromised as well, so they decide to pick a new address for the withdrawal. The story will play out the same as Scenario 1 as the claim is uncontested. + +### 3: Same as #2, but the attacker submits a contested claim + +* User A: Controls the CL keys/mnemonic and the EL key used for the deposit, and submits a claim to move to a new address +* User B: Controls the CL keys/mnemonic and the EL key used for the deposit, and submits a claim to move to a new address + +This is a contested claim and as such there is no way to prove who is in control using on chain data. Instead, either user may try to persuade the community they are the rightful owner (identity verification, social media, etc.) in an attempt to get node operators to load their contested claim into their "`BLSToExecutionChange` Broadcast" file. However, there is no way to fully prove it. + +### 4: A user has lost either their CL key and/or mnemonic (no withdrawal key) + +* User A: Lacks the CL keys and mnemonic + +There is no way to recover this scenario with this proposal as we cannot prove a user has lost their keys, and the mnemonic is required to generate the withdrawal key. + +### 5: End game - attacker + +* User A: Controls EL and CL key/mnemonic, successfully achieves a set address withdrawal +* User B: Controls CL key, decides to attack + +Upon noticing User A has submitted a successful set address withdrawal, User B may run a validator and attempt to get User A slashed. Users who suspect their validator key or seed phrase is compromised should take action to exit their validator as early as possible. + +### 6: Compromised key, but not vulnerable to withdrawal + +* User A: Controls EL and CL key/mnemonic, but has a vulnerability which leaks their CL key but NOT their CL mnemonic +* User B: Controls the CL key, but lacks the CL mnemonic + +User A may generate the withdrawal key (requires the mnemonic). User B can attack User A by getting them slashed, but will be unable to generate the withdrawal key. + +### 7: Attacker loads a malicious `BLSToExecutionChange` Broadcast file into one or multiple nodes, User A submits claim + +* User A: Submits a valid uncontested claim which is broadcast out as soon as possible by many nodes +* User B: Submits no claim, but broadcasts a valid malicious claim out through their `BLSToExecutionChange` Broadcast list, and blocks User A's claim from their node. + +User B's claim will make it into many nodes, but when it hits nodes that have adopted User A's signature they will be dropped and not rebroadcast. Statistically, User B will have a harder time achieving consensus among the entire community, but it will be down to chance. + +### 8: Same as #7, but User A submits no claim + +The attacker will statistically likely win as they will be first to have their message broadcast to many nodes and, unless User A submits a request exactly at the time of support, it is unlikely to be heard by enough nodes to gain consensus. All users are encouraged to submit claims for this reason because nobody can be certain their mnemonic has not been compromised until it is too late. + +### Second Order Effects + +1. A user who participates in the "`BLSToExecutionChange` Broadcast" may cause the attacker to give up early and instead start to slash. For some users, the thought of getting slashed is preferable to giving an adversary any funds. As the proposal is voluntary, users may choose not to participate if they fear this scenario. +2. The attacker may set up their own `BLSToExecutionChange` Broadcast to reject signatures not matching their attack. This is possible with or without this proposal. +3. The attacker may be the one collecting "`BLSToExecutionChange` Broadcast" claims for this proposal and may purposefully reject legitimate requests. Anyone is free to set up their own community claim collection and gather their own community support using the same mechanisms described in this proposal to form an alternative social consensus. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4747.md b/EIPS/eip-4747.md new file mode 100644 index 0000000..2a710d3 --- /dev/null +++ b/EIPS/eip-4747.md @@ -0,0 +1,312 @@ +--- +eip: 4747 +title: Simplify EIP-161 +description: Simplify EIP-161 and retroactively deprecate unused aspects of it +author: Peter Davies (@petertdavies) +discussions-to: https://ethereum-magicians.org/t/eip-4747-simplify-eip-161/8246 +status: Stagnant +type: Standards Track +category: Core +created: 2022-02-02 +requires: 161 +--- + + +## Abstract + +Simplify the definition of [EIP-161](./eip-161.md), removing the requirement for implementors to support edge cases that are impossible on Ethereum Mainnet. + +## Motivation + +EIP-161 is overly complex and has a number of edge cases that are poorly documented and tested. This EIP takes advantage of the complete removal of all remaining empty accounts in block 14049881 (tx `0xf955834bfa097458a9cf6b719705a443d32e7f43f20b9b0294098c205b4bcc3d`) to clarify it, and allows implementors to not implement various edge cases that never occurred and are not possible in the future. + +In particular, this EIP permits implementors who do not wish to support historical blocks to not implement state clearing at all. + +## Specification +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. + +Retroactively replace EIP-161, starting from its introduction in block 2675000, with the following rules: + +a. When creating an account, set it's nonce to `1` prior to executing initcode. + +b. When performing EVM execution treat all empty accounts as if they do not exist. Any operation that would create an empty account instead leaves it non-existent. + +c. If one of the following events happens to an empty account `X`, it is deleted: + +1. `X` receives a zero value `CALL`. +2. `X` is the recipient of a zero value transaction. +3. `X` is the beneficiary of a `SELFDESTRUCT` with zero value. + +If the transaction, `CALL` or `SELFDESTRUCT` is reverted, then the state clearing is also reverted. As a special case on Ethereum Mainnet, in block 2675119 (tx `0xcf416c536ec1a19ed1fb89e4ec7ffb3cf73aa413b3aa9b77d60e4fd81a4296ba`), an empty call is made to an empty account at `RIPEMD160` (precompile `0x3`), the call fails out of gas, but the empty account is removed anyway. + +The deletion MAY happen immediately or be processed at the end of the transaction. A client may assume that `STATICCALL`s to empty accounts never occur and that `CALL`s to empty accounts in `STATICCALL` contexts never occur. + +On Ethereum Mainnet, the only state clearing events after the start of Byzantium are the two listed below. Clients MAY implement post Byzantium state clearing by hardcoding those events. + +* In block 4457731 (tx `0x63791f962e13e6b01ec13d38a8ab66c87c2f5a1768276f866300d900cca808fe`), `0xd29DFe5aE95B5C067a91F472Dac0d9be6700A4A9` receives a zero value `SELFDESTRUCT` and is cleared. + +* In block 14049881 (tx `0xf955834bfa097458a9cf6b719705a443d32e7f43f20b9b0294098c205b4bcc3d`), the following accounts receive zero value calls and are cleared. +
+ Accounts cleared + + ``` + 0x01a3dd7d158e3b4c9d8d2af0ddcf3df0f5e14463 + 0x0366c731dd7c095dc08896806765a649c6c0885a + 0x056c68da52395f1d42c5ba15c4fb956146a4f2c1 + 0x070ba92497cd4b88a8a9a60795ca7d7f7de0faa3 + 0x07a1648ce2bed6721a5d25de3c228a296d03fd52 + 0x07b14ba68f474529cc0bd6a9bffee2bc4090d185 + 0x07ea32232e37d44134a3071319d228bdab249a60 + 0x096b7382500fa11c22c54c0422c5e38899a2e933 + 0x09f3200441bd60522bcf28f3666f8e8dbd19fb62 + 0x0ad0f3c60696adece09367a9a11c968fb88560bb + 0x0af6181e1db22071f38fc162e1610e29d288de04 + 0x0cdc7fef8f8d0ee77360060930aada1263b26ff7 + 0x0dac3d571eb5b884a2550db2791d5ac1efca306b + 0x0ec857faba49392080b68dd5074d85f34724d04a + 0x0f5054f9c674b37d15915ca8925f231edb3afa8c + 0x0f78d535e1faad9a982dca2a76d16da4649f7021 + 0x104c5b235166f26df54f52666d5e77d9e03e353e + 0x106b47175965b6d607008544267c91490672a54f + 0x1223d5c03b4d52ebed43f781251098c9138c3dd7 + 0x1251d13cde439378349f039379e83c2641b6269f + 0x12c814cebee6bb08a5d1b9d009332bf8b536d645 + 0x150c63df3da35e590a6d2f7accf2e6f241ea5f1a + 0x15ddf20e4eb8b53b823bc45c9bad2670aad907dd + 0x1712b1c428f89bc695b1871abfff6b5097350150 + 0x178df2e27b781f024e90ab0abe9cff7e2f66a5fc + 0x1c2bd83dc29095173c4bcc14927811f5141c1373 + 0x1d12f2fad3603ea871fcb13ac3e30674f9ad903f + 0x1f7391b6881b6f025aef25cff737ff3fcb9d7660 + 0x219a3d724f596a4b75656e9b1569289c71782804 + 0x21a7fd9228c46ec72f926978f791fc8bfcd277fa + 0x23acb760cebd01fe7c92361274a4077d37b59f4c + 0x23b249eeeeedd86bc40349f8bb8e2df34bd28f78 + 0x28d006b1a2309e957005ee575f422af8034f93df + 0x28ef72d5614b2833d645aecf8ef7add075eb21e2 + 0x292966802ffedb6f34f2c8c59df35c9d8f612c24 + 0x2c2661ddd320017138075aba06999440a902695f + 0x2c632be2dc2f47affd07bfce91bd4a27c02f4563 + 0x2f86de22ced85a7dd0d680fc4266929a72775e27 + 0x2fa04f15123025ab487dce71668f5265649d0598 + 0x30f78fd12c17855453e0db166fecf684bb239b8c + 0x31534e95353323209cd18ad35c22c2528db6d164 + 0x336e0e1a14e0136c02bf8dcf0a9a3fe408548262 + 0x340399588bba5b843883d1ad7afd771a3651447a + 0x341d2b82d0924ef42d75ce053654295d34839459 + 0x34c2b8975b47e13818f496cf80b40566798cf968 + 0x370e67f45db9c18d6551000e6c0918bc8d346ebf + 0x37149dae898296173d309f1de6981922ec1dc495 + 0x377cb0d3427af7f06df47d2ab420458834bed1fc + 0x3d473af3e6ce45183c781b414e8a9edcb8b26f72 + 0x42794c1d807079e16735e47e193825cec80ee28c + 0x45603aa97b67965b42b38ddc8884373edbcf2d56 + 0x465cb9df2f6d3c8a1c1ce3f2338823f0638fefa5 + 0x49fbe69c2897bce0340b5983a0f719213a8c6e6f + 0x4a84cbd3ef642e301aa59bedf4fa4d28e24e6204 + 0x4d4d551bd6244b854e732572902f19f4ccaa6996 + 0x4f62af4ec150ea121859b3368e6a61fb7bcf9002 + 0x4fd1c530f73ddfff5c609a4a8b25af6ca489d1fd + 0x50010a4f0e429b398c66876dea7694d5f8b1a639 + 0x522c9f65bc77ad9eed6bcdc3ec220236451c9583 + 0x52b30ca3c2f8656e2c022e896bef7fad9a0449ca + 0x537a7030ecd9d159e8231ce31b0c2e83b4f9ed75 + 0x5483a4c5583d5ba3db23676a3db346f47ba357e1 + 0x55ec1a78a1187428dc0c67cbb77ae9fbdd61cc2a + 0x56cc1c0aadc2b8beb71f1ac61f03645483abe165 + 0x58bea8cea61fad5c453731aaeed377f3d77a04cc + 0x58f632327fbc4f449bda3bd51e13f590e67a8627 + 0x59d122afcbd68c731de85c2597004c6ddafbc7ed + 0x5da0228024cc084b9475470a7b7ae1d478d51bb7 + 0x5e51d6621883afcbd4e999b93180a96909bdc766 + 0x5e9a0a1bdfdd868706f4554aae21bb2c46da32c2 + 0x5f3f0d3215db85faa693d99acfb03cca66556671 + 0x5f6aa25f22edb2347b464312e2508cbc4c6e0162 + 0x6006f79e4104850ab7c9b0f75918c1e2cf6311df + 0x60f5da58bccb716f58b5759a06fc2167fe237c26 + 0x62d3a444d0af59f9de79f8abeb5c942fcfbfbef5 + 0x630ea66c8c5dc205d45a978573fa86df5af1fe7a + 0x6464f0f96a29934087a955c67a6b53d5ed852e49 + 0x6653cedb0b7f51c4b0c44079eb45c514df24ecfd + 0x66d69ac12b573299f36b108792be75a1e2ccdfdc + 0x690ed837d25b46dbf46727fcda7392d997c2bc97 + 0x696eecbc97189c5b2a8245a8e32517db9960c171 + 0x69aaff0b7babe85e0a95adfc540e689399db7f24 + 0x6b71d2ceab5678b607aa1e69b6781f5c7abc9aaf + 0x6e03d9cce9d60f3e9f2597e13cd4c54c55330cfd + 0x6e278cfecfe96fa5e6d5411ba6eeb765dff4f118 + 0x6e557f01c9dcb573b03909c9a5b3528aec263472 + 0x6ec268f8bef9c685d7e04d5cdb61fbb544869a9f + 0x6f2ba051b3ce06a90705c22e0241c2b7e32c1af0 + 0x7063732ced55cfa08aea520f3fe200c39b3df0f5 + 0x7073a17a0172dfb1e46a62f054d11a775aeac32e + 0x71d3718cfa0f9ee8173688fe52bb499e1f36534b + 0x74e20aec156674945894d404f8dea602570e62f5 + 0x783e45c2989e675ffc9d067914d7de3ff68aee58 + 0x7a5f843f884bb15d070806e1ff59b6c6f74bbe2d + 0x7c6b1706c86ea76a0e232324f249e1508ca2dfda + 0x7d23a23584c83c1f6636124255cfd8e9cfc0e529 + 0x7e8b5df0dec9168741c93d52d7045aca7ea632d3 + 0x7ec5da0f1036750688084252b802befe41551205 + 0x82c9fcef4dd2d374b000063d4899a38a7219cdc7 + 0x82fa2ab30a566ceeac987eb5510485be9382f130 + 0x83d927aca3266f94e8163eaa32700c70e9b76e6e + 0x8476f7e193c930f21e88dae84888e0d8bfaf3ed8 + 0x85ec166cb81f5010b4a8d365821473dac0c0aa88 + 0x8883c55943d5caf06b6484de9c1d73da8307cd82 + 0x8c07456cffd4254c89aaaa9d4e95c8b3e36c2a3b + 0x8fef965e5db6f7f1a165008499e8b7901cd766b2 + 0x9018e2967c15e1faed9b5d6439522f075535a683 + 0x903f1d8a086c6af1afe24648b6409aade83c4340 + 0x9127c398827d8db6b6d5f17b71f5db69d06e8b74 + 0x917b5be6e3acd96d40a33c13e6748e4a88576c6d + 0x91edfd05112f0bc9d6cd43b65361713a50e9eb7f + 0x93026a2c4a0bc69de31515070bf086e0c1f789e5 + 0x94863bbbc12ec5be148f60a7020fd49236fc1937 + 0x94befc001e203f141462f16bde60873bcefae401 + 0x94c408cf5934f241d4fdd55ff3825131635c6af2 + 0x94cfdec548de92301735dc0b82d8e1f79404ff94 + 0x96527f3311f44340887c926acc16f0997eb3b955 + 0x974117faf194885c01513e8d87b38a2291083ed5 + 0x993424827a5fb2fa97818814ea4027e28150f187 + 0x9a6f30a5cb46840076edd780da2dbb4bc7c39f24 + 0x9a74a096b0bb82adfd28494107f2c07f4545723e + 0x9af82ec46185641c0ea44679aac8a7e7570be202 + 0x9e2287a60ed85f6bd80c62c1b7b4130ea1b521dd + 0x9fee5b81ee0cbf34c18c52061f1b257d4ccb2702 + 0xa017226377e775af8e56450301cc035ae72267f8 + 0xa1b423e024daf925f25296ea2efcf009cc328873 + 0xa23c0cbfe59e8650277ffa635c59f287cece9087 + 0xa340b7625eec76b372f2c317fe08a7733f05d09c + 0xa4cb6be13c2eace6c0f1157553e3c446f7b38b10 + 0xa54326267784fae3ffd6800af38099753bb7f470 + 0xa580086125d040fddd3af9d563285bd0ec4d13e3 + 0xa88fc7a34ca36b952aa45d94c1e13155042b5e7d + 0xac8f4ce2e4eff39c738bf1941350b3b57e8eec4f + 0xacb17dca110db022b1aceb5399acba1e9bf577e3 + 0xae0b03c8d8bf9cf71eda758e9e8b59c70a3b4580 + 0xae365ff4b0c64413baf6f7dfdb5cd3fb65ad1376 + 0xaf7e60d02b425b54730b7281a97d1640233704b0 + 0xaf9846f8098656e7c2f0e53e9ff7d38ec7b7f679 + 0xb2784c0a95e9b6b865aca13556fb32e2f37cb775 + 0xb385fa211cd08326ff84b0d4f37cc8c3735aa3aa + 0xb3fb883cbbccb0551daf1507f87426fd38da087e + 0xb6515cfb82fa877fbadae5a87006a8d3deeeb7c9 + 0xb78c4f0b8c9ec0b3058724eca65292d0d65586b9 + 0xba25f341e16ee81ab80ea246d45bdead7cc339e5 + 0xbab14024437285c2e3b3c521abff96b0ef2e919f + 0xbaf0996297cc70fca1bee30162eabcd892f0574a + 0xbb01ea95321a94242c89479995b7e3f264cb46a0 + 0xc1b37a3b7f76947e24cc2470e0e948aab0181346 + 0xc24431c1a1147456414355b1f1769de450e524da + 0xc467b893e29277f9b62b4ed6c9ba054bd8225bff + 0xc4bc101a168ea2228973a65564a7d40a68528dd2 + 0xc784626571c2c25cd2cfe24192a149cad86d40d8 + 0xc7acf90a9f442855b8f291288bb5fb612536ed9b + 0xc9956593dbfb46cfd24686a365b34051a55abce6 + 0xca2eb2af7dd7a90777c8c6456efcc00fe56dbd6f + 0xcb4bb078edaae9393c8da27b809aa9c0f4c920b7 + 0xcc8f68e8e2d8196e2ecd0caf2f35b1611739a21f + 0xcd67903318a805d63fe79bf9b8401c1b79c6babf + 0xcd7a2fe9cb80c95b03950daf5b6d476bec9ac24d + 0xd09476f5ee7979becca8ffe6dc22a72565fc3cea + 0xd1c4bd2b583f445354d1b644ea4b8353f2d23048 + 0xd32bb8bceafc89ff59ba43ce8b6cd65bb06dd7b0 + 0xd49e9fa792db9d9398c57eabf94ba1b2c709ace7 + 0xd6b862cf0d009bde0f020ab9d8f96e475069c5c6 + 0xd747c05d9c8057db608ef7aedabf07e4db0bbe97 + 0xdb9b40d1b691ced3680e539261b6bc195388b3c0 + 0xdbcc502093cadd0feb709708c633e2427aeb9c2d + 0xdc53001181ddc6a279deea6419443ea0ac0aec9c + 0xde3b38cb1050e7b5db39b4cbb2b2b63a1e32cbf6 + 0xdf1b687a99216ad4ebf9176983bf165be7b25bbe + 0xe000662c02a02d8b40aabfcd661594312992311d + 0xe30c59e4dc19d7c9ed6eb10d734d4d7ef28403ac + 0xe415114089b4b4933e542a5c79af4b6e6cd7abc9 + 0xe47f0a0e93241d390fe9b99de852682522e847bc + 0xe54abbd51e324bf8cf349b6b31c01b043d1ee0e4 + 0xe57838f777b11fdc428d9e7e67f1187d6251ba1f + 0xe5e4b26325d0fbf551367f2cf3b5d01caed6abcf + 0xe6655208bd812d833238b560e847014b0aab3b51 + 0xe6e16a1023af4a8fe54669f3fce7c406801bb333 + 0xe727bba699fbe82a731dad9476b5234d0038cfa1 + 0xec361d34a55e24e2f77de7121ae2b7bf11ed0d65 + 0xed3bf94976eb11d55b955d1369a478620872b57c + 0xee93ad447fe6a0e2bbac4952e651b21c0175acad + 0xefc5d9cabc0bda8124e1b821e8c86c7e7bf1e4bc + 0xf272f72a00f166f491d994642c8243099b72d2cd + 0xf45f642034bbce869e31b05d1da919125c7331ee + 0xf4883b21724405b19e240f3309a64d16dd89adc7 + 0xf5cb2a87ff1095f6d93e7b4bfc1bc47542380550 + 0xf6ddd386c4f7f0b460032c8055d7f9c3503d7140 + 0xf72093096c81b3e9e991f5b737baec9570a56927 + 0xf7412232a7a731bca2e5554c8ee051274373c17c + 0xfc2321dc32c2e6e96a0e41c911fb73a7b278d5c8 + 0xfc4dc782bf7e81a2ed5cc0519f80de36e7931bd9 + 0xfcde1c261eb257e14491b4e7cb1949a7623c00c5 + 0xfd17a22fd80075f2716e93268aa01bcdd7d70b22 + ``` +
+ + +## Rationale + +EIP-161 provides that empty accounts (accounts that have zero nonce, zero balance and no code, but that might have storage) can no longer be created and provides mechanism to remove old empty accounts. The last empty accounts were removed in block 14049881 (tx `0xf955834bfa097458a9cf6b719705a443d32e7f43f20b9b0294098c205b4bcc3d`). + +The complete removal of all empty accounts ensures that certain edgecases of EIP-161 can never occur on Ethereum Mainnet. Continuing to define and test those cases as part of the Ethereum Specification burdens future client implementors with unnecessary technical debt. This EIP declares those cases undefined and leaves clients free to assume they will not occur. + +## Backwards Compatibility + +This EIP is identical to EIP-161 except for the following differences, none of which affect Ethereum Mainnet. The differences are: + +### "Potentially state-changing operations" + +EIP-161 specifies 11 "potentially state-changing operations" that trigger state clearing. All but the 3 listed in this EIP are irrelevant, for the following reasons: + +#### Impossible + +* Receiving zero value mining reward/fees (this would become possible after the merge). + +#### Cannot happen to an empty account + +* Being the source of a `CREATE`. +* Being the source of a `CALL`. +* Being refunded by a `SELFDESTRUCT` + +#### Causes the account to become non-empty + +* Being the sender of a message call transaction. +* Being the sender of a contract creation transaction. +* Being created by a `CREATE`. +* Being created by a contract creation transaction. + +### Interaction with `STATICCALL` + +The interaction between `STATICCALL` and account clearing has never been specified in an EIP. The Ethereum currently testsuite requires that `STATICCALL` triggers state clearing. + +This EIP formally undefines all interactions between `STATICCALL` and state clearing as it has never happened on Ethereum Mainnet and cannot happen in future. + +### "At the end of the transaction" + +This only makes a difference if an account is deleted and later recreated in the same transaction. This never happens on Ethereum Mainnet. + +### Test Cases + +All test cases involving empty accounts in the Ethereum execution layer test suite shall be removed unless they relate to the Spurious Dragon Hardfork. If a Spurious Dragon test relates involved deprecated edgecase the test must be removed or reworked. + +### Other networks + +Ropsten had empty accounts seeded at genesis. They appear to have been cleared early in Ropsten's history before the Byzantium hardfork. Ropsten has never been checked for edgecases occurring. All other Ethereum testnets have had EIP-161 from genesis. + +As a security precaution all empty accounts on Ethereum Classic have been cleared, but no checks for edgecases occurring have been done. Due to EIP-161's age the vast majority of EVM compatible networks have supported it from genesis. + +## Security considerations + +This EIP is only equivalent to EIP-161 on Ethereum Mainnet if the following facts are true: + +1. No empty accounts are ever touched and then reinstated in the same transaction. +2. The transactions in the Appendix are the only state clearing transactions on Ethereum Mainnet after block 4370000 (start of Byzantium). +3. All empty accounts have been removed on Ethereum Mainnet. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4750.md b/EIPS/eip-4750.md new file mode 100644 index 0000000..61e226d --- /dev/null +++ b/EIPS/eip-4750.md @@ -0,0 +1,156 @@ +--- +eip: 4750 +title: EOF - Functions +description: Individual sections for functions with `CALLF` and `RETF` instructions +author: Andrei Maiboroda (@gumb0), Alex Beregszaszi (@axic), Paweł Bylica (@chfast) +discussions-to: https://ethereum-magicians.org/t/eip-4750-eof-functions/8195 +status: Review +type: Standards Track +category: Core +created: 2022-01-10 +requires: 3540, 3670, 5450 +--- + +## Abstract + +Introduce the ability to have several code sections in EOF-formatted ([EIP-3540](./eip-3540.md)) bytecode, each one representing a separate subroutine/function. Two new opcodes,`CALLF` and `RETF`, are introduced to call and return from such a function. Dynamic jump instructions are disallowed. + +## Motivation + +Currently, in the EVM everything is a dynamic jump. Languages like Solidity generate most jumps in a static manner (i.e. the destination is pushed to the stack right before, `PUSHn .. JUMP`). Unfortunately however this cannot be used by most EVM interpreters, because of added requirement of validation/analysis. This also restricts them from making optimisations and potentially reducing the cost of jumps. + +[EIP-4200](./eip-4200.md) introduces static jump instructions, which remove the need for *most* dynamic jump use cases, but not everything can be solved with them. + +This EIP aims to remove the need and disallow dynamic jumps as it offers the most important feature those are used for: calling into and returning from functions. + +Furthermore, it aims to improve analysis opportunities by encoding the number of inputs and outputs for each given function, and isolating the stack of each function (i.e. a function cannot read the stack of the caller/callee). + +## Specification + +### Type Section + +The type section of EOF containers must adhere to following requirements: + +1. The section is comprised of a list of metadata where the metadata index in the type section corresponds to a code section index. Therefore, the type section size MUST be `n * 4` bytes, where `n` is the number of code sections. +2. Each metadata item has 3 attributes: a uint8 `inputs`, a uint8 `outputs`, and a uint16 `max_stack_height`. *Note:* This implies that there is a limit of 255 stack for the input and in the output. This is further restricted to 127 stack items, because the upper bit of both the input and output bytes are reserved for future use. `max_stack_height` is further defined in [EIP-5450](./eip-5450.md). +3. The first code section MUST have 0 inputs and 0 outputs. + +Refer to [EIP-3540](./eip-3540.md) to see the full structure of a well-formed EOF bytecode. + +### New execution state in EVM + +A return stack is introduced, separate from the operand stack. It is a stack of items representing execution state to return to after function execution is finished. Each item is comprised of: code section index, offset in the code section (PC value), calling function stack height. + +Note: Implementations are free to choose particular encoding for a stack item. In the specification below we assume that representation is three unsigned integers: `code_section_index`, `offset`, `stack_height`. + +The return stack is limited to a maximum 1024 items. + +Additionally, EVM keeps track of the index of currently executing section - `current_section_index`. + +### New instructions + +We introduce two new instructions: + +1. `CALLF` (`0xb0`) - call a function +2. `RETF` (`0xb1`) - return from a function + +If the code is legacy bytecode, any of these instructions results in an *exceptional halt*. (*Note: This means no change to behaviour.*) + +First we define several helper values: + +- `caller_stack_height = return_stack.top().stack_height` - stack height value saved in the top item of return stack +- `type[i].inputs = type_section_contents[i * 4]` - number of inputs of ith section +- `type[i].outputs = type_section_contents[i * 4 + 1]` - number of outputs of ith section + +If the code is valid EOF1, the following execution rules apply: + +#### `CALLF` + +1. Has one immediate argument,`code_section_index`, encoded as a 16-bit unsigned big-endian value. +2. EOF validation guarantees that operand stack has at least `caller_stack_height + type[code_section_index].inputs` items. +3. If operand stack size exceeds `1024 - type[code_section_index].max_stack_height` (i.e. if the called function may exceed the global stack height limit), execution results in exceptional halt. This also guarantees that the stack height after the call is within the limits. +4. If return stack already has `1024` items, execution results in exceptional halt. +5. Charges 5 gas. +6. Pops nothing and pushes nothing to operand stack. +7. Pushes to return stack an item: + + ``` + (code_section_index = current_section_index, + offset = PC_post_instruction, + stack_height = data_stack.height - types[code_section_index].inputs) + ``` + + Under `PC_post_instruction` we mean the PC position after the entire immediate argument of `CALLF`. Operand stack height is saved as it was before function inputs were pushed. + + *Note:* Code validation rules of [EIP-5450](./eip-5450.md) guarantee there is always an instruction following `CALLF` (since terminating instruction or unconditional jump is required to be final one in the section), therefore `PC_post_instruction` always points to an instruction inside section bounds. +8. Sets `current_section_index` to `code_section_index` and `PC` to `0`, and execution continues in the called section. + +#### `RETF` + +1. Does not have immediate arguments. +2. EOF validation guarantees that operand stack has exactly `caller_stack_height + type[current_section_index].outputs` items. +3. Charges 3 gas. +4. Pops nothing and pushes nothing to operand stack. +5. Pops an item from return stack and sets `current_section_index` and `PC` to values from this item. + 1. If return stack is empty after this, execution halts with success. + +### Code Validation + +In addition to container format validation rules above, we extend code section validation rules (as defined in [EIP-3670](./eip-3670.md)). + +1. Code validation rules of EIP-3670 are applied to every code section. +2. Code section is invalid in case an immediate argument of any `CALLF` is greater than or equal to the total number of code sections. +3. `RJUMP`, `RJUMPI` and `RJUMPV` immediate argument value (jump destination relative offset) validation: + 1. Code section is invalid in case offset points to a position outside of section bounds. + 2. Code section is invalid in case offset points to one of two bytes directly following `CALLF` instruction. + +### Disallowed instructions + +Dynamic jump instructions `JUMP` (`0x56`) and `JUMPI` (`0x57`) are invalid and their opcodes are undefined. + +`JUMPDEST` (`0x5b`) instruction is renamed to `NOP` ("no operation") without the change in behaviour: it pops nothing and pushes nothing to operand stack and has no other effects except for `PC` increment and charging 1 gas. + +`PC` (0x58) instruction becomes invalid and its opcode is undefined. + +*Note:* This change implies that JUMPDEST analysis is no longer required for EOF code. + +### Execution + +1. Execution starts at the first byte of the first code section, and PC is set to 0. +2. Return stack is initialized to contain one item: `(code_section_index = 0, offset = 0, stack_height = 0)` +3. If any instruction access the operand stack item below `caller_stack_height`, execution results in exceptional halt. This rule replaces the old stack underflow check. +4. No change in stack overflow check: if any instruction causes the operand stack height to exceed `1024`, execution results in exceptional halt. + +## Rationale + +### `RETF` in the top frame ends execution vs exceptionally halts + +Alternative logic for executing `RETF` in the top frame could be to exceptionally halt execution, because there is arguably no caller for the starting function. This would mean that return stack is initialized as empty, and `RETF` exceptionally aborts when return stack is empty. + +We have decided in favor of always having at least one item in the return stack, because it allows to avoid having a special case for empty stack in the interpreter loop stack underflow check. We keep the stack underflow rule general by having `caller_stack_height = 0` in the top frame. + +### Code section limit and instruction size + +The number of code sections is limited to 1024. This requires 2-byte immediate for `CALLF` and leaves room for increasing the limit in the future. The 256 limit (1-byte immediate) was discussed and concerns were raised that it might not be sufficient. + +### `NOP` instruction + +Instead of deprecating `JUMPDEST` we repurpose it as `NOP` instruction, because `JUMPDEST` effectively was a "no-operation" instruction and was already used as such in various contexts. It can be useful for some off-chain tooling, e.g. benchmarking EVM implementations (performance of `NOP` instruction is performance of EVM interpreter loop), as a padding to force code alignment, as a placeholder in dynamic code composition. + +### Deprecating `JUMPDEST` analysis + +The purpose of `JUMPDEST` analysis was to find in code the valid `JUMPDEST` bytes that do not happen to be inside `PUSH` immediate data. Only dynamic jump instructions (`JUMP`, `JUMPI`) required destination to be `JUMPDEST` instruction. Relative static jumps (`RJUMP` and `RJUMPI`) do not have this requirement and are validated once at deploy-time EOF instruction validation. Therefore, without dynamic jump instructions, `JUMPDEST` analysis is not required. + +## Backwards Compatibility + +This change poses no risk to backwards compatibility, as it is introduced only for EOF1 contracts, for which deploying undefined instructions is not allowed, therefore there are no existing contracts using these instructions. The new instructions are not introduced for legacy bytecode (code which is not EOF formatted). + +The new execution state and multi-section control flow pose no risk to backwards compatibility, because it is a generalization of executing a single code section. Executing existing contracts (both legacy and EOF1) has no user-observable changes. + +## Security Considerations + +TBA + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4758.md b/EIPS/eip-4758.md new file mode 100644 index 0000000..e5d5692 --- /dev/null +++ b/EIPS/eip-4758.md @@ -0,0 +1,50 @@ +--- +eip: 4758 +title: Deactivate SELFDESTRUCT +description: Deactivate SELFDESTRUCT by changing it to SENDALL, which does recover all funds to the caller but does not delete any code or storage. +author: Guillaume Ballet (@gballet), Vitalik Buterin (@vbuterin), Dankrad Feist (@dankrad) +discussions-to: https://ethereum-magicians.org/t/eip-4758-deactivate-selfdestruct/8710 +status: Review +type: Standards Track +category: Core +created: 2022-02-03 +--- + +## Abstract + +This EIP renames the `SELFDESTRUCT` opcode to `SENDALL`, and replaces its functionality. The new functionality will be only to send all Ether in the account to the caller. + +## Motivation + +The `SELFDESTRUCT` opcode requires large changes to the state of an account, in particular removing all code and storage. This will not be possible in the future with Verkle trees: Each account will be stored in many different account keys, which will not be obviously connected to the root account. + +This EIP implements this change. Applications that only use `SELFDESTRUCT` to retrieve funds will still work. + +## Specification + + * The `SELFDESTRUCT` opcode is renamed to `SENDALL`, and now only immediately moves all ETH in the account to the target; it no longer destroys code or storage or alters the nonce + * All refunds related to `SELFDESTRUCT` are removed + +## Rationale + +Getting rid of the `SELFDESTRUCT` opcode has been considered in the past, and there are currently no strong reasons to use it. Disabling it will be a requirement for statelessness. + +## Backwards Compatibility + +This EIP requires a hard fork, since it modifies consensus rules. + +Few applications are affected by this change. The only use that breaks is where a contract is re-created at the same address using `CREATE2` (after a `SELFDESTRUCT`). + +## Security Considerations + +The following applications of `SELFDESTRUCT` will be broken and applications that use it in this way are not safe anymore: + +1. Any use where `SELFDESTRUCT` is used to burn non-ETH token balances, such as [EIP-20](./eip-20.md)), inside a contract. We do not know of any such use (since it can easily be done by sending to a burn address this seems an unlikely way to use `SELFDESTRUCT`) +2. Where `CREATE2` is used to redeploy a contract in the same place. There are two ways in which this can fail: + * The destruction prevents the contract from being used outside of a certain context. For example, the contract allows anyone to withdraw funds, but `SELFDESTRUCT` is used at the end of an operation to prevent others from doing this. This type of operation can easily be modified to not depend on `SELFDESTRUCT`. + * The `SELFDESTRUCT` operation is used in order to make a contract upgradable. This is not supported anymore and delegates should be used. + + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4760.md b/EIPS/eip-4760.md new file mode 100644 index 0000000..aafcbfd --- /dev/null +++ b/EIPS/eip-4760.md @@ -0,0 +1,63 @@ +--- +eip: 4760 +title: SELFDESTRUCT bomb +description: Deactivate SELFDESTRUCT by changing it to SENDALL and stage this via a stage of exponential gas cost increases. +author: Guillaume Ballet (@gballet), Vitalik Buterin (@vbuterin), Dankrad Feist (@dankrad) +discussions-to: https://ethereum-magicians.org/t/eip-4760-selfdestruct-bomb/8713 +status: Stagnant +type: Standards Track +category: Core +created: 2022-02-03 +--- +## Abstract + +This EIP renames the `SELFDESCRUCT` opcode to `SENDALL`, and replaces its functionality. The new functionality will be only to send all Ether in the account to the caller. + +In order to give apps more warning even if their developers are completely unaware of the EIP process, this version will exponentially increase the gas costs of the opcode, so any developer has time to see this change and react by implementing a version of their contract that does not rely on `SELFDESTRUCT` . + +## Motivation + +The `SELFDESTRUCT` opcode requires large changes to the state of an account, in particular removing all code and storage. This will not be possible in the future with Verkle trees: Each account will be stored in many different account keys, which will not be obviously connected to the root account. + +This EIP implements this change. Applications that only use `SELFDESTRUCT` to retrieve funds will still work. + +## Specification + +### Constants + +| Name | Value | Comment | +|------|-------|---------| +| `OLD_SELFDESTRUCT_COST` | 5000 | Current gas cost of `SELFDESTRUCT` opcode | +| `HARD_FORK_BLOCK` | TBD | (Shanghai HF block height) | +| `DOUBLING_SLOTS` | `2**16` | (Time for gas price to double, ca. 9 days) | +| `DOUBLINGS_BEFORE_SENDALL` | `13` | `SELFDESTRUCT` will be converted to `SENDALL` at `HARD_FORK_BLOCK + DOUBLING_SLOTS * DOUBLINGS_BEFORE_SENDALL` | + + * If `HARD_FORK_BLOCK <= slot < HARD_FORK_BLOCK + DOUBLING_SLOTS * DOUBLINGS_BEFORE_SENDALL` + * `SELFDESTRUCT` functionality remains unchanged + * `SELFDESTRUCT` gas cost is now `OLD_SELFDESTRUCT_COST * 2 ** ((slot - HARD_FORK_BLOCK) // DOUBLING_SLOTS)` + * For `slot >= HARD_FORK_BLOCK + DOUBLING_SLOTS * DOUBLINGS_BEFORE_SENDALL` + * The cost reverts back to `OLD_SELFDESTRUCT_COST` + * The `SELFDESTRUCT` opcode is renamed to `SENDALL`, and now only immediately moves all ETH in the account to the target; it no longer destroys code or storage or alters the nonce + * All refunds related to `SELFDESTRUCT` are removed + +## Rationale + +The idea behind this EIP is to disable `SELFDESTRUCT` in a way that gives ample warning to Dapp developers. Many developers do not watch the EIP process closely and can therefore be caught by surprise when an opcode is deactivated and does not fulfill its original purpose anymore. However, at least if the smart contract has regular use, then users will notice the price of the operation going up tremendously. The period over which this is happening (`HARD_FORK_BLOCK + DOUBLING_SLOTS * DOUBLINGS_BEFORE_SENDALL`) is chosen to be long enough (ca. 4 months) such that it gives developers time to react to this change and prepare their application. + +## Backward Compatibility + +This EIP requires a hard fork, since it modifies consensus rules. + +Few applications are affected by this change. The only use that breaks is where a contract is re-created at the same address using `CREATE2` (after a `SELFDESTRUCT`). The only application that is significantly affected (and where code can be analyzed) is able to switch to a different model, and should have ample time to do so. + +## Security Considerations + +The following applications of `SELFDESTRUCT` will be broken and applications that use it in this way are not safe anymore: +1. Any use where `SELFDESTRUCT` is used to burn non-ETH token balances, such as ERC20, inside a contract. We do not know of any such use (since it can easily be done by sending to a burn address this seems an unlikely way to use `SELFDESTRUCT`) +2. Where `CREATE2` is used to redeploy a contract in the same place. There are two ways in which this can fail: + - The destruction prevents the contract from being used outside of a certain context. For example, the contract allows anyone to withdraw funds, but `SELFDESTRUCT` is used at the end of an operation to prevent others from doing this. This type of operation can easily be modified to not depend on `SELFDESTRUCT`. + - The `SELFDESTRUCT` operation is used in order to make a contract upgradable. This is not supported anymore and delegates should be used. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). \ No newline at end of file diff --git a/EIPS/eip-4762.md b/EIPS/eip-4762.md new file mode 100644 index 0000000..03bcdc5 --- /dev/null +++ b/EIPS/eip-4762.md @@ -0,0 +1,308 @@ +--- +eip: 4762 +title: Statelessness gas cost changes +description: Changes the gas schedule to reflect the costs of creating a witness by requiring clients update their database layout to match. +author: Guillaume Ballet (@gballet), Vitalik Buterin (@vbuterin), Dankrad Feist (@dankrad) +discussions-to: https://ethereum-magicians.org/t/eip-4762-statelessness-gas-cost-changes/8714 +status: Stagnant +type: Standards Track +category: Core +created: 2022-02-03 +--- +## Abstract + +This EIP introduces changes in the gas schedule to reflect the costs of creating a witness. It requires clients to update their database layout to match this, so as to avoid potential DoS attacks. + +## Motivation + +The introduction of Verkle trees into Ethereum requires fundamental changes and as a preparation, this EIP is targeting the fork coming right before the verkle tree fork, in order to incentivize Dapp developers to adopt the new storage model, and ample time to adjust to it. It also incentivizes client developers to migrate their database format ahead of the verkle fork. + +## Specification + +### Access events + +We define access events as follows. When an access event takes place, the accessed data is saved to the Verkle tree (even if it was not modified). An access event is of the form`(address, sub_key, leaf_key)`, determining what data is being accessed. + +#### Access events for account headers + +When a non-precompile address is the target of a `CALL`, `CALLCODE`, `DELEGATECALL`, `SELFDESTRUCT`, `EXTCODESIZE`, or `EXTCODECOPY` opcode, or is the target address of a contract creation whose initcode starts execution, process these access events: + +``` +(address, 0, VERSION_LEAF_KEY) +(address, 0, CODE_SIZE_LEAF_KEY) +``` + +If a call is value-bearing (ie. it transfers nonzero wei), whether or not the callee is a precompile, process these two access events: + +``` +(caller_address, 0, BALANCE_LEAF_KEY) +(callee_address, 0, BALANCE_LEAF_KEY) +``` + +When a contract is created, process these access events: + +``` +(contract_address, 0, VERSION_LEAF_KEY) +(contract_address, 0, NONCE_LEAF_KEY) +(contract_address, 0, BALANCE_LEAF_KEY) +(contract_address, 0, CODE_KECCAK_LEAF_KEY) +(contract_address, 0, CODE_SIZE_LEAF_KEY) +``` + +If the `BALANCE` opcode is called targeting some address, process this access event: + +``` +(address, 0, BALANCE_LEAF_KEY) +``` + +If the `SELFDESTRUCT` opcode is called by some caller_address targeting some target_address (regardless of whether it’s value-bearing or not), process access events of the form: + +``` +(caller_address, 0, BALANCE_LEAF_KEY) +(target_address, 0, BALANCE_LEAF_KEY) +``` + +If the `EXTCODEHASH` opcode is called targeting some address, process an access event of the form: + +``` +(address, 0, CODEHASH_LEAF_KEY) +``` + +#### Access events for storage + +`SLOAD` and `SSTORE` opcodes with a given address and key process an access event of the form + +``` +(address, tree_key, sub_key) +``` + +Where tree_key and sub_key are computed as follows: + +```python +def get_storage_slot_tree_keys(storage_key: int) -> [int, int]: + if storage_key < (CODE_OFFSET - HEADER_STORAGE_OFFSET): + pos = HEADER_STORAGE_OFFSET + storage_key + else: + pos = MAIN_STORAGE_OFFSET + storage_key + return ( + pos // 256, + pos % 256 + ) +``` + +#### Access events for code + +In the conditions below, “chunk chunk_id is accessed” is understood to mean an access event of the form + +``` +(address, (chunk_id + 128) // 256, (chunk_id + 128) % 256) +``` + + * At each step of EVM execution, if and only if PC < len(code), chunk PC // CHUNK_SIZE (where PC is the current program counter) of the callee is accessed. In particular, note the following corner cases: + * The destination of a `JUMP` (or positively evaluated JUMPI) is considered to be accessed, even if the destination is not a jumpdest or is inside pushdata + * The destination of a `JUMPI` is not considered to be accessed if the jump conditional is false. + * The destination of a jump is not considered to be accessed if the execution gets to the jump opcode but does not have enough gas to pay for the gas cost of executing the `JUMP` opcode (including chunk access cost if the `JUMP` is the first opcode in a not-yet-accessed chunk) + * The destination of a jump is not considered to be accessed if it is beyond the code (`destination >= len(code)`) + * If code stops execution by walking past the end of the code, `PC = len(code)` is not considered to be accessed + * If the current step of EVM execution is a `PUSH{n}`, all chunks `(PC // CHUNK_SIZE) <= chunk_index <= ((PC + n) // CHUNK_SIZE)`` of the callee are accessed. + * If a nonzero-read-size `CODECOPY` or `EXTCODECOPY` read bytes `x...y` inclusive, all chunks ``(x // CHUNK_SIZE) <= chunk_index <= (min(y, code_size - 1) // CHUNK_SIZE)`` of the accessed contract are accessed. + * Example 1: for a `CODECOPY` with start position 100, read size 50, `code_size = 200`, `x = 100` and `y = 149` + * Example 2: for a `CODECOPY` with start position 600, read size 0, no chunks are accessed + * Example 3: for a `CODECOPY` with start position 1500, read size 2000, `code_size = 3100`, `x = 1500` and `y = 3099` + * `CODESIZE`, `EXTCODESIZE` and `EXTCODEHASH` do NOT access any chunks. + When a contract is created, access chunks `0 ... (len(code)+30)//31` + +### Write Events + +We define **write events** as follows. Note that when a write takes place, an access event also takes place (so the definition below should be a subset of the definition of access lists) A write event is of the form `(address, sub_key, leaf_key)`, determining what data is being written to. + +#### Write events for account headers + +When a nonzero-balance-sending CALL or SELFDESTRUCT with a given sender and recipient takes place, process these write events: + +``` +(sender, 0, BALANCE_LEAF_KEY) +(recipient, 0, BALANCE_LEAF_KEY) +``` + +When a contract creation is initialized, process these write events: + +``` +(contract_address, 0, VERSION_LEAF_KEY) +(contract_address, 0, NONCE_LEAF_KEY) +``` + +Only if the value sent with the creation is nonzero, also process: + +``` +(contract_address, 0, BALANCE_LEAF_KEY) +``` + +When a contract is created, process these write events: + +``` +(contract_address, 0, VERSION_LEAF_KEY) +(contract_address, 0, NONCE_LEAF_KEY) +(contract_address, 0, BALANCE_LEAF_KEY) +(contract_address, 0, CODE_KECCAK_LEAF_KEY) +(contract_address, 0, CODE_SIZE_LEAF_KEY) +``` + +#### Write events for storage + +SSTORE opcodes with a given `address` and `key` process a write event of the form + +``` +(address, tree_key, sub_key) +``` + +Where `tree_key` and `sub_key` are computed as follows: + +```python +def get_storage_slot_tree_keys(storage_key: int) -> [int, int]: + if storage_key < (CODE_OFFSET - HEADER_STORAGE_OFFSET): + pos = HEADER_STORAGE_OFFSET + storage_key + else: + pos = MAIN_STORAGE_OFFSET + storage_key + return ( + pos // 256, + pos % 256 + ) +``` + +#### Write events for code + +When a contract is created, make write events: + +```python +( + address, + (CODE_OFFSET + i) // VERKLE_NODE_WIDTH, + (CODE_OFFSET + i) % VERKLE_NODE_WIDTH +) +``` + +For `i` in `0 ... (len(code)+30)//31`. + +### Transactions + +#### Access events +For a transaction, make these access events: + +``` +(tx.origin, 0, VERSION_LEAF_KEY) +(tx.origin, 0, BALANCE_LEAF_KEY) +(tx.origin, 0, NONCE_LEAF_KEY) +(tx.origin, 0, CODE_SIZE_LEAF_KEY) +(tx.origin, 0, CODE_KECCAK_LEAF_KEY) +(tx.target, 0, VERSION_LEAF_KEY) +(tx.target, 0, BALANCE_LEAF_KEY) +(tx.target, 0, NONCE_LEAF_KEY) +(tx.target, 0, CODE_SIZE_LEAF_KEY) +(tx.target, 0, CODE_KECCAK_LEAF_KEY) +``` + +#### Write events + +``` +(tx.origin, 0, NONCE_LEAF_KEY) +``` + +if `value` is non-zero: + +``` +(tx.origin, 0, BALANCE_LEAF_KEY) +(tx.target, 0, BALANCE_LEAF_KEY) +``` + +### Witness gas costs + +Remove the following gas costs: + + * Increased gas cost of `CALL` if it is nonzero-value-sending + * EIP-2200 `SSTORE` gas costs except for the `SLOAD_GAS` + * 200 per byte contract code cost + +Reduce gas cost: + + * `CREATE` to 1000 + +|Constant |Value| +|-|-| +|WITNESS_BRANCH_COST |1900| +|WITNESS_CHUNK_COST |200| +|SUBTREE_EDIT_COST |3000| +|CHUNK_EDIT_COST |500| +|CHUNK_FILL_COST |6200| + +When executing a transaction, maintain four sets: + + * `accessed_subtrees: Set[Tuple[address, int]]` + * `accessed_leaves: Set[Tuple[address, int, int]]` + * `edited_subtrees`: `Set[Tuple[address, int]]` + * `edited_leaves`: `Set[Tuple[address, int, int]]` + + +When an **access** event of `(address, sub_key, leaf_key)` occurs, perform the following checks: + + * If ``(address, sub_key)`` is not in accessed_subtrees, charge WITNESS_BRANCH_COST gas and add that tuple to accessed_subtrees. + * If `leaf_key` is not `None` and ``(address, sub_key, leaf_key)`` is not in `accessed_leaves`, charge `WITNESS_CHUNK_COST` gas and add it to `accessed_leaves` + +When a **write** event of `(address, sub_key, leaf_key)` occurs, perform the following checks: + + * If (address, sub_key) is not in edited_subtrees, charge `SUBTREE_EDIT_COST` gas and add that tuple to edited_subtrees. + * If leaf_key is not None and `(address, sub_key, leaf_key)` is not in `edited_leaves`, charge `CHUNK_EDIT_COST` gas and add it to `edited_leaves` + * Additionally, if there was no value stored at `(address, sub_key, leaf_key)` (ie. the state held None at that position), charge `CHUNK_FILL_COST` + +Note that tree keys can no longer be emptied: only the values `0...2**256-1` can be written to a tree key, and 0 is distinct from None. Once a tree key is changed from `None` to not-`None`, it can never go back to `None`. + +### Replacement for access lists + +We replace EIP 2930 access lists with an SSZ structure of the form: + +```python +class AccessList(Container): + addresses: List[AccountAccessList, ACCESS_LIST_MAX_ELEMENTS] + +class AccountAccessList(Container): + address: Address32 + subtrees: List[AccessSubtree, ACCESS_LIST_MAX_ELEMENTS] + +class AccessSubtree(Container): + subtree_key: uint256 + elements: BitVector[256] +``` + +## Rationale + +### Gas reform + +Gas costs for reading storage and code are reformed to more closely reflect the gas costs under the new Verkle tree design. `WITNESS_CHUNK_COST` is set to charge 6.25 gas per byte for chunks, and `WITNESS_BRANCH_COST` is set to charge ~13,2 gas per byte for branches on average (assuming 144 byte branch length) and ~2.5 gas per byte in the worst case if an attacker fills the tree with keys deliberately computed to maximize proof length. + +The main differences from gas costs in Berlin are: + + * 200 gas charged per 31 byte chunk of code. This has been estimated to increase average gas usage by ~6-12% suggesting 10-20% gas usage increases at a 350 gas per chunk level). + * Cost for accessing adjacent storage slots (`key1 // 256 == key2 // 256`) decreases from 2100 to 200 for all slots after the first in the group, + * Cost for accessing storage slots 0…63 decreases from 2100 to 200, including the first storage slot. This is likely to significantly improve performance of many existing contracts, which use those storage slots for single persistent variables. + +Gains from the latter two properties have not yet been analyzed, but are likely to significantly offset the losses from the first property. It’s likely that once compilers adapt to these rules, efficiency will increase further. + +The precise specification of when access events take place, which makes up most of the complexity of the gas repricing, is necessary to clearly specify when data needs to be saved to the period 1 tree. + +## Backward Compatibility + +This EIP requires a hard fork, since it modifies consensus rules. + +The main backwards-compatibility-breaking changes is the gas costs for code chunk access making some applications less economically viable. It can be mitigated by increasing the gas limit at the same time as implementing this EIP, reducing the risk that applications will no longer work at all due to transaction gas usage rising above the block gas limit. + +## Security Considerations + +This EIP will mean that certain operations, mostly reading and writing several elements in the same suffix tree, become cheaper. If clients retain the same database structure as they have now, this would result in a DOS vector. + +So some adaptation of the database is required in order to make this work. + * In all possible futures, it is important to logically separate the commitment scheme from data storage. In particular, no traversal of the commitment scheme tree should be necessary to find any given state element + * In order to make accesses to the same stem cheap as required for this EIP, the best way is probably to store each stem in the same location in the database. Basically the 256 leaves of 32 bytes each would be stored in an 8kB BLOB. The overhead of reading/writing this BLOB is small because most of the cost of disk access is seeking and not the amount transferred. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4788.md b/EIPS/eip-4788.md new file mode 100644 index 0000000..24f6516 --- /dev/null +++ b/EIPS/eip-4788.md @@ -0,0 +1,157 @@ +--- +eip: 4788 +title: Beacon state root in the EVM +description: Expose beacon chain state roots in the EVM +author: Alex Stokes (@ralexstokes), Danny Ryan (@djrtwo) +discussions-to: https://ethereum-magicians.org/t/eip-4788-beacon-state-root-in-evm/8281 +status: Stagnant +type: Standards Track +category: Core +created: 2022-02-10 +--- + +## Abstract + +Commit to the state root of the beacon chain in the `ommers` field in the post-merge execution block. Reflect the changes in the `ommersHash` field of the execution block header. + +Store each beacon chain state root into a contract and add a new opcode that reads this contract. + +## Motivation + +Exposing the beacon chain state root allows for proofs about the beacon state to be verified inside the EVM. This functionality supports a wide variety of use cases in smart contracts involving validator status and finality produced by the consensus layer. + +In particular, this functionality is required for beacon chain validator withdrawals to the EVM. + +## Specification + +| constants | value | units +|--- |--- |--- +| `FORK_TIMESTAMP` | TBD | +| `FORK_EPOCH` | TBD | +| `HISTORY_STORAGE_ADDRESS` | `0xfffffffffffffffffffffffffffffffffffffffd` | +| `OPCODE_VALUE` | `0x48` | +| `G_beacon_state_root` | 20 | gas + +### Background + +The method of injecting the beacon state root in this EIP follows the general strategy of [EIP-4399](./eip-4399.md) to make a post-merge change to the EVM integrating information from the beacon chain. This EIP along with [EIP-3675](./eip-3675.md) should be taken as relevant background to understand the particular approach of this EIP. + +The method for exposing the state root data via opcode is inspired by [EIP-2935](./eip-2935.md). + +### Block structure and validity + +Beginning at the execution timestamp `FORK_TIMESTAMP`, execution clients **MUST**: + +1. set the value of the `ommers` field in the block to an RLP list with one element: the 32 byte [hash tree root](https://github.com/ethereum/consensus-specs/blob/dev/ssz/simple-serialize.md#merkleization) of the [beacon state](https://github.com/ethereum/consensus-specs/blob/dev/specs/bellatrix/beacon-chain.md#beaconstate) from the previous slot to this block. + +2. set the value of the `ommersHash` field in the block header to the Keccak256 hash of the `ommers` field. + +```python +beaconStateRoot = <32 byte value> # provided by consensus client +ommers = RLP([beaconStateRoot]) # in the block body +ommersHash = Keccak256(ommers) # in the block header +``` + +3. Add the block validation that the `ommersHash` does indeed match the expected commitment given the `ommers` value. + +### EVM changes + +#### Block processing + +At the start of processing any execution block where `block.timestamp >= FORK_TIMESTAMP` (i.e. before processing any transactions), write the beacon state root provided in the block into the storage of the smart contract at `HISTORY_STORAGE_ADDRESS`. This data is keyed by the block number. + +In pseudocode: + +```python +beacon_state_root = block.ommers[0] +sstore(HISTORY_STORAGE_ADDRESS, block.number, beacon_state_root) +``` + +#### New opcode + +Beginning at the execution timestamp `FORK_TIMESTAMP`, introduce a new opcode `BEACON_STATE_ROOT` at `OPCODE_VALUE`. This opcode consumes one word from the stack encoding the block number for the root. The opcode has a gas cost of `G_beacon_state_root`. + +The result of executing this opcode leaves one word on the stack corresponding to a read of the history contract's storage; in pseudocode: + +```python +block_number = evm.stack.pop() +sload(HISTORY_STORAGE_ADDRESS, block_number) +``` + +If there is no root stored at the requested block number, the opcode follows the existing EVM semantics of `sload` returning `0`. + +## Rationale + +### General strategy + +See the rationale for EIP-4399 for discussion about this general strategy of reusing execution block elements for beacon chain data. + +### Fork mechanics + +This EIP requires the consensus layer and execution layer to execute a network upgrade in lockstep. +To carry out this task, a `FORK_EPOCH` (of the beacon chain) will be chosen and then used to compute a timestamp `FORK_TIMESTAMP`. +This `FORK_TIMESTAMP` can be used in the execution layer to identify when the protocol change should be deployed. + +This technique works because the timestamps in post-merge execution blocks are aligned to beacon chain slots and thus serve as a proxy for the slot number. + +Another option for the fork definition would be to pick a beacon chain epoch and an execution payload block number. +This design however is not reliable due to the presence of skipped slots on the beacon chain. + +### Execution layer validations + +By including the beacon state root in the execution block in the deprecated `ommers` field, execution clients can still verify the chain in a self-contained way without relying on an available consensus client. +This property is important during syncing (and likely other phases of execution node operation). + +### Minimizing client code change + +By including the `ommersHash` validation, clients can use existing code with only minimal changes (supplying the actual state root) during block production and verification. +Having the beacon state root value in the `ommers` field means that it is fairly straightforward to provide the value from the block data to the EVM execution context for client implementations as they stand today. + +### Gas cost of opcode + +The suggested gas cost is just using the value for the `BLOCKHASH` opcode as `BEACON_STATE_ROOT` is an analogous operation. + +### Why not repurpose `BLOCKHASH`? + +The `BLOCKHASH` opcode could be repurposed to provide a beacon state root instead of the current execution block hash. +To minimize code change and simplify deployment to mainnet, this EIP suggests leaving `BLOCKHASH` alone and adding a new opcode with the desired semantics. + +### Why not bound history of state roots? + +Marginal state growth; adding every single root results in an additional ~84MB of state growth per year compared to ~30 GB of state overall. + +TODO: say something about statelessness +TODO: get latest numbers on state size, and compare against predicted growth + +### Beacon state root instead of block root + +Each slot on the beacon chain containing a block has both a block root and a state root (reflecting the state after applying said block). +The beacon block includes the state root so a proof about the state could also be authored against a block root at the cost of a few additional hashes. +Given that most use cases want to prove data encapsulated in a given state, rather than a given block, this EIP suggests exposing state roots over block roots. + +### Block number in lieu of slot + +The state roots are keyed by the `number` of the execution block. +Another option is to key roots by the beacon chain slot they belong to. +While at first pass this may seem more direct, the beacon chain can have "skipped" slots where a beacon proposer failed to produce a block that was included at a given slot. +Handling roots of skipped slots would complicate the EVM mechanism so this EIP suggests to use the execution block number where each distinct block number is guaranteed to have a distinct root. + +## Backwards Compatibility + +No issues. + +## Test Cases + +TODO + +## Reference Implementation + +TODO + +## Security Considerations + +TODO + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4799.md b/EIPS/eip-4799.md new file mode 100644 index 0000000..922d682 --- /dev/null +++ b/EIPS/eip-4799.md @@ -0,0 +1,200 @@ +--- +eip: 4799 +title: Non-Fungible Token Ownership Designation Standard +description: A standardized interface for designating ownership of an NFT +author: David Buckman (@davidbuckman), Isaac Buckman (@isaacbuckman) +discussions-to: https://ethereum-magicians.org/t/erc-4799-non-fungible-token-wrapping-standard/8396 +status: Stagnant +type: Standards Track +category: ERC +created: 2022-02-13 +requires: 165 +--- + +## Abstract + +The following defines a standard interface for designating ownership of an NFT to someone while the NFT is held in escrow by a smart contract. The standard allows for the construction of a directed acyclic graph of NFTs, where the designated owner of every NFT in a given chain is the terminal address of that chain. This enables the introduction of additional functionality to pre-existing NFTs, without having to give up the authenticity of the original. In effect, this means that all NFTs are composable and can be rented, used as collateral, fractionalized, and more. + +## Motivation + +Many NFTs aim to provide their holders with some utility - utility that can come in many forms. This can be the right to inhabit an apartment, access to tickets to an event, an airdrop of tokens, or one of the infinitely many other potential applications. However, in their current form, NFTs are limited by the fact that the only verifiable wallet associated with an NFT is the owner, so clients that want to distribute utility are forced to do so to an NFT's listed owner. This means that any complex ownership agreements must be encoded into the original NFT contract - there is no mechanism by which an owner can link the authenticity of their original NFT to any external contract. + +The goal of this standard is to allow users and developers the ability to define arbitrarily complex ownership agreements on NFTs that have already been minted. This way, new contracts with innovative ownership structures can be deployed, but they can still leverage the authenticity afforded by established NFT contracts - in the past a wrapping contract meant brand new NFTs with no established authenticity. + +Prior to this standard, wrapping an NFT inside another contract was the only way to add functionality after the NFT contract had been deployed, but this meant losing access to the utility of holding the original NFT. Any application querying for the owner of that NFT would determine the wrapping smart contract to be the owner. Using this standard, applications will have a standardized method of interacting with wrapping contracts so that they can continue to direct their utility to users even when the NFT has been wrapped. + +## Specification + +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. + +```solidity +import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; + +interface IERC4799NFT is IERC165 { + /// @dev This emits when ownership of any NFT changes by any mechanism. + /// This event emits when NFTs are created (`from` == 0) and destroyed + /// (`to` == 0). Exception: during contract creation, any number of NFTs + /// may be created and assigned without emitting Transfer. At the time of + /// any transfer, the approved address for that NFT (if any) is reset to none. + event Transfer( + address indexed from, + address indexed to, + uint256 indexed tokenId + ); + + /// @notice Find the owner of an NFT + /// @dev NFTs assigned to zero address are considered invalid, and queries + /// about them throw + /// @param tokenId The identifier for an NFT + /// @return The address of the owner of the NFT + function ownerOf(uint256 tokenId) external view returns (address); +} +``` +```solidity +/// @title ERC-4799 Non-Fungible Token Ownership Designation Standard +/// @dev See https://eips.ethereum.org/EIPS/eip-4799 +/// Note: the ERC-165 identifier for this interface is [TODO]. + +import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +import "./IERC4799NFT.sol"; + +interface IERC4799 is IERC165 { + /// @dev Emitted when a source token designates its ownership to the owner of the target token + event OwnershipDesignation( + IERC4799NFT indexed sourceContract, + uint256 sourceTokenId, + IERC4799NFT indexed targetContract, + uint256 targetTokenId + ); + + /// @notice Find the designated NFT + /// @param sourceContract The contract address of the source NFT + /// @param sourceTokenId The tokenId of the source NFT + /// @return (targetContract, targetTokenId) contract address and tokenId of the parent NFT + function designatedTokenOf(IERC4799NFT sourceContract, uint256 sourceTokenId) + external + view + returns (IERC4799NFT, uint256); +} +``` + +The authenticity of designated ownership of an NFT is conferred by the designating ERC-4799 contract’s ownership of the original NFT according to the source contract. This MUST be verified by clients by querying the source contract. + +Clients respecting this specification SHALL NOT distribute any utility to the address of the ERC-4799 contract. Instead, they MUST distribute it to the owner of the designated token that the ERC-4799 contract points them to. + +## Rationale + +To maximize the future compatibility of the wrapping contract, we first defined a canonical NFT interface. We created `IERC4799NFT`, an interface implicitly implemented by virtually all popular NFT contracts, including all deployed contracts that are [ERC-721](./eip-721.md) compliant. This interface represents the essence of an NFT: a mapping from a token identifier to the address of a singular owner, represented by the function `ownerOf`. + +The core of our proposal is the `IERC4799` interface, an interface for a standard NFT ownership designation contract (ODC). ERC4799 requires the implementation of a `designatedTokenOf` function, which maps a source NFT to exactly one target NFT. Through this function, the ODC expresses its belief of designated ownership. This designated ownership is only authentic if the ODC is listed as the owner of the original NFT, thus maintaining the invariant that every NFT has exactly one designated owner. + +## Backwards Compatibility + +The `IERC4799NFT` interface is backwards compatible with `IERC721`, as `IERC721` implicitly extends `IERC4799NFT`. This means that the ERC-4799 standard, which wraps NFTs that implement `ERC4799NFT`, is fully backwards compatible with ERC-721. + +## Reference Implementation + +```solidity +// SPDX-License-Identifier: CC0-1.0 +pragma solidity >=0.8.0 <0.9.0; + +import "./IERC4799.sol"; +import "./IERC4799NFT.sol"; +import "./ERC721.sol"; +import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; + +contract ERC721Composable is IERC4799, IERC721Receiver { + mapping(IERC4799NFT => mapping(uint256 => IERC4799NFT)) private _targetContracts; + mapping(IERC4799NFT => mapping(uint256 => uint256)) private _targetTokenIds; + + function designatedTokenOf(IERC4799NFT sourceContract, uint256 sourceTokenId) + external + view + override + returns (IERC4799NFT, uint256) + { + return ( + IERC4799NFT(_targetContracts[sourceContract][sourceTokenId]), + _targetTokenIds[sourceContract][sourceTokenId] + ); + } + + function designateToken( + IERC4799NFT sourceContract, + uint256 sourceTokenId, + IERC4799NFT targetContract, + uint256 targetTokenId + ) external { + require( + ERC721(address(sourceContract)).ownerOf(sourceTokenId) == msg.sender || + ERC721(address(sourceContract)).getApproved(sourceTokenId) == msg.sender, + "ERC721Composable: Only owner or approved address can set a designate ownership"); + _targetContracts[sourceContract][sourceTokenId] = targetContract; + _targetTokenIds[sourceContract][sourceTokenId] = targetTokenId; + emit OwnershipDesignation( + sourceContract, + sourceTokenId, + targetContract, + targetTokenId + ); + } + + function onERC721Received( + address, + address from, + uint256 sourceTokenId, + bytes calldata + ) external override returns (bytes4) { + ERC721(msg.sender).approve(from, sourceTokenId); + return IERC721Receiver.onERC721Received.selector; + } + + function supportsInterface(bytes4 interfaceId) + public + view + virtual + override + returns (bool) + { + return + (interfaceId == type(IERC4799).interfaceId || + interfaceId == type(IERC721Receiver).interfaceId); + } +} +``` +```solidity +// SPDX-License-Identifier: CC0-1.0 +pragma solidity >=0.8.0 <0.9.0; + +import "./IERC4799.sol"; +import "./IERC4799NFT.sol"; +import "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; + +contract DesignatedOwner { + function designatedOwnerOf( + IERC4799NFT tokenContract, + uint256 tokenId, + uint256 maxDepth + ) public view returns (address owner) { + owner = tokenContract.ownerOf(tokenId); + if (ERC165Checker.supportsInterface(owner, type(IERC4799).interfaceId)) { + require(maxDepth > 0, "designatedOwnerOf: depth limit exceeded"); + (tokenContract, tokenId) = IERC4799(owner).designatedTokenOf( + tokenContract, + tokenId + ); + return designatedOwnerOf(tokenContract, tokenId, maxDepth - 1); + } + } +} +``` + +## Security Considerations + +### Long/Cyclical Chains of Ownership + +The primary security concern is that of malicious actors creating excessively long or cyclical chains of ownership, leading applications that attempt to query for the designated owner of a given token to run out of gas and be unable to function. To address this, clients are expected to always query considering a `maxDepth` parameter, cutting off computation after a certain number of chain traversals. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4803.md b/EIPS/eip-4803.md new file mode 100644 index 0000000..f5f81a8 --- /dev/null +++ b/EIPS/eip-4803.md @@ -0,0 +1,51 @@ +--- +eip: 4803 +title: Limit transaction gas to a maximum of 2^63-1 +description: Valid transactions must have a reasonable gas limit +author: Alex Beregszaszi (@axic) +discussions-to: https://ethereum-magicians.org/t/eip-4803-limit-transaction-gas-to-a-maximum-of-2-63-1/8296 +status: Stagnant +type: Standards Track +category: Core +created: 2022-02-02 +--- + +## Abstract + +Limit transaction gas to be between `0` and `2^63-1`. + +## Motivation + +The gas limit field in the transaction is specified to be an arbitrary long unsigned integer, but various clients put limits on this value. This EIP brings a reasonable limit into consensus. + +## Specification + +Introduce one new restrictions retroactively from genesis: any transaction is invalid and not includeable in a block, where the gas limit exceeds `2^63-1`. + +## Rationale + +### `2^63-1` vs `2^64-1` + +`2^63-1` is chosen because it allows representing the gas value as a signed integer, and so the out of gas check can be done as a simple "less than zero" check after subtraction. + +### Current limit + +Due to the nature of RLP encoding, there is no fixed upper bound for the value, but most implementations limit it to 256-bits. Furthermore, most client implementations (such as geth) internally handle gas as a 64-bit value. + +## Backwards Compatibility + +While this is a breaking change, no actual effect should be visible. + +Before [EIP-1559](./eip-1559.md) it was possible to include transactions with `gasPrice = 0` and thus the `gasLimit * gasPrice <= accountBalance` calculation could have allowed for arbitrarily large values of `gasLimit`. However, the rule that the transaction list cannot exceed the block gas limit, and the strict rules about how the block gas limit can change, prevented arbitrarily large values of `gasLimit` to be in the historical state. + +## Security Considerations + +None. + +## Test Cases + +TBA + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4804.md b/EIPS/eip-4804.md new file mode 100644 index 0000000..4a86e99 --- /dev/null +++ b/EIPS/eip-4804.md @@ -0,0 +1,147 @@ +--- +eip: 4804 +title: Web3 URL to EVM Call Message Translation +description: A translation of an HTTP-style Web3 URL to an EVM call message +author: Qi Zhou (@qizhou), Chao Pi (@pichaoqkc), Sam Wilson (@SamWilsn) +discussions-to: https://ethereum-magicians.org/t/eip-4804-web3-url-to-evm-call-message-translation/8300 +status: Final +type: Standards Track +category: ERC +created: 2022-02-14 +requires: 137 +--- + +## Abstract + +This standard translates an RFC 2396 URI like `web3://uniswap.eth/` to an EVM message such as: + +``` +EVMMessage { + To: 0xaabbccddee.... // where uniswap.eth's address registered at ENS + Calldata: 0x + ... +} +``` + +## Motivation + +Currently, reading data from Web3 generally relies on a translation done by a Web2 proxy to Web3 blockchain. The translation is mostly done by the proxies such as dApp websites/node service provider/etherscan, which are out of the control of users. The standard here aims to provide a simple way for Web2 users to directly access the content of Web3, especially on-chain Web contents such as SVG/HTML. Moreover, this standard enables interoperability with other standards already compatible with URIs, like SVG/HTML. + +## Specification + +This specification only defines read-only (i.e. Solidity's `view` functions) semantics. State modifying functions may be defined as a future extension. + +A Web3 URL is in the following form + +``` +web3URL = web3Schema [userinfo "@"] contractName [":" chainid] path ["?" query] +web3Schema = [ "ethereum-web3://" | "eth-web3://" | "web3://" ] +contractName = address | [name "." [ subDomain0 "." ... ]] nsProviderSuffix +path = ["/" method ["/" argument_0 ["/" argument_1 ... ]]] +argument = [type "!"] value +query = "attribute_1=value_1 [ "&" attribute_2=value_2 ... ] +attribute = "returns" | "returnTypes" | other_attribute +``` + +where + +- **web3Schema** indicates the schema of the URL, which is `web3://` or `w3://` for short. +- **userinfo** indicates which user is calling the EVM, i.e., "From" field in EVM call message. If not specified, the protocol will use 0x0 as the sender address. +- **contractName** indicates the contract to be called, i.e., "To" field in the EVM call message. If the **contractName** is an **address**, i.e., 0x + 20-byte-data hex, then "To" will be the address. Otherwise, the name is from a name service. In the second case, **nsProviderSuffix** will be the suffix from name service providers such as "eth", etc. The way to translate the name from a name service to an address will be discussed in later EIPs. +- **chainid** indicates which chain to resolve **contractName** and call the message. If not specified, the protocol will use the same chain as the name service provider, e.g., 1 for eth. If no name service provider is available, the default chainid is 1. +- **query** is an optional component containing a sequence of attribute-value pairs separated by "&". + +### Resolve Mode + +Once the "To" address and chainid are determined, the protocol will check the resolver mode of contract by calling "resolveMode" method. The protocol currently supports two resolve modes: + +#### Manual Mode + +The manual mode will not do any interpretation of **path** and **query**, and put **path** [ "?" **query** ] as the calldata of the message directly. + +#### Auto Mode + +The auto mode is the default mode to resolve (also applies when the "resolveMode" method is unavailable in the target contract). In the auto mode, if **path** is empty, then the protocol will call the target contract with empty calldata. Otherwise, the calldata of the EVM message will use standard Solidity contract ABI, where + +- **method** is a string of function method be called +- **argument_i** is the ith argument of the method. If **type** is specified, the value will be translated to the corresponding type. The protocol currently supports the basic types such as uint256, bytes32, address, bytes, and string. If **type** is not specified, then the type will be automatically detected using the following rule in a sequential way: + +1. **type**="uint256", if **value** is numeric; or +2. **type**="bytes32", if **value** is in the form of 0x+32-byte-data hex; or +3. **type**="address", if **value** is in the form of 0x+20-byte-data hex; or +4. **type**="bytes", if **value** is in the form of 0x followed by any number of bytes besides 20 or 32; or +5. else **type**="address" and parse the argument as a domain name in the form of `[name "." [ subDomain0 "." ... ]] nsProviderSuffix`. In this case, the actual value of the argument will be obtained from **nsProviderSuffix**, e.g., eth. If **nsProviderSuffix** is not supported, an unsupported NS provider error will be returned. + +Note that if **method** does not exist, i.e., **path** is empty or "/", then the contract will be called with empty calldata. + +- **returns** attribute in **query** tells the format of the returned data. If not specified, the returned message data will be parsed in "(bytes32)" and MIME will be set based on the suffix of the last argument. If **returns** is "()", the returned data will be parsed in raw bytes in JSON. Otherwise, the returned message will be parsed in the specified **returns** attribute in JSON. If multiple **returns** attributes are present, the value of the last **returns** attribute will be applied. Note that **returnTypes** is the alias of **returns**, but it is not recommended to use and is mainly for backward-compatible purpose. + +### Examples + +#### Example 1 + +``` +web3://w3url.eth/ +``` + +The protocol will find the address of **w3url.eth** from ENS in chainid 1 (Mainnet), and then the protocol will call the address with "From" = "0x..." and "Calldata" = "0x2F". + +#### Example 2 + +``` +web3://cyberbrokers-meta.eth/renderBroker/9999 +``` + +The protocol will find the address of **cyberbrokers-meta.eth** from ENS on chainid 1 (Mainnet), and then call the address with "To" = "0x..." and "Calldata" = "0x" + `keccak("view(uint256)")[0:4] + abi.encode(uint256(9999))`. + +#### Example 3 + +``` +web3://vitalikblog.eth:5/ +``` + +The protocol will find the address of **vitalikblog.eth** from ENS on chainid 5 (Goerli), and then call the address with "From" = "0x..." and "Calldata" = "0x2F" with chainid = 5. + +#### Example 4 + +``` +web3://0xe4ba0e245436b737468c206ab5c8f4950597ab7f:42170/ +``` + +The protocol will call the address with "To" = "0x9e081Df45E0D167636DB9C61C7ce719A58d82E3b" and "Calldata" = "0x" with chainid = 42170 (Arbitrum Nova). + +#### Example 5 + +``` +web3://0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/balanceOf/vitalik.eth?returns=(uint256) +``` + +The protocol will find the addresses of **vitalik.eth** from ENS on chainid 1 (Mainnet) and then call the method "balanceOf(address)" of the contract with the **charles.eth**'s address. The returned data will be parsed as uint256 like `[ "10000000000000" ]`. + +#### Example 6 + +``` +web3://0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/balanceOf/vitalik.eth?returns=() +``` + +The protocol will find the address of **vitalik.eth** from ENS on chainid 1 (Mainnet) and then call the method "balanceOf(address)" of the address. The returned data will be parsed as raw bytes like `["0x000000000000000000000000000000000000000000000000000009184e72a000"]`. + +## Rationale + +The purpose of the proposal is to add a decentralized presentation layer for Ethereum. With the layer, we are able to render any web content (including HTML/CSS/JPG/PNG/SVG, etc) on-chain using human-readable URLs, and thus EVM can be served as decentralized Backend. The design of the standard is based on the following principles: + +- **Human-readable**. The Web3 URL should be easily recognized by human similar to Web2 URL (`http://`). As a result, we support names from name services to replace address for better readability. In addition, instead of using calldata in hex, we use human-readable method + arguments and translate them to calldata for better readability. + +- **Maximum-Compatible with HTTP-URL standard**. The Web3 URL should be compatible with HTTP-URL standard including relative pathing, query, fragment, etc so that the support of existing HTTP-URL (e.g., by browser) can be easily extended to Web3 URL with minimal modification. This also means that existing Web2 users can easily migrate to Web3 with minimal extra knowledge of this standard. + +- **Simple**. Instead of providing explicit types in arguments, we use a "maximum likelihood" principle of auto-detecting the types of the arguments such as address, bytes32, and uint256. This could greatly minimize the length of URL, while avoiding confusion. In addition, explicit types are also supported to clear the confusion if necessary. + +- **Flexible**. The contract is able to override the encoding rule so that the contract has fine-control of understanding the actual Web resources that the users want to locate. + +## Security Considerations + +No security considerations were found. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4824.md b/EIPS/eip-4824.md new file mode 100644 index 0000000..ed7404e --- /dev/null +++ b/EIPS/eip-4824.md @@ -0,0 +1,313 @@ +--- +eip: 4824 +title: Common Interfaces for DAOs +description: An API for decentralized autonomous organizations (DAOs). +author: Joshua Tan (@thelastjosh), Isaac Patka (@ipatka), Ido Gershtein , Eyal Eithcowich , Michael Zargham (@mzargham), Sam Furter (@nivida) +discussions-to: https://ethereum-magicians.org/t/eip-4824-decentralized-autonomous-organizations/8362 +status: Draft +type: Standards Track +category: ERC +created: 2022-02-17 +--- + +## Abstract + +An API standard for decentralized autonomous organizations (DAOs), focused on relating on-chain and off-chain representations of membership and proposals. + +## Motivation + +DAOs, since being invoked in the Ethereum whitepaper, have been vaguely defined. This has led to a wide range of patterns but little standardization or interoperability between the frameworks and tools that have emerged. Standardization and interoperability are necessary to support a variety of use-cases. In particular, a standard daoURI, similar to tokenURI in [EIP-721](./eip-721), will enhance DAO discoverability, legibility, proposal simulation, and interoperability between tools. More consistent data across the ecosystem is also a prerequisite for future DAO standards. + +## Specification + +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. + +Every contract implementing this EIP MUST implement the `EIP4824` interface below: + +```solidity +pragma solidity ^0.8.1; + +/// @title EIP-4824 Common Interfaces for DAOs +/// @dev See https://eips.ethereum.org/EIPS/eip-4824 + +interface EIP4824 { + /// @notice A distinct Uniform Resource Identifier (URI) pointing to a JSON object following the "EIP-4824 DAO JSON-LD Schema". This JSON file splits into four URIs: membersURI, proposalsURI, activityLogURI, and governanceURI. The membersURI should point to a JSON file that conforms to the "EIP-4824 Members JSON-LD Schema". The proposalsURI should point to a JSON file that conforms to the "EIP-4824 Proposals JSON-LD Schema". The activityLogURI should point to a JSON file that conforms to the "EIP-4824 Activity Log JSON-LD Schema". The governanceURI should point to a flatfile, normatively a .md file. Each of the JSON files named above can be statically-hosted or dynamically-generated. + function daoURI() external view returns (string _daoURI); +} +``` + +The DAO JSON-LD Schema mentioned above: + +```json +{ + "@context": "http://www.daostar.org/schemas", + "type": "DAO", + "name": "", + "description": "", + "membersURI": "", + "proposalsURI": "", + "activityLogURI": "", + "governanceURI": "" +} +``` + +A DAO MAY inherit the above interface above or it MAY create an external registration contract that is compliant with this EIP. The external registration contract MUST store the DAO’s primary address. + +```solidity +pragma solidity ^0.8.1; + +/// @title EIP-4824 Common Interfaces for DAOs +/// @dev See + +error NotOwner(); +error NotOffered(); + +contract EIP4824Registration is EIP4824 { + string private _daoURI; + address daoAddress; + + event NewURI(string daoURI); + + constructor() { + daoAddress = address(0xdead); + } + + function initialize(address _daoAddress, string memory daoURI_) external { + if (daoAddress != address(0)) revert AlreadyInitialized(); + daoAddress = _daoAddress; + _daoURI = daoURI_; + } + + function setURI(string memory daoURI_) external { + if (msg.sender != daoAddress) revert NotOwner(); + _daoURI = daoURI_; + emit NewURI(daoURI_); + } + + function daoURI() external view returns (string memory daoURI_) { + return _daoURI; + } +} +``` + +If a DAO uses an external registration contract, the DAO SHOULD use a common registration factory contract to enable efficient network indexing. + +```solidity +pragma solidity ^0.8.1; + +/// @title EIP-4824 Common Interfaces for DAOs +/// @dev See + +contract CloneFactory { + // implementation of eip-1167 - see https://eips.ethereum.org/EIPS/eip-1167 + function createClone(address target) internal returns (address result) { + bytes20 targetBytes = bytes20(target); + assembly { + let clone := mload(0x40) + mstore( + clone, + 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000 + ) + mstore(add(clone, 0x14), targetBytes) + mstore( + add(clone, 0x28), + 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000 + ) + result := create(0, clone, 0x37) + } + } +} + +contract EIP4824RegistrationFactory is CloneFactory { + event NewRegistration( + address indexed daoAddress, + string daoURI, + address registration + ); + + address public template; /*Template contract to clone*/ + + constructor(address _template) public { + template = _template; + } + + function summonRegistration(string calldata daoURI_) external { + EIP4824Registration reg = EIP4824Registration(createClone(template)); /*Create a new clone of the template*/ + reg.initialize(msg.sender, daoURI_); + emit NewRegistration(msg.sender, daoURI_, address(reg)); + } +} +``` + +### Members + +Members JSON-LD Schema. + +```json +{ + "@context": "", + "type": "DAO", + "name": "", + "members": [ + { + "type": "EthereumAddress", + "id": "
" + }, + { + "type": "EthereumAddress", + "id": "
" + } + ] +} +``` + +### Proposals + +Proposals JSON-LD Schema. Every contract implementing this EIP should implement a proposalsURI pointing to a JSON object satisfying this schema. + +In particular, any on-chain proposal MUST be associated to an id of the form CAIP10_ADDRESS + “?proposalId=” + PROPOSAL_COUNTER, where CAIP10_ADDRESS is an address following the CAIP-10 standard and PROPOSAL_COUNTER is an arbitrary identifier such as a uint256 counter or a hash that is locally unique per CAIP-10 address. Off-chain proposals MAY use a similar id format where CAIP10_ADDRESS is replaced with an appropriate URI or URL. + +```json +{ + "@context": "http://www.daostar.org/schemas", + "type": "DAO", + "name": "", + "proposals": [ + { + "type": "proposal", + "id": "", + "name": "", + "contentURI": "", + "status": "", + "calls": [ + { + "type": "CallDataEVM", + "operation": "", + "from": "", + "to": "", + "value": "", + "data": "" + } + ] + } + ] +} +``` + +### Activity Log + +Activity Log JSON-LD Schema. + +```json +{ + "@context": "", + "type": "DAO", + "name": "", + "activities": [ + { + "id": "", + "type": "activity", + "proposal": { + "id": "", + "type": "proposal" + }, + "member": { + "type": "EthereumAddress", + "id": "
" + } + }, + ], + "activities": [ + { + "id": "", + "type": "activity", + "proposal": { + "id": "", + "type": "proposal" + }, + "member": { + "type": "EthereumAddress", + "id": "
" + } + } + ] +} +``` + +## Rationale + +In this standard, we assume that all DAOs possess at least two primitives: *membership* and *behavior*. *Membership* is defined by a set of addresses. *Behavior* is defined by a set of possible contract actions, including calls to external contracts and calls to internal functions. *Proposals* relate membership and behavior; they are objects that members can interact with and which, if and when executed, become behaviors of the DAO. + +### APIs, URIs, and off-chain data + +DAOs themselves have a number of existing and emerging use-cases. But almost all DAOs need to publish data off-chain for a number of reasons: communicating to and recruiting members, coordinating activities, powering user interfaces and governance applications such as Snapshot or Tally, or enabling search and discovery via platforms like DeepDAO, Messari, and Etherscan. Having a standardized schema for this data organized across multiple URIs, i.e. an API specification, would strengthen existing use-cases for DAOs, help scale tooling and frameworks across the ecosystem, and build support for additional forms of interoperability. + +While we considered standardizing on-chain aspects of DAOs in this standard, particularly on-chain proposal objects and proposal IDs, we felt that this level of standardization was premature given (1) the relative immaturity of use-cases, such as multi-DAO proposals or master-minion contracts, that would benefit from such standardization, (2) the close linkage between proposal systems and governance, which we did not want to standardize (see “governanceURI”, below), and (3) the prevalence of off-chain and L2 voting and proposal systems in DAOs (see “proposalsURI”, below). Further, a standard URI interface is relatively easy to adopt and has been actively demanded by frameworks (see “Community Consensus”, below). + +### membersURI + +Approaches to membership vary widely in DAOs. Some DAOs and DAO frameworks (e.g. Gnosis Safe, Tribute), maintain an explicit, on-chain set of members, sometimes called owners or stewards. But many DAOs are structured so that membership status is based on the ownership of a token or tokens (e.g. Moloch, Compound, DAOstack, 1Hive Gardens). In these DAOs, computing the list of current members typically requires some form of off-chain indexing of events. + +In choosing to ask only for an (off-chain) JSON schema of members, we are trading off some on-chain functionality for more flexibility and efficiency. We expect different DAOs to use membersURI in different ways: to serve a static copy of on-chain membership data, to contextualize the on-chain data (e.g. many Gnosis Safe stewards would not say that they are the only members of the DAO), to serve consistent membership for a DAO composed of multiple contracts, or to point at an external service that computes the list, among many other possibilities. We also expect many DAO frameworks to offer a standard endpoint that computes this JSON file, and we provide a few examples of such endpoints in the implementation section. + +We encourage extensions of the Membership JSON-LD Schema, e.g. for DAOs that wish to create a state variable that captures active/inactive status or different membership levels. + +### proposalsURI + +Proposals have become a standard way for the members of a DAO to trigger on-chain actions, e.g. sending out tokens as part of grant or executing arbitrary code in an external contract. In practice, however, many DAOs are governed by off-chain decision-making systems on platforms such as Discourse, Discord, or Snapshot, where off-chain proposals may function as signaling mechanisms for an administrator or as a prerequisite for a later on-chain vote. (To be clear, on-chain votes may also serve as non-binding signaling mechanisms or as “binding” signals leading to some sort of off-chain execution.) The schema we propose is intended to support both on-chain and off-chain proposals, though DAOs themselves may choose to report only on-chain, only off-chain, or some custom mix of proposal types. + +**Proposal ID**. Every unique on-chain proposal MUST be associated to a proposal ID of the form CAIP10_ADDRESS + “?proposalId=” + PROPOSAL_COUNTER, where PROPOSAL_COUNTER is an arbitrary string which is unique per CAIP10_ADDRESS. Note that PROPOSAL_COUNTER may not be the same as the on-chain representation of the proposal; however, each PROPOSAL_COUNTER should be unique per CAIP10_ADDRESS, such that the proposal ID is a globally unique identifier. We endorse the CAIP-10 standard to support multi-chain / layer 2 proposals and the “?proposalId=” query syntax to suggest off-chain usage. + +**ContentURI**. In many cases, a proposal will have some (off-chain) content such as a forum post or a description on a voting platform which predates or accompanies the actual proposal. + +**Status**. Almost all proposals have a status or state, but the actual status is tied to the governance system, and there is no clear consensus between existing DAOs about what those statuses should be (see table below). Therefore, we have defined a “status” property with a generic, free text description field. + +| Project | Proposal Statuses | +| --- | --- | +| Aragon | Not specified | +| Colony | [‘Null’, ‘Staking’, ‘Submit’, ‘Reveal’, ‘Closed’, ‘Finalizable’, ‘Finalized’, ‘Failed’] | +| Compound | [‘Pending’, ‘Active’, ‘Canceled’, ‘Defeated’, ‘Succeeded’, ‘Queued’, ‘Expired’, ‘Executed’] | +| DAOstack/ Alchemy | [‘None’, ‘ExpiredInQueue’, ‘Executed’, ‘Queued’, ‘PreBoosted’, ‘Boosted’, ‘QuietEndingPeriod’] | +| Moloch v2 | [sponsored, processed, didPass, cancelled, whitelist, guildkick] | +| Tribute | [‘EXISTS’, ‘SPONSORED’, ‘PROCESSED’] | + +**ExecutionData**. For on-chain proposals with non-empty execution, we include an array field to expose the call data. The main use-case for this data is execution simulation of proposals. + +### activityLogURI + +The activity log JSON is intended to capture the interplay between a member of a DAO and a given proposal. Examples of activities include the creation/submission of a proposal, voting on a proposal, disputing a proposal, and so on. + +*Alternatives we considered: history, interactions* + +### governanceURI + +Membership, to be meaningful, usually implies rights and affordances of some sort, e.g. the right to vote on proposals, the right to ragequit, the right to veto proposals, and so on. But many rights and affordances of membership are realized off-chain (e.g. right to vote on a Snapshot, gated access to a Discord). Instead of trying to standardize these wide-ranging practices or forcing DAOs to locate descriptions of those rights on-chain, we believe that a flatfile represents the easiest and most widely-acceptable mechanism for communicating what membership means and how proposals work. These flatfiles can then be consumed by services such as Etherscan, supporting DAO discoverability and legibility. + +We chose the word “governance” as an appropriate word that reflects (1) the widespread use of the word in the DAO ecosystem and (2) the common practice of emitting a governance.md file in open-source software projects. + +*Alternative names considered: description, readme, constitution* + +### Why JSON-LD + +We chose to use JSON-LD rather than the more widespread and simpler JSON standard because (1) we want to support use-cases where a DAO wants to include members using some other form of identification than their Ethereum address and (2) we want this standard to be compatible with future multi-chain standards. Either use-case would require us to implement a context and type for addresses, which is already implemented in JSON-LD. + +Further, given the emergence of patterns such as subDAOs and DAOs of DAOs in large organizations such as Synthetix, as well as L2 and multi-chain use-cases, we expect some organizations will point multiple DAOs to the same URI, which would then serve as a gateway to data from multiple contracts and services. The choice of JSON-LD allows for easier extension and management of that data. + +### **Community Consensus** + +The initial draft standard was developed as part of the DAOstar One roundtable series, which included representatives from all major EVM-based DAO frameworks (Aragon, Compound, DAOstack, Gnosis, Moloch, OpenZeppelin, and Tribute), a wide selection of DAO tooling developers, as well as several major DAOs. Thank you to all the participants of the roundtable. We would especially like to thank Auryn Macmillan, Fabien of Snapshot, Selim Imoberdorf, Lucia Korpas, and Mehdi Salehi for their contributions. + +In-person events will be held at Schelling Point 2022 and at ETHDenver 2022, where we hope to receive more comments from the community. We also plan to schedule a series of community calls through early 2022. + +## Backwards Compatibility +Existing contracts that do not wish to use this specification are unaffected. DAOs that wish to adopt the standard without updating or migrating contracts can do so via an external registration contract. + +## Security Considerations + +This standard defines the interfaces for the DAO URIs but does not specify the rules under which the URIs are set, or how the data is prepared. Developers implementing this standard should consider how to update this data in a way aligned with the DAO’s governance model, and keep the data fresh in a way that minimizes reliance on centralized service providers. + +Indexers that rely on the data returned by the URI should take caution if DAOs return executable code from the URIs. This executable code might be intended to get the freshest information on membership, proposals, and activity log, but it could also be used to run unrelated tasks. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). + diff --git a/EIPS/eip-4834.md b/EIPS/eip-4834.md new file mode 100644 index 0000000..2f86485 --- /dev/null +++ b/EIPS/eip-4834.md @@ -0,0 +1,229 @@ +--- +eip: 4834 +title: Hierarchical Domains +description: Extremely generic name resolution +author: Gavin John (@Pandapip1) +discussions-to: https://ethereum-magicians.org/t/erc-4834-hierarchical-domains-standard/8388 +status: Final +type: Standards Track +category: ERC +created: 2022-02-22 +--- + +## Abstract + +This is a standard for generic name resolution with arbitrarily complex access control and resolution. It permits a contract that implements this EIP (referred to as a "domain" hereafter) to be addressable with a more human-friendly name, with a similar purpose to [ERC-137](./eip-137.md) (also known as "ENS"). + +## Motivation + +The advantage of this EIP over existing standards is that it provides a minimal interface that supports name resolution, adds standardized access control, and has a simple architecture. ENS, although useful, has a comparatively complex architecture and does not have standard access control. + +In addition, all domains (including subdomains, TLDs, and even the root itself) are actually implemented as domains, meaning that name resolution is a simple iterative algorithm, not unlike DNS itself. + +## Specification + +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. + +### Contract Interface + +```solidity +interface IDomain { + /// @notice Query if a domain has a subdomain with a given name + /// @param name The subdomain to query, in right to left order + /// @return `true` if the domain has a subdomain with the given name, `false` otherwise + function hasDomain(string[] memory name) external view returns (bool); + + /// @notice Fetch the subdomain with a given name + /// @dev This should revert if `hasDomain(name)` is `false` + /// @param name The subdomain to fetch, in right to left order + /// @return The subdomain with the given name + function getDomain(string[] memory name) external view returns (address); +} +``` + +### Name Resolution + +To resolve a name (like `"a.b.c"`), split it by the delimiter (resulting in something like `["a", "b", "c"]`). Set `domain` initially to the root domain, and `path` to be an empty list. + +Pop off the last element of the array (`"c"`) and add it to the path, then call `domain.hasDomain(path)`. If it's `false`, then the domain resolution fails. Otherwise, set the domain to `domain.getDomain(path)`. Repeat until the list of split segments is empty. + +There is no limit to the amount of nesting that is possible. For example, `0.1.2.3.4.5.6.7.8.9.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z` would be valid if the root contains `z`, and `z` contains `y`, and so on. + +Here is a solidity function that resolves a name: + +```solidity +function resolve(string[] calldata splitName, IDomain root) public view returns (address) { + IDomain current = root; + string[] memory path = []; + for (uint i = splitName.length - 1; i >= 0; i--) { + // Append to back of list + path.push(splitName[i]); + // Require that the current domain has a domain + require(current.hasDomain(path), "Name resolution failed"); + // Resolve subdomain + current = current.getDomain(path); + } + return current; +} +``` + +### Optional Extension: Registerable + +```solidity +interface IDomainRegisterable is IDomain { + //// Events + + /// @notice Must be emitted when a new subdomain is created (e.g. through `createDomain`) + /// @param sender msg.sender for createDomain + /// @param name name for createDomain + /// @param subdomain subdomain in createDomain + event SubdomainCreate(address indexed sender, string name, address subdomain); + + /// @notice Must be emitted when the resolved address for a domain is changed (e.g. with `setDomain`) + /// @param sender msg.sender for setDomain + /// @param name name for setDomain + /// @param subdomain subdomain in setDomain + /// @param oldSubdomain the old subdomain + event SubdomainUpdate(address indexed sender, string name, address subdomain, address oldSubdomain); + + /// @notice Must be emitted when a domain is unmapped (e.g. with `deleteDomain`) + /// @param sender msg.sender for deleteDomain + /// @param name name for deleteDomain + /// @param subdomain the old subdomain + event SubdomainDelete(address indexed sender, string name, address subdomain); + + //// CRUD + + /// @notice Create a subdomain with a given name + /// @dev This should revert if `canCreateDomain(msg.sender, name, pointer)` is `false` or if the domain exists + /// @param name The subdomain name to be created + /// @param subdomain The subdomain to create + function createDomain(string memory name, address subdomain) external payable; + + /// @notice Update a subdomain with a given name + /// @dev This should revert if `canSetDomain(msg.sender, name, pointer)` is `false` of if the domain doesn't exist + /// @param name The subdomain name to be updated + /// @param subdomain The subdomain to set + function setDomain(string memory name, address subdomain) external; + + /// @notice Delete the subdomain with a given name + /// @dev This should revert if the domain doesn't exist or if `canDeleteDomain(msg.sender, name)` is `false` + /// @param name The subdomain to delete + function deleteDomain(string memory name) external; + + + //// Parent Domain Access Control + + /// @notice Get if an account can create a subdomain with a given name + /// @dev This must return `false` if `hasDomain(name)` is `true`. + /// @param updater The account that may or may not be able to create/update a subdomain + /// @param name The subdomain name that would be created/updated + /// @param subdomain The subdomain that would be set + /// @return Whether an account can update or create the subdomain + function canCreateDomain(address updater, string memory name, address subdomain) external view returns (bool); + + /// @notice Get if an account can update or create a subdomain with a given name + /// @dev This must return `false` if `hasDomain(name)` is `false`. + /// If `getDomain(name)` is also a domain implementing the subdomain access control extension, this should return `false` if `getDomain(name).canMoveSubdomain(msg.sender, this, subdomain)` is `false`. + /// @param updater The account that may or may not be able to create/update a subdomain + /// @param name The subdomain name that would be created/updated + /// @param subdomain The subdomain that would be set + /// @return Whether an account can update or create the subdomain + function canSetDomain(address updater, string memory name, address subdomain) external view returns (bool); + + /// @notice Get if an account can delete the subdomain with a given name + /// @dev This must return `false` if `hasDomain(name)` is `false`. + /// If `getDomain(name)` is a domain implementing the subdomain access control extension, this should return `false` if `getDomain(name).canDeleteSubdomain(msg.sender, this, subdomain)` is `false`. + /// @param updater The account that may or may not be able to delete a subdomain + /// @param name The subdomain to delete + /// @return Whether an account can delete the subdomain + function canDeleteDomain(address updater, string memory name) external view returns (bool); +} +``` + +### Optional Extension: Enumerable + +```solidity +interface IDomainEnumerable is IDomain { + /// @notice Query all subdomains. Must revert if the number of domains is unknown or infinite. + /// @return The subdomain with the given index. + function subdomainByIndex(uint256 index) external view returns (string memory); + + /// @notice Get the total number of subdomains. Must revert if the number of domains is unknown or infinite. + /// @return The total number of subdomains. + function totalSubdomains() external view returns (uint256); +} +``` + +### Optional Extension: Access Control + +```solidity +interface IDomainAccessControl is IDomain { + /// @notice Get if an account can move the subdomain away from the current domain + /// @dev May be called by `canSetDomain` of the parent domain - implement access control here!!! + /// @param updater The account that may be moving the subdomain + /// @param name The subdomain name + /// @param parent The parent domain + /// @param newSubdomain The domain that will be set next + /// @return Whether an account can update the subdomain + function canMoveSubdomain(address updater, string memory name, IDomain parent, address newSubdomain) external view returns (bool); + + /// @notice Get if an account can unset this domain as a subdomain + /// @dev May be called by `canDeleteDomain` of the parent domain - implement access control here!!! + /// @param updater The account that may or may not be able to delete a subdomain + /// @param name The subdomain to delete + /// @param parent The parent domain + /// @return Whether an account can delete the subdomain + function canDeleteSubdomain(address updater, string memory name, IDomain parent) external view returns (bool); +} +``` + +## Rationale + +This EIP's goal, as mentioned in the abstract, is to have a simple interface for resolving names. Here are a few design decisions and why they were made: + +- Name resolution algorithm + - Unlike ENS's resolution algorithm, this EIP's name resolution is fully under the control of the contracts along the resolution path. + - This behavior is more intuitive to users. + - This behavior allows for greater flexibility - e.g. a contract that changes what it resolves to based on the time of day. +- Parent domain access control + - A simple "ownable" interface was not used because this specification was designed to be as generic as possible. If an ownable implementation is desired, it can be implemented. + - This also gives parent domains the ability to call subdomains' access control methods so that subdomains, too, can choose whatever access control mechanism they desire +- Subdomain access control + - These methods are included so that subdomains aren't always limited to their parent domain's access control + - The root domain can be controlled by a DAO with a non-transferable token with equal shares, a TLD can be controlled by a DAO with a token representing stake, a domain of that TLD can be controlled by a single owner, a subdomain of that domain can be controlled by a single owner linked to an NFT, and so on. + - Subdomain access control functions are suggestions: an ownable domain might implement an owner override, so that perhaps subdomains might be recovered if the keys are lost. + +## Backwards Compatibility + +This EIP is general enough to support ENS, but ENS is not general enough to support this EIP. + +## Security Considerations + +### Malicious canMoveSubdomain (Black Hole) + +#### Description: Malicious `canMoveSubdomain` + +Moving a subdomain using `setDomain` is a potentially dangerous operation. + +Depending on the parent domain's implementation, if a malicious new subdomain unexpectedly returns `false` on `canMoveSubdomain`, that subdomain can effectively lock the ownership of the domain. + +Alternatively, it might return `true` when it isn't expected (i.e. a backdoor), allowing the contract owner to take over the domain. + +#### Mitigation: Malicious `canMoveSubdomain` + +Clients should help by warning if `canMoveSubdomain` or `canDeleteSubdomain` for the new subdomain changes to `false`. It is important to note, however, that since these are functions, it is possible for the value to change depending on whether or not it has already been linked. It is also still possible for it to unexpectedly return true. It is therefore recommended to **always** audit the new subdomain's source code before calling `setDomain`. + +### Parent Domain Resolution + +#### Description: Parent Domain Resolution + +Parent domains have full control of name resolution for their subdomains. If a particular domain is linked to `a.b.c`, then `b.c` can, depending on its code, set `a.b.c` to any domain, and `c` can set `b.c` itself to any domain. + +#### Mitigation: Parent Domain Resolution + +Before acquiring a domain that has been pre-linked, it is recommended to always have the contract **and** all the parents up to the root audited. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4844.md b/EIPS/eip-4844.md new file mode 100644 index 0000000..34bf3f0 --- /dev/null +++ b/EIPS/eip-4844.md @@ -0,0 +1,489 @@ +--- +eip: 4844 +title: Shard Blob Transactions +description: Shard Blob Transactions scale data-availability of Ethereum in a simple, forwards-compatible manner. +author: Vitalik Buterin (@vbuterin), Dankrad Feist (@dankrad), Diederik Loerakker (@protolambda), George Kadianakis (@asn-d6), Matt Garnett (@lightclient), Mofi Taiwo (@Inphi), Ansgar Dietrichs (@adietrichs) +discussions-to: https://ethereum-magicians.org/t/eip-4844-shard-blob-transactions/8430 +status: Review +type: Standards Track +category: Core +created: 2022-02-25 +requires: 1559, 2718, 2930, 4895 +--- + +## Abstract + +Introduce a new transaction format for "blob-carrying transactions" which contain a large amount of data that cannot be +accessed by EVM execution, but whose commitment can be accessed. +The format is intended to be fully compatible with the format that will be used in full sharding. + +## Motivation + +Rollups are in the short and medium term, and possibly in the long term, the only trustless scaling solution for Ethereum. +Transaction fees on L1 have been very high for months and there is greater urgency in doing anything required to help facilitate an ecosystem-wide move to rollups. +Rollups are significantly reducing fees for many Ethereum users: Optimism and Arbitrum frequently provide fees that are ~3-8x lower than the Ethereum base layer itself, +and ZK rollups, which have better data compression and can avoid including signatures, have fees ~40-100x lower than the base layer. + +However, even these fees are too expensive for many users. The long-term solution to the long-term inadequacy of rollups +by themselves has always been data sharding, which would add ~16 MB per block of dedicated data space to the chain that rollups could use. +However, data sharding will still take a considerable amount of time to finish implementing and deploying. + +This EIP provides a stop-gap solution until that point by implementing the _transaction format_ that would be used in sharding, +but not actually sharding those transactions. Instead, the data from this transaction format is simply part of the beacon chain and is fully downloaded +by all consensus nodes (but can be deleted after only a relatively short delay). +Compared to full data sharding, this EIP has a reduced cap on the number of these transactions that can be included, corresponding to a target of ~0.25 MB per block and a limit of ~0.5 MB. + +## Specification + +### Parameters + +| Constant | Value | +| - | - | +| `BLOB_TX_TYPE` | `Bytes1(0x05)` | +| `FIELD_ELEMENTS_PER_BLOB` | `4096` | +| `BLS_MODULUS` | `52435875175126190479447740508185965837690552500527637822603658699938581184513` | +| `BLOB_COMMITMENT_VERSION_KZG` | `Bytes1(0x01)` | +| `POINT_EVALUATION_PRECOMPILE_ADDRESS` | `Bytes20(0x14)` | +| `POINT_EVALUATION_PRECOMPILE_GAS` | `50000` | +| `MAX_DATA_GAS_PER_BLOCK` | `2**19` | +| `TARGET_DATA_GAS_PER_BLOCK` | `2**18` | +| `MIN_DATA_GASPRICE` | `1` | +| `DATA_GASPRICE_UPDATE_FRACTION` | `2225652` | +| `MAX_VERSIONED_HASHES_LIST_SIZE` | `2**24` | +| `MAX_CALLDATA_SIZE` | `2**24` | +| `MAX_ACCESS_LIST_SIZE` | `2**24` | +| `MAX_ACCESS_LIST_STORAGE_KEYS` | `2**24` | +| `MAX_TX_WRAP_KZG_COMMITMENTS` | `2**12` | +| `LIMIT_BLOBS_PER_TX` | `2**12` | +| `DATA_GAS_PER_BLOB` | `2**17` | +| `HASH_OPCODE_BYTE` | `Bytes1(0x49)` | +| `HASH_OPCODE_GAS` | `3` | + +### Type aliases + +| Type | Base type | Additional checks | +| - | - | - | +| `BLSFieldElement` | `uint256` | `x < BLS_MODULUS` | +| `Blob` | `Vector[BLSFieldElement, FIELD_ELEMENTS_PER_BLOB]` | | +| `VersionedHash` | `Bytes32` | | +| `KZGCommitment` | `Bytes48` | Same as BLS standard "is valid pubkey" check but also allows `0x00..00` for point-at-infinity | +| `KZGProof` | `Bytes48` | Same as for `KZGCommitment` | + +### Cryptographic Helpers + +Throughout this proposal we use cryptographic methods and classes defined in the corresponding [consensus 4844 specs](https://github.com/ethereum/consensus-specs/blob/23d3aeebba3b5da0df4bd25108461b442199f406/specs/eip4844). + +Specifically, we use the following methods from [`polynomial-commitments.md`](https://github.com/ethereum/consensus-specs/blob/23d3aeebba3b5da0df4bd25108461b442199f406/specs/eip4844/polynomial-commitments.md): + +- [`verify_kzg_proof()`](https://github.com/ethereum/consensus-specs/blob/23d3aeebba3b5da0df4bd25108461b442199f406/specs/eip4844/polynomial-commitments.md#verify_kzg_proof) +- [`verify_aggregate_kzg_proof()`](https://github.com/ethereum/consensus-specs/blob/23d3aeebba3b5da0df4bd25108461b442199f406/specs/eip4844/polynomial-commitments.md#verify_aggregate_kzg_proof) + +### Helpers + +```python +def kzg_to_versioned_hash(kzg: KZGCommitment) -> VersionedHash: + return BLOB_COMMITMENT_VERSION_KZG + sha256(kzg)[1:] +``` + +Approximates `factor * e ** (numerator / denominator)` using Taylor expansion: + +```python +def fake_exponential(factor: int, numerator: int, denominator: int) -> int: + i = 1 + output = 0 + numerator_accum = factor * denominator + while numerator_accum > 0: + output += numerator_accum + numerator_accum = (numerator_accum * numerator) // (denominator * i) + i += 1 + return output // denominator +``` + +### New transaction type + +We introduce a new [EIP-2718](./eip-2718.md) transaction type, +with the format being the single byte `BLOB_TX_TYPE` followed by an SSZ encoding of the +`SignedBlobTransaction` container comprising the transaction contents: + +```python +class SignedBlobTransaction(Container): + message: BlobTransaction + signature: ECDSASignature + +class BlobTransaction(Container): + chain_id: uint256 + nonce: uint64 + max_priority_fee_per_gas: uint256 + max_fee_per_gas: uint256 + gas: uint64 + to: Union[None, Address] # Address = Bytes20 + value: uint256 + data: ByteList[MAX_CALLDATA_SIZE] + access_list: List[AccessTuple, MAX_ACCESS_LIST_SIZE] + max_fee_per_data_gas: uint256 + blob_versioned_hashes: List[VersionedHash, MAX_VERSIONED_HASHES_LIST_SIZE] + +class AccessTuple(Container): + address: Address # Bytes20 + storage_keys: List[Hash, MAX_ACCESS_LIST_STORAGE_KEYS] + +class ECDSASignature(Container): + y_parity: boolean + r: uint256 + s: uint256 +``` + +The `max_priority_fee_per_gas` and `max_fee_per_gas` fields follow [EIP-1559](./eip-1559.md) semantics, +and `access_list` as in [`EIP-2930`](./eip-2930.md). + +[`EIP-2718`](./eip-2718.md) is extended with a "wrapper data", the typed transaction can be encoded in two forms, dependent on the context: + +- Network (default): `TransactionType || TransactionNetworkPayload`, or `LegacyTransaction` +- Minimal (as in execution payload): `TransactionType || TransactionPayload`, or `LegacyTransaction` + +Execution-payloads / blocks use the minimal encoding of transactions. +In the transaction-pool and local transaction-journal the network encoding is used. + +For previous types of transactions the network encoding is no different, i.e. `TransactionNetworkPayload == TransactionPayload`. + +The `TransactionNetworkPayload` wraps a `TransactionPayload` with additional data: +this wrapping data SHOULD be verified directly before or after signature verification. + +When a blob transaction is passed through the network (see the [Networking](#networking) section below), +the `TransactionNetworkPayload` version of the transaction also includes `blobs` and `kzgs` (commitments list). +The execution layer verifies the wrapper validity against the inner `TransactionPayload` after signature verification as: + +- All hashes in `blob_versioned_hashes` must start with the byte `BLOB_COMMITMENT_VERSION_KZG` +- There may be at most `MAX_DATA_GAS_PER_BLOCK // DATA_GAS_PER_BLOB` total blob commitments in a valid block. +- There is an equal amount of versioned hashes, kzg commitments and blobs. +- The KZG commitments hash to the versioned hashes, i.e. `kzg_to_versioned_hash(kzg[i]) == versioned_hash[i]` +- The KZG commitments match the blob contents. (Note: this can be optimized with additional data, using a proof for a + random evaluation at two points derived from the commitment and blob data) + + +The signature is verified and `tx.origin` is calculated as follows: + +```python +def unsigned_tx_hash(tx: SignedBlobTransaction) -> Bytes32: + # The pre-image is prefixed with the transaction-type to avoid hash collisions with other tx hashers and types + return keccak256(BLOB_TX_TYPE + ssz.serialize(tx.message)) + +def get_origin(tx: SignedBlobTransaction) -> Address: + sig = tx.signature + # v = int(y_parity) + 27, same as EIP-1559 + return ecrecover(unsigned_tx_hash(tx), int(sig.y_parity)+27, sig.r, sig.s) +``` + +The hash of a signed blob transaction should be computed as: + +```python +def signed_tx_hash(tx: SignedBlobTransaction) -> Bytes32: + return keccak256(BLOB_TX_TYPE + ssz.serialize(tx)) +``` + +### Header extension + +The current header encoding is extended with a new 256-bit unsigned integer field `excess_data_gas`. This is the running +total of excess data gas consumed on chain since this EIP was activated. If the total amount of data gas is below the +target, `excess_data_gas` is capped at zero. + +The resulting RLP encoding of the header is therefore: + +``` +rlp([ + parent_hash, + 0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347, # ommers hash + coinbase, + state_root, + txs_root, + receipts_root, + logs_bloom, + 0, # difficulty + number, + gas_limit, + gas_used, + timestamp, + extradata, + prev_randao, + 0x0000000000000000, # nonce + base_fee_per_gas, + withdrawals_root, + excess_data_gas +]) +``` + +The value of `excess_data_gas` can be calculated using the parent header and number of blobs in the block. + +```python +def calc_excess_data_gas(parent: Header, new_blobs: int) -> int: + consumed_data_gas = new_blobs * DATA_GAS_PER_BLOB + if parent.excess_data_gas + consumed_data_gas < TARGET_DATA_GAS_PER_BLOCK: + return 0 + else: + return parent.excess_data_gas + consumed_data_gas - TARGET_DATA_GAS_PER_BLOCK +``` + +For the first post-fork block, `parent.excess_data_gas` is evaluated as `0`. + +### Beacon chain validation + +On the consensus-layer the blobs are now referenced, but not fully encoded, in the beacon block body. +Instead of embedding the full contents in the body, the contents of the blobs are propagated separately, as a "sidecar". + +This "sidecar" design provides forward compatibility for further data increases by black-boxing `is_data_available()`: +with full sharding `is_data_available()` can be replaced by data-availability-sampling (DAS) thus avoiding all blobs being downloaded by all beacon nodes on the network. + +Note that the consensus-layer is tasked with persisting the blobs for data availability, the execution-layer is not. + +The `ethereum/consensus-specs` repository defines the following beacon-node changes involved in this EIP: + +- Beacon chain: process updated beacon blocks and ensure blobs are available. +- P2P network: gossip and sync updated beacon block types and new blobs sidecars. +- Honest validator: produce beacon blocks with blobs, publish the blobs sidecars. + +### Opcode to get versioned hashes + +We add an opcode `DATAHASH` (with byte value `HASH_OPCODE_BYTE`) which reads `index` from the top of the stack +as big-endian `uint256`, and replaces it on the stack with `tx.message.blob_versioned_hashes[index]` +if `index < len(tx.message.blob_versioned_hashes)`, and otherwise with a zeroed `bytes32` value. +The opcode has a gas cost of `HASH_OPCODE_GAS`. + +### Point evaluation precompile + +Add a precompile at `POINT_EVALUATION_PRECOMPILE_ADDRESS` that verifies a KZG proof which claims that a blob +(represented by a commitment) evaluates to a given value at a given point. + +The precompile costs `POINT_EVALUATION_PRECOMPILE_GAS` and executes the following logic: + +```python +def point_evaluation_precompile(input: Bytes) -> Bytes: + """ + Verify p(z) = y given commitment that corresponds to the polynomial p(x) and a KZG proof. + Also verify that the provided commitment matches the provided versioned_hash. + """ + # The data is encoded as follows: versioned_hash | z | y | commitment | proof | + versioned_hash = input[:32] + z = input[32:64] + y = input[64:96] + commitment = input[96:144] + kzg_proof = input[144:192] + + # Verify commitment matches versioned_hash + assert kzg_to_versioned_hash(commitment) == versioned_hash + + # Verify KZG proof + assert verify_kzg_proof(commitment, z, y, kzg_proof) + + # Return FIELD_ELEMENTS_PER_BLOB and BLS_MODULUS as padded 32 byte big endian values + return Bytes(U256(FIELD_ELEMENTS_PER_BLOB).to_be_bytes32() + U256(BLS_MODULUS).to_be_bytes32()) +``` + +The precompile MUST reject non-canonical field elements (i.e. provided field elements MUST be strictly less than `BLS_MODULUS`). + +### Gas accounting + +We introduce data gas as a new type of gas. It is independent of normal gas and follows its own targeting rule, similar to EIP-1559. +We use the `excess_data_gas` header field to store persistent data needed to compute the data gas price. For now, only blobs are priced in data gas. + +```python +def calc_data_fee(tx: SignedBlobTransaction, parent: Header) -> int: + return get_total_data_gas(tx) * get_data_gasprice(header) + +def get_total_data_gas(tx: SignedBlobTransaction) -> int: + return DATA_GAS_PER_BLOB * len(tx.message.blob_versioned_hashes) + +def get_data_gasprice(header: Header) -> int: + return fake_exponential( + MIN_DATA_GASPRICE, + header.excess_data_gas, + DATA_GASPRICE_UPDATE_FRACTION + ) +``` + +The block validity conditions are modified to include data gas checks: + +```python +def validate_block(block: Block) -> None: + ... + + num_blobs = 0 + for tx in block.transactions: + ... + + # the signer must be able to afford the transaction + assert signer(tx).balance >= tx.message.gas * tx.message.max_fee_per_gas + get_total_data_gas(tx) * tx.message.max_fee_per_data_gas + + # ensure that the user was willing to at least pay the current data gasprice + assert tx.message.max_fee_per_data_gas >= get_data_gasprice(parent(block).header) + + num_blobs += len(tx.message.blob_versioned_hashes) + + # check that the excess data gas is correct + expected_edg = calc_excess_data_gas(parent(block).header, num_blobs) + assert expected_edg == block.excess_data_gas +``` + +The actual `data_fee` as calculated via `calc_data_fee` is deducted from the sender balance before transaction execution and burned, and is not refunded in case of transaction failure. + +### Networking + +Nodes must not automatically broadcast blob transactions to their peers. +Instead, those transactions are only announced using `NewPooledTransactionHashes` messages, and can then be manually requested via `GetPooledTransactions`. + +Transactions are presented as `TransactionType || TransactionNetworkPayload` on the execution layer network, +the payload is a SSZ encoded container: + +```python +class BlobTransactionNetworkWrapper(Container): + tx: SignedBlobTransaction + # KZGCommitment = Bytes48 + blob_kzgs: List[KZGCommitment, MAX_TX_WRAP_KZG_COMMITMENTS] + # BLSFieldElement = uint256 + blobs: List[Vector[BLSFieldElement, FIELD_ELEMENTS_PER_BLOB], LIMIT_BLOBS_PER_TX] + # KZGProof = Bytes48 + kzg_aggregated_proof: KZGProof +``` + +We do network-level validation of `BlobTransactionNetworkWrapper` objects as follows: + +```python +def validate_blob_transaction_wrapper(wrapper: BlobTransactionNetworkWrapper): + versioned_hashes = wrapper.tx.message.blob_versioned_hashes + commitments = wrapper.blob_kzgs + blobs = wrapper.blobs + # note: assert blobs are not malformatted + assert len(versioned_hashes) == len(commitments) == len(blobs) + + # Verify that commitments match the blobs by checking the KZG proof + assert verify_aggregate_kzg_proof(blobs, commitments, wrapper.kzg_aggregated_proof) + + # Now that all commitments have been verified, check that versioned_hashes matches the commitments + for versioned_hash, commitment in zip(versioned_hashes, commitments): + assert versioned_hash == kzg_to_versioned_hash(commitment) +``` + +## Rationale + +### On the path to sharding + +This EIP introduces blob transactions in the same format in which they are expected to exist in the final sharding specification. +This provides a temporary but significant scaling relief for rollups by allowing them to initially scale to 0.25 MB per slot, +with a separate fee market allowing fees to be very low while usage of this system is limited. + +The core goal of rollup scaling stopgaps is to provide temporary scaling relief, +without imposing extra development burdens on rollups to take advantage of this relief. +Today, rollups use calldata. In the future, rollups will have no choice but to use sharded data (also called "blobs") +because sharded data will be much cheaper. +Hence, rollups cannot avoid making a large upgrade to how they process data at least once along the way. +But what we _can_ do is ensure that rollups need to _only_ upgrade once. +This immediately implies that there are exactly two possibilities for a stopgap: (i) reducing the gas costs of existing calldata, +and (ii) bringing forward the format that will be used for sharded data, but not yet actually sharding it. +Previous EIPs were all a solution of category (i); this EIP is a solution of category (ii). + +The main tradeoff in designing this EIP is that of implementing more now versus having to implement more later: +do we implement 25% of the work on the way to full sharding, or 50%, or 75%? + +The work that is already done in this EIP includes: + +- A new transaction type, of the exact same format that will need to exist in "full sharding" +- _All_ of the execution-layer logic required for full sharding +- _All_ of the execution / consensus cross-verification logic required for full sharding +- Layer separation between `BeaconBlock` verification and data availability sampling blobs +- Most of the `BeaconBlock` logic required for full sharding +- A self-adjusting independent gasprice for blobs + +The work that remains to be done to get to full sharding includes: + +- A low-degree extension of the `blob_kzgs` in the consensus layer to allow 2D sampling +- An actual implementation of data availability sampling +- PBS (proposer/builder separation), to avoid requiring individual validators to process 32 MB of data in one slot +- Proof of custody or similar in-protocol requirement for each validator to verify a particular part of the sharded data in each block + +This EIP also sets the stage for longer-term protocol cleanups: + +- It adds an SSZ transaction type, and paves the precedent that all new transaction types should be SSZ +- It defines `TransactionNetworkPayload` to separate network and block encodings of a transaction type +- Its (cleaner) gas price update rule could be applied to the primary basefee + +### How rollups would function + +Instead of putting rollup block data in transaction calldata, rollups would expect rollup block submitters +to put the data into blobs. This guarantees availability (which is what rollups need) but would be much cheaper than calldata. +Rollups need data to be available once, long enough to ensure honest actors can construct the rollup state, but not forever. + +Optimistic rollups only need to actually provide the underlying data when fraud proofs are being submitted. +The fraud proof can verify the transition in smaller steps, loading at most a few values of the blob at a time through calldata. +For each value it would provide a KZG proof and use the point evaluation precompile to verify the value against the versioned hash that was submitted before, +and then perform the fraud proof verification on that data as is done today. + +ZK rollups would provide two commitments to their transaction or state delta data: +the kzg in the blob and some commitment using whatever proof system the ZK rollup uses internally. +They would use a commitment proof of equivalence protocol, using the point evaluation precompile, +to prove that the kzg (which the protocol ensures points to available data) and the ZK rollup's own commitment refer to the same data. + +### Versioned hashes & precompile return data + +We use versioned hashes (rather than kzgs) as references to blobs in the execution layer to ensure forward compatibility with future changes. +For example, if we need to switch to Merkle trees + STARKs for quantum-safety reasons, then we would add a new version, +allowing the point evaluation precompile to work with the new format. +Rollups would not have to make any EVM-level changes to how they work; +sequencers would simply have to switch over to using a new transaction type at the appropriate time. + +However, the point evaluation happens inside a finite field, and it is only well defined if the field modulus is known. Smart contracts could contain a table mapping the commitment version to a modulus, but this would not allow smart contract to take into account future upgrades to a modulus that is not known yet. By allowing access to the modulus inside the EVM, the smart contract can be built so that it can use future commitments and proofs, without ever needing an upgrade. + +In the interest of not adding another precompile, we return the modulus and the polynomial degree directly from the point evaluation precompile. It can then be used by the caller. It is also "free" in that the caller can just ignore this part of the return value without incurring an extra cost -- systems that remain upgradable for the foreseeable future will likely use this route for now. + +### Data gasprice update rule + +The data gasprice update rule is intended to approximate the formula `data_gasprice = MIN_DATA_GASPRICE * e**(excess_data_gas / DATA_GASPRICE_UPDATE_FRACTION)`, +where `excess_data_gas` is the total "extra" amount of data gas that the chain has consumed relative to the "targeted" number (`TARGET_DATA_GAS_PER_BLOCK` per block). +Like EIP-1559, it's a self-correcting formula: as the excess goes higher, the `data_gasprice` increases exponentially, reducing usage and eventually forcing the excess back down. + +The block-by-block behavior is roughly as follows. +If block `N` consumes `X` data gas, then in block `N+1` `excess_data_gas` increases by `X - TARGET_DATA_GAS_PER_BLOCK`, +and so the `data_gasprice` of block `N+1` increases by a factor of `e**((X - TARGET_DATA_GAS_PER_BLOCK) / DATA_GASPRICE_UPDATE_FRACTION)`. +Hence, it has a similar effect to the existing EIP-1559, but is more "stable" in the sense that it responds in the same way to the same total usage regardless of how it's distributed. + +The parameter `DATA_GASPRICE_UPDATE_FRACTION` controls the maximum rate of change of the blob gas price. It is chosen to target a maximum change rate of `e(TARGET_DATA_GAS_PER_BLOCK / DATA_GASPRICE_UPDATE_FRACTION) ≈ 1.125` per block. + +### Throughput + +The values for `TARGET_DATA_GAS_PER_BLOCK` and `MAX_DATA_GAS_PER_BLOCK` are chosen to correspond to a target of 2 blobs (0.25 MB) and maximum of 4 blobs (0.5 MB) per block. These small initial limits are intended to minimize the strain on the network created by this EIP and are expected to be increased in future upgrades as the network demonstrates reliability under larger blocks. + +## Backwards Compatibility + +### Blob non-accessibility + +This EIP introduces a transaction type that has a distinct mempool version (`BlobTransactionNetworkWrapper`) and execution-payload version (`SignedBlobTransaction`), +with only one-way convertibility between the two. The blobs are in the `BlobTransactionNetworkWrapper` and not in the `SignedBlobTransaction`; +instead, they go into the `BeaconBlockBody`. This means that there is now a part of a transaction that will not be accessible from the web3 API. + +### Mempool issues + +Blob transactions have a large data size at the mempool layer, which poses a mempool DoS risk, +though not an unprecedented one as this also applies to transactions with large amounts of calldata. + +By only broadcasting announcements for blob transactions, receiving nodes will have control over which and how many transactions to receive, +allowing them to throttle throughput to an acceptable level. +[EIP-5793](./eip-5793.md) will give further fine-grained control to nodes by extending the `NewPooledTransactionHashes` announcement messages to include the transaction type and size. + +In addition, we recommend including a 1.1x data gasprice bump requirement to the mempool transaction replacement rules. + +## Test Cases + +TBD + +## Security Considerations + +This EIP increases the storage requirements per Beacon block by a maximum of ~0.5 MB. +This is 4x larger than the theoretical maximum size of a block today (30M gas / 16 gas per calldata byte = 1.875M bytes), and so it will not greatly increase worst-case bandwidth. +Post-merge, block times are expected to be static rather than an unpredictable Poisson distribution, giving a guaranteed period of time for large blocks to propagate. + +The _sustained_ load of this EIP is much lower than alternatives that reduce calldata costs, even if the calldata is limited, +because there is no existing software that stores the blobs indefinitely and there is no expectation that they need to be stored for as long as an execution payload. +This makes it easier to implement a policy that these blobs should be deleted after e.g. 30-60 days, +a much shorter delay compared to proposed (but yet to be implemented) one-year rotation times for execution payload history. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4863.md b/EIPS/eip-4863.md new file mode 100644 index 0000000..869b9a9 --- /dev/null +++ b/EIPS/eip-4863.md @@ -0,0 +1,107 @@ +--- +eip: 4863 +title: Beacon chain push withdrawals +description: Support validator withdrawals from the beacon chain to the EVM via a new "push-style" transaction type. +author: Alex Stokes (@ralexstokes), Danny Ryan (@djrtwo) +discussions-to: https://ethereum-magicians.org/t/eip-4863-beacon-chain-push-withdrawals/8465 +status: Stagnant +type: Standards Track +category: Core +created: 2022-02-28 +--- + +## Abstract + +Introduce a new [EIP-2718 transaction type](./eip-2718.md) to support validator withdrawals that are "pushed" from the beacon chain to the EVM. + +Add block validations to ensure the withdrawal transactions are sound with respect to withdrawal processing on the beacon chain. + +## Motivation + +This EIP provides a way for validator withdrawals made on the beacon chain to enter into the EVM. +The architecture is "push"-based, rather than "pull"-based, where withdrawals are required to be processed in the execution block as soon as they are dequeued from the beacon chain. + +This approach is more involved than "pull"-based alternatives (e.g. [EIP-4788](./eip-4788.md) + user-space withdrawal contract) with respect to the core protocol (by providing a new transaction type with special semantics) but does provide tighter integration of a critical feature into the protocol itself. + +## Specification + +| constants | value | units +|--- |--- |--- +| `FORK_TIMESTAMP` | TBD | +| `WITHDRAWAL_TX_TYPE` | `0x3` | byte + +Beginning with the execution timestamp `FORK_TIMESTAMP`, execution clients **MUST** introduce the following extensions to transaction processing and block validation: + +### New transaction type + +Define a new [EIP-2718](./eip-2718.md) transaction type with `TransactionType` `WITHDRAWAL_TX_TYPE`. + +The `TransactionPayload` is an RLP-encoded list `RLP([index, address, amount])` where the `index` is a 64-bit value uniquely labeling a specific withdrawal, the `address` refers to an execution layer account and the `amount` refers to an ether value given in units of wei. + +These values are provided by the consensus layer. + +### Block validity + +If a block contains *any* transactions with `WITHDRAWAL_TX_TYPE` type, they **MUST** come after **ALL** other transactions in the block. + +If the execution client receives a block where this is not the case, it **MUST** consider the block invalid. + +### Transaction processing + +When processing a transaction with `WITHDRAWAL_TX_TYPE` type, the implementation should increase the balance of the `address` specified by +the `WithdrawalTransaction` by the `amount` of wei specified. + +This balance change is unconditional and **MUST** not fail. + +This transaction type has no associated gas costs. + +TODO: add logs? + +## Rationale + +### Push vs pull approach + +This push approach gives validators a small subsidy with respect to processing, in lieu of needing to buy gas via normal EVM processing that would be required for a pull-based approach. + +This style also happens automatically when the requisite conditions are met on the beacon chain which is nicer UX for validators. + +### Why a new transaction type? + +This EIP suggests a new transaction type as it has special semantics different from other existing types of EVM transactions. + +An entirely new transaction type firewalls off generic EVM execution from this type of processing to simplify testing and security review of withdrawals. + +### Why no (gas) costs for new transaction type? + +The maximum number of this transaction type that can reach the execution layer at a given time is bounded (enforced by the consensus layer) and this limit is kept small so that +any execution layer operational costs are negligible in the context of the broader block execution. + +### Why only balance updates? No general EVM execution? + +More general processing introduces the risk of failures, which complicates accounting on the beacon chain. + +This EIP suggests a route for withdrawals that provides most of the benefits for a minimum of the (complexity) cost. + +### Why new block validations? + +The beacon chain must be able to efficiently validate that the withdrawal transactions in a given execution block are +the ones expected based on its own internal scheduling logic to maintain the soundness of the withdrawal mechanism. + +By requiring all withdrawal transactions to be at the back of every block where they are applicable, the algorithm to +check consistency becomes a straightforward linear walk from the start of the set until a known, bounded (small) number. + +Having a simple ordering scheme like this facilitates optimizations clients may do with respect to withdrawal processing, which +would be hampered if withdrawal transactions could be placed in the block freely. + +## Backwards Compatibility + +No issues. + +## Security Considerations + +Consensus-layer validation of withdrawal transactions is critical to ensure that the proper amount of ETH is withdrawn back into the execution layer. +This consensus-layer to execution-layer ETH transfer does not have a current analog in the EVM and thus deserves very high security scrutiny. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4881.md b/EIPS/eip-4881.md new file mode 100644 index 0000000..1535dac --- /dev/null +++ b/EIPS/eip-4881.md @@ -0,0 +1,322 @@ +--- +eip: 4881 +title: Deposit Contract Snapshot Interface +description: Establishing the format and endpoint for transmitting a snapshot of the deposit Merkle tree +author: Mark Mackey (@ethDreamer) +discussions-to: https://ethereum-magicians.org/t/eip-4881-deposit-contract-snapshot-interface/ +status: Draft +type: Standards Track +category: Interface +created: 2021-01-29 +--- + +## Abstract + +This EIP defines a standard format for transmitting the deposit contract Merkle tree in a compressed form during weak subjectivity sync. This allows newly syncing consensus clients to reconstruct the deposit tree much faster than downloading all historical deposits. The format proposed also allows clients to prune deposits that are no longer needed to participate fully in consensus (see [Deposit Finalization Flow](#deposit-finalization-flow)). + +## Motivation + +Most client implementations require beacon nodes to download and store every deposit log since the launch of the deposit contract in order to reconstruct the deposit Merkle tree. This approach requires nodes to store far more deposits than necessary to fully participate in consensus. It also needlessly increases the time it takes for new nodes to fully sync, which is especially noticeable during weak subjectivity sync. Furthermore, if [EIP-4444](./eip-4444.md) is adopted, it will not always be possible to download all historical deposit logs from full nodes. + +## Specification + +Consensus clients MAY continue to implement the deposit Merkle tree however they choose. However, when transmitting the tree to newly syncing nodes, clients MUST use the following format: + +```python +class DepositTreeSnapshot: + finalized: List[Hash32, DEPOSIT_CONTRACT_DEPTH] + deposit_root: Hash32 + deposit_count: uint64 + execution_block_hash: Hash32 + execution_block_height: uint64 +``` + +Where `finalized` is a variable-length list (of maximum size `DEPOSIT_CONTRACT_DEPTH`) containing the hashes defined in the [Deposit Finalization Flow](#deposit-finalization-flow) section below. The fields `deposit_root`, `deposit_count`, and `execution_block_hash` store the same information as the [`Eth1Data`](https://github.com/ethereum/consensus-specs/blob/2b45496fe48fa75450ad29a05bdd48866f86528a/specs/phase0/beacon-chain.md#eth1data) object that corresponds to the snapshot, and `execution_block_height` is the height of the execution block with hash `execution_block_hash`. Consensus clients MUST make this structure available via the Beacon Node API endpoint: + +``` +/eth/v1/beacon/deposit_snapshot +``` + +### Deposit Finalization Flow + +During deposit processing, the beacon chain requires deposits to be submitted along with a Merkle path to the deposit root. This is required exactly once for each deposit. When a deposit has been processed by the beacon chain and the [deposit finalization conditions](#deposit-finalization-conditions) have been met, many of the hashes along the path to the deposit root will never be required again to construct Merkle proofs on chain. These unnecessary hashes MAY be pruned to save space. The image below illustrates the evolution of the deposit Merkle tree under this process alongside the corresponding `DepositTreeSnapshot` as new deposits are added and older deposits become finalized: + +![deposit tree evolution](../assets/eip-4881/deposit_tree_evolution.svg) + +## Rationale + +The format in this specification was chosen to achieve several goals simultaneously: + +1. Enable reconstruction of the deposit contract Merkle tree under the adoption of [EIP-4444](./eip-4444.md) +2. Avoid requiring consensus nodes to retain more deposits than necessary to fully participate in consensus +3. Simplicity of implementation (see [Reference Implementation](#reference-implementation) section) +4. Increase speed of weak subjectivity sync +5. Compatibility with existing implementations of this mechanism (see discussion) + +The proposed `DepositTreeSnapshot` structure includes both `execution_block_hash` and `execution_block_height` for convenience to consensus node implementors. While only one of these fields is strictly necessary, different clients may have already designed their block cache logic around one or the other. Sending only one of these would force some consensus clients to query the execution engine for the other information, but as this is happening in the context of a newly syncing consensus node, it is very likely that the execution engine will not be synced, especially post-merge. The `deposit_root` field is also not strictly necessary, but by including it, newly syncing consensus nodes can cheaply validate any received snapshot against itself (see the `calculate_root()` method in the [Reference Implementation](#reference-implementation)). + +### Why not Reconstruct the Tree Directly from the Deposit Contract? + +The deposit contract can only provide the tree at the head of the chain. Because the beacon chain's view of the deposit contract lags behind the execution chain by `ETH1_FOLLOW_DISTANCE`, there are almost always deposits which haven't yet been included in the chain that need proofs constructed from an earlier version of the tree than exists at the head. + +### Why not Reconstruct the Tree from a Deposit in the Beacon Chain? + +In principle, a node could scan backwards through the chain starting from the weak subjectivity checkpoint to locate a suitable [`Deposit`](https://github.com/ethereum/consensus-specs/blob/2b45496fe48fa75450ad29a05bdd48866f86528a/specs/phase0/beacon-chain.md#deposit), and then extract the rightmost branch of the tree from that. The node would also need to extract the `execution_block_hash` from which to start syncing new deposits from the `Eth1Data` in the corresponding `BeaconState`. This approach is less desirable for a few reasons: + +* More difficult to implement due to the edge cases involved in finding a suitable deposit to anchor to (the rightmost branch of the latest not-yet-included deposit is required) +* This would make backfilling beacon blocks a requirement for reconstructing the deposit tree and therefore a requirement for block production +* This is inherently slower than getting this information from the weak subjectivity checkpoint + +## Backwards Compatibility + +This proposal is fully backwards compatible. + +## Test Cases + +Test cases are included in [test_cases.yaml](../assets/eip-4881/test_cases.yaml). Each case is structured as follows: + +```python +class DepositTestCase: + deposit_data: DepositData # These are all the inputs to the deposit contract's deposit() function + deposit_data_root: Hash32 # The tree hash root of this deposit (calculated for convenience) + eth1_data: Eth1Data # An Eth1Data object that can be used to finalize the tree after pushing this deposit + block_height: uint64 # The height of the execution block with this Eth1Data + snapshot: DepositTreeSnapshot # The resulting DepositTreeSnapshot object if the tree were finalized after this deposit +``` + +This EIP also includes other files for testing: + +* [deposit_snapshot.py](../assets/eip-4881/deposit_snapshot.py) contains the same code as the [Reference Implementation](#reference-implementation) +* [eip_4881.py](../assets/eip-4881/eip_4881.py) contains boilerplate declarations +* [test_deposit_snapshot.py](../assets/eip-4881/test_deposit_snapshot.py) includes code for running test cases against the reference implementation + +If these files are downloaded to the same directory, the test cases can be run by executing `pytest` in that directory. + +## Reference Implementation + +This implementation lacks full error checking and is optimized for readability over efficiency. If `tree` is a `DepositTree`, then the `DepositTreeSnapshot` can be obtained by calling `tree.get_snapshot()` and a new instance of the tree can be recovered from the snapshot by calling `DepositTree.from_snapshot()`. See the [Deposit Finalization Conditions](#deposit-finalization-conditions) section for discussion on when the tree can be pruned by calling `tree.finalize()`. + +Generating proofs for deposits against an earlier version of the tree is relatively fast in this implementation; just create a copy of the finalized tree with `copy = DepositTree.from_snapshot(tree.get_snapshot())` and then append the remaining deposits to the desired count with `copy.push_leaf(deposit)`. Proofs can then be obtained with `copy.get_proof(index)`. + +```python +from __future__ import annotations +from typing import List, Optional, Tuple +from dataclasses import dataclass +from abc import ABC,abstractmethod +from eip_4881 import DEPOSIT_CONTRACT_DEPTH,Hash32,sha256,to_le_bytes,zerohashes + +@dataclass +class DepositTreeSnapshot: + finalized: List[Hash32, DEPOSIT_CONTRACT_DEPTH] + deposit_root: Hash32 + deposit_count: uint64 + execution_block_hash: Hash32 + execution_block_height: uint64 + + def calculate_root(self) -> Hash32: + size = self.deposit_count + index = len(self.finalized) + root = zerohashes[0] + for level in range(0, DEPOSIT_CONTRACT_DEPTH): + if (size & 1) == 1: + index -= 1 + root = sha256(self.finalized[index] + root) + else: + root = sha256(root + zerohashes[level]) + size >>= 1 + return sha256(root + to_le_bytes(self.deposit_count)) + def from_tree_parts(finalized: List[Hash32], + deposit_count: uint64, + execution_block: Tuple[Hash32, uint64]) -> DepositTreeSnapshot: + snapshot = DepositTreeSnapshot( + finalized, zerohashes[0], deposit_count, execution_block[0], execution_block[1]) + snapshot.deposit_root = snapshot.calculate_root() + return snapshot + +@dataclass +class DepositTree: + tree: MerkleTree + mix_in_length: uint + finalized_execution_block: Optional[Tuple[Hash32, uint64]] + def new() -> DepositTree: + merkle = MerkleTree.create([], DEPOSIT_CONTRACT_DEPTH) + return DepositTree(merkle, 0, None) + def get_snapshot(self) -> DepositTreeSnapshot: + assert(self.finalized_execution_block is not None) + finalized = [] + deposit_count = self.tree.get_finalized(finalized) + return DepositTreeSnapshot.from_tree_parts( + finalized, deposit_count, self.finalized_execution_block) + def from_snapshot(snapshot: DepositTreeSnapshot) -> DepositTree: + # decent validation check on the snapshot + assert(snapshot.deposit_root == snapshot.calculate_root()) + finalized_execution_block = (snapshot.execution_block_hash, snapshot.execution_block_height) + tree = MerkleTree.from_snapshot_parts( + snapshot.finalized, snapshot.deposit_count, DEPOSIT_CONTRACT_DEPTH) + return DepositTree(tree, snapshot.deposit_count, finalized_execution_block) + def finalize(self, eth1_data: Eth1Data, execution_block_height: uint64): + self.finalized_execution_block = (eth1_data.block_hash, execution_block_height) + self.tree.finalize(eth1_data.deposit_count, DEPOSIT_CONTRACT_DEPTH) + def get_proof(self, index: uint) -> Tuple[Hash32, List[Hash32]]: + assert(self.mix_in_length > 0) + # ensure index > finalized deposit index + assert(index > self.tree.get_finalized([]) - 1) + leaf, proof = self.tree.generate_proof(index, DEPOSIT_CONTRACT_DEPTH) + proof.append(to_le_bytes(self.mix_in_length)) + return leaf, proof + def get_root(self) -> Hash32: + return sha256(self.tree.get_root() + to_le_bytes(self.mix_in_length)) + def push_leaf(self, leaf: Hash32): + self.mix_in_length += 1 + self.tree = self.tree.push_leaf(leaf, DEPOSIT_CONTRACT_DEPTH) + +class MerkleTree(): + @abstractmethod + def get_root(self) -> Hash32: + pass + @abstractmethod + def is_full(self) -> bool: + pass + @abstractmethod + def push_leaf(self, leaf: Hash32, level: uint) -> MerkleTree: + pass + @abstractmethod + def finalize(self, deposits_to_finalize: uint, level: uint) -> MerkleTree: + pass + @abstractmethod + def get_finalized(self, result: List[Hash32]) -> uint: + # returns the number of finalized deposits in the tree + # while populating result with the finalized hashes + pass + def create(leaves: List[Hash32], depth: uint) -> MerkleTree: + if not(leaves): + return Zero(depth) + if not(depth): + return Leaf(leaves[0]) + split = min(2**(depth - 1), len(leaves)) + left = MerkleTree.create(leaves[0:split], depth - 1) + right = MerkleTree.create(leaves[split:], depth - 1) + return Node(left, right) + def from_snapshot_parts(finalized: List[Hash32], deposits: uint, level: uint) -> MerkleTree: + if not(finalized) or not(deposits): + # empty tree + return Zero(level) + if deposits == 2**level: + return Finalized(deposits, finalized[0]) + left_subtree = 2**(level - 1) + if deposits <= left_subtree: + left = MerkleTree.from_snapshot_parts(finalized, deposits, level - 1) + right = Zero(level - 1) + return Node(left, right) + else: + left = Finalized(left_subtree, finalized[0]) + right = MerkleTree.from_snapshot_parts(finalized[1:], deposits - left_subtree, level - 1) + return Node(left, right) + def generate_proof(self, index: uint, depth: uint) -> Tuple[Hash32, List[Hash32]]: + proof = [] + node = self + while depth > 0: + ith_bit = (index >> (depth - 1)) & 0x1 + if ith_bit == 1: + proof.append(node.left.get_root()) + node = node.right + else: + proof.append(node.right.get_root()) + node = node.left + depth -= 1 + proof.reverse() + return node.get_root(), proof + +@dataclass +class Finalized(MerkleTree): + deposit_count: uint + hash: Hash32 + def get_root(self) -> Hash32: + return self.hash + def is_full(self) -> bool: + return True + def finalize(self, deposits_to_finalize: uint, level: uint) -> MerkleTree: + return self + def get_finalized(self, result: List[Hash32]) -> uint: + result.append(self.hash) + return self.deposit_count + +@dataclass +class Leaf(MerkleTree): + hash: Hash32 + def get_root(self) -> Hash32: + return self.hash + def is_full(self) -> bool: + return True + def finalize(self, deposits_to_finalize: uint, level: uint) -> MerkleTree: + return Finalized(1, self.hash) + def get_finalized(self, result: List[Hash32]) -> uint: + return 0 + +@dataclass +class Node(MerkleTree): + left: MerkleTree + right: MerkleTree + def get_root(self) -> Hash32: + return sha256(self.left.get_root() + self.right.get_root()) + def is_full(self) -> bool: + return self.right.is_full() + def push_leaf(self, leaf: Hash32, level: uint) -> MerkleTree: + if not(self.left.is_full()): + self.left = self.left.push_leaf(leaf, level - 1) + else: + self.right = self.right.push_leaf(leaf, level - 1) + return self + def finalize(self, deposits_to_finalize: uint, level: uint) -> MerkleTree: + deposits = 2**level + if deposits <= deposits_to_finalize: + return Finalized(deposits, self.get_root()) + self.left = self.left.finalize(deposits_to_finalize, level - 1) + if deposits_to_finalize > deposits / 2: + remaining = deposits_to_finalize - deposits / 2 + self.right = self.right.finalize(remaining, level - 1) + return self + def get_finalized(self, result: List[Hash32]) -> uint: + return self.left.get_finalized(result) + self.right.get_finalized(result) + +@dataclass +class Zero(MerkleTree): + n: uint64 + def get_root(self) -> Hash32: + if self.n == DEPOSIT_CONTRACT_DEPTH: + # Handle the entirely empty tree case. This is included for + # consistency/clarity as the zerohashes array is typically + # only defined from 0 to DEPOSIT_CONTRACT_DEPTH - 1. + return sha256(zerohashes[self.n - 1] + zerohashes[self.n - 1]) + return zerohashes[self.n] + def is_full(self) -> bool: + return False + def push_leaf(self, leaf: Hash32, level: uint) -> MerkleTree: + return MerkleTree.create([leaf], level) + def get_finalized(self, result: List[Hash32]) -> uint: + return 0 +``` + +## Security Considerations + +### Relying on Weak Subjectivity Sync + +The upcoming switch to PoS will require newly synced nodes to rely on valid weak subjectivity checkpoints because of long-range attacks. This proposal relies on the weak subjectivity assumption that clients will not bootstrap with an invalid WS checkpoint. + +### Deposit Finalization Conditions + +Care must be taken not to send a snapshot which includes deposits that haven't been fully included in the finalized checkpoint. Let `state` be the [`BeaconState`](https://github.com/ethereum/consensus-specs/blob/2b45496fe48fa75450ad29a05bdd48866f86528a/specs/phase0/beacon-chain.md#beaconstate) at a given block in the chain. Under normal operation, the [`Eth1Data`](https://github.com/ethereum/consensus-specs/blob/2b45496fe48fa75450ad29a05bdd48866f86528a/specs/phase0/beacon-chain.md#eth1data) stored in `state.eth1_data` is replaced every `EPOCHS_PER_ETH1_VOTING_PERIOD` epochs. Thus, finalization of the deposit tree proceeds with increments of `state.eth1_data`. Let `eth1data` be some `Eth1Data`. Both of the following conditions MUST be met to consider `eth1data` finalized: + +1. A finalized checkpoint exists where the corresponding `state` has `state.eth1_data == eth1data` +2. A finalized checkpoint exists where the corresponding `state` has `state.eth1_deposit_index >= eth1data.deposit_count` + +When these conditions are met, the tree can be pruned in the [reference implementation](#reference-implementation) by calling `tree.finalize(eth1data, execution_block_height)` + +### Deposit Queue Exceeds EIP-4444 Pruning Period + +The proposed design could fail if the deposit queue becomes so large that deposits cannot be processed within the [EIP-4444 Pruning Period](./eip-4444.md) (currently set to 1 year). The beacon chain can process `MAX_DEPOSITS/SECONDS_PER_SLOT` deposits/second without skipped slots. Even under extreme conditions where 25% of slots are skipped, the deposit queue would need to be >31.5 million to hit this limit. This is more than 8x the total supply of ether assuming each deposit is a full validator. The minimum deposit is 1 ETH so an attacker would need to burn >30 Million ETH to create these conditions. + + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4883.md b/EIPS/eip-4883.md new file mode 100644 index 0000000..22c8c6d --- /dev/null +++ b/EIPS/eip-4883.md @@ -0,0 +1,71 @@ +--- +eip: 4883 +title: Composable SVG NFT +description: Compose an SVG NFT by concatenating the SVG with the rendered SVG of another NFT. +author: Andrew B Coathup (@abcoathup), Alex (@AlexPartyPanda), Damian Martinelli (@damianmarti), blockdev (@0xbok), Austin Griffith (@austintgriffith) +discussions-to: https://ethereum-magicians.org/t/eip-4883-composable-svg-nft/8765 +status: Draft +type: Standards Track +category: ERC +created: 2022-03-08 +requires: 165, 721 +--- + +## Abstract + +Compose an SVG (Scalable Vector Graphics) NFT by concatenating the SVG with the SVG of another NFT rendered as a string for a specific token ID. + +## Motivation + +On-chain SVG NFTs allow for NFTs to be entirely on-chain by returning artwork as SVG in a data URI of the `tokenUri` function. Composability allows on-chain SVG NFTs to be crafted. e.g. adding glasses & hat NFTs to a profile pic NFT or a fish NFT to a fish tank NFT. + +## Specification +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. + +```solidity +/// @title EIP-4883 Non-Fungible Token Standard +interface IERC4883 is IERC165 { + function renderTokenById(uint256 id) external view returns (string memory); +} +``` + +`renderTokenById` must return the SVG body for the specified token `id` and must either be an empty string or valid SVG element(s). + +## Rationale + +SVG elements can be string concatenated to compose an SVG. + +### Ordering of concatenation + +SVG uses a "painters model" of rendering. + +**Scalable Vector Graphics (SVG) 1.1 (Second Edition)**, section: **3.3 Rendering Order** +>Elements in an SVG document fragment have an implicit drawing order, with the first elements in the SVG document fragment getting "painted" first. Subsequent elements are painted on top of previously painted elements. + +The ordering of the SVG concatenation determines the drawing order rather than any concept of a z-index. + +This EIP only specifies the rendering of the rendered SVG NFT and does not require any specific ordering when composing. This allows the SVG NFT to use a rendered SVG NFT as a foreground or a background as required. + +### Alternatives to concatenation + +SVG specifies a `link` tag. Linking could allow for complex SVGs to be composed but would require creating a URI format and then getting ecosystem adoption. As string concatenation of SVG's is already supported, the simpler approach of concatenation is used. + +### Sizing + +This EIP doesn't specify any requirements on the size of the rendered SVG. Any scaling based on sizing can be performed by the SVG NFT as required. + +### Render function name + +The render function is named `renderTokenById` as this function name was first used by Loogies and allows existing deployed NFTs to be compatible with this EIP. + +## Backwards Compatibility +This EIP has no backwards compatibility concerns + + +## Security Considerations + +- SVG uses a "painters model" of rendering. A rendered SVG body could be added and completely obscure the existing SVG NFT artwork. +- SVG is XML and can contain malicious content, and while it won't impact the contract, it could impact the use of the SVG. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4885.md b/EIPS/eip-4885.md new file mode 100644 index 0000000..ce7959e --- /dev/null +++ b/EIPS/eip-4885.md @@ -0,0 +1,201 @@ +--- +eip: 4885 +title: Subscription NFTs and Multi Tokens +description: An interface for subscription tokens that gives holders subscriptions to NFTs and multi tokens +author: Jules Lai (@julesl23) +discussions-to: https://ethereum-magicians.org/t/eip-subscription-token-standard/8531 +status: Stagnant +type: Standards Track +category: ERC +created: 2022-03-08 +requires: 165, 721, 1155 +--- + +## Abstract + +The following standard allows for the implementation of a standard API for subscribing to non-fungible and multi tokens. [EIP-20](./eip-20.md) tokens are deposited in exchange for subscription tokens that give the right to use said non-fungible and multi tokens for a specified time limited or unlimited period. + +## Motivation + +This standard offers a flexible, general purpose way to subscribe to the use of assets or services offered by [EIP-721](./eip-721.md) or [EIP-1155](./eip-1155.md) contracts. From here on in, for the sake of simplicity, these contracts will be known as NFTs; the provider is the issuer of said NFTs and the subscriber(s) uses them. + +This proposal was originally conceived from the want to give creators of music and film, back control. The distribution and delivery of digital content is currently the purview of centralised tech corporations who offer homogeneous subscription models to their customers. This proposal specifies a standard for dapp developers to give creators the ability to set their own custom subscription models and hence, open up new revenue streams that can lead to decentralised distribution and delivery models. + +Use cases include any sort of periodic (e.g. daily, weekly, monthly, quarterly, yearly/annual, or seasonal) use of or access to assets or services such as: + +- Subscriptions for streaming music, video, e-learning or book/news services +- Sharing of digital assets among subscribers +- Club memberships such as health clubs +- Season tickets for sports and e-sports +- Agreement between parties to exchange fixed rate subscription stream with variable income in DeFi +- Renting in-game assets +- Etc. + +The subscription token borrows a few functions from the EIP-20 specification. An implementer is free to implement the rest of the standard; allowing for example subscription tokens to be transferred in secondary markets, sent as gifts or for refunds etc. + +## Specification + +The subscriber deposits EIP-20 to receive an NFT and subscription. Subscription tokens balance automatically decreases linearly over the lifetime of usage of the NFT, and use of the NFT is disabled once the subscription token balance falls to zero. The subscriber can top up the balance to extend the lifetime of the subscription by depositing EIP-20 tokens in exchange for more subscription tokens. + +Smart contracts implementing this EIP standard MUST implement the [EIP-165](./eip-165.md) supportsInterface function and MUST return the constant value true if 0xC1A48422 is passed through the interfaceID argument. Note that revert in this document MAY mean a require, throw (not recommended as depreciated) or revert solidity statement with or without error messages. + +```solidity +interface ISubscriptionToken { + /** + @dev This emits when the subscription token constructor or initialize method is + executed. + @param name The name of the subscription token + @param symbol The symbol of the subscription token + @param provider The provider of the subscription whom receives the deposits + @param subscriptionToken The subscription token contract address + @param baseToken The ERC-20 compatible token to use for the deposits. + @param nft Address of the `nft` contract that the provider mints/transfers from. + All tokenIds referred to in this interface MUST be token instances of this `nft` contract. + */ + event InitializeSubscriptionToken( + string name, + string symbol, + address provider, + address indexed subscriptionToken, + address indexed baseToken, + address indexed nft, + string uri + ); + + /** + @dev This emits for every new subscriber to `nft` contract of token `tokenId`. + `subscriber` MUST have received `nft` of token `tokenId` in their account. + @param subscriber The subscriber account + @param tokenId MUST be token id of `nft` sent to `subscriber` + @param uri MUST be uri of the `nft` that was sent to `subscriber` or empty string + */ + event SubscribeToNFT( + address indexed subscriber, + uint256 indexed tokenId, + string uri + ); + + /** + @dev Emits when `subscriber` deposits ERC-20 of token type `baseToken` via the `deposit method. + This tops up `subscriber` balance of subscription tokens + @param depositAmount The amount of ERC-20 of type `baseToken` deposited + @param subscriptionTokenAmount The amount of subscription tokens sent in exchange to `subscriber` + @param subscriptionPeriod Amount of additional time in seconds subscription is extended + */ + event Deposit( + address indexed subscriber, + uint256 indexed tokenId, + uint256 depositAmount, + uint256 subscriptionTokenAmount, + uint256 subscriptionPeriod + ); + + /** + @return The name of the subscription token + */ + function name() external view returns (string memory); + + /** + @return The symbol of the subscription token + */ + function symbol() external view returns (string memory); + + /** + @notice Subscribes `subscriber` to `nft` of 'tokenId'. `subscriber` MUST receive `nft` + of token `tokenId` in their account. + @dev MUST revert if `subscriber` is already subscribed to `nft` of 'tokenId' + MUST revert if 'nft' has not approved the `subscriptionToken` contract address as operator. + @param subscriber The subscriber account. MUST revert if zero address. + @param tokenId MUST be token id of `nft` contract sent to `subscriber` + `tokenId` emitted from event `SubscribeToNFT` MUST be the same as tokenId except when + tokenId is zero; allows OPTIONAL tokenid that is then set internally and minted by + `nft` contract + @param uri The OPTIONAL uri of the `nft`. + `uri` emitted from event `SubscribeToNFT` MUST be the same as uri except when uri is empty. + */ + function subscribeToNFT( + address subscriber, + uint256 tokenId, + string memory uri + ) external; + + /** + @notice Top up balance of subscription tokens held by `subscriber` + @dev MUST revert if `subscriber` is not subscribed to `nft` of 'tokenId' + MUST revert if 'nft' has not approved the `subscriptionToken` contract address as operator. + @param subscriber The subscriber account. MUST revert if zero address. + @param tokenId The token id of `nft` contract to subscribe to + @param depositAmount The amount of ERC-20 token of contract address `baseToken` to deposit + in exchange for subscription tokens of contract address `subscriptionToken` + */ + function deposit( + address subscriber, + uint256 tokenId, + uint256 depositAmount + ) external payable; + + /** + @return The balance of subscription tokens held by `subscriber`. + RECOMMENDED that the balance decreases linearly to zero for time limited subscriptions + RECOMMENDED that the balance remains the same for life long subscriptions + MUST return zero balance if the `subscriber` does not hold `nft` of 'tokenId' + MUST revert if subscription has not yet started via the `deposit` function + When the balance is zero, the use of `nft` of `tokenId` MUST NOT be allowed for `subscriber` + */ + function balanceOf(address subscriber) external view returns (uint256); +} +``` + +### Subscription token balances + +An example implementation mints an amount of subscription token that totals to one subscription token per day of the subscription period length paid for by the subscriber; for example a week would be for seven subscription tokens. The subscription token balance then decreases automatically at a rate of one token per day continuously and linearly over time until zero. The `balanceOf` function can be implemented lazily by calculating the amount of subscription tokens left only when it is called as a view function, thus has no gas cost. + +### Subscription token price + +Subscription token price paid per token per second can be calculated from the `Deposit` event parameters as +`depositAmount` / (`subscriptionTokenAmount` \* `subscriptionPeriod`) + +### NFT metadata + +The NFT's metadata can store information of the asset/service offered to the subscriber by the provider for the duration of the subscription. This MAY be the terms and conditions of the agreed subscription service offered by the provider to the subscriber. It MAY also be the metadata of the NFT asset if this is offered directly. This standard is kept purposely general to cater for many different use cases of NFTs. + +### Subscription expiry + +When the subscription token balance falls to zero for a subscriber (signifying that the subscription has expired) then it is up to the implementer on how to handle this for their particular use case. For example, a provider may stop streaming media service to a subscriber. For an NFT that represents an image stored off-chain, perhaps the NFT's `uri` function no longer returns back a link to its metadata. + +### Caveats + +With some traditional subscription models based on fiat currencies, the subscribers' saved payment credentials are used to automatically purchase to extend the subscription period, at or just before expiry. This feature is not possible in this proposal specification as recurring payments will have to have allowance approved for signed by a subscriber for each payment when using purely cryptocurrencies. + +This proposal does not deal with pausing subscriptions directly, implementers can write their own or inherit off 3rd party smart contract abstractions such as OpenZeppelin's Pausable. In that case, `balanceOf` method would need extra logic and storage to account for the length of time the subscription tokens were paused. + +## Rationale + +### Tokenisation of subscriptions + +The subscription itself has value when it is exchanged for a deposit. This proposal enables subscriptions to be 'tokenised' thus secondary markets can exist where the subscription tokens can be bought and sold. For example, a fan might want to sell their season ticket, that gives access to live sporting events, on to another fan. This would not be as easily possible if there was only a date expiry extension feature added to NFTs. +An implementer can simply implement the rest of the EIP-20 functions for subscription tokens to be traded. It is left to the implementer to decide if the subscription service offered is non-fungible or fungible. If non-fungible then buying the subscription tokens would simply give the same period left to expiration. If fungible and the purchaser already had an existing subscription for the same service then their total subscription period can be extended by the amount of subscription tokens bought. + +### Cater for current and future uses of NFTs + +This proposal purposely keeps `tokenId` and `uri` optional in the `subcribeToNFT` method to keep the specification general purpose. Some use cases such as pre-computed image NFT collections don't require a different 'uri', just a different `tokenId` for each NFT. However, in other use cases such as those that require legal contracts between both parties, individual `uri` links are probably required as the NFT's metadata may require information from both parties to be stored on immutable storage. + +### Giving back users control + +Traditional subscription models, particularly with streaming services, control of the subscription model is totally with that of the central service provider. This proposal gives decentralised services a standard way to give control back to their users. Hence each user is able to develop their own subscription eco system and administer it towards one that suits theirs and their subscribers' needs. + +## Backwards Compatibility + +A subscription token contract can be fully compatible with EIP-20 specification to allow, for example, transfers from one subscriber to another subscriber or user. EIP-20 methods `name`, `symbol` and `balanceOf` are already part of the specification of this proposal, and it is left to the implementer to choose whether to implement the rest of EIP-20's interface by considering their own use case. + +Use of subscription tokens is in effect an indirect way to control the lifetime of an NFT. As such it is assumed that this arrangement would work best when the NFTs and subscription token contracts subscribing to the NFTs, are deployed by the same platform or decentralised app. It MUST NOT have an impact or dependencies to existing NFTs that have not approved the subscription token as an operator. Indeed in this case, any other parties wouldn't be aware of and any NFT lifetime dependencies will be ignored, hence should not work anyway. To this end, this proposal specifies that the 'nft' MUST have approved the `subscriptionToken` contract address as operator. + +## Security Considerations + +It is normal for service providers to receive subscriber payments upfront before the subscriber gets to use the service. Indeed this proposal via the `deposit` method follows this remit. It would therefore be possible that a service provider sets up, receives the deposits and then does not provide or provides the service poorly to its subscribers. This happens in the traditional world too and this proposal does not cover how to resolve this. + +The `subscribeToNFT` method takes a parameter `uri` link to the `nft` metadata. It is possible if stored on centralised storage that the owners can change the metadata, or perhaps the metadata is hacked which is an issue with vanilla NFT contracts too. But because the `uri` is provided at the time of subscription rather then deployment, it is RECOMMENDED that where the use case requires, implementers ensure that the `uri` link is to immutable storage. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4886.md b/EIPS/eip-4886.md new file mode 100644 index 0000000..c9800fe --- /dev/null +++ b/EIPS/eip-4886.md @@ -0,0 +1,295 @@ +--- +eip: 4886 +title: Proxy Ownership Register +description: A proxy ownership register allowing trustless proof of ownership between Ethereum addresses, with delegated asset delivery +author: Omnus Sunmo (@omnus) +discussions-to: https://ethereum-magicians.org/t/eip-4886-a-proxy-ownership-and-asset-delivery-register/8559 +status: Stagnant +type: Standards Track +category: ERC +created: 2022-09-03 +--- + +## Abstract + +A proxy protocol that allows users to nominate a proxy address to act on behalf of another wallet address, together with a delivery address for new assets. Smart contracts and applications making use of the protocol can take a proxy address and lookup holding information for the nominator address. This has a number of practical applications, including allowing users to store valuable assets safely in a cold wallet and interact with smart contracts using a proxy address of low value. The assets in the nominator are protected as all contract interactions take place with the proxy address. This eliminates a number of exploits seen recently where users' assets are drained through a malicious contract interaction. In addition, the register holds a delivery address, allowing new assets to be delivered directly to a cold wallet address. + +## Motivation + +To make full use of Ethereum users often need to prove their ownership of existing assets. For example: + * Discord communities require users to sign a message with their wallet to prove they hold the tokens or NFTs of that community. + * Whitelist events (for example recent airdrops, or NFT mints), require the user to interact using a given address to prove eligibility. + * Voting in DAOs and other protocols require the user to sign using the address that holds the relevant assets. + + There are more examples, with the unifying theme being that the user must make use of the address with the assets to derive the platform benefit. This means the addresses holding these assets cannot be truly 'cold', and is a gift to malicious developers seeking to steal valuable assets. For example, a new project can offer free NFTs to holders of an existing NFT asset. The existing holders have to prove ownership by minting from the wallet with the asset that determined eligibility. This presents numerous possible attack vectors for a malicious developer who knows that all users interacting with the contract have an asset of that type. + + Possibly even more damaging is the effect on user confidence across the whole ecosystem. Users become reluctant to interact with apps and smart contracts for fear of putting their assets at risk. They may also decide not to store assets in cold wallet addresses as they need to prove they own them on a regular basis. A pertinent example is the user trying to decide whether to 'vault' their NFT and lose access to a discord channel, or keep their NFT in another wallet, or even to connect their 'vault' to discord. + + Ethereum is amazing at providing trustless proofs. The *only* time a user should need to interact using the wallet that holds an asset is if they intend to sell or transfer that asset. If a user merely wishes to prove ownership (to access a resource, get an airdrop, mint an NFT, or vote in a DAO), they should do this through a trustless proof stored on-chain. + + Furthermore, users should be able to decide where new assets are delivered, rather than them being delivered to the wallet providing the interaction. This allows hot wallets to acquire assets sent directly to a cold wallet 'vault', possibly even the one they are representing in terms of asset ownership. + + The aim of this EIP is to provide a convenient method to avoid this security concern and empower more people to feel confident leveraging the full scope of Ethereum functionality. Our vision is an Ethereum where users setup a new hardware wallet for assets they wish to hold long-term, then make one single contract interaction with that wallet: to nominate a hot wallet proxy. That user can always prove they own assets on that address, and they can specify it as a delivery address for new asset delivery. + +## Specification + +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. + +### Definitions + + * Delivery address: The address that assets will be delivered to for the current Proxy Record, i.e. a new NFT minted by the Proxy address, representing the Nominator address, should be delivered to the Delivery address. + * Nomination: Where a Nominator has nominated a Proxy address. Will only be active when the Proxy has accepted the nomination. + * Nominator address: The address that proposes a proxy relationship. This address nominates another address to act as its proxy, representing it and its holdings in all interactions. + * Proxy address: The address that will represent a Nominator on-chain. + * Proxy Record: An active proxy relationship encompassing a Nominator, Proxy and Delivery. + * Register: The main EPS contract, which holds details of both Nominations and Proxy Records. + +### EPS Specification + +There are two main parts to the register - a nomination and a proxy record: + + Contract / Dapp Register + + Nominator: 0x1234.. Nominator: 0x1234.. + Proxy: 0x5678.. ---------> Proxy: 0x4567.. + Delivery: 0x9876.. + +The first step to creating a proxy record is for an address to nominate another address as its proxy. This creates a nomination that maps the nominator (the address making the nomination) to the proposed proxy address. + +This is not a proxy record on the register at this stage, as the proxy address needs to first accept the nomination. Until the nomination is accepted it can be considered to be pending. Once the proxy address has accepted the nomination a proxy record is added to the register. + +When accepting a nomination the proxy address sets the delivery address for that proxy record. The proxy address remains in control of updating that delivery address as required. Both the nominator and proxy can delete the proxy record and nomination at any time. The proxy will continue forever if not deleted - it is eternal. + +The register is a single smart contract that stores all nomination and register records. The information held for each is as follows: + * Nomination: + * The address of the Nominator + * The address of the Proposed Proxy + +* Proxy Record: + * The address of the Nominator + * The address of the Proxy + * The delivery address for proxied deliveries + +Any address can act as a Nominator or a Proxy. A Nomination must have been made first in order for an address to accept acting as a Proxy. + +A Nomination cannot be made to an address that is already active as either a Proxy or a Nominator, i.e. that address is already in an active proxy relationship. + +The information for both Nominations and Proxy records is held as a mapping. For the Nomination this is address => address for the Nominator to the Proxy address. For the Proxy Record the mapping is from address => struct for the Proxy Address to a struct containing the Nominator and Delivery address. + +Mapping between an address and its Nominator and Delivery address is a simple process as shown below: + + Contract / Dapp Register + + | | + |------------- 0x4567..---------------> | + | | + | <-------nominator: 0x1234..---------- | + | delivery: 0x9876.. | + | | + +The protocol is fully backwards compatible. If it is passed an address that does not have an active mapping it will pass back the received address as both the Nominator and Delivery address, thereby preserving functionality as the address is acting on its own behalf. + + Contract / Dapp Register + + | | + |------------- 0x0222..---------------> | + | | + | <-------nominator: 0x0222..---------- | + | delivery: 0x0222.. | + | | + +If the EPS register is passed the address of a Nominator it will revert. This is of vital importance. The purpose of the proxy is that the Proxy address is operating on behalf of the Nominator. The Proxy address therefore can derive the same benefits as the Nominator (for example discord roles based on the Nominator's holdings, or mint NFTs that require another NFT to be held). It is therefore imperative that the Nominator in an active proxy cannot also interact and derive these benefits, otherwise two addresses represent the same holding. A Nominator can of course delete the Proxy Record at any time and interact on it's own behalf, with the Proxy address instantly losing any benefits associated with the proxy relationship. + +### Solidity Interface Definition + +**Nomination Exists** + + function nominationExists(address _nominator) external view returns (bool); + +Returns true if a Nomination exists for the address specified. + +**Nomination Exists for Caller** + + function nominationExistsForCaller() external view returns (bool); + +Returns true if a Nomination exists for the msg.sender. + +**Proxy Record Exists** + + function proxyRecordExists(address _proxy) external view returns (bool); + +Returns true if a Proxy Record exists for the passed Proxy address. + +**Proxy Record Exists for Caller** + + function proxyRecordExistsForCaller() external view returns (bool); + +Returns true if a Proxy Record exists for the msg.sender. + +**Nominator Record Exists** + + function nominatorRecordExists(address _nominator) external view returns (bool); + +Returns true if a Proxy Record exists for the passed Nominator address. + +**Nominator Record Exists for Caller** + + function nominatorRecordExistsForCaller() external view returns (bool); + +Returns true if a Proxy Record exists for the msg.sender. + +**Get Proxy Record** + + function getProxyRecord(address _proxy) external view returns (address nominator, address proxy, address delivery); + +Returns Nominator, Proxy and Delivery address for a passed Proxy address. + +**Get Proxy Record for Caller** + + function getProxyRecordForCaller() external view returns (address nominator, address proxy, address delivery); + +Returns Nominator, Proxy and Delivery address for msg.sender as Proxy address. + +**Get Nominator Record** + + function getNominatorRecord(address _nominator) external view returns (address nominator, address proxy, address delivery); + +Returns Nominator, Proxy and Delivery address for a passed Nominator address. + +**Get Nominator Record for Caller** + + function getNominatorRecordForCaller() external view returns (address nominator, address proxy, address delivery); + +Returns Nominator, Proxy and Delivery address for msg.sender address as Nominator. + +**Address Is Active** + + function addressIsActive(address _receivedAddress) external view returns (bool); + +Returns true if the passed address is Nominator or Proxy address on an active Proxy Record. + +**Address Is Active for Caller** + + function addressIsActiveForCaller() external view returns (bool); + +Returns true if msg.sender is Nominator or Proxy address on an active Proxy Record. + +**Get Nomination** + +function getNomination(address _nominator) external view returns (address proxy); + +Returns the proxy address for a Nomination when passed a Nominator. + +**Get Nomination for Caller** + +function getNominationForCaller() external view returns (address proxy); + +Returns the proxy address for a Nomination if msg.sender is a Nominator + +**Get Addresses** + + function getAddresses(address _receivedAddress) external view returns (address nominator, address delivery, bool isProxied); + +Returns the Nominator, Proxy, Delivery and a boolean isProxied for the passed address. If you pass an address that is not a Proxy address it will return address(0) for the Nominator, Proxy and Delivery address and isProxied of false. If you pass an address that is a Proxy address it will return the relvant addresses and isProxied of true. + +**Get Addresses for Caller** + + function getAddressesForCaller() external view returns (address nominator, address delivery, bool isProxied); + +Returns the Nominator, Proxy, Delivery and a boolean isProxied for msg.sender. If msg.sender is not a Proxy address it will return address(0) for the Nominator, Proxy and Delivery address and isProxied of false. If msg.sender is a Proxy address it will return the relvant addresses and isProxied of true. + +**Get Role** + + function getRole(address _roleAddress) external view returns (string memory currentRole); + +Returns a string value with a role for the passed address. Possible roles are: + +None The address does not appear on the Register as either a Record or a Nomination. + +Nominator - Pending The address is the Nominator on a Nomination which has yet to be accepted by the nominated Proxy address. + +Nominator - Active The address is a Nominator on an active Proxy Record (i.e. the Nomination has been accepted). + +Proxy - Active The address is a Proxy on an active Proxy Record. + +**Get Role for Caller** + + function getRoleForCaller() external view returns (string memory currentRole); + +Returns a string value with a role for msg.sender. Possible roles are: + +None The msg.sender does not appear on the Register as either a Record or a Nomination. + +Nominator - Pending The msg.sender is the Nominator on a Nomination which has yet to be accepted by the nominated Proxy address. + +Nominator - Active The msg.sender is a Nominator on an active Proxy Record (i.e. the Nomination has been accepted). + +Proxy - Active The msg.sender is a Proxy on an active Proxy Record. + +**Make Nomination** + + function makeNomination(address _proxy, uint256 _provider) external payable; + +Can be passed a Proxy address to create a Nomination for the msg.sender. + +Provider is a required argument. If you do not have a Provider ID you can pass 0 as the default EPS provider. For details on the EPS Provider Program please see . + +**Accept Nomination** + + function acceptNomination(address _nominator, address _delivery, uint256 _provider) external; + +Can be passed a Nominator and Delivery address to accept a Nomination for a msg.sender. Note that to accept a Nomination the Nomination needs to exists with the msg.sender as the Proxy. The Nominator passed to the function and that on the Nomination must match. + +Provider is a required argument. If you do not have a Provider ID you can pass 0 as the default EPS provider. For details on the EPS Provider Program please see . + +**Update Delivery Record** + + function updateDeliveryAddress(address _delivery, uint256 _provider) external; + +Can be passed a new Delivery address where the msg.sender is the Proxy on a Proxy Record. + +Provider is a required argument. If you do not have a Provider ID you can pass 0 as the default EPS provider. For details on the EPS Provider Program please see . + +**Delete Record by Nominator** + + function deleteRecordByNominator(uint256 _provider) external; + +Can be called to delete a Record and Nomination when the msg.sender is a Nominator. + +Note that when both a Record and Nomination exist both are deleted. If no Record exists (i.e. the Nomination hasn't been accepted by the Proxy address) the Nomination is deleted. + +Provider is a required argument. If you do not have a Provider ID you can pass 0 as the default EPS provider. For details on the EPS Provider Program please see . + +**Delete Record by Proxy** + + function deleteRecordByProxy(uint256 _provider) external; + +Can be called to delete a Record and Nomination when the msg.sender is a Proxy. + +## Rationale + +The rationale for this EIP was to provide a way for all existing and future Ethereum assets to be have a 'beneficial owner' (the proxy) that is different to the address custodying the asset. The use of a register to achieve this ensures that changes to existing tokens are not required. The register stores a trustless proof, signed by both the nominator and proxy, that can be relied upon as a true representation of asset ownership. + +## Backwards Compatibility + +The EIP is fully backwards compatible. + +## Test Cases + +The full SDLC for this proposal has been completed and it is operation at 0xfa3D2d059E9c0d348dB185B32581ded8E8243924 on mainnet, ropsten and rinkeby. The contract source code is validated and available on etherscan. The full unit test suite is available in `../assets/eip-4886/`, as is the source code and example implementations. + +## Reference Implementation + +Please see `../assets/eip-4886/contracts` + +## Security Considerations + +The core intention of the EIP is to improve user security by better safeguarding assets and allowing greater use of cold wallet storage. + +Potential negative security implications have been considered and none are envisaged. The proxy record can only become operational when a nomination has been confirmed by a proxy address, both addresses therefore having provided signed proof. + +From a usability perspective the key risk is in users specifying the incorrect asset delivery address, though it is noted that this burden of accuracy is no different to that currently on the network. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4895.md b/EIPS/eip-4895.md new file mode 100644 index 0000000..6dab0cd --- /dev/null +++ b/EIPS/eip-4895.md @@ -0,0 +1,173 @@ +--- +eip: 4895 +title: Beacon chain push withdrawals as operations +description: Support validator withdrawals from the beacon chain to the EVM via a new "system-level" operation type. +author: Alex Stokes (@ralexstokes), Danny Ryan (@djrtwo) +discussions-to: https://ethereum-magicians.org/t/eip-4895-beacon-chain-withdrawals-as-system-level-operations/8568 +status: Review +type: Standards Track +category: Core +created: 2022-03-10 +--- + +## Abstract + +Introduce a system-level "operation" to support validator withdrawals that are "pushed" from the beacon chain to the EVM. + +These operations create unconditional balance increases to the specified recipients. + +## Motivation + +This EIP provides a way for validator withdrawals made on the beacon chain to enter into the EVM. +The architecture is "push"-based, rather than "pull"-based, where withdrawals are required to be processed in the execution layer as soon as they are dequeued from the consensus layer. + +Withdrawals are represented as a new type of object in the execution payload -- an "operation" -- that separates the withdrawals feature from user-level transactions. +This approach is more involved than the prior approach introducing a new transaction type but it cleanly separates this "system-level" operation from regular transactions. +The separation simplifies testing (so facilitates security) by reducing interaction effects generated by mixing this system-level concern with user data. + +Moreover, this approach is more complex than "pull"-based alternatives with respect to the core protocol but does provide tighter integration of a critical feature into the protocol itself. + +## Specification + +| constants | value | units +|--- |--- |--- +| `FORK_TIMESTAMP` | 1681338455 | + +Beginning with the execution timestamp `FORK_TIMESTAMP`, execution clients **MUST** introduce the following extensions to payload validation and processing: + +### System-level operation: withdrawal + +Define a new payload-level object called a `withdrawal` that describes withdrawals that have been validated at the consensus layer. +`Withdrawal`s are syntactically similar to a user-level transaction but live in a different domain than user-level transactions. + +`Withdrawal`s provide key information from the consensus layer: + +1. a monotonically increasing `index`, starting from 0, as a `uint64` value that increments by 1 per withdrawal to uniquely identify each withdrawal +2. the `validator_index` of the validator, as a `uint64` value, on the consensus layer the withdrawal corresponds to +3. a recipient for the withdrawn ether `address` as a 20-byte value +4. a nonzero `amount` of ether given in Gwei (1e9 wei) as a `uint64` value. + +*NOTE*: the `index` for each withdrawal is a global counter spanning the entire sequence of withdrawals. + +`Withdrawal` objects are serialized as a RLP list according to the schema: `[index, validator_index, address, amount]`. + +### New field in the execution payload: withdrawals + +The execution payload gains a new field for the `withdrawals` which is an RLP list of `Withdrawal` data. + +For example: + +```python +withdrawal_0 = [index_0, validator_index_0, address_0, amount_0] +withdrawal_1 = [index_1, validator_index_1, address_1, amount_1] +withdrawals = [withdrawal_0, withdrawal_1] +``` + +This new field is encoded after the existing fields in the execution payload structure and is considered part of the execution payload's body. + +```python +execution_payload_rlp = RLP([header, transactions, [], withdrawals]) + +execution_payload_body_rlp = RLP([transactions, [], withdrawals]) +``` + +NOTE: the empty list in this schema is due to [EIP-3675](./eip-3675.md) that sets the `ommers` value to a fixed constant. + +### New field in the execution payload header: withdrawals root + +The execution payload header gains a new field committing to the `withdrawals` in the execution payload. + +This commitment is constructed identically to the transactions root in the existing execution payload header by inserting +each withdrawal into a Merkle-Patricia trie keyed by index in the list of `withdrawals`. + +```python +def compute_trie_root_from_indexed_data(data): + trie = Trie.from([(i, obj) for i, obj in enumerate(data)]) + return trie.root + +execution_payload_header.withdrawals_root = compute_trie_root_from_indexed_data(execution_payload.withdrawals) +``` + +The execution payload header is extended with a new field containing the 32 byte root of the trie committing to the list of withdrawals provided in a given execution payload. + +To illustrate: + +```python +execution_payload_header_rlp = RLP([ + parent_hash, + 0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347, # ommers hash + coinbase, + state_root, + txs_root, + receipts_root, + logs_bloom, + 0, # difficulty + number, + gas_limit, + gas_used, + timestamp, + extradata, + prev_randao, + 0x0000000000000000, # nonce + base_fee_per_gas, + withdrawals_root, +]) +``` + +NOTE: field names and constant value in this example reflect [EIP-3675](./eip-3675.md) and [EIP-4399](./eip-4399.md). Refer to those EIPs for further information. + +### Execution payload validity + +Assuming the execution payload is well-formatted, the execution client has an additional payload validation to ensure that the `withdrawals_root` matches the expected value given the list in the payload. + +```python +assert execution_payload_header.withdrawals_root == compute_trie_root_from_indexed_data(execution_payload.withdrawals) +``` + +### State transition + +The `withdrawals` in an execution payload are processed **after** any user-level transactions are applied. + +For each `withdrawal` in the list of `execution_payload.withdrawals`, the implementation increases the balance of the `address` specified by the `amount` given. + +Recall that the `amount` is given in units of Gwei so a conversion to units of wei must be performed when working with account balances in the execution state. + +This balance change is unconditional and **MUST** not fail. + +This operation has no associated gas costs. + +## Rationale + +### Why not a new transaction type? + +This EIP suggests a new type of object -- the "withdrawal operation" -- as it has special semantics different from other existing types of EVM transactions. + +Operations are initiated by the overall system, rather than originating from end users like typical transactions. + +An entirely new type of object firewalls off generic EVM execution from this type of processing to simplify testing and security review of withdrawals. + +### Why no (gas) costs for the withdrawal type? + +The maximum number of withdrawals that can reach the execution layer at a given time is bounded (enforced by the consensus layer) and this limit has been chosen so that +any execution layer operational costs are negligible in the context of the broader payload execution. + +This bound applies to both computational cost (only a few balance updates in the state) and storage/networking cost as the additional payload footprint is kept small (current parameterizations put the additional overhead at ~1% of current average payload size). + +### Why only balance updates? No general EVM execution? + +More general processing introduces the risk of failures, which complicates accounting on the beacon chain. + +This EIP suggests a route for withdrawals that provides most of the benefits for a minimum of the (complexity) cost. + +## Backwards Compatibility + +No issues. + +## Security Considerations + +Consensus-layer validation of withdrawal transactions is critical to ensure that the proper amount of ETH is withdrawn back into the execution layer. +This consensus-layer to execution-layer ETH transfer does not have a current analog in the EVM and thus deserves very high security scrutiny. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4906.md b/EIPS/eip-4906.md new file mode 100644 index 0000000..93e6492 --- /dev/null +++ b/EIPS/eip-4906.md @@ -0,0 +1,93 @@ +--- +eip: 4906 +title: EIP-721 Metadata Update Extension +description: Add a MetadataUpdate event to EIP-721. +author: Anders (@0xanders), Lance (@LanceSnow), Shrug , Nathan +discussions-to: https://ethereum-magicians.org/t/eip4906-erc-721-erc-1155-metadata-update-extension/8588 +status: Final +type: Standards Track +category: ERC +created: 2022-03-13 +requires: 165, 721 +--- + +## Abstract + +This standard is an extension of [EIP-721](./eip-721.md). It adds a `MetadataUpdate` event to EIP-721 tokens. + +## Motivation + +Many [EIP-721](./eip-721.md) contracts emit an event when one of its tokens' metadata are changed. While tracking changes based on these different events is possible, it is an extra effort for third-party platforms, such as an NFT marketplace, to build individualized solutions for each NFT collection. + +Having a standard `MetadataUpdate` event will make it easy for third-party platforms to timely update the metadata of many NFTs. + +## Specification + +The keywords “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. + +The **metadata update extension** is OPTIONAL for EIP-721 contracts. + + +```solidity +/// @title EIP-721 Metadata Update Extension +interface IERC4906 is IERC165, IERC721 { + /// @dev This event emits when the metadata of a token is changed. + /// So that the third-party platforms such as NFT market could + /// timely update the images and related attributes of the NFT. + event MetadataUpdate(uint256 _tokenId); + + /// @dev This event emits when the metadata of a range of tokens is changed. + /// So that the third-party platforms such as NFT market could + /// timely update the images and related attributes of the NFTs. + event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId); +} +``` + +The `MetadataUpdate` or `BatchMetadataUpdate` event MUST be emitted when the JSON metadata of a token, or a consecutive range of tokens, is changed. + +Not emitting `MetadataUpdate` event is RECOMMENDED when a token is minted. + +Not emitting `MetadataUpdate` event is RECOMMENDED when a token is burned. + +Not emitting `MetadataUpdate` event is RECOMMENDED when the tokenURI changes but the JSON metadata does not. + +The `supportsInterface` method MUST return `true` when called with `0x49064906`. + +## Rationale + +Different NFTs have different metadata, and metadata generally has multiple fields. `bytes data` could be used to represents the modified value of metadata. It is difficult for third-party platforms to identify various types of `bytes data`, so as to avoid unnecessary complexity, arbitrary metadata is not included in the `MetadataUpdate` event. + +After capturing the `MetadataUpdate` event, a third party can update the metadata with information returned from the `tokenURI(uint256 _tokenId)` of EIP-721. When a range of token ids is specified, the third party can query each token URI individually. + +## Backwards Compatibility + +No backwards compatibility issues were found + +## Reference Implementation + +```solidity +// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; +import "./IERC4906.sol"; + +contract ERC4906 is ERC721, IERC4906 { + + constructor(string memory name_, string memory symbol_) ERC721(name_, symbol_) { + } + + /// @dev See {IERC165-supportsInterface}. + function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721) returns (bool) { + return interfaceId == bytes4(0x49064906) || super.supportsInterface(interfaceId); + } +} +``` + +## Security Considerations + +If there is an off-chain modification of metadata, a method that triggers `MetadataUpdate` can be added, but ensure that the function's permission controls are correct. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4907.md b/EIPS/eip-4907.md new file mode 100755 index 0000000..ed02d6c --- /dev/null +++ b/EIPS/eip-4907.md @@ -0,0 +1,249 @@ +--- +eip: 4907 +title: Rental NFT, an Extension of EIP-721 +description: Add a time-limited role with restricted permissions to EIP-721 tokens. +author: Anders (@0xanders), Lance (@LanceSnow), Shrug +discussions-to: https://ethereum-magicians.org/t/idea-erc-721-user-and-expires-extension/8572 +status: Final +type: Standards Track +category: ERC +created: 2022-03-11 +requires: 165, 721 +--- + +## Abstract + +This standard is an extension of [EIP-721](./eip-721.md). It proposes an additional role (`user`) which can be granted to addresses, and a time where the role is automatically revoked (`expires`). The `user` role represents permission to "use" the NFT, but not the ability to transfer it or set users. + +## Motivation + +Some NFTs have certain utilities. For example, virtual land can be "used" to build scenes, and NFTs representing game assets can be "used" in-game. In some cases, the owner and user may not always be the same. There may be an owner of the NFT that rents it out to a “user”. The actions that a “user” should be able to take with an NFT would be different from the “owner” (for instance, “users” usually shouldn’t be able to sell ownership of the NFT).  In these situations, it makes sense to have separate roles that identify whether an address represents an “owner” or a “user” and manage permissions to perform actions accordingly. + +Some projects already use this design scheme under different names such as “operator” or “controller” but as it becomes more and more prevalent, we need a unified standard to facilitate collaboration amongst all applications. + +Furthermore, applications of this model (such as renting) often demand that user addresses have only temporary access to using the NFT. Normally, this means the owner needs to submit two on-chain transactions, one to list a new address as the new user role at the start of the duration and one to reclaim the user role at the end. This is inefficient in both labor and gas and so an “expires” function is introduced that would facilitate the automatic end of a usage term without the need of a second transaction. + +## Specification + +The keywords "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. + +### Contract Interface +Solidity Interface with NatSpec & OpenZeppelin v4 Interfaces (also available at [`IERC4907.sol`](../assets/eip-4907/contracts/IERC4907.sol)): + +```solidity +interface IERC4907 { + + // Logged when the user of an NFT is changed or expires is changed + /// @notice Emitted when the `user` of an NFT or the `expires` of the `user` is changed + /// The zero address for user indicates that there is no user address + event UpdateUser(uint256 indexed tokenId, address indexed user, uint64 expires); + + /// @notice set the user and expires of an NFT + /// @dev The zero address indicates there is no user + /// Throws if `tokenId` is not valid NFT + /// @param user The new user of the NFT + /// @param expires UNIX timestamp, The new user could use the NFT before expires + function setUser(uint256 tokenId, address user, uint64 expires) external; + + /// @notice Get the user address of an NFT + /// @dev The zero address indicates that there is no user or the user is expired + /// @param tokenId The NFT to get the user address for + /// @return The user address for this NFT + function userOf(uint256 tokenId) external view returns(address); + + /// @notice Get the user expires of an NFT + /// @dev The zero value indicates that there is no user + /// @param tokenId The NFT to get the user expires for + /// @return The user expires for this NFT + function userExpires(uint256 tokenId) external view returns(uint256); +} +``` + +The `userOf(uint256 tokenId)` function MAY be implemented as `pure` or `view`. + +The `userExpires(uint256 tokenId)` function MAY be implemented as `pure` or `view`. + +The `setUser(uint256 tokenId, address user, uint64 expires)` function MAY be implemented as `public` or `external`. + +The `UpdateUser` event MUST be emitted when a user address is changed or the user expires is changed. + +The `supportsInterface` method MUST return `true` when called with `0xad092b5c`. + +## Rationale + +This model is intended to facilitate easy implementation. Here are some of the problems that are solved by this standard: + +### Clear Rights Assignment + +With Dual “owner” and “user” roles, it becomes significantly easier to manage what lenders and borrowers can and cannot do with the NFT (in other words, their rights). Additionally, owners can control who the user is and it’s easy for other projects to assign their own rights to either the owners or the users. + +### Simple On-chain Time Management + +Once a rental period is over, the user role needs to be reset and the “user” has to lose access to the right to use the NFT. This is usually accomplished with a second on-chain transaction but that is gas inefficient and can lead to complications because it’s imprecise. With the `expires` function, there is no need for another transaction because the “user” is invalidated automatically after the duration is over. + +### Easy Third-Party Integration + +In the spirit of permission less interoperability, this standard makes it easier for third-party protocols to manage NFT usage rights without permission from the NFT issuer or the NFT application. Once a project has adopted the additional `user` role and `expires`, any other project can directly interact with these features and implement their own type of transaction. For example, a PFP NFT using this standard can be integrated into both a rental platform where users can rent the NFT for 30 days AND, at the same time, a mortgage platform where users can use the NFT while eventually buying ownership of the NFT with installment payments. This would all be done without needing the permission of the original PFP project. + +## Backwards Compatibility + +As mentioned in the specifications section, this standard can be fully EIP-721 compatible by adding an extension function set. + +In addition, new functions introduced in this standard have many similarities with the existing functions in EIP-721. This allows developers to easily adopt the standard quickly. + +## Test Cases + +### Test Contract +`ERC4907Demo` Implementation: [`ERC4907Demo.sol`](../assets/eip-4907/contracts/ERC4907Demo.sol) + +```solidity +// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.0; + +import "./ERC4907.sol"; + +contract ERC4907Demo is ERC4907 { + + constructor(string memory name, string memory symbol) + ERC4907(name,symbol) + { + } + + function mint(uint256 tokenId, address to) public { + _mint(to, tokenId); + } + +} +``` + +### Test Code +[test.js](../assets/eip-4907/test/test.js) + +```JavaScript +const { assert } = require("chai"); + +const ERC4907Demo = artifacts.require("ERC4907Demo"); + +contract("test", async accounts => { + + it("should set user to Bob", async () => { + // Get initial balances of first and second account. + const Alice = accounts[0]; + const Bob = accounts[1]; + + const instance = await ERC4907Demo.deployed("T", "T"); + const demo = instance; + + await demo.mint(1, Alice); + let expires = Math.floor(new Date().getTime()/1000) + 1000; + await demo.setUser(1, Bob, BigInt(expires)); + + let user_1 = await demo.userOf(1); + + assert.equal( + user_1, + Bob, + "User of NFT 1 should be Bob" + ); + + let owner_1 = await demo.ownerOf(1); + assert.equal( + owner_1, + Alice , + "Owner of NFT 1 should be Alice" + ); + }); +}); + + +``` + +run in Terminal: +``` +truffle test ./test/test.js +``` + +## Reference Implementation +Implementation: [`ERC4907.sol`](../assets/eip-4907/contracts/ERC4907.sol) +```solidity +// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; +import "./IERC4907.sol"; + +contract ERC4907 is ERC721, IERC4907 { + struct UserInfo + { + address user; // address of user role + uint64 expires; // unix timestamp, user expires + } + + mapping (uint256 => UserInfo) internal _users; + + constructor(string memory name_, string memory symbol_) + ERC721(name_, symbol_) + { + } + + /// @notice set the user and expires of an NFT + /// @dev The zero address indicates there is no user + /// Throws if `tokenId` is not valid NFT + /// @param user The new user of the NFT + /// @param expires UNIX timestamp, The new user could use the NFT before expires + function setUser(uint256 tokenId, address user, uint64 expires) public virtual{ + require(_isApprovedOrOwner(msg.sender, tokenId), "ERC4907: transfer caller is not owner nor approved"); + UserInfo storage info = _users[tokenId]; + info.user = user; + info.expires = expires; + emit UpdateUser(tokenId, user, expires); + } + + /// @notice Get the user address of an NFT + /// @dev The zero address indicates that there is no user or the user is expired + /// @param tokenId The NFT to get the user address for + /// @return The user address for this NFT + function userOf(uint256 tokenId) public view virtual returns(address){ + if( uint256(_users[tokenId].expires) >= block.timestamp){ + return _users[tokenId].user; + } + else{ + return address(0); + } + } + + /// @notice Get the user expires of an NFT + /// @dev The zero value indicates that there is no user + /// @param tokenId The NFT to get the user expires for + /// @return The user expires for this NFT + function userExpires(uint256 tokenId) public view virtual returns(uint256){ + return _users[tokenId].expires; + } + + /// @dev See {IERC165-supportsInterface}. + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + return interfaceId == type(IERC4907).interfaceId || super.supportsInterface(interfaceId); + } + + function _beforeTokenTransfer( + address from, + address to, + uint256 tokenId + ) internal virtual override{ + super._beforeTokenTransfer(from, to, tokenId); + + if (from != to && _users[tokenId].user != address(0)) { + delete _users[tokenId]; + emit UpdateUser(tokenId, address(0), 0); + } + } +} +``` + +## Security Considerations + +This EIP standard can completely protect the rights of the owner, the owner can change the NFT user and expires at any time. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4910.md b/EIPS/eip-4910.md new file mode 100644 index 0000000..2deb8cd --- /dev/null +++ b/EIPS/eip-4910.md @@ -0,0 +1,694 @@ +--- +eip: 4910 +title: Royalty Bearing NFTs +description: Extension of ERC-721 to correctly define, process, and pay (hierarchical) onchain NFT royalties. +author: Andreas Freund (@Therecanbeonlyone1969) +discussions-to: https://ethereum-magicians.org/t/royalty-bearing-nfts/8453 +status: Last Call +last-call-deadline: 2023-03-31 +type: Standards Track +category: ERC +created: 2022-03-14 +requires: 165, 721 +--- + +## Abstract +The proposal directly connects NFTs and royalties in a smart contract architecture extending the [ERC-721](./eip-721.md) standard, with the aim of precluding central authorities from manipulating or circumventing payments to those who are legally entitled to them. + +The proposal builds upon the OpenZeppelin Smart Contract Toolbox architecture, and extends it to include royalty account management (CRUD), royalty balance and payments management, simple trading capabilities -- Listing/De-Listing/Buying -- and capabilities to trace trading on exchanges. The royalty management capabilities allow for hierarchical royalty structures, referred to herein as royalty trees, to be established by logically connecting a "parent" NFT to its "children", and recursively enabling NFT "children" to have more children. + +## Motivation +The management of royalties is an age-old problem characterized by complex contracts, opaque management, plenty of cheating and fraud. + +The above is especially true for a hierarchy of royalties, where one or more assets is derived from an original asset such as a print from an original painting, or a song is used in the creation of another song, or distribution rights and compensation are managed through a series of affiliates. + +In the example below, the artist who created the original is eligible to receive proceeds from every sale, and resale, of a print. + +![Fig1](../assets/eip-4910/eip-4910-print-families.png) + +The basic concept for hierarchical royalties utilizing the above "ancestry concept" is demonstrated in the figure below. + +![Fig2](../assets/eip-4910/eip-4910-royalties.png) + + +In order to solve for the complicated inheritance problem, this proposal breaks down the recursive problem of the hierarchy tree of depth N into N separate problems, one for each layer. This allows us to traverse the tree from its lowest level upwards to its root most efficiently. + +This affords creators, and the distributors of art derived from the original, the opportunity to achieve passive income from the creative process, enhancing the value of an NFT, since it now not only has intrinsic value but also comes with an attached cash flow. + +## Specification +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. + +### Outline + +This proposal introduces several new concepts as extensions to the ERC-721 standard that warrant explanation: +* **Royalty Account (RA)** + * A Royalty Account is attached to each NFT through its `tokenId` and consists of several sub-accounts which can be accounts of individuals or other RAs. A Royalty Account is identified by an account identifier. +* **Account Type** + * This specifies if an RA Sub Account belongs to an individual (user) or is another RA. If there is another RA as an RA Sub Account, the allocated balance needs to be reallocated to the Sub Accounts making up the referenced RA. +* **Royalty Split** + * The percentage each Sub Account receives based on a sale of an NFT that is associated with an RA +* **Royalty Balance** + * The royalty balance associated with an RA +* **Sub Account Royalty Balance** + * The royalty balance associated to each RA Sub Account. Note that only individual accounts can carry a balance that can be paid out. That means that if an RA Sub Account is an RA, its final Sub Account balance must be zero, since all RA balances must be allocated to individual accounts. +* **Token Type** + * Token Type is given as either ETH or the symbol of the supported utility tokens such as `DAI` +* **Asset ID** + * This is the `tokenId` the RA belongs to. +* **Parent** + * This indicates which `tokenId` is the immediate parent of the `tokenId` to which an RA belongs. + +Below a non-normative overview is given of the data structures and functionality that are covered by the requirements in this document. + +#### Data Structures + +In order to create an interconnected data structure linking NFTs to RAs certain global data structures are required: + +* A Royalty Account and associated Royalty Sub Accounts to establish the concept of a Royalty Account with sub accounts. +* Connecting a `tokenId` to a Royalty Account identifier. +* A structure mapping parent-to-child NFT relationships. +* A listing of token types and last validated balance (for trading and royalty payment purposes) +* A listing of registered payments to be made in the `executePayment` function and validated in `safeTransferFrom`. This is sufficient, because a payment once received and distributed in the `safeTransferFrom` function will be removed from the listing. +* A listing of NFTs to be sold + +#### Royalty Account Functions + +Definitions and interfaces for the Royalty Account RUD (Read-Update-Delete) functions. Because the RA is created in the minting function, there is no need to have a function to create a royalty account separately. + +#### Minting of a Royalty Bearing NFT + +When an NFT is minted, an RA must be created and associated with the NFT and the NFT owner, and, if there is an ancestor, with the ancestor's RA. To this end the specification utilizes the `_safemint` function in a newly defined `mint` function and applies various business rules on the input variables. + +#### Listing NFTs for Sale and removing a Listing + +Authorized user addresses can list NFTs for sale for non-exchange mediated NFT purchases. + +#### Payment Function from Buyer to Seller + +To avoid royalty circumvention, a buyer will always pay the NFT contract directly and not the seller. The seller is paid through the royalty distribution and can later request a payout. + +The payment process depends on whether the payment is received in ETH or an [ERC-20](./eip-20.md) token: +* ERC-20 Token + 1. The Buyer must `approve` the NFT contract for the purchase price, `payment` for the selected payment token (ERC-20 contract address). + 2. For an ERC-20 payment token, the Buyer must then call the `executePayment` in the NFT contract -- the ERC-20 is not directly involved. +* For a non-ERC-20 payment, the Buyer must send a protocol token (ETH) to the NFT contract, and is required to send `msg.data` encoded as an array of purchased NFTs `uint256[] tokenId`. + +#### Modified NFT Transfer Function including required Trade data to allocate Royalties + +The input parameters must satisfy several requirements for the NFT to be transferred AFTER the royalties have been properly distributed. Furthermore, the ability to transfer more than one token at a time is also considered. + +The proposal defines: +* Input parameter validation +* Payment Parameter Validation +* Distributing Royalties +* Update Royalty Account ownership with payout +* Transferring Ownership of the NFT +* Removing the Payment entry in `registeredPayment` after successful transfer + +Lastly, the approach to distributing royalties is to break down the hierarchical structure of interconnected Royalty Accounts into layers and then process one layer at time, where each relationship between a token and its ancestor is utilized to traverse the Royalty Account chain until the root ancestor and associated RA is reached. + +#### Paying out Royalties to the NFT Owner -- `from` address in `safeTransferFrom` Function + +This is the final part of the proposal. + +There are two versions of the payout function -- a `public` function and an `internal` function. + +The public function has the following interface: +``` +function royaltyPayOut (uint256 tokenId, address RAsubaccount, address payable payoutAccount, uint256 amount) public virtual nonReentrant returns (bool) +``` +where we only need the `tokenId`, the RA Sub Account address, `_RAsubaccount` which is the `owner`, and the amount to be paid out, `_amount`. Note that the function has `nonReentrant` modifier protection, because funds are being payed out. + +To finally send a Payout payment, the following steps need to be taken: +* find the RA Sub Account based on `RAaccount` and the `subaccountPos` and extract the balance +* extract `tokenType` from the Sub Account +* based on the token type, send the payout payment (not exceeding the available balance) + +### Data Structures + +#### Royalty Account and Royalty Sub Accounts + +In order to create an interconnected data structure linking NFTs to RAs that is search optimized requires to make the following additions to the global data structures of an ERC-721. + +Note, a Royalty Account is defined as a collection of Royalty Sub Accounts linked to a meta account. This meta account is comprised of general account identifiers particular to the NFT it is linked to such as asset identifier, parent identifier etc. + +**[R1]** *One or more Royalty Sub-Account MUST be linked to a Royalty Account.* + +**[R2]** *The account identifier of a Royalty Account, `raAccountId`, MUST be unique.* + +**[R3]** *The `tokenId` of a NFT MUST be linked to a `raAccountID` in order to connect an `raAccountId` to a `tokenId`.* + + +#### Print (Child) NFTs + +The set of requirement to manage Parent-Child NFT Relationships and constraints at each level of the NFT (family) tree e.g. number of children permitted, NFT parents have to be linked to their immediate NFT children are as follows. + +**[R4]** *There MUST be a link for direct parent-child relationships* + +#### NFT Payment Tokens + +In order to capture royalties, an NFT contract must be involved in NFT trading. Therefore, the NFT contract needs to be aware of NFT payments, which in turn requires the NFT contract to be aware which tokens can be used for trading. + +**[R5]** *There MUST be a listing of supported token types* + +Since the NFT contract is managing royalty distributions and payouts as well as sales, it needs to track the last available balances of the allowed token types owned by the contract. + +**[R6]** *There MUST be a link of the last validated balance of an allowed token type in the contract to the respective allowed token contract.* + +#### NFT Listings and Payments + +Since the contract is directly involved in the sales process, a capability to list one or more NFTs for sale is required. + +**[R7]** *There MUST be a list of NFTs for sale.* + +**[R8]** *A sales listing MUST have a unique identifier.* + +Besides listings, the contract is required to manage sales as well. This requires the capability to register a payment, either for immediate execution or for later payment such as in an auction situation. + +**[R9]** *There MUST be a listing for registered payments* + +**[R10]** *A registered payment MUST have a unique identifier.* + +#### Contract Constructor and Global Variables and their update functions + +This standard extends the current ERC-721 constructor, and adds several global variables to recognize the special role of the creator of an NFT, and the fact that the contract is now directly involved in managing sales and royalties. + +**[R11]** *The minimal contract constructor MUST contain the following input elements.* + +``` +/// +/// @dev Definition of the contract constructor +/// +/// @param name as in ERC-721 +/// @param symbol as in ERC-721 +/// @param baseTokenURI as in ERC-721 +/// @param allowedTokenTypes is the array of allowed tokens for payment + +constructor( + string memory name, + string memory symbol, + string memory baseTokenURI, + address[] memory allowedTokenTypes + ) ERC721(name, symbol) {...} +``` + + +### Royalty Account Management + +Below are the definitions and interfaces for the Royalty Account RUD (Read-Update-Delete) functions. Since a Royalty Account is created in the NFT minting function, there is no need to have a separate function to create a royalty account. + +#### Get a Royalty Account + +There is only one get function required because a Royalty Account and its sub accounts can be retrieved through the `tokenId` in the `ancestry` field of the Royalty Account. + +**[R12]** *The `getRoyaltyAccount` function interface MUST adhere to the definition below:* +``` +/// @dev Function to fetch a Royalty Account for a given tokenId +/// @param tokenId is the identifier of the NFT to which a Royalty Account is attached +/// @param RoyaltyAccount is a data structure containing the royalty account information +/// @param RASubAccount[] is an array of data structures containing the information of the royalty sub accounts associated with the royalty account + +function getRoyaltyAccount (uint256 tokenId) public view virtual returns (address, + RoyaltyAccount memory, + RASubAccount[] memory); +``` + + +**[R13]** *The following business rules MUST be enforced in the `getRoyaltyAccount` function:* +* *`tokenId` exists and is not burned* + +#### Update a Royalty Account + +In order to update a Royalty Account, the caller must have both the 'tokenId' and the `RoyaltyAccount` itself which can be obtained from the Royalty Account getter function. + + +**[R14]** *The `updateRoyaltyAccount` function interface MUST adhere to the definition below:* +``` +/// @dev Function to update a Royalty Account and its Sub Accounts +/// @param tokenId is the identifier of the NFT to which the Royalty Account to be updated is attached +/// @param RoyaltyAccount is the Royalty Account and associated Royalty Sub Accounts with updated values + +function updateRoyaltyAccount (uint256 _tokenId, `RoyaltyAccount memory _raAccount) public virtual returns (bool) +``` + +The update functionality of a Royalty Account, while straightforward, is also highly nuanced. To avoid complicated change control rules such as multi-signature rules, Royalty Account changes are kept simple. + +**[R15]** *The business rules for the update function are as follows:* +1. *An NFTs asset identifier MUST NOT be changed.* +2. *An NFTs ancestor MUST NOT be updated.* +3. *An NFTs token type accepted for payment MUST NOT be updated.* +4. *The royalty balance in a Royalty Sub Account MUST NOT be changed.* +5. *The royalty split inherited by the children from the NFT parent MUST NOT be changed.* +6. *New royalty split values MUST be larger than, or less than, or equal to any established boundary value for royalty splits, if it exists.* +7. *The number of existing Royalty Sub Account plus the number of new Royalty Sub Accounts to be added MUST be smaller or equal to an established boundary value, if it exists.* +8. *The sum of all royalty splits across all existing and new Royalty Sub Accounts MUST equal to 1 or its equivalent numerical value at all times.* +9. *'msg.sender` MUST be equal to an account identifier in the Royalty Sub Account of the Royalty Account to be modified and that royalty sub account must be identified as not belonging to the parent NFT* + + 9.1 *the Sub Account belonging to the account identifier MUST NOT be removed* + + 9.2 * A royalty split MUST only be decreased, and either the existing sub account's royalty split MUST be increased accordingly such that the sum of all royalty splits remains equal to 1 or its numerical equivalent, or one or more new Royalty Sub Accounts MUST be added according to rule 10.* + + 9.3 * a royalty balance MUST NOT be changed* + + 9.4 * an account identifier MUST NOT be NULL* + +10. *If `msg.sender` is equal to the account identifier of one of the Sub Account owners which is not the parent NFT, an additional Royalty Sub Accounts MAY be added* + + 10.1 *if the royalty split of the Royalty Sub Account belonging to `msg.sender` is reduced* + + 10.1.1 *then the royalty balance in each new Royalty Sub Account MUST be zero* + + 10.1.2 *the sum of the new royalty splits data MUST be equal to the royalty split of the Royalty Sub Account of `msg.sender` before it was modified* + + 10.2 *new account identifier MUST not be NULL* + +11. *If the Royalty Account update is correct, the function returns `true`, otherwise `false`.* + +#### Deleting a Royalty Account + +While sometimes deleting a Royalty Account is necessary, even convenient, it is a very costly function in terms of gas, and should not be used unless one is absolutely sure that the conditions enumerated below are met. + +**[R16]** *The `deleteRoyaltyAccount` function interface MUST adhere to the definition below:* +``` +/// @dev Function to delete a Royalty Account +/// @param tokenId is the identifier of the NFT to which the Royalty Account to be updated is attached + +function deleteRoyaltyAccount (uint256 _tokenId) public virtual returns (bool) +``` + +**[R17]** *The business rules for this function are as follows:* +* *`_tokenId` MUST be burned, i.e., have owner `address(0)`.* +* *all `tokenId` numbers genealogically related to `_tokenId` either as ancestors or offspring MUST also be burnt.* +* *all balances in the Royalty Sub Accounts MUST be zero.* + +### NFT Minting + +In extension to the ERC-721 minting capability, a Royalty Account with Royalty Sub Accounts are required to be added during the minting, besides establishing the NFT token specific data structures supporting constraints such as the maximum number of children an NFT can have. + +**[R18]** *When a new NFT is minted a Royalty Account with one or more Royalty Sub Accounts MUST be created and associated with the NFT and the NFT owner, and, if there is an ancestor, with the ancestor's Royalty Account.* + +To this end the specification utilizes the ERC-721 `_safemint` function in a newly defined `mint` function, and applies various business rules on the function's input variables. + +**[D1]** *Note, that the `mint` function SHOULD have the ability to mint more than one NFT at a time.* + +**[R19]** *Also, note that the `owner` of a new NFT MUST be the NFT contract itself.* + +**[R20]** *The non-contract owner of the NFT MUST be set as `isApproved` which allows the non-contract owner to operate just like the `owner`.* + +This strange choice in the two requirements above is necessary, because the NFT contract functions as an escrow for payments and royalties, and, hence, needs to be able to track payments received from buyers and royalties due to recipients, and to associate them with a valid `tokenId`. + +**[R21]** *For compactness of the input, and since the token meta data might vary from token to token the MUST be a minimal data structure containing:* +``` +/// @param parent is the parent tokenId of the (child) token, and if set to 0 then there is no parent. +/// @param canBeParent indicates if a tokenId can have children or not. +/// @param maxChildren defines how many children an NFT can have. +/// @param royaltySplitForItsChildren is the royalty percentage split that a child has to pay to its parent. +/// @param uri is the unique token URI of the NFT +``` + +**[R22]** *The `mint` function interface MUST adhere to the definition below:* +``` +/// @dev Function creates one or more new NFTs with its relevant meta data necessary for royalties, and a Royalty Account with its associated met data for `to` address. The tokenId(s) will be automatically assigned (and available on the emitted {IERC-721-Transfer} event). +/// @param to is the address to which the NFT(s) are minted +/// @param nfttoken is an array of struct type NFTToken for the meta data of the minted NFT(s) +/// @param tokenType is the type of allowed payment token for the NFT + +function mint(address to, NFTToken[] memory nfttoken, address tokenType) public virtual +``` + +**[R23]** *The following business rules for the `mint` function's input data MUST be fulfilled:* +- *The number of tokens to be minted MUST NOT be zero.* +- *`msg.sender` MUST have either the `MINTER_ROLE` or the `CREATOR_Role` identifying the creator of the first NFT.* +- *`to` address MUST NOT be the zero address.* +- *`to` address MUST NOT be a contract, unless it has been whitelisted -- see [Security Considerations](#security-considerations) for more details.* +- *`tokenType` MUST be a token type supported by the contract.* +- *`royaltySplitForItsChildren` MUST be less or equal to 100% or numerical equivalent thereof less any constraints such as platform fees* +- *If the new NFT(s) cannot have children, `royaltySplitForItsChildren` MUST be zero.* +- *If the new NFT(s) has a parent, the parent NFT `tokenId` MUST exist.* +- *The ancestry level of the parent MUST be less than the maximum number of allowed NFT generations, if specified.* +- *The number of allowed children for an NFT to be minted MUST be less than the maximum number of allowed children, if specified.* + +### Listing and De-Listing of NFTs for Direct Sales + +In the sales process, we need to minimally distinguish two types of transactions +- Exchange-mediated sales +- Direct sales + +The first type of transaction does not require that the smart contract is aware of a sales listing since the exchange contract will trigger payment and transfer transactions directly with the NFT contract as the owner. However, for the latter transaction type it is essential, since direct sales are required to be mediated at every step by the smart contract. + +**[R24]** *For direct sales, NFT listing, und de-listing, transactions MUST be executed through the NFT smart contract.* + +Exchange-mediated sales will be discussed when this document discusses payments. + +In direct sales, authorized user addresses can list NFTs for sale, see the business rules below. + +**[R25]** *The `listNFT` function interface MUST adhere to the definition below:* +``` +/// @dev Function to list one or more NFTs for direct sales +/// @param tokenIds is the array of tokenIds to be included in the listing +/// @param price is the price set by the owner for the listed NFT(s) +/// @param tokenType is the payment token type allowed for the listing + +function listNFT (uint256[] calldata tokenIds, uint256 price, address tokenType) public virtual returns (bool) +``` +The Boolean return value is `true` for a successful function execution, and `false` for an unsuccessful function execution. + +**[R26]** *The business rules of the `listNFT` function are as follows:* +- there MUST NOT already be a listing for one or more NFTs in the `listedNFT` mapping of the proposed listing. +- `seller` MUST be equal to `getApproved(tokenId[i])` for all NFTs in the proposed listing. +- `tokenType` MUST be supported by the smart contract. +- `price` MUST be larger than `0`. + +**[R27]** *If the conditions in [**[R26]**](#r26) are met, then the NFT sales list MUST be updated.* + +Authorized user addresses can also remove a direct sale listing of NFTs. + +**[R28]** *The `removeNFTListing` function interface MUST adhere to the definition below:* +``` +/// @dev Function to de-list one or more NFTs for direct sales +/// @param listingId is the identifier of the NFT listing + +function removeNFTListing (uint256 listingId) public virtual returns (bool) +``` +The Boolean return value is `true` for a successful function execution, and `false` for an unsuccessful function execution. + +**[R29]** *The business rules of the `removeNFTListing` function below MUST be adhered to:* +- * the registered payment entry MUST be NULL* +- *`msg.sender = getApproved(tokenId)` for the NFT listing* + +**[R30]** *If the conditions in [**[R29]**](#r29) are met, then the NFT sales listing MUST be removed.* + +### Payments for NFT Sales + +As noted before, a buyer will always pay the NFT contract directly and not the seller. The seller is paid through the royalty distribution and can later request a payout to their wallet. + +**[R31]** *The payment process requires either one or two steps:* +1. *For an ERC-20 token* + - *The buyer MUST `approve` the NFT contract for the purchase price, `payment`, for the selected payment token type.* + - *The buyer MUST call the `executePayment` function.* +2. *For a protocol token* + - *The buyer MUST call a payment fallback function with `msg.data` not NULL.* + +**[R32]** *For an ERC-20 token type, the required `executePayment` function interface MUST adhere to the definition below*: +``` +/// @dev Function to make a NFT direct sales or exchange-mediate sales payment +/// @param receiver is the address of the receiver of the payment +/// @param seller is the address of the NFT seller +/// @param tokenIds are the tokenIds of the NFT to be bought +/// @param payment is the amount of that payment to be made +/// @param tokenType is the type of payment token +/// @param trxnType is the type of payment transaction -- minimally direct sales or exchange-mediated + +function executePayment (address receiver, address seller, uint 256[] tokenIds, uint256 payment, string tokenType, int256 trxnType) public virtual nonReentrant returns (bool) +``` +The Boolean return value is `true` for a successful function execution, and `false` for an unsuccessful function execution. + +**[R33]** *Independent of `trxnType`, the business rules for the input data are as follows:* +- *All purchased NFTs in the `tokenIds` array MUST exist and MUST NOT be burned.* +- *`tokenType` MUST be a supported token.* +- *`trxnType` MUST be set to either `0` (direct sale) or `1` (exchange-mediate sale), or another supported type.* +- *`receiver` MAY be NULL but MUST NOT be the Zero Address.* +- *`seller` MUST be the address in the corresponding listing.* +- *`msg.sender` MUST not be a contract, unless it is whitelisted in the NFT contract.* + +In the following, this document will only discuss the differences between the two minimally required transaction types. + +**[R34]** *For `trxnType = 0`, the payment data MUST to be validated against the listing, based on the following rules:* +- *NFT(s) MUST be listed* +- *`payment` MUST be larger or equal to the listing price.* +- *The listed NFT(s) MUST match the NFT(s) in the payment data.* +- *The listed NFT(s) MUST be controlled by `seller`.* + +**[R35]** *If all checks in [**[R33]**](#r33), and in [**[R34]**](#r34) for `trxnType = 0`, are passed, the `executePayment` function MUST call the `transfer` function in the ERC-20 contract identified by `tokenType` with `recipient = address(this)` and `amount = payment`.* + +Note the NFT contract pays itself from the available allowance set in the `approve` transaction from the buyer. + +**[R36]** *For `trxnType = 1`, and for a successful payment, the `registeredPayment` mapping MUST updated with the payment, such that it can be validated when the NFT is transferred in a separate `safeTransferFrom` call, and `true` MUST be returned as the return value of the function, if successful, `false` otherwise.* + +**[R37]** *For `trxnType = 0`, an `internal` version of the `safeTransferFrom` function with message data MUST be called to transfer the NFTs to the buyer, and upon success, the buyer MUST be given the `MINTER_ROLE`, unless the buyer already has that role.* + +Note, the `_safeTransferFrom` function has the same structure as `safeTransferFrom` but skips the input data validation. + +**[R38]** *For `trxnType = 0`, and if the NFT transfer is successful, the listing of the NFT MUST be removed.* + +**[R39]** *For a protocol token as a payment token, and independent of `trxnType`, the buyer MUST send protocol tokens to the NFT contract as the escrow, and `msg.data` MUST encode the array of paid for NFTs `uint256[] tokenIds`.* + +**[R40]** *For the NFT contract to receive a protocol token, a payable fallback function (`fallback() external payable`) MUST be implemented.* + +Note that since the information for which NFTs the payment was for must be passed, a simple `receive()` fallback function cannot be allowed since it does not allow for `msg.data` to be sent with the transaction. + +**[R41]** *`msg.data` for the fallback function MUST minimally contain the following data: +`address memory seller, uint256[] memory _tokenId, address memory receiver, int256 memory trxnType`* + +**[R42]** *If `trxnType` is not equal to either '0' or '1', or another supported type, then the fallback function MUST `revert`.* + +**[R43]** *For `trxnType` equal to either '0' or '1', the requirements [**[R33]**](#r33) through [**[R38]**](#r38) MUST be satisfied for the fallback function to successfully execute, otherwise the fallback function MUST `revert`.* + +**[R44]** *In case of a transaction failure (for direct sales, `trxnType = 0`), or the buyer of the NFT listing changing their mind (for exchange-mediated sales, `trxnType = 1`), the submitted payment MUST be able to revert using the `reversePayment` function where the function interface is defined below:* +``` +/// @dev Definition of the function enabling the reversal of a payment before the sale is complete +/// @param paymentId is the unique identifier for which a payment was made +/// @param tokenType is the type of payment token used in the payment +function reversePayment(uint256 paymentId, string memory tokenType) public virtual returns (bool) +``` +The Boolean return value is `true` for a successful function execution, and `false` for an unsuccessful function execution. + +Note, `reentrancy` protection through e.g. `nonReentrant` from the Open Zeppelin library is strongly advised since funds are being paid out. + +**[R45]** *The business rules for the `reversePayment` function are as follows:* +- *There MUST be registered payment for a given `paymentId` and `tokenType`.* +- *`msg.sender` MUST be the buyer address in the registered payment.* +- *The payment amount must be larger than `0`.* +- *The registered payment MUST be removed when the payment has been successfully reverted, otherwise the function must fail.* + + +### Modified NFT Transfer function + +This document adheres to the ERC-721 interface format for the `safeTransferFrom` function as given below: +``` +function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) external virtual override +``` + +Note, that the input parameters must satisfy several requirements for the NFT(s) to be transferred AFTER royalties have been properly distributed. Note also, that the ability to transfer more than one token at a time is required. However, the standard interface only allows one token to be transferred at a time. In order to remain compliant with the ERC-721 standard, this document uses `tokenId` only for the first NFT to be transferred. All other transfer relevant data is encoded in `_data`. + +The high-level requirements are as follows: +* The payment parameters of the trade encoded in `_data` must be validated. +* The seller and the sold NFT token(s) must exist, and the seller must be the owner of the token. +* `msg.sender` must be the seller address or an approved address. +* the payment of the trade received by the NFT smart contract is correctly disbursed to all Royalty Sub Account owners. +* the NFT token is transferred after all Royalty Sub Accounts and their holders associated with the NFT token(s) have been properly credited. + +Also, note that in order to avoid royalty circumvention attacks, there is only one NFT transfer function. + +**[R46]** *Therefore, `transferFrom` and `safeTransferFrom` without `data` MUST be disabled.* + +This can be achieved through for example a `revert` statement in an `override` function. + +**[R47]** *The requirements on input parameters of the function are as follows*: +* *`from` MUST not be `address(0)`.* +* *`from` MUST be the owner or `approved` for `tokenId` and the other tokens included in `_data`.* +* *`from` MUST not be a smart contract unless whitelisted.* +* *a Royalty Account MUST be associated to `tokenId` and the other tokens included in `_data`.* +* *`_data` MUST NOT be NULL.* +* *`msg.sender` MUST be equal to `from` or an `approved` address, or a whitelisted contract.* + +Note, that in the context of this document only the scenario where the calling contract is still being created, i.e., the constructor being executed is a possible attack vector, and should to be carefully treated in the transfer scenario. + +Turning to the `_data` object. + +**[R48]** *The `_data` object MUST minimally contain the following payment parameters:* +* *Seller Address as `address`.* +* *Buyer Address as `address`.* +* *Receiver Address as `address.* +* *Token identifiers as `uint256[]`.* +* *Token type used for payment.* +* *Payment amount paid to NFT contract as `uint256`.* +* *a registered payment identifier.* +* *blockchain ID, `block.chainid`, of the underlying blockchain.* + +**[R49]** *The following business rules MUST be met for the payment data in '_data':* +* *`seller == from`.* +* *`tokenId[0] == tokenId`.* +* *Each token in `_tokenId` has an associated Royalty Account.* +* *`chainid == block.chainid`.* +* *`buyer` is equal to the buyer address in the registered payment for the given ``paymentId.* +* *`receiver == to`.* +* *the receiver of the token is not the seller.* +* *the receiver of the token is not a contract or is a whitelisted contract* +* *For all NFTs in the payment, `tokenId[i] = registeredPayment[paymentId].boughtTokens[i]`.* +* *`tokenType` is supported in the contract.* +* *`allowedToken[tokenType]` is not NULL.* +* *`tokenType = registeredPayment[paymentId].tokenType`.* +* *`payment > lastBalanceAllowedToken[allowedToken[listingId]]`.* +* *`payment = registeredPayment[paymentId].payment`.* + +### Distributing Royalties in the Transfer Function + +The approach to distributing royalties is to break down the hierarchical structure of interconnected Royalty Accounts into layers, and then process one layer at time, where each relationship between a NFT and its ancestor is utilized to traverse the Royalty Account chain until the root ancestor and its associated Royalty Account. + +Note, that the distribution function assumes that the payment made is for ALL tokens in the requested transfer. That means, that `payment` for the distribution function is equally divided between all NFTs included in the payment. + +**[R50]** *The `distributePayment` function interface MUST adhere to the definition below: +``` +/// @dev Function to distribute a payment as royalties to a chain of Royalty Accounts +/// @param tokenId is a tokenId included in the sale and used to look up the associated Royalty Account +/// @param payment is the payment (portion) to be distributed as royalties + +function distributePayment (uint256 tokenId, uint265 payment) internal virtual returns (bool) +``` +The Boolean return value is `true` for a successful function execution, and `false` for an unsuccessful function execution. + +As mentioned before, the internal `distributePayment` function is called within the modified `safeTransferFrom` function. + +Note, that it is necessary to multiply two `uint256` numbers with each other -- the payment amount with the royalty split percentage expressed as a whole number e.g. `10000 = 100%`. And then divide the result by the whole number representing `100%` in order to arrive at the correct application of the royalty split percentage to the payment amount. This requires careful treatment of numbers in the implementation to prevent issues such as buffer over or under runs. + +**[R51]** *The processing logic of `distributePayment` function MUST be as follows:* +* *Load the Royalty Account (`RA`) and associated Royalty Sub Accounts using the passed `tokenId`.* +* *For each Royalty Sub Account in `RA` apply the following rules:* + * *If a Royalty Sub Account in `RA` has `isIndividual` set to `true` then* + * *apply the royalty percentage of that Royalty Sub Account to `payment` and add the calculated amount, e.g. `royaltyAmountTemp`, to the `royaltybalance` of that Royalty Sub Account.* + * *emit an event as a notification of payment to the `accountId` of the Royalty Sub Account containing: assetId, accountId, tokenType, royaltybalance.* + * *in the RA add `royaltyamountTemp` amount to `balance`* + * *If a Royalty Sub Account in `RA` has `isIndividual` set to `false` then* + * *apply the royalty percentage of that Royalty Sub Account to `payment` and store temporarily in a new variable e.g. `RApaymenttemp`, but do not update the `royaltybalance` of the Royalty Sub Account which remains `0`.* + * *then use `ancestor` to obtain the `RA` connected to `ancestor` e.g. via a look up through a Royalty Account mapping.* + * *load the new RA* + * *if `isIndividual` of the Royalty Sub Account is set to `true`, pass through the Royalty Sub Accounts of the next `RA`, and apply the rule for `isIndividual = true`.* + * *if `isIndividual` of the Royalty Sub Account is set to `false`, pass through the Royalty Sub Accounts of the next `RA`, and apply the rule for `isIndividual = false`.* + * *Repeat the procedures for `isIndividual` equal to `true` and `false` until a `RA` is reached that does not have an `ancestor`, and where all Royalty Sub Accounts have`isIndividual` set to `true`, and apply the rule for a Royalty Sub Account that has `isIndividual` set to `true` to all Royalty Sub Accounts in that `RA`.* + +### Update Royalty Sub Account Ownership with Payout to approved Address (`from`) + +In order to simplify the ownership transfer, first the approved address -- the non-contract NFT owner --, `from`, is paid out its share of the royalties. And then the Royalty Sub Account is updated with the new owner, `to`. This step repeats for each token to be transferred. + +**[R52]** *The business rules are as follows:* +* *the internal version of the`royaltyPayOut` function MUST pay out the entire royalty balance of the Royalty Sub Account owned by the `from` address to the `from` address.* +* *the Royalty Sub Account MUST only be updated with the new owner only once the payout function has successfully completed and the `royaltybalance = 0`.* + +The last step in the process chain is transferring the NFTs in the purchase to the `to` address. + +**[R53]** *For every NFT (in the batch) the 'to' address MUST be `approved' (ERC-721 function) to complete the ownership transfer:* + +``` +_approve(to, tokenId[i]); +``` + +The technical NFT owner remains the NFT contract. + +### Removing the Payment Entry after successful Transfer + +Only after the real ownership of the NFT, the approved address, has been updated, the payment registry entry can be removed to allow the transferred NFTs to be sold again. + +**[R54]** *After the `approve` relationship has been successfully updated to the `to` address, the registered payment MUST be removed.* + +### Paying out Royalties to the `from` Address in `safeTransferFrom` Function + +There are two versions of the payout function -- a `public` and an `internal` function -- depending on whether there is a payout during a purchase, or a payout is requested by a Royalty Sub Account owner. + +**[R55]** *The public `royaltyPayOut` function interface MUST adhere to the definition below:* +``` +/// @dev Function to payout a royalty payment +/// @param tokenId is the identifier of the NFT token +/// @param RAsubaccount is the address of the Royalty Sub Account from which the payout should happen +/// @param receiver is the address to receive the payout +/// @param amount is the amount to be paid out + +function royaltyPayOut (uint256 tokenId, address RAsubaccount, address payable payoutAccount, uint256 amount) public virtual nonReentrant returns (bool) +``` +The Boolean return value is `true` for a successful function execution, and `false` for an unsuccessful function execution. + +Note, that the function has `reentrancy` protection through `nonReentrant` from the Open Zeppelin library since funds are being paid out. + +**[R56]** *The input parameters of the `royaltyPayOut` function MUST satisfy the following requirements:* +* *`msg.sender == RAsubaccount`.* +* *`tokenId` must exist and must not be burned.* +* *`tokenId` must be associated with a Royalty Account.* +* *`RAsubaccount` must be a valid `accountId` in a Royalty Sub Account of the Royalty Account of the `tokenId'.* +* *`isIndividual == true` for the Royalty Sub Account, `RAsubaccount`.* +* *`amount <= royaltybalance` of the Royalty Sub Account, `RAsubaccount.*` + +**[R57]** *The internal `_royaltyPayOut` function interface MUST adhere to the definition below*: +``` +function _royaltyPayOut (uint256 tokenId, address RAsubaccount, address payable payoutAccount, uint256 amount) public virtual returns (bool) +``` + +**[R58]** *The internal `_royaltyPayOut` function MUST perform the following actions: +* *send the payment to the `payoutaccount`.* +* *update the `royaltybalance` of the `RAsubaccount` of the Royalty Account upon successful transfer.* + +**[R59]** *The following steps MUST be taken to send out a royalty payment to its recipient:* +* *find the Royalty Sub Account.* +* *extract `tokenType` from the Royalty Sub Account.* +* *based on the token type send to the `payoutAccount` either* + * *'ETH' / relevant protocol token or* + * *another token based on token type* +* *and only if the payout transaction is successful, deduct `amount` from `royaltybalance` of the Royalty Sub Account,`RAsubaccount`, and then return `true` as the function return parameter, otherwise return `false`.* + +## Rationale + +Royalties for NFTs is at its core a distribution licensing problem. A buyer obtains the right to an asset/content which might or might not be reproducible, alterable etc. by the buyer or agents of the buyer. Therefore, a comprehensive specification must address a hierarchy of royalties, where one or more assets are derived from an original asset as described in the Motivation section in detail. Consequently, a design must solve for a multi-level inheritance, and thus, recursion problem. + +In order to solve for the complicated inheritance problem, this proposal design breaks down the recursive problem of the hierarchy first into a tree of depth N. And the further breaks down the tree structure into N separate problems, one for each layer. This design allows one to traverse the tree from its lowest level upwards to its root most efficiently. This is achieved with the design for the `distributePayment` function and the NFT data structures allowing for the tree structure e.g. `ancestry`,`royaltyAccount`, `RAsubaccount`. + +In order to avoid massive gas costs during the payout of royalties, possibly exceeding block gas limits for large royalty trees, the design needed to create a royalty accounting system to maintain royalty balances for recipients as done with the `royaltyAccount`, 'RAsubaccount' data structures and the associated CRUD operations, as well as require that royalty payouts are done by indvidual and by request, only, as is achieved with the `royaltyPayout` function design. + +Furthermore, the design had to ensure that in order to account for and payout royalties the smart contract must be in the "know" of all buying and selling of an NFT including the exchange of monies. This buying and selling can be either direct through the NFT contract or can be exchange-mediated as is most often the case today -- which is a centralizing factor! The chosen design for purchasing is accounting for those two modes. + +Keeping the NFT contract in the "know" at the beginning of the purchase process requires that authorized user addresses can list NFTs for sale for direct sales , whereas for exchange-mediated purchases, a payment must be registered with the NFT contract before the purchase can be completed. + +The design needed to avoid royalty circumvention during the purchase process, therefore, the NFT must be kept in the "know", a buyer will always have to pay the NFT contract directly and not the seller for both purchasing modes. The seller is subsequently paid through the royalty distribution function in the NFT contract. As a consequence, and a key design choice, and to stay compliant with ERC-721, the NFT contract must be the owner of the NFT, and the actual owner is an `approved` address. + +The specification design also needed to account for that the payment process depends on whether the payment is received in ETH or an ERC-20 token: +* ERC-20 Token + 1. The Buyer must `approve` the NFT contract for the purchase price, `payment` for the selected payment token (ERC-20 contract address). + 2. For an ERC-20 payment token, the Buyer must then call the `executePayment` in the NFT contract -- the ERC-20 is not directly involved. +* For a non-ERC-20 payment, the Buyer must send a protocol token (ETH) to the NFT contract, and is required to send encoded listing and payment information. + +In addition, the `executePayment` function had to be designed to handle both direct sales (through the NFT contract) and exchange-mediated sales which required the introduction of an indicator whether the purchase is direct or exchange-mediated. + +The `executePayment` function also has to handle the NFT transfer and purchase clean up -- removal of a listing, or removal of a registered payment, distribution of royalties, payment to the seller, and finally transfer to the seller. + +To stay compliant with the ERC-721 design but avoid royalty circumvention, all transfer functions must be disabled save the one that allows for additional information to be submitted with the function in order to manage the complicated purchase cleanup process -- `safeTransferFrom`. To ensure safety, the design enforces that input parameters must satisfy several requirements for the NFT to be transferred AFTER the royalties have been properly distributed, not before. The design accounts for the fact that we need to treat transfer somewhat differently for direct sales versus exchange mediated sales. + +Finally the specification needed to take into account that NFTs must be able to be `minted` and `burned` to maintain compliance with the ERC-721 specification while also having to set up all the data structures for the tree. + +The design enforces that when an NFT is minted, a royalty account for that NFT must be created and associated with the NFT and the NFT owner, and, if there is an ancestor of the NFT with the ancestor's royalty account to enforces the tree structure. To this end the specification utilizes the ERC-721 `_safemint` function in a newly defined `mint` function and applies various business rules on the input variables required to ensure proper set-up. + +An NFT with a royalty account can be burned. However, several things have to be true to avoid locking funds not only for the royalty account of the NFT but also its descendants, if they exist. That means that all royalties for the NFT and its descendants, if they exists, must be paid out. Furthermore, if descendants exist, they must have been burned before an ancestor can be burned. If those rules are not enforced the cleanly, the hierarchical royalty structure in part of the tree can break down and lead to lost funds, not paid out royalties etc. + + +## Backwards Compatibility +This EIP is backwards compatible to the ERC-721 standard introducing new interfaces and functionality but retaining the core interfaces and functionality of the ERC-721 standard. + +## Test Cases +A full test suite is part of the reference implementation. + +## Reference Implementation +The Treetrunk reference implementation of the standard can be found in the public treetrunkio Github repo under treetrunk-nft-reference-implementation. + +## Security Considerations +Given that this EIP introduces royalty collection, distribution, and payouts to the ERC-721 standard, the number of attack vectors increases. The most important attack vector categories and their mitigation are discussed below: + +- **Payments and Payouts**: + - Reentrancy attacks are mitigated through a reentrancy protection on all payment functions. See for example the Open Zeppelin reference implementation . + - Payouts from unauthorized accounts. Mitigation: Royalty Sub Accounts require at least that `msg.sender` is the Royalty Sub Account owner. + - Payments could get stuck in the NFT contract if the `executePayment` function fails. Mitigation: For exchange-mediated sales, a buyer can always reverse a payment with `reversePayment` if the `executePayment` function fails. For direct sales, `reversePayment` will be directly triggered in the `executePayment` function. +- **Circumventing Royalties**: + - Offchain Key exchanges + - Exchanging a private key for money off chain can not be prevented in any scenario. + - Smart Contract Wallets as NFT owners + - A Smart Contract Wallet controlled by multiple addresses could own an NFT and the owners could transfer the asset within the wallet with an off chain money exchange. Mitigation: Prohibit that Smart Contracts can own an NFT unless explicitly allowed to accommodate special scenarios such as collections. + - Denial of Royalty Disbursement + - An attacker who has purchased one or more NFTs in a given generation of an NFT family can cause out of gas errors or run time errors for the contract, if they add many spurious royalty sub-accounts with very low royalty split percentages, and then mint more prints of those purchased NFTs, and then repeat that step until the set `maxGeneration` limit is reached. An NFT trade at the bottom of the hierarchy will then require a lot of code cycles because of the recursive nature of the royalty distribution function. Mitigation: Limit the number of royalty sub-accounts per NFT and impose a royalty split percentage limit. + - Following the same approach as above but now targeting the `addListNFT` function, an attacker can force an out of gas error or run time errors in the `executePayment` function by listing many NFTs at a low price, and then performing a purchase from another account. Mitigation: Limit the number of NFTs that can be included in one listing. + - The creator of the NFT family could set the number of generations too high such that the royalty distribution function could incur and out of gas or run time error because of the recursive nature of the function. Mitigation: Limiting the `maxNumberGeneration` by the creator. + - General Considerations: The creator of an NFT family must carefully consider the business model for the NFT family and then set the parameters such as maximum number of generations, royalty sub-accounts, number of prints per print, number of NFTs in a listing, and the maximum and minimum royalty split percentage allowed. + +- **Phishing Attacks** + - NFT phishing attacks often target the `approve` and `setApprovalForAll` functions by tricking owners of NFTs to sign transactions adding the attacker account as approved for one or all NFTs of the victim. Mitigation: This contract is not vulnerable to these type of phishing attacks because all NFT transfers are sales, and the NFT contract itself is the owner of all NFTs. This means that transfers after a purchase are achieved by setting the new owner in the `_approve` function. Calling the public `approve` function will cause the function call to error out because `msg.sender` of the malicious transaction cannot be the NFT owner. + - NFT phishing attack targeting the `addListNFT` function to trick victim to list one or more NFTs at a very low price and the attacker immediately registering a payment, and executing that payment right away. Mitigation: Implement a waiting period for a purchase can be affected giving the victim time to call the `removeListNFT` function. In addition, an implementer could require Two-Factor-Authentication either built into the contract or by utilizing an authenticator app such as Google Authenticator built into a wallet software. + +Besides the usage of professional security analysis tools, it is also recommended that each implementation performs a security audit of its implementation. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4931.md b/EIPS/eip-4931.md new file mode 100644 index 0000000..5fbf719 --- /dev/null +++ b/EIPS/eip-4931.md @@ -0,0 +1,350 @@ +--- +eip: 4931 +title: Generic Token Upgrade Standard +description: Create a standard interface for upgrading ERC20 token contracts. +author: John Peterson (@John-peterson-coinbase), Roberto Bayardo (@roberto-bayardo), David Núñez (@cygnusv) +discussions-to: https://ethereum-magicians.org/t/eip-4931-generic-token-upgrade-standard/8687 +status: Stagnant +type: Standards Track +category: ERC +created: 2021-11-02 +requires: 20 +--- + + +## Abstract + +The following standard allows for the implementation of a standard API for [ERC-20](./eip-20.md) token upgrades. This standard specifies an interface that supports the conversion of tokens from one contract (called the "source token") to those from another (called the "destination token"), as well as several helper methods to provide basic information about the token upgrade (i.e. the address of the source and destination token contracts, the ratio that source will be upgraded to destination, etc.). + +## Motivation + +Token contract upgrades typically require each asset holder to exchange their old tokens for new ones using a bespoke interface provided by the developers. This standard interface will allow asset holders as well as centralized and decentralized exchanges to conduct token upgrades more efficiently since token contract upgrade scripts will be essentially reusable. Standardization will reduce the security overhead involved in verifying the functionality of the upgrade contracts. It will also provide asset issuers clear guidance on how to effectively implement a token upgrade. + +## Specification +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. + +Please Note: Methods marked with (Optional Ext.) are a part of the optional extension for downgrade functionality and may remain unimplemented if downgrade functionality is not required. +### Token Upgrade Interface Contract +``` solidity +interface IEIP4931 { +``` +#### Methods + +##### upgradeSource + +Returns the address of the original (source) token that will be upgraded. + +``` solidity +/// @dev A getter to determine the contract that is being upgraded from ("source contract") +/// @return The address of the source token contract +function upgradeSource() external view returns(address) +``` + +##### upgradeDestination + +Returns the address of the token contract that is being upgraded to. + +``` solidity +/// @dev A getter to determine the contract that is being upgraded to ("destination contract") +/// @return The address of the destination token contract +function upgradeDestination() external view returns(address) +``` + +##### isUpgradeActive + +Returns the current status of the upgrade functionality. Status MUST return `true` when the upgrade contract is functional and serving upgrades. It MUST return `false` when the upgrade contract is not currently serving upgrades. + +``` solidity +/// @dev The method will return true when the contract is serving upgrades and otherwise false +/// @return The status of the upgrade as a boolean +function isUpgradeActive() external view returns(bool) +``` +##### isDowngradeActive + +Returns the current status of the downgrade functionality. Status MUST return `true` when the upgrade contract is functional and serving downgrades. It MUST return `false` when the upgrade contract is not currently serving downgrades. When the downgrade Optional Ext. is not implemented, this method will always return `false` to signify downgrades are not available. + +``` solidity +/// @dev The method will return true when the contract is serving downgrades and otherwise false +/// @return The status of the downgrade as a boolean +function isDowngradeActive() external view returns(bool) +``` +##### ratio + +Returns the ratio of destination token to source token, expressed as a 2-tuple, that the upgrade will use. E.g. `(3, 1)` means the upgrade will provide 3 destination tokens for every 1 source token being upgraded. + +``` solidity +/// @dev A getter for the ratio of destination tokens to source tokens received when conducting an upgrade +/// @return Two uint256, the first represents the numerator while the second represents +/// the denominator of the ratio of destination tokens to source tokens allotted during the upgrade +function ratio() external view returns(uint256, uint256) +``` + +##### totalUpgraded + +Returns the total number of tokens that have been upgraded from source to destination. If the downgrade Optional Ext. is implemented, calls to `downgrade` will reduce the `totalUpgraded` return value making it possible for the value to decrease between calls. The return value will be strictly increasing if downgrades are not implemented. + +``` solidity +/// @dev A getter for the total amount of source tokens that have been upgraded to destination tokens. +/// The value may not be strictly increasing if the downgrade Optional Ext. is implemented. +/// @return The number of source tokens that have been upgraded to destination tokens +function totalUpgraded() external view returns(uint256) +``` +##### computeUpgrade + +Computes the `destinationAmount` of destination tokens that correspond to a given `sourceAmount` of source tokens, according to the predefined conversion ratio, as well as the `sourceRemainder` amount of source tokens that can't be upgraded. For example, let's consider a (3, 2) ratio, which means that 3 destination tokens are provided for every 2 source tokens; then, for a source amount of 5 tokens, `computeUpgrade(5)` must return `(6, 1)`, meaning that 6 destination tokens are expected (in this case, from 4 source tokens) and 1 source token is left as remainder. +``` solidity +/// @dev A method to mock the upgrade call determining the amount of destination tokens received from an upgrade +/// as well as the amount of source tokens that are left over as remainder +/// @param sourceAmount The amount of source tokens that will be upgraded +/// @return destinationAmount A uint256 representing the amount of destination tokens received if upgrade is called +/// @return sourceRemainder A uint256 representing the amount of source tokens left over as remainder if upgrade is called +function computeUpgrade(uint256 sourceAmount) external view + returns (uint256 destinationAmount, uint256 sourceRemainder) +``` + +##### computeDowngrade (Optional Ext.) + +Computes the `sourceAmount` of source tokens that correspond to a given `destinationAmount` of destination tokens, according to the predefined conversion ratio, as well as the `destinationRemainder` amount of destination tokens that can't be downgraded. For example, let's consider a (3, 2) ratio, which means that 3 destination tokens are provided for every 2 source tokens; for a destination amount of 13 tokens, `computeDowngrade(13)` must return `(4, 1)`, meaning that 4 source tokens are expected (in this case, from 12 destination tokens) and 1 destination token is left as remainder. +``` solidity +/// @dev A method to mock the downgrade call determining the amount of source tokens received from a downgrade +/// as well as the amount of destination tokens that are left over as remainder +/// @param destinationAmount The amount of destination tokens that will be downgraded +/// @return sourceAmount A uint256 representing the amount of source tokens received if downgrade is called +/// @return destinationRemainder A uint256 representing the amount of destination tokens left over as remainder if upgrade is called +function computeDowngrade(uint256 destinationAmount) external view + returns (uint256 sourceAmount, uint256 destinationRemainder) +``` + + +##### upgrade + +Upgrades the `amount` of source token to the destination token in the specified ratio. The destination tokens will be sent to the `_to` address. The function MUST lock the source tokens in the upgrade contract or burn them. If the downgrade Optional Ext. is implemented, the source tokens MUST be locked instead of burning. The function MUST `throw` if the caller's address does not have enough source token to upgrade or if `isUpgradeActive` is returning `false`. The function MUST also fire the `Upgrade` event. `approve` MUST be called first on the source contract. +``` solidity +/// @dev A method to conduct an upgrade from source token to destination token. +/// The call will fail if upgrade status is not true, if approve has not been called +/// on the source contract, or if sourceAmount is larger than the amount of source tokens at the msg.sender address. +/// If the ratio would cause an amount of tokens to be destroyed by rounding/truncation, the upgrade call will +/// only upgrade the nearest whole amount of source tokens returning the excess to the msg.sender address. +/// Emits the Upgrade event +/// @param _to The address the destination tokens will be sent to upon completion of the upgrade +/// @param sourceAmount The amount of source tokens that will be upgraded +function upgrade(address _to, uint256 sourceAmount) external +``` + + +##### downgrade (Optional Ext.) +Downgrades the `amount` of destination token to the source token in the specified ratio. The source tokens will be sent to the `_to` address. The function MUST unwrap the destination tokens back to the source tokens. The function MUST `throw` if the caller's address does not have enough destination token to downgrade or if `isDowngradeActive` is returning `false`. The function MUST also fire the `Downgrade` event. `approve` MUST be called first on the destination contract. +``` solidity +/// @dev A method to conduct a downgrade from destination token to source token. +/// The call will fail if downgrade status is not true, if approve has not been called +/// on the destination contract, or if destinationAmount is larger than the amount of destination tokens at the msg.sender address. +/// If the ratio would cause an amount of tokens to be destroyed by rounding/truncation, the downgrade call will only downgrade +/// the nearest whole amount of destination tokens returning the excess to the msg.sender address. +/// Emits the Downgrade event +/// @param _to The address the source tokens will be sent to upon completion of the downgrade +/// @param destinationAmount The amount of destination tokens that will be downgraded +function downgrade(address _to, uint256 destinationAmount) external +``` + +#### Events + +##### Upgrade + +MUST trigger when tokens are upgraded. + +``` solidity +/// @param _from Address that called upgrade +/// @param _to Address that destination tokens were sent to upon completion of the upgrade +/// @param sourceAmount Amount of source tokens that were upgraded +/// @param destinationAmount Amount of destination tokens sent to the _to address +event Upgrade(address indexed _from, address indexed _to, uint256 sourceAmount, uint256 destinationAmount) +``` + +##### Downgrade (Optional Ext.) + +MUST trigger when tokens are downgraded. + +``` solidity +/// @param _from Address that called downgrade +/// @param _to Address that source tokens were sent to upon completion of the downgrade +/// @param sourceAmount Amount of source tokens sent to the _to address +/// @param destinationAmount Amount of destination tokens that were downgraded +event Downgrade(address indexed _from, address indexed _to, uint256 sourceAmount, uint256 destinationAmount) +} +``` + +## Rationale +There have been several notable ERC20 upgrades (Ex. Golem: GNT -> GLM) where the upgrade functionality is written directly into the token contracts. We view this as a suboptimal approach to upgrades since it tightly couples the upgrade with the existing tokens. This EIP promotes the use of a third contract to facilitate the token upgrade to decouple the functionality of the upgrade from the functionality of the token contracts. Standardizing the upgrade functionality will allow asset holders and exchanges to write simplified reusable scripts to conduct upgrades which will reduce the overhead of conducting upgrades in the future. The interface aims to be intentionally broad leaving much of the specifics of the upgrade to the implementer, so that the token contract implementations do not interfere with the upgrade process. Finally, we hope to create a greater sense of security and validity for token upgrades by enforcing strict means of disposing of the source tokens during the upgrade. This is achieved by the specification of the `upgrade` method. The agreed upon norm is that burnable tokens shall be burned. Otherwise, tokens shall be effectively burned by being sent to the `0x00` address. When downgrade Optional Ext. is implemented, the default is instead to lock source tokens in the upgrade contract to avoid a series of consecutive calls to `upgrade` and `downgrade` from artificially inflating the supply of either token (source or destination). + +## Backwards Compatibility +There are no breaking backwards compatibility issues. There are previously implemented token upgrades that likely do not adhere to this standard. In these cases, it may be relevant for the asset issuers to communicate that their upgrade is not EIP-4931 compliant. + +## Reference Implementation +``` solidity +//SPDX-License-Identifier: Apache-2.0 +pragma solidity 0.8.9; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import "./IEIP4931.sol"; + +contract SourceUpgrade is IEIP4931 { + using SafeERC20 for IERC20; + + uint256 constant RATIO_SCALE = 10**18; + + IERC20 private source; + IERC20 private destination; + bool private upgradeStatus; + bool private downgradeStatus; + uint256 private numeratorRatio; + uint256 private denominatorRatio; + uint256 private sourceUpgradedTotal; + + mapping(address => uint256) public upgradedBalance; + + constructor(address _source, address _destination, bool _upgradeStatus, bool _downgradeStatus, uint256 _numeratorRatio, uint256 _denominatorRatio) { + require(_source != _destination, "SourceUpgrade: source and destination addresses are the same"); + require(_source != address(0), "SourceUpgrade: source address cannot be zero address"); + require(_destination != address(0), "SourceUpgrade: destination address cannot be zero address"); + require(_numeratorRatio > 0, "SourceUpgrade: numerator of ratio cannot be zero"); + require(_denominatorRatio > 0, "SourceUpgrade: denominator of ratio cannot be zero"); + + source = IERC20(_source); + destination = IERC20(_destination); + upgradeStatus = _upgradeStatus; + downgradeStatus = _downgradeStatus; + numeratorRatio = _numeratorRatio; + denominatorRatio = _denominatorRatio; + } + + /// @dev A getter to determine the contract that is being upgraded from ("source contract") + /// @return The address of the source token contract + function upgradeSource() external view returns(address) { + return address(source); + } + + /// @dev A getter to determine the contract that is being upgraded to ("destination contract") + /// @return The address of the destination token contract + function upgradeDestination() external view returns(address) { + return address(destination); + } + + /// @dev The method will return true when the contract is serving upgrades and otherwise false + /// @return The status of the upgrade as a boolean + function isUpgradeActive() external view returns(bool) { + return upgradeStatus; + } + + /// @dev The method will return true when the contract is serving downgrades and otherwise false + /// @return The status of the downgrade as a boolean + function isDowngradeActive() external view returns(bool) { + return downgradeStatus; + } + + /// @dev A getter for the ratio of destination tokens to source tokens received when conducting an upgrade + /// @return Two uint256, the first represents the numerator while the second represents + /// the denominator of the ratio of destination tokens to source tokens allotted during the upgrade + function ratio() external view returns(uint256, uint256) { + return (numeratorRatio, denominatorRatio); + } + + /// @dev A getter for the total amount of source tokens that have been upgraded to destination tokens. + /// The value may not be strictly increasing if the downgrade Optional Ext. is implemented. + /// @return The number of source tokens that have been upgraded to destination tokens + function totalUpgraded() external view returns(uint256) { + return sourceUpgradedTotal; + } + + /// @dev A method to mock the upgrade call determining the amount of destination tokens received from an upgrade + /// as well as the amount of source tokens that are left over as remainder + /// @param sourceAmount The amount of source tokens that will be upgraded + /// @return destinationAmount A uint256 representing the amount of destination tokens received if upgrade is called + /// @return sourceRemainder A uint256 representing the amount of source tokens left over as remainder if upgrade is called + function computeUpgrade(uint256 sourceAmount) + public + view + returns (uint256 destinationAmount, uint256 sourceRemainder) + { + sourceRemainder = sourceAmount % (numeratorRatio / denominatorRatio); + uint256 upgradeableAmount = sourceAmount - (sourceRemainder * RATIO_SCALE); + destinationAmount = upgradeableAmount * (numeratorRatio / denominatorRatio); + } + + /// @dev A method to mock the downgrade call determining the amount of source tokens received from a downgrade + /// as well as the amount of destination tokens that are left over as remainder + /// @param destinationAmount The amount of destination tokens that will be downgraded + /// @return sourceAmount A uint256 representing the amount of source tokens received if downgrade is called + /// @return destinationRemainder A uint256 representing the amount of destination tokens left over as remainder if upgrade is called + function computeDowngrade(uint256 destinationAmount) + public + view + returns (uint256 sourceAmount, uint256 destinationRemainder) + { + destinationRemainder = destinationAmount % (denominatorRatio / numeratorRatio); + uint256 upgradeableAmount = destinationAmount - (destinationRemainder * RATIO_SCALE); + sourceAmount = upgradeableAmount / (denominatorRatio / numeratorRatio); + } + + /// @dev A method to conduct an upgrade from source token to destination token. + /// The call will fail if upgrade status is not true, if approve has not been called + /// on the source contract, or if sourceAmount is larger than the amount of source tokens at the msg.sender address. + /// If the ratio would cause an amount of tokens to be destroyed by rounding/truncation, the upgrade call will + /// only upgrade the nearest whole amount of source tokens returning the excess to the msg.sender address. + /// Emits the Upgrade event + /// @param _to The address the destination tokens will be sent to upon completion of the upgrade + /// @param sourceAmount The amount of source tokens that will be upgraded + function upgrade(address _to, uint256 sourceAmount) external { + require(upgradeStatus == true, "SourceUpgrade: upgrade status is not active"); + (uint256 destinationAmount, uint256 sourceRemainder) = computeUpgrade(sourceAmount); + sourceAmount -= sourceRemainder; + require(sourceAmount > 0, "SourceUpgrade: disallow conversions of zero value"); + + upgradedBalance[msg.sender] += sourceAmount; + source.safeTransferFrom( + msg.sender, + address(this), + sourceAmount + ); + destination.safeTransfer(_to, destinationAmount); + sourceUpgradedTotal += sourceAmount; + emit Upgrade(msg.sender, _to, sourceAmount, destinationAmount); + } + + /// @dev A method to conduct a downgrade from destination token to source token. + /// The call will fail if downgrade status is not true, if approve has not been called + /// on the destination contract, or if destinationAmount is larger than the amount of destination tokens at the msg.sender address. + /// If the ratio would cause an amount of tokens to be destroyed by rounding/truncation, the downgrade call will only downgrade + /// the nearest whole amount of destination tokens returning the excess to the msg.sender address. + /// Emits the Downgrade event + /// @param _to The address the source tokens will be sent to upon completion of the downgrade + /// @param destinationAmount The amount of destination tokens that will be downgraded + function downgrade(address _to, uint256 destinationAmount) external { + require(upgradeStatus == true, "SourceUpgrade: upgrade status is not active"); + (uint256 sourceAmount, uint256 destinationRemainder) = computeDowngrade(destinationAmount); + destinationAmount -= destinationRemainder; + require(destinationAmount > 0, "SourceUpgrade: disallow conversions of zero value"); + require(upgradedBalance[msg.sender] >= sourceAmount, + "SourceUpgrade: can not downgrade more than previously upgraded" + ); + + upgradedBalance[msg.sender] -= sourceAmount; + destination.safeTransferFrom( + msg.sender, + address(this), + destinationAmount + ); + source.safeTransfer(_to, sourceAmount); + sourceUpgradedTotal -= sourceAmount; + emit Downgrade(msg.sender, _to, sourceAmount, destinationAmount); + } +} +``` + + +## Security Considerations +The main security consideration is ensuring the implementation of the interface handles the source tokens during the upgrade in such a way that they are no longer accessible. Without careful handling, the validity of the upgrade may come into question since source tokens could potentially be upgraded multiple times. This is why EIP-4931 will strictly enforce the use of `burn` for source tokens that are burnable. For non-burnable tokens, the accepted method is to send the source tokens to the `0x00` address. When the downgrade Optional Ext. is implemented, the constraint will be relaxed, so that the source tokens can be held by the upgrade contract. + +## Copyright +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). + diff --git a/EIPS/eip-4938.md b/EIPS/eip-4938.md new file mode 100644 index 0000000..2879fca --- /dev/null +++ b/EIPS/eip-4938.md @@ -0,0 +1,52 @@ +--- +eip: 4938 +title: "eth/67 - Removal of GetNodeData" +description: "Remove GetNodeData and NodeData messages from the wire protocol" +author: Marius van der Wijden (@MariusVanDerWijden), Felix Lange , Gary Rong +discussions-to: https://ethereum-magicians.org/t/eip-4938-removal-of-getnodedata/8893 +status: Review +type: Standards Track +category: Networking +created: 2022-03-23 +requires: 2464, 2481 +--- + +## Abstract + +The [Ethereum Wire Protocol](https://github.com/ethereum/devp2p/blob/40ab248bf7e017e83cc9812a4e048446709623e8/caps/eth.md) defines request and response messages for exchanging data between clients. The `GetNodeData` request retrieves a set of trie nodes or contract code from the state trie by hash. We propose to remove the `GetNodeData` and `NodeData` messages from the wire protocol. + +## Motivation + +`GetNodeData` and `NodeData` were introduced in protocol version `eth/63` to allow for a sync mode called "fast sync", which downloads the Ethereum state without executing all blocks. The sync algorithm works by requesting all state trie nodes and contract codes by their hash. + +Serving `GetNodeData` requests requires clients to store state as a mapping of hashes to trie nodes. Avoiding the need to store such a mapping in the database is the main motivation for removing this request type. + +At this time, some client implementations cannot serve `GetNodeData` requests because they do not store the state in a compatible way. The Ethereum Wire Protocol should accurately reflect the capabilities of clients, and should not contain messages which are impossible to implement in some clients. + +## Specification + +Remove the following message types from the `eth` protocol: + +* `GetNodeData (0x0d)` + * **(eth/66)**: `[request_id: P, [hash_0: B_32, hash_1: B_32, ...]]` +* `NodeData (0x0e)` + * **(eth/66)**: `[request_id: P, [value_0: B, value_1: B, ...]]` + +## Rationale + +A replacement for `GetNodeData` is available in the [snap protocol](https://github.com/ethereum/devp2p/blob/40ab248bf7e017e83cc9812a4e048446709623e8/caps/snap.md). Specifically, clients can use the [GetByteCodes](https://github.com/ethereum/devp2p/blob/40ab248bf7e017e83cc9812a4e048446709623e8/caps/snap.md#getbytecodes-0x04) and [GetTrieNodes](https://github.com/ethereum/devp2p/blob/40ab248bf7e017e83cc9812a4e048446709623e8/caps/snap.md#gettrienodes-0x06) messages instead of `GetNodeData`. The snap protocol can be used to implement the "fast sync" algorithm, though it is recommended to use it for "snap sync". + +## Backwards Compatibility + +This EIP changes the `eth` protocol and requires rolling out a new version, `eth/67`. Supporting multiple versions of a wire protocol is possible. Rolling out a new version does not break older clients immediately, since they can keep using protocol version `eth/66`. + +This EIP does not change consensus rules of the EVM and does not require a hard fork. + +## Security Considerations + +None + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). + diff --git a/EIPS/eip-4944.md b/EIPS/eip-4944.md new file mode 100644 index 0000000..9a6c451 --- /dev/null +++ b/EIPS/eip-4944.md @@ -0,0 +1,82 @@ +--- +eip: 4944 +title: Contract with Exactly One Non-fungible Token +description: An ERC-721 compatible single-token NFT +author: Víctor Muñoz (@victormunoz), Josep Lluis de la Rosa (@peplluis7), Andres El-Fakdi (@Bluezfish) +discussions-to: https://ethereum-magicians.org/t/erc721-minting-only-one-token/8602/2 +status: Draft +type: Standards Track +category: ERC +created: 2022-03-25 +requires: 721 +--- + +## Abstract + +The following describes standard functions for an [ERC-721](./eip-721.md) compatible contract with a total supply of one. +This allows an NFT to be associated uniquely with a single contract address. + +## Motivation + +If the ERC-721 was modified to mint only 1 token (per contract), then the contract address could be identified uniquely with that minted token (instead of the tuple contract address + token id, as ERC-721 requires). +This change would enable automatically all the capabilities of composable tokens [ERC-998](./eip-998.md) (own other ERC-721 or [ERC-20](./eip-20.md)) natively without adding any extra code, just forbidding to mint more than one token per deployed contract. +Then the NFT minted with this contract could operate with his "budget" (the ERC-20 he owned) and also trade with the other NFTs he could own. Just like an autonomous agent, that could decide what to do with his properties (sell his NFTs, buy other NFTs, etc). + +The first use case that is devised is for value preservation. Digital assets, as NFTs, have value that has to be preserved in order to not be lost. If the asset has its own budget (in other ERC-20 coins), could use it to autopreserve itself. + +## Specification + +The constructor should mint the unique token of the contract, and then the mint function should add a restriction to avoid further minting. + +Also, a `tokenTransfer` function should be added in order to allow the contract owner to transact with the ERC-20 tokens owned by the contract/NFT itself. So that if the contract receives a transfer of ERC-20 tokens, the owner of the NFT could spend it from the contract wallet. + +## Rationale + +The main motivation is to keep the contract compatible with current ERC-721 platforms. + +## Backwards Compatibility + +There are no backwards compatibility issues. + +## Reference Implementation + +Add the variable `_minted` in the contract: + +``` solidity + bool private _minted; +``` + +In the constructor, automint the first token and set the variable to true: + +``` solidity + constructor(string memory name, string memory symbol, string memory base_uri) ERC721(name, symbol) { + baseUri = base_uri; + mint(msg.sender,0); + _minted = true; + } +``` + +Add additional functions to interact with the NFT properties (for instance, ERC-20): + +``` solidity + modifier onlyOwner() { + require(balanceOf(msg.sender) > 0, "Caller is not the owner of the NFT"); + _; + } + + function transferTokens(IERC20 token, address recipient, uint256 amount) public virtual onlyOwner { + token.transfer(recipient, amount); + } + + function balanceTokens(IERC20 token) public view virtual returns (uint256) { + return token.balanceOf(address(this)); + } +``` + +## Security Considerations + +No security issues found. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4950.md b/EIPS/eip-4950.md new file mode 100644 index 0000000..4088221 --- /dev/null +++ b/EIPS/eip-4950.md @@ -0,0 +1,93 @@ +--- +eip: 4950 +title: Entangled Tokens +description: ERC-721 extension with two tokens minted that are tied together +author: Víctor Muñoz (@victormunoz), Josep Lluis de la Rosa (@peplluis7), Easy Innova (@easyinnova) +discussions-to: https://ethereum-magicians.org/t/entangled-tokens/8702 +status: Draft +type: Standards Track +category: ERC +created: 2022-03-28 +requires: 20, 721, 1155 +--- + +## Abstract + +This EIP defines an interface for delegating control of a smart contract wallet to pairs of users using entangled [ERC-721](./eip-721.md) non-fungible tokens. + +## Motivation + +The motivation is to provide an easy way to share a wallet through NFTs, so that the act of buying an NFT (in a marketplace) gives the buyer the privilege to have access to a given wallet. This wallet could have budget in many tokens, or even be the owner of other NFTs. + +A use case is to keep contact between an artist and an buyer of its NFTs. If an artist T has created a digital piece of art P with an NFT, then T creates 2 entangled tokens A and B so that he keeps A and transfer B to P. By construction of entangled tokens, only one transfer is possible for them, thus the artist proofs he’s been the creator of P by sending a transaction to A that is visible from B. Otherwise, the owner of P might check the authenticity of the artist by sending a transaction to B so that the artist might proof by showing the outcome out of A. + +A version of this use case is when one user U mints his piece of art directly in the form of an entangled token A; then the user U sells/transfers it while keeping the entangled token B in the U's wallet. The piece of art and the artists will be entangled whoever is the A's owner. + +These applications of entangled tokens are envisaged to be useful for: + +1. NFT authorship / art creation +2. Distribution of royalties by the creator. +3. Authenticity of a work of art: creation limited to the author (e.g. only 1000 copies if there are 1000 1000 entangled tokens in that NFT). +4. Usowners (users that consume an NFT also become -partial- owners of the NFT) +5. Reformulation of property rights: the one who owns the property receives it without having to follow in the footsteps of the owners. +6. Identity: Only those credentials that have an entangled token with you are related to you. +7. Vreservers (value-reservers). + +## Specification + +An entangled token contract implements [ERC-721](./eip-721.md) with the additional restriction that it only ever mints exactly two tokens at contract deployment: one with a `tokenId` of `0`, the other with a `tokenId` of `1`. The entangled token contract also implements a smart contract wallet that can be operated by the owners of those two tokens. + +Also, a `tokenTransfer` function is to be be added in order to allow the token owners to transact with the [ERC-20](./eip-20.md) tokens owned by the contract/NFT itself. The function signature is as follows: + +```solidity + function tokenTransfer(IERC20 token, address recipient, uint256 amount) public onlyOwners; +``` + +## Rationale + +We decide to extend [ERC-721](./eip-721.md) ([ERC-1155](./eip-1155.md) could be also possible) because the main purpose of this is to be compatible with current marketplaces platforms. This entangled NFTs will be listed in a marketplace, and the user who buys it will have then the possibility to transact with the wallet properties (fungible and non fungible tokens). + +## Backwards Compatibility + +No backwards compatibility issues. + +## Reference Implementation + +Mint two tokens, and only two, at the contract constructor, and set the `minted` property to true: + +```solidity +bool private _minted; + +constructor(string memory name, string memory symbol, string memory base_uri) ERC721(name, symbol) { + baseUri = base_uri; + _mint(msg.sender,0); + _mint(msg.sender,1); + _minted = true; + } + +function _mint(address to, uint256 tokenId) internal virtual override { + require(!_minted, "ERC4950: already minted"); + super._mint(to, tokenId); +} +``` + +Add additional functions to allow both NFT user owners to operate with other ERC-20 tokens owned by the contract: + +```solidity + modifier onlyOwners() { + require(balanceOf(msg.sender) > 0, "Caller does not own any of the tokens"); + _; + } + +function tokenTransfer(IERC20 token, address recipient, uint256 amount) public onlyOwners { + token.transfer(recipient, amount); + } +``` + +## Security Considerations + +There are no security considerations. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4955.md b/EIPS/eip-4955.md new file mode 100644 index 0000000..ee650eb --- /dev/null +++ b/EIPS/eip-4955.md @@ -0,0 +1,198 @@ +--- +eip: 4955 +title: Vendor Metadata Extension for NFTs +description: Add a new field to NFT metadata to store vendor specific data +author: Ignacio Mazzara (@nachomazzara) +discussions-to: https://ethereum-magicians.org/t/eip-4955-non-fungible-token-metadata-namespaces-extension/8746 +status: Final +type: Standards Track +category: ERC +created: 2022-03-29 +requires: 721, 1155 +--- + +## Abstract + +This EIP standardizes a schema for NFTs metadata to add new field namespaces to the JSON schema for [EIP-721](./eip-721.md) and [EIP-1155](./eip-1155.md) NFTs. + +## Motivation + +A standardized NFT metadata schema allows wallets, marketplaces, metaverses, and sililar applications to interoperate with any NFT. Applications such as NFT marketplaces and metaverses could usefully leverage NFTs by rendering them using custom 3D representations or any other new attributes. + +Some projects like Decentraland, TheSandbox, Cryptoavatars, etc. need their own 3D model in order to represent an NFT. These models are not cross-compatible because of distinct aesthetics and data formats. + +## Specification + +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. + +### Schema + +(subject to "caveats" below) + +A new property called `namespaces` is introduced. This property expects one object per project as shown in the example below. + +```jsonc +{ + "title": "Asset Metadata", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Identifies the asset that this NFT represents" + }, + "description": { + "type": "string", + "description": "Describes the asset that this NFT represents" + }, + "image": { + "type": "string", + "description": "A URI pointing to a resource with mime type image/* representing the asset that this NFT represents. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive." + }, + "namespaces": { + "type": "object", + "description": "Application-specific NFT properties" + } + } +} +``` + +### Example + +```jsonc +{ + "name": "My NFT", + "description": "NFT description", + "image": "ipfs://QmZfmRZHuawJDtDVMaEaPWfgWFV9iXoS9SzLvwX76wm6pa", + "namespaces": { + "myAwesomeCompany": { + "prop1": "value1", + "prop2": "value2", + }, + "myAwesomeCompany2": { + "prop3": "value3", + "prop4": "value4", + }, + } +} + +// Or by simply using a `URI` to reduce the size of the JSON response. + +{ + "name": "My NFT", + "description": "NFT description", + "image": "ipfs://QmZfmRZHuawJDtDVMaEaPWfgWFV9iXoS9SzLvwX76wm6pa", + "namespaces": { + "myAwesomeCompany": "URI", + "myAwesomeCompany2": "URI", + } +} +``` + +## Rationale + +There are many projects which need custom properties in order to display a current NFT. Each project may have its own way to render the NFTs and therefore they need different values. An example of this is the metaverses like Decentraland or TheSandbox where they need different 3d models to render the NFT based on the visual/engine of each. NFTs projects like Cryptopunks, Bored Apes, etc. can create the 3d models needed for each project and therefore be supported out of the box. + +The main differences between the projects that are rendering 3d NFTs (models) are: + +### Armatures + +Every metaverse uses its own armature. There is a standard for humanoids but it is not being used for every metaverse and not all the metaverses use humanoids. For example, Decentraland has a different aesthetic than Cryptovoxels and TheSandbox. It means that every metaverse will need a different model and they may have the same extension (GLB, GLTF) + +![](../assets/eip-4955/different-renders.jpeg) + +### Metadata (Representations Files) + +For example, every metaverse uses its own metadata representation files to make it work inside the engine depending on its game needs. + +This is the JSON config of a wearable item in Decentraland: + +```jsonc +"data": { + "replaces": [], + "hides": [], + "tags": [], + "category": "upper_body", + "representations": [ + { + "bodyShapes": [ + "urn:decentraland:off-chain:base-avatars:BaseMale" + ], + "mainFile": "male/Look6_Tshirt_A.glb", + "contents": [ + { + "key": "male/Look6_Tshirt_A.glb", + "url": "https://peer-ec2.decentraland.org/content/contents/QmX3yMhmx4AvGmyF3CM5ycSQB4F99zXh9rL5GvdxTTcoCR" + } + ], + "overrideHides": [], + "overrideReplaces": [] + }, + { + "bodyShapes": [ + "urn:decentraland:off-chain:base-avatars:BaseFemale" + ], + "mainFile": "female/Look6_Tshirt_B (1).glb", + "contents": [ + { + "key": "female/Look6_Tshirt_B (1).glb", + "url": "https://peer-ec2.decentraland.org/content/contents/QmcgddP4L8CEKfpJ4cSZhswKownnYnpwEP4eYgTxmFdav8" + } + ], + "overrideHides": [], + "overrideReplaces": [] + } + ] +}, +"image": "https://peer-ec2.decentraland.org/content/contents/QmPnzQZWAMP4Grnq6phVteLzHeNxdmbRhKuFKqhHyVMqrK", +"thumbnail": "https://peer-ec2.decentraland.org/content/contents/QmcnBFjhyFShGo9gWk2ETbMRDudiX7yjn282djYCAjoMuL", +"metrics": { + "triangles": 3400, + "materials": 2, + "textures": 2, + "meshes": 2, + "bodies": 2, + "entities": 1 +} +``` + +`replaces`, `overrides`, `hides`, and different body shapes representation for the same asset are needed for Decentraland in order to render the 3D asset correctly. + +Using `namespaces` instead of objects like the ones below make it easy for the specific vendor/third-parties to access and index the required models. Moreover, `styles` do not exist because there are no standards around for how an asset will be rendered. As I mentioned above, each metaverse for example uses its own armature and aesthetic. There is no Decentraland-style or TheSandbox-style that other metaverses use. Each of them is unique and specific for the sake of the platform's reason of being. Projects like Cryptoavatars are trying to push different standards but without luck for the same reasons related to the uniquity of the armature/animations/metadata. + +```jsonc +{ + "id": "model", + "type": "model/gltf+json", + "style": "Decentraland", + "uri": "..." +}, + +// Or + +{ + "id": "model", + "type": "model/gltf+json", + "style": "humanoide", + "uri": "..." +}, +``` + +With `namespaces`, each vendor will know how to render an asset by doing: + +```ts +fetch(metadata.namespaces["PROJECT_NAME"].uri).then(res => render(res)) +``` + +The idea behind extending the [EIP-721](./eip-721.md) metadata schema is for backward compatibility. Most projects on Ethereum use non-upgradeable contracts. If this EIP required new implementations of those contracts, they would have to be re-deployed. This is time-consuming and wastes money. Leveraging EIP-721's existing metadata field minimizes the number of changes necessary. Finally, the JSON metadata is already used to store representations using the `image` field. It seems reasonable to have all the representations of an asset in the same place. + +## Backwards Compatibility + +Existing projects that can't modify the metadata response (schema), may be able to create a new smart contract that based on the `tokenId` returns the updated metadata schema. Of course, the projects may need to accept these linked smart contracts as valid in order to fetch the metadata by the `tokenURI` function. + +## Security Considerations + +The same security considerations as with [EIP-721](./eip-721.md) apply related to using http gateways or IPFS for the tokenURI method. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4972.md b/EIPS/eip-4972.md new file mode 100644 index 0000000..4ca1ed8 --- /dev/null +++ b/EIPS/eip-4972.md @@ -0,0 +1,162 @@ +--- +eip: 4972 +title: Name-Owned Account +description: Name-Owned Account for Social Identity +author: Shu Dong (@dongshu2013), Qi Zhou (@qizhou) +discussions-to: https://ethereum-magicians.org/t/eip-4972-name-owned-account/8822 +status: Stagnant +type: Standards Track +category: ERC +created: 2022-04-04 +requires: 20, 721 +--- + +## Abstract + +This ERC proposes a new type of account - name-owned account (NOA) that is controlled by the owner of the name besides existing externally-owned account (EOA) and contract account (CA). With the new account type, users will be able to transfer/receive tokens using the name-derived address directly instead of the address of the name owner. A NOA can be as a social identity with all states on-chain even under 3rd-party or self custody. It also simplifies porting the social identity from one custody to another. + +## Motivation + +A popular way to onboard Web2 users to the Web3 world is custody. However, current custody models have severe drawbacks. Considering the following widely adopted custody models: +1. The custodian uses one EOA/CA to hold the assets of all users. This is not compatible with on-chain social protocols since all user activities are off-chain. +2. One EOA per user. The social identity is not portable, which means there is no way for users to migrate their social identity across different custody platforms. +3. One CA (e.g. Gnosis Safe) per user. The one time deployment cost is super high and the user experience is not good. + +To solve all these problems, this ERC proposes a new type of account - name-owned account (NOA). Using NOA as social identity instead of EOA/CA brings huge benefits for users: +- **Easy Web2 user onboarding**. We are providing standard Web2 user experiences with human readable names and 3rd-party custody. Every user of a centralized platform can immediately have a NOA by using the username as the name of NOA custodied by the platform. +- **On-chain states**. All user states are on-chain even under custody so it’s 100% compatible with social protocols. +- **Portable Account**. A NOA can be easily ported to different custody platforms by changing the owner. +- **Flexible Account Management**. We can use one EOA/CA to control any number of NOAs. + +## Specification + +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. + +### Name-Owned Account + +An NOA has +1. a name for social identity; and +2. an address derived from the name to receive tokens; and +3. owner(s) of the name that can transfer the token. + +The name should be human-readable and can be easily recognized socially. An example is the username of a centralized platform such as FB, Twitter. The name-derived address (NDA) is a normal Ethereum address that should not collide with the existing addresses of EOA/CA. Since we cannot use NDA as msg.sender, the right to transfer the tokens of the NDA is controlled by the owner/owners of the name. The name to owner/owners mapping is managed by an on-chain name service, and the owner/owners are EOA/CA, which can be the addresses of 3-rd custodians (e.g. FB) or self-custodian. By changing the owner of the name to the EOA of the user (can be done by requesting the custodian), the NDA becomes self-custodian, and no one should be able to transfer the assets unless the approved by the self-custodian user. + + +### Name Representation + +A name is represented by a bytes array which is ABI encoded. It **MAY** contain metadata of the name such as the name service the name belongs to. Examples of the name are "vitalik.eth", "vitalik@0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e", or "qizhou.fb". + +### Interface +#### INameOwnedAccount +```solidity +interface INameOwnedAccount { + /// @notice This function resolves the _name to its derived address + /// @dev The implementation SHOULD avoid collision between name + /// derived address and EOA/CA + function addressOfName(bytes memory _name) public view returns(address); + + /// @notice This function returns true if and only if the operator is the owner of the _name + /// @dev The ownership MAY be defined by a name service such as ENS + function isNameOwner(bytes memory _from, address operator) public view returns(bool); +} +``` + +#### `IERC721NOA` + +```solidity +interface IERC721NOA is IERC721, INameOwnedAccount { + /// @notice Transfers the ownership of an NFT from a name to an address + /// @dev Throws unless `msg.sender` is the owner of _from. Throw if _from is + /// not the current owner. Throws if `_to` is the zero address. Throws if + /// `_tokenId` is not a valid NFT. When transfer is complete, this function + /// checks if `_to` is a smart contract (code size > 0). If so, it calls + /// `onERC721Received` on `_to` and throws if the return value is not + /// `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`. + function safeTransferFromName(bytes memory _from, address _to, uint256 _tokenId, bytes _data) public returns(bool); + + /// @notice Transfers the ownership of an NFT from a name to another address + /// @dev This works identically to the other function with an extra data parameter, + /// except this function just sets data to "". + function safeTransferFromName(bytes memory _from, address _to, uint256 _tokenId) public returns(bool); + + /// @notice Change or reaffirm the approved address for an NFT + /// @dev The zero address indicates there is no approved address. + /// Throws unless `msg.sender` is the owner of _owner. Throw if _owner is not + /// the current owner. + function approveFromName(bytes memory _owner, address _operator, uint256 _tokenId) public returns(bool); + + /// @notice Enable or disable approval for a third party ("operator") to manage + /// all of _owner’s assets + /// @dev Throws unless `msg.sender` is the owner of _owner. Throw if _owner is not + /// the current owner. Emits the ApprovalForAll event. The contract MUST allow + /// multiple operators per owner. + function setApprovalForAllFromName(bytes memory _owner, address _operator, bool _approved) public returns(bool); + + /// @notice This function returns true if interfaceId is the id of IERC721NOA + /// @dev see {IERC165-supportsInterface} + function supportsInterface(bytes4 interfaceId) external view returns(bool); +} +``` + +#### `IERC20NOA` + +```solidity +interface IERC20NOA is IERC20, INameOwnedAccount { + /// @notice Transfers _value amount of tokens from name _from to address _to, + /// @dev Throws unless `msg.sender` is the owner of _owner. Throw if _owner is not + /// the current owner. Throw if the balance of _from does not have enough tokens to + /// spend. Emits the Transfer event. + function transferFromName(bytes memory _from, address _to, uint256 _value) public returns(bool); + + /// @notice Allows _spender to withdraw from _owner multiple times, up to + /// the _value amount. + /// @dev Throws unless `msg.sender` is the owner of _owner. Throw if _owner is + /// not the current owner. If this function is called again it overwrites the current + /// allowance with _value. + function approveFromName(bytes memory _owner, address _spender, uint256 _value) public returns(bool); + + /// @notice This function returns true if interfaceId is the id of IERC721NOA + /// @dev see {IERC165-supportsInterface} + function supportsInterface(bytes4 interfaceId) external view returns(bool); +} +``` + +### Authentication + +The transfer and approve function is authenticated if and only if the message sender is the owner of the name. + +## Rationale + +We use bytes array to represent a name to ensure it’s flexible enough to deal with different use cases. E.g. one can encode the name service contract address the name belongs to into the bytes array. One can also encode extra authentication data, such as zero knowledge proofs, into the bytes array. In the future, we may propose a standard to formalize the name for wider adoption. + +The isNameOwner function is sufficient for authenticating the message sender. One can verify the owner by looking up the name owner from a name service, or check zero knowledge proofs encoded in name to prove the ownership directly without looking up anything. + +The addressOfName interface decouples the implementation from specific hashing algorithms, as long as the generated address doesn’t collide with EOA/CA address space. + +## Backwards Compatibility + +The new account type is compatible with existing ERC token standards. + +## Reference Implementation +### Name Format + +The decoded format of bytes name is not defined at this standard. One straightforward implementation would be: +```solidity +bytes memory name = abi.encode((string, ‘address’), (username, nameService)) +``` +where the username is the string representation of the username and nameService is the name service contract address. This will decouple the implementation from specific name services such as ENS. + +### Name Derived Address (INameOwnedAccount.addressOfName()) + +With the bytes format mentioned above, we can follow the similar rule of CREATE2 opcode to compute the NOA address from nameService and hash of the username as `address(keccak256(0xff, keccak256(“eip-4972.addressOfName”), nameService, keccak256(username)))`. This can ensure it won’t collide with existing smart contract account addresses. + +### Ownership of a Name (INameOwnedAccount.isNameOwner()) + +Normally we can get the owner from the name service and compare it with the message sender. We recommend the name service to define an owner function in the same format as ENS. + +## Security Considerations + +No security considerations were found. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4973.md b/EIPS/eip-4973.md new file mode 100644 index 0000000..7a8f4e8 --- /dev/null +++ b/EIPS/eip-4973.md @@ -0,0 +1,197 @@ +--- +eip: 4973 +title: Account-bound Tokens +description: An interface for non-transferrable NFTs binding to an Ethereum account like a legendary World of Warcraft item binds to a character. +author: Tim Daubenschütz (@TimDaub) +discussions-to: https://ethereum-magicians.org/t/eip-4973-non-transferrable-non-fungible-tokens-soulbound-tokens-or-badges/8825 +status: Review +type: Standards Track +category: ERC +created: 2022-04-01 +requires: 165, 712, 721, 1271 +--- + +## Abstract + +Proposes a standard API for account-bound Tokens (ABT) within smart contracts. An ABT is a non-fungible token bound to a single account. ABTs don't implement a canonical interface for transfers. This EIP defines basic functionality to mint, assign, revoke and track ABTs. + +## Motivation + +In the popular MMORPG World of Warcraft, its game designers intentionally took some items out of the world's auction house market system to prevent them from having a publicly-discovered price and limit their accessibility. + +Vanilla WoW's "Thunderfury, Blessed Blade of the Windseeker" was one such legendary item, and it required a forty-person raid, among other sub-tasks, to slay the firelord "Ragnaros" to gain the "Essence of the Firelord," a material needed to craft the sword once. + +Upon voluntary pickup, the sword permanently **binds** to a character's "soul," making it impossible to trade, sell or even swap it between a player's characters. + +In other words, "Thunderfury"'s price was the aggregate of all social costs related to completing the difficult quest line with friends and guild members. Other players spotting Thunderfuries could be sure their owner had slain "Ragnaros," the blistering firelord. + +World of Warcraft players could **trash** legendary and soulbound items like the Thunderfury to permanently remove them from their account. It was their choice to visibly **equip** or **unequip** an item and hence show their achievements to everyone. + +The Ethereum community has expressed a need for non-transferrable, non-fungible, and socially-priced tokens similar to WoW's soulbound items. Popular contracts implicitly implement account-bound interaction rights today. A principled standardization helps interoperability and improves on-chain data indexing. + +The purpose of this document is to make ABTs a reality on Ethereum by creating consensus around a **maximally backward-compatible** but otherwise **minimal** interface definition. + +## Specification + +### Solidity Interface + +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. + +ABTs _must_ implement the interfaces: + +- [EIP-165](./eip-165.md)'s `ERC165` (`0x01ffc9a7`) +- [EIP-721](./eip-721.md)'s `ERC721Metadata` (`0x5b5e139f`) + +ABTs _must not_ implement the interfaces: + +- [EIP-721](./eip-721.md)'s `ERC721` (`0x80ac58cd`) + +An ABT receiver must be able to always call `function unequip(address _tokenId)` to take their ABT off-chain. + +```solidity +// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.6; + +/// @title Account-bound tokens +/// @dev See https://eips.ethereum.org/EIPS/eip-4973 +/// Note: the ERC-165 identifier for this interface is 0xeb72bb7c +interface IERC4973 { + /// @dev This emits when ownership of any ABT changes by any mechanism. + /// This event emits when ABTs are given or equipped and unequipped + /// (`to` == 0). + event Transfer( + address indexed from, address indexed to, uint256 indexed tokenId + ); + /// @notice Count all ABTs assigned to an owner + /// @dev ABTs assigned to the zero address are considered invalid, and this + /// function throws for queries about the zero address. + /// @param owner An address for whom to query the balance + /// @return The number of ABTs owned by `address owner`, possibly zero + + function balanceOf(address owner) external view returns (uint256); + /// @notice Find the address bound to an ERC4973 account-bound token + /// @dev ABTs assigned to zero address are considered invalid, and queries + /// about them do throw. + /// @param tokenId The identifier for an ABT. + /// @return The address of the owner bound to the ABT. + function ownerOf(uint256 tokenId) external view returns (address); + /// @notice Removes the `uint256 tokenId` from an account. At any time, an + /// ABT receiver must be able to disassociate themselves from an ABT + /// publicly through calling this function. After successfully executing this + /// function, given the parameters for calling `function give` or + /// `function take` a token must be re-equipable. + /// @dev Must emit a `event Transfer` with the `address to` field pointing to + /// the zero address. + /// @param tokenId The identifier for an ABT. + function unequip(uint256 tokenId) external; + /// @notice Creates and transfers the ownership of an ABT from the + /// transaction's `msg.sender` to `address to`. + /// @dev Throws unless `bytes signature` represents a signature of the + // EIP-712 structured data hash + /// `Agreement(address active,address passive,bytes metadata)` expressing + /// `address to`'s explicit agreement to be publicly associated with + /// `msg.sender` and `bytes metadata`. A unique `uint256 tokenId` must be + /// generated by type-casting the `bytes32` EIP-712 structured data hash to a + /// `uint256`. If `bytes signature` is empty or `address to` is a contract, + /// an EIP-1271-compatible call to `function isValidSignatureNow(...)` must + /// be made to `address to`. A successful execution must result in the + /// `event Transfer(msg.sender, to, tokenId)`. Once an ABT exists as an + /// `uint256 tokenId` in the contract, `function give(...)` must throw. + /// @param to The receiver of the ABT. + /// @param metadata The metadata that will be associated to the ABT. + /// @param signature A signature of the EIP-712 structured data hash + /// `Agreement(address active,address passive,bytes metadata)` signed by + /// `address to`. + /// @return A unique `uint256 tokenId` generated by type-casting the `bytes32` + /// EIP-712 structured data hash to a `uint256`. + function give(address to, bytes calldata metadata, bytes calldata signature) + external + returns (uint256); + /// @notice Creates and transfers the ownership of an ABT from an + /// `address from` to the transaction's `msg.sender`. + /// @dev Throws unless `bytes signature` represents a signature of the + /// EIP-712 structured data hash + /// `Agreement(address active,address passive,bytes metadata)` expressing + /// `address from`'s explicit agreement to be publicly associated with + /// `msg.sender` and `bytes metadata`. A unique `uint256 tokenId` must be + /// generated by type-casting the `bytes32` EIP-712 structured data hash to a + /// `uint256`. If `bytes signature` is empty or `address from` is a contract, + /// an EIP-1271-compatible call to `function isValidSignatureNow(...)` must + /// be made to `address from`. A successful execution must result in the + /// emission of an `event Transfer(from, msg.sender, tokenId)`. Once an ABT + /// exists as an `uint256 tokenId` in the contract, `function take(...)` must + /// throw. + /// @param from The origin of the ABT. + /// @param metadata The metadata that will be associated to the ABT. + /// @param signature A signature of the EIP-712 structured data hash + /// `Agreement(address active,address passive,bytes metadata)` signed by + /// `address from`. + /// @return A unique `uint256 tokenId` generated by type-casting the `bytes32` + /// EIP-712 structured data hash to a `uint256`. + function take(address from, bytes calldata metadata, bytes calldata signature) + external + returns (uint256); + /// @notice Decodes the opaque metadata bytestring of an ABT into the token + /// URI that will be associated with it once it is created on chain. + /// @param metadata The metadata that will be associated to an ABT. + /// @return A URI that represents the metadata. + function decodeURI(bytes calldata metadata) external returns (string memory); +} +``` + +See [EIP-721](./eip-721.md) for a definition of its metadata JSON Schema. + +### [EIP-712](./eip-712.md) Typed Structured Data Hashing and Bytearray Signature Creation + +To invoke `function give(...)` and `function take(...)` a bytearray signature must be created using [EIP-712](./eip-712.md). A tested reference implementation in Node.js is attached at [../assets/eip-4973/sdk/src/index.mjs](../assets/eip-4973/sdk/src/index.mjs), [../assets/eip-4973/sdk/test/index_test.mjs](../assets/eip-4973/sdk/test/index_test.mjs) and [../assets/eip-4973/package.json](../assets/eip-4973/package.json). In Solidity, this bytearray signature can be created as follows: + +```solidity +bytes32 r = 0x68a020a209d3d56c46f38cc50a33f704f4a9a10a59377f8dd762ac66910e9b90; +bytes32 s = 0x7e865ad05c4035ab5792787d4a0297a43617ae897930a6fe4d822b8faea52064; +uint8 v = 27; +bytes memory signature = abi.encodePacked(r, s, v); +``` + +## Rationale + +### Interface + +ABTs shall be maximally backward-compatible but still only expose a minimal and simple to implement interface definition. + +As [EIP-721](./eip-721.md) tokens have seen widespread adoption with wallet providers and marketplaces, using its `ERC721Metadata` interface with [EIP-165](./eip-165.md) for feature-detection potentially allows implementers to support ABTs out of the box. + +If an implementer of [EIP-721](./eip-721.md) properly built [EIP-165](./eip-165.md)'s `function supportsInterface(bytes4 interfaceID)` function, already by recognizing that [EIP-721](./eip-721.md)'s track and transfer interface component with the identifier `0x80ac58cd` is not implemented, transferring of a token should not be suggested as a user interface option. + +Still, since ABTs support [EIP-721](./eip-721.md)'s `ERC721Metadata` extension, wallets and marketplaces should display an account-bound token with no changes needed. + +Although other implementations of account-bound tokens are possible, e.g., by having all transfer functions revert, ABTs are superior as it supports feature detection through [EIP-165](./eip-165.md). + +We expose `function unequip(address _tokenId)` and require it to be callable at any time by an ABT's owner as it ensures an owner's right to publicly disassociate themselves from what has been issued towards their account. + +### Exception handling + +Given the non-transferable between accounts property of ABTs, if a user's keys to an account or a contract get compromised or rotated, a user may lose the ability to associate themselves with the token. In some cases, this can be the desired effect. Therefore, ABT implementers should build re-issuance and revocation processes to enable recourse. We recommend implementing strictly decentralized, permissionless, and censorship-resistant re-issuance processes. + +But this document is deliberately abstaining from offering a standardized form of exception handling in cases where user keys are compromised or rotated. + +In cases where implementers want to make account-bound tokens shareable among different accounts, e.g., to avoid losing access when keys get compromised, we suggest issuing the account-bound token towards a contract's account that implements a multi-signature functionality. + +### Provenance Indexing + +ABTs can be indexed by tracking the emission of `event Transfer(address indexed from, address indexed to, uint256 indexed tokenId)`. As with [EIP-721](./eip-721.md), transfers between two accounts are represented by `address from` and `address to` being non-zero addresses. Unequipping a token is represented through emitting a transfer with `address to` being set to the zero address. Mint operations where `address from` is set to zero don't exist. To avoid being spoofed by maliciously-implemented `event Transfer` emitting contracts, an indexer should ensure that the transaction's sender is equal to `event Transfer`'s `from` value. + +## Backwards Compatibility + +We have adopted the [EIP-165](./eip-165.md) and `ERC721Metadata` functions purposefully to create a high degree of backward compatibility with [EIP-721](./eip-721.md). We have deliberately used [EIP-721](./eip-721.md) terminology such as `function ownerOf(...)`, `function balanceOf(...)` to minimize the effort of familiarization for ABT implementers already familiar with, e.g., [EIP-20](./eip-20.md) or [EIP-721](./eip-721.md). For indexers, we've re-used the widely-implemented `event Transfer` event signature. + +## Reference Implementation + +You can find an implementation of this standard in [../assets/eip-4973](../assets/eip-4973/ERC4973-flat.sol). + +## Security Considerations + +There are no security considerations related directly to the implementation of this standard. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4974.md b/EIPS/eip-4974.md new file mode 100644 index 0000000..304bbeb --- /dev/null +++ b/EIPS/eip-4974.md @@ -0,0 +1,161 @@ +--- +eip: 4974 +title: Ratings +description: An interface for assigning and managing numerical ratings +author: Daniel Tedesco (@dtedesco1) +discussions-to: https://ethereum-magicians.org/t/8805 +status: Review +type: Standards Track +category: ERC +created: 2022-04-02 +requires: 165 +--- + +## Abstract + +This standard defines a standardized interface for assigning and managing numerical ratings on the Ethereum blockchain. This allows ratings to be codified within smart contracts and recognized by other applications, enabling a wide range of new use cases for tokens. + +## Motivation + +Traditionally, blockchain applications have focused on buying and selling digital assets. However, the asset-centric model has often been detrimental to community-based blockchain projects, as seen in the pay-to-play dynamics of many EVM-based games and DAOs in 2021. + +This proposal addresses this issue by allowing ratings to be assigned to contracts and wallets, providing a new composable primitive for blockchain applications. This allows for a diverse array of new use cases, such as: + +- Voting weight in a DAO: Ratings assigned using this standard can be used to determine the voting weight of members in a decentralized autonomous organization (DAO). For example, a DAO may assign higher ratings to members who have demonstrated a strong track record of contributing to the community, and use these ratings to determine the relative influence of each member in decision-making processes. + +- Experience points in a decentralized game ecosystem: Ratings can be used to track the progress of players in a decentralized game ecosystem, and to reward them for achieving specific milestones or objectives. For example, a game may use ratings to assign experience points to players, which can be used to unlock new content or abilities within the game. + +- Loyalty points for customers of a business: Ratings can be used to track the loyalty of customers to a particular business or service, and to reward them for their continued support. For example, a business may use ratings to assign loyalty points to customers, which can be redeemed for special offers or discounts. + +- Asset ratings for a decentralized insurance company: Ratings can be used to evaluate the risk profile of assets in a decentralized insurance company, and to determine the premiums and coverage offered to policyholders. For example, a decentralized insurance company may use ratings to assess the risk of different types of assets, and to provide lower premiums and higher coverage to assets with lower risk ratings. + +This standard is influenced by the [EIP-20](./eip-20.md) and [EIP-721](./eip-721.md) token standards and takes cues from each in its structure, style, and semantics. + +## Specification + +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. + +Every compliant contract MUST implement the following interfaces: + +``` +// SPDX-License-Identifier: CC0 + +pragma solidity ^0.8.0; + +/// @title EIP-4974 Ratings +/// @dev See https://eips.ethereum.org/EIPS/EIP-4974 +/// Note: the EIP-165 identifier for this interface is #######. +/// Must initialize contracts with an `operator` address that is not `address(0)`. +interface IERC4974 /* is ERC165 */ { + + /// @dev Emits when operator changes. + /// MUST emit when `operator` changes by any mechanism. + /// MUST ONLY emit by `setOperator`. + event NewOperator(address indexed _operator); + + /// @dev Emits when operator issues a rating. + /// MUST emit when rating is assigned by any mechanism. + /// MUST ONLY emit by `rate`. + event Rating(address _rated, int8 _rating); + + /// @dev Emits when operator removes a rating. + /// MUST emit when rating is removed by any mechanism. + /// MUST ONLY emit by `remove`. + event Removal(address _removed); + + /// @notice Appoint operator authority. + /// @dev MUST throw unless `msg.sender` is `operator`. + /// MUST throw if `operator` address is either already current `operator` + /// or is the zero address. + /// MUST emit an `Appointment` event. + /// @param _operator New operator of the smart contract. + function setOperator(address _operator) external; + + /// @notice Rate an address. + /// MUST emit a Rating event with each successful call. + /// @param _rated Address to be rated. + /// @param _rating Total EXP tokens to reallocate. + function rate(address _rated, int8 _rating) external; + + /// @notice Remove a rating from an address. + /// MUST emit a Remove event with each successful call. + /// @param _removed Address to be removed. + function removeRating(address _removed) external; + + /// @notice Return a rated address' rating. + /// @dev MUST register each time `Rating` emits. + /// SHOULD throw for queries about the zero address. + /// @param _rated An address for whom to query rating. + /// @return int8 The rating assigned. + function ratingOf(address _rated) external view returns (int8); +} + +interface IERC165 { + /// @notice Query if a contract implements an interface. + /// @dev Interface identification is specified in EIP-165. This function + /// uses less than 30,000 gas. + /// @param interfaceID The interface identifier, as specified in EIP-165. + /// @return bool `true` if the contract implements `interfaceID` and + /// `interfaceID` is not 0xffffffff, `false` otherwise. + function supportsInterface(bytes4 interfaceID) external view returns (bool); +} +``` + +## Rationale + +### Rating Assignment + +Ratings SHALL be at the sole discretion of the contract operator. This party may be a sports team coach or a multisig DAO wallet. We decide not to specify how governance occurs, but only *that* governance occurs. This allows for a wider range of potential use cases than optimizing for particular decision-making forms. + +This proposal standardizes a control mechanism to allocate community reputation without encouraging financialization of that recognition. While it does not ensure meritocracy, it opens the door. + +### Choice of int8 + +It's signed: Reviewers should be able to give neutral and negative ratings for the wallets and contracts they interact with. This is especially important for decentralized applications that may be subject to malicious actors. + +It's 8bit: The objective here is to keep ratings within some fathomably comparable range. Longer term, this could encourage easy aggregation of ratings, versus using larger numbers where users might employ a great variety of scales. + +### Rating Changes + +Ratings SHOULD allow rating updates by contract operators. If Bob has contributed greatly to the community, but then is caught stealing from Alice, the community may decide this should lower Bob's standing and influence in the community. Again, while this does not ensure an ethical standard within the community, it opens the door. + +Relatedly, ratings SHOULD allow removal of ratings to rescind a rating if the rater does not have confidence in their ability to rate effectively. + +### Interface Detection + +We chose Standard Interface Detection ([EIP-165](./eip-165.md)) to expose the interfaces that a compliant smart contract supports. + +### Metadata Choices + +We have required `name` and `description` functions in the metadata extension. `name` common among major standards for blockchain-based primitives. We included a `description` function that may be helpful for games or other applications with multiple ratings systems. + +We remind implementation authors that the empty string is a valid response to `name` and `description` if you protest to the usage of this mechanism. We also remind everyone that any smart contract can use the same name and description as your contract. How a client may determine which ratings smart contracts are well-known (canonical) is outside the scope of this standard. + +### Drawbacks + +One potential drawback of using this standard is that ratings are subjective and may not always accurately reflect the true value or quality of a contract or wallet. However, the standard provides mechanisms for updating and removing ratings, allowing for flexibility and evolution over time. + +Users identified in the motivation section have a strong need to identify how a contract or community evaluates another. While some users may be proud of ratings they receive, others may rightly or wrongly receive negative ratings from certain contracts. Negative ratings may allow for nefarious activities such as bullying and discrimination. We implore all implementers to be mindful of the consequences of any ratings systems they create with this standard. + +## Backwards Compatibility + +We have adopted the `name` semantics from the EIP-20 and EIP-721 specifications. + +## Reference Implementation + +A reference implementation of this standard can be found in the assets folder. + + +## Security Considerations + +One potential security concern with this standard is the risk of malicious actors assigning false or misleading ratings to contracts or wallets. This could be used to manipulate voting weights in a DAO, or to deceive users into making poor decisions based on inaccurate ratings. + +To address this concern, the standard includes mechanisms for updating and removing ratings, allowing for corrections to be made in cases of false or misleading ratings. Additionally, the use of a single operator address to assign and update ratings provides a single point of control, which can be used to enforce rules and regulations around the assignment of ratings. + +Another potential security concern is the potential for an attacker to gain control of the operator address and use it to manipulate ratings for their own benefit. To mitigate this risk, it is recommended that the operator address be carefully managed and protected, and that multiple parties be involved in its control and oversight. + +Overall, the security of compliant contracts will depend on the careful management and protection of the operator address, as well as the development of clear rules and regulations around the assignment of ratings. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4987.md b/EIPS/eip-4987.md new file mode 100644 index 0000000..3b44d09 --- /dev/null +++ b/EIPS/eip-4987.md @@ -0,0 +1,269 @@ +--- +eip: 4987 +title: Held token interface +description: Interface to query ownership and balance of held tokens +author: Devin Conley (@devinaconley) +discussions-to: https://ethereum-magicians.org/t/eip-4987-held-token-standard-nfts-defi/7117 +status: Review +type: Standards Track +category: ERC +created: 2021-09-21 +requires: 20, 165, 721, 1155 +--- + +## Abstract + +The proposed standard defines a lightweight interface to expose functional ownership and balances of held tokens. A held token is a token owned by a contract. This standard may be implemented by smart contracts which hold [EIP-20](./eip-20.md), [EIP-721](./eip-721.md), or [EIP-1155](./eip-1155.md) tokens and is intended to be consumed by both on-chain and off-chain systems that rely on ownership and balance verification. + +## Motivation + +As different areas of crypto (DeFi, NFTs, etc.) converge and composability improves, there will more commonly be a distinction between the actual owner (likely a contract) and the functional owner (likely a user) of a token. Currently, this results in a conflict between mechanisms that require token deposits and systems that rely on those tokens for ownership or balance verification. + +This proposal aims to address that conflict by providing a standard interface for token holders to expose ownership and balance information. This will allow users to participate in these DeFi mechanisms without giving up existing token utility. Overall, this would greatly increase interoperability across systems, benefiting both users and protocol developers. + +Example implementers of this ERC standard include + +- staking or farming contracts +- lending pools +- time lock or vesting vaults +- fractionalized NFT contracts +- smart contract wallets + +Example consumers of this ERC standard include + +- governance systems +- gaming +- PFP verification +- art galleries or showcases +- token based membership programs + +## Specification + +Smart contracts implementing the `ERC20` held token standard MUST implement all of the functions in the `IERC20Holder` interface. + +Smart contracts implementing the `ERC20` held token standard MUST also implement `ERC165` and return true when the interface ID `0x74c89d54` is passed. + +```solidity +/** + * @notice the ERC20 holder standard provides a common interface to query + * token balance information + */ +interface IERC20Holder is IERC165 { + /** + * @notice emitted when the token is transferred to the contract + * @param owner functional token owner + * @param tokenAddress held token address + * @param tokenAmount held token amount + */ + event Hold( + address indexed owner, + address indexed tokenAddress, + uint256 tokenAmount + ); + + /** + * @notice emitted when the token is released back to the user + * @param owner functional token owner + * @param tokenAddress held token address + * @param tokenAmount held token amount + */ + event Release( + address indexed owner, + address indexed tokenAddress, + uint256 tokenAmount + ); + + /** + * @notice get the held balance of the token owner + * @dev should throw for invalid queries and return zero for no balance + * @param tokenAddress held token address + * @param owner functional token owner + * @return held token balance + */ + function heldBalanceOf(address tokenAddress, address owner) + external + view + returns (uint256); +} + +``` + +Smart contracts implementing the `ERC721` held token standard MUST implement all of the functions in the `IERC721Holder` interface. + +Smart contracts implementing the `ERC721` held token standard MUST also implement `ERC165` and return true when the interface ID `0x16b900ff` is passed. + +```solidity +/** + * @notice the ERC721 holder standard provides a common interface to query + * token ownership and balance information + */ +interface IERC721Holder is IERC165 { + /** + * @notice emitted when the token is transferred to the contract + * @param owner functional token owner + * @param tokenAddress held token address + * @param tokenId held token ID + */ + event Hold( + address indexed owner, + address indexed tokenAddress, + uint256 indexed tokenId + ); + + /** + * @notice emitted when the token is released back to the user + * @param owner functional token owner + * @param tokenAddress held token address + * @param tokenId held token ID + */ + event Release( + address indexed owner, + address indexed tokenAddress, + uint256 indexed tokenId + ); + + /** + * @notice get the functional owner of a held token + * @dev should throw for invalid queries and return zero for a token ID that is not held + * @param tokenAddress held token address + * @param tokenId held token ID + * @return functional token owner + */ + function heldOwnerOf(address tokenAddress, uint256 tokenId) + external + view + returns (address); + + /** + * @notice get the held balance of the token owner + * @dev should throw for invalid queries and return zero for no balance + * @param tokenAddress held token address + * @param owner functional token owner + * @return held token balance + */ + function heldBalanceOf(address tokenAddress, address owner) + external + view + returns (uint256); +} +``` + +Smart contracts implementing the `ERC1155` held token standard MUST implement all of the functions in the `IERC1155Holder` interface. + +Smart contracts implementing the `ERC1155` held token standard MUST also implement `ERC165` and return true when the interface ID `0xced24c37` is passed. + +```solidity +/** + * @notice the ERC1155 holder standard provides a common interface to query + * token balance information + */ +interface IERC1155Holder is IERC165 { + /** + * @notice emitted when the token is transferred to the contract + * @param owner functional token owner + * @param tokenAddress held token address + * @param tokenId held token ID + * @param tokenAmount held token amount + */ + event Hold( + address indexed owner, + address indexed tokenAddress, + uint256 indexed tokenId, + uint256 tokenAmount + ); + + /** + * @notice emitted when the token is released back to the user + * @param owner functional token owner + * @param tokenAddress held token address + * @param tokenId held token ID + * @param tokenAmount held token amount + */ + event Release( + address indexed owner, + address indexed tokenAddress, + uint256 indexed tokenId, + uint256 tokenAmount + ); + + /** + * @notice get the held balance of the token owner + * @dev should throw for invalid queries and return zero for no balance + * @param tokenAddress held token address + * @param owner functional token owner + * @param tokenId held token ID + * @return held token balance + */ + function heldBalanceOf( + address tokenAddress, + address owner, + uint256 tokenId + ) external view returns (uint256); +} +``` + +## Rationale + +This interface is designed to be extremely lightweight and compatible with any existing token contract. Any token holder contract likely already stores all relevant information, so this standard is purely adding a common interface to expose that data. + +The token address parameter is included to support contracts that can hold multiple token contracts simultaneously. While some contracts may only hold a single token address, this is more general to either scenario. + +Separate interfaces are proposed for each token type (EIP-20, EIP-721, EIP-1155) because any contract logic to support holding these different tokens is likely independent. In the scenario where a single contract does hold multiple token types, it can simply implement each appropriate held token interface. + + +## Backwards Compatibility + +Importantly, the proposed specification is fully compatible with all existing EIP-20, EIP-721, and EIP-1155 token contracts. + +Token holder contracts will need to be updated to implement this lightweight interface. + +Consumer of this standard will need to be updated to respect this interface in any relevant ownership logic. + + +## Reference Implementation + +A full example implementation including [interfaces](../assets/eip-4987/IERC721Holder.sol), a vault [token holder](../assets/eip-4987/Vault.sol), and a [consumer](../assets/eip-4987/Consumer.sol), can be found at `assets/eip-4987/`. + +Notably, consumers of the `IERC721Holder` interface can do a chained lookup for the owner of any specific token ID using the following logic. + +```solidity + /** + * @notice get the functional owner of a token + * @param tokenId token id of interest + */ + function getOwner(uint256 tokenId) external view returns (address) { + // get raw owner + address owner = token.ownerOf(tokenId); + + // if owner is not contract, return + if (!owner.isContract()) { + return owner; + } + + // check for token holder interface support + try IERC165(owner).supportsInterface(0x16b900ff) returns (bool ret) { + if (!ret) return owner; + } catch { + return owner; + } + + // check for held owner + try IERC721Holder(owner).heldOwnerOf(address(token), tokenId) returns (address user) { + if (user != address(0)) return user; + } catch {} + + return owner; + } +``` + + +## Security Considerations + +Consumers of this standard should be cautious when using ownership information from unknown contracts. A bad actor could implement the interface, but report invalid or malicious information with the goal of manipulating a governance system, game, membership program, etc. + +Consumers should also verify the overall token balance and ownership of the holder contract as a sanity check. + + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5.md b/EIPS/eip-5.md new file mode 100644 index 0000000..5c87511 --- /dev/null +++ b/EIPS/eip-5.md @@ -0,0 +1,121 @@ +--- +eip: 5 +title: Gas Usage for `RETURN` and `CALL*` +author: Christian Reitwiessner +status: Final +type: Standards Track +category: Core +created: 2015-11-22 +--- + +### Abstract + +This EIP makes it possible to call functions that return strings and other dynamically-sized arrays. +Currently, when another contract / function is called from inside the Ethereum Virtual Machine, +the size of the output has to be specified in advance. It is of course possible to give a larger +size, but gas also has to be paid for memory that is not written to, which makes returning +dynamically-sized data both costly and inflexible to the extent that it is actually unusable. + +The solution proposed in this EIP is to charge gas only for memory that is actually written to at +the time the `CALL` returns. + +### Specification + +The gas and memory semantics for `CALL`, `CALLCODE` and `DELEGATECALL` (called later as `CALL*`) +are changed in the following way (`CREATE` does not write to memory and is thus unaffected): + +Suppose the arguments to `CALL*` are `gas, address, value, input_start, input_size, output_start, output_size`, +then, at the beginning of the opcode, gas for growing memory is only charged for `input_start + input_size`, but not +for `output_start + output_size`. + +If the called contract returns data of size `n`, the memory of the calling contract is grown to +`output_start + min(output_size, n)` (and the calling contract is charged gas for that) and the +output is written to the area `[output_start, output_start + min(n, output_size))`. + +The calling contract can run out of gas both at the beginning of the opcode and at the end +of the opcode. + +After the call, the `MSIZE` opcode should return the size the memory was actually grown to. + +### Motivation + +In general, it is good practise to reserve a certain memory area for the output of a call, +because letting a subroutine write to arbitrary areas in memory might be dangerous. On the +other hand, it is often hard to know the output size of a call prior to performing the call: +The data could be in the storage of another contract which is generally inaccessible and +determining its size would require another call to that contract. + +Furthermore, charging gas for areas of memory that are not actually written to is unnecessary. + +This proposal tries to solve both problems: A caller can choose to provide a gigantic area of +memory at the end of their memory area. The callee can "write" to it by returning and the +caller is only charged for the memory area that is actually written. + +This makes it possible to return dynamic data like strings and dynamically-sized arrays +in a very flexible way. It is even possible to determine the size of the returned data: +If the caller uses `output_start = MSIZE` and `output_size = 2**256-1`, the area of +memory that was actually written to is `(output_start, MSIZE)` (here, `MSIZE` as evaluated +after the call). This is important because it allows "proxy" contracts +which call other contracts whose interface they do not know and just return their output, +i.e. they both forward the input and the output. For this, it is important that the caller +(1) does not need to know the size of the output in advance and (2) can determine the +size of the output after the call. + + +### Rationale + +This way of dealing with the problem requires a minimal change to the Ethereum Virtual Machine. +Other means of achieving a similar goal would have changed the opcodes themselves or +the number of their arguments. Another possibility would have been to only change the +gas mechanics if `output_size` is equal to `2**256-1`. Since the main difficulty in the +implementation is that memory has to be enlarged at two points in the code around `CALL`, +this would not have been a simplification. + +At an earlier stage, it was proposed to also add the size of the returned data on the stack, +but the `MSIZE` mechanism described above should be sufficient and is much better +backwards compatible. + +Some comments are available at https://github.com/ethereum/EIPs/issues/8 + +### Backwards Compatibility + +This proposal changes the semantics of contracts because contracts can access the gas counter +and the size of memory. + +On the other hand, it is unlikely that existing contracts will suffer from this change due to +the following reasons: + +Gas: + +The VM will not charge more gas than before. Usually, contracts are written in a way such +that their semantics do not change if they use up less gas. If more gas were used, contracts +might go out-of-gas if they perform a tight estimation for gas needed by sub-calls. Here, +contracts might only return more gas to their callers. + +Memory size: + +The `MSIZE` opcode is typically used to allocate memory at a previously unused spot. +The change in semantics affects existing contracts in two ways: + +1. Overlaps in allocated memory. By using `CALL`, a contract might have wanted to allocate + a certain slice of memory, even if that is not written to by the called contract. + Subsequent uses of `MSIZE` to allocate memory might overlap with this slice that is + now smaller than before the change. It is though unlikely that such contracts exist. + +2. Memory addresses change. Rather general, if memory is allocated using `MSIZE`, the + addresses of objects in memory will be different after the change. Contract should + all be written in a way, though, such that objects in memory are _relocatable_, + i.e. their absolute position in memory and their relative position to other + objects does not matter. This is of course not the case for arrays, but they + are allocated in a single allocation and not with an intermediate `CALL`. + + +### Implementation + +VM implementers should take care not to grow the memory until the end of the call and after a check that sufficient +gas is still available. Typical uses of the EIP include "reserving" `2**256-1` bytes of memory for the output. + +Python implementation: + + old: http://vitalik.ca/files/old.py + new: http://vitalik.ca/files/new.py diff --git a/EIPS/eip-5000.md b/EIPS/eip-5000.md new file mode 100644 index 0000000..26804d4 --- /dev/null +++ b/EIPS/eip-5000.md @@ -0,0 +1,117 @@ +--- +eip: 5000 +title: MULDIV instruction +description: Introduce a new instruction to perform x * y / z in 512-bit precision +author: Harikrishnan Mulackal (@hrkrshnn), Alex Beregszaszi (@axic), Paweł Bylica (@chfast) +discussions-to: https://ethereum-magicians.org/t/muldiv-instruction/9930 +status: Draft +type: Standards Track +category: Core +created: 2022-03-14 +--- + +## Abstract + +Introduce a new instruction, `MULDIV(x, y, z)`, to perform `((x * y) / z) % 2**256` in 512-bit precision. `z = 0` is a special case for `(x * y) % 2**256`. + +## Motivation + +Fixed point operations in high level languages are very commonly used on Ethereum, especially in the domain of financial applications. + +While fixed point addition and subtraction can be done with merely `add` and `sub` respectively, being able to efficiently do fixedpoint multiplication and division is a very sought after feature. A commonly used workaround relies on a `mulmod`-based, rather complex implementation (taking around 50 instructions, excluding stack manipulation). This instruction reduces that to a single opcode. + +A secondary use case is likely in cryptographic applications, where the `muldiv` instruction allows full precision 256x256->512 multiplication. `mul(x y)` (or `muldiv(x, y, 1)`) computes the lower order 256 bits and `muldiv(x, y, 0)` computes the higher order 256 bits. + +Finally we aimed to provide an instruction which can be efficiently used both in *checked* and *unchecked arithmetic* use cases. By *checked* we mean to abort on conditions including division-by-zero and wrapping behaviour. + +## Specification + +A new instruction is introduced: `MULDIV` (`0x1e`). + +- Pops 3 values from the stack, first `x`, then `y` and `z`. +- If `z == 0`, `r = (uint512(x) * y) / 2**256`. +- Otherwise `r = (uint512(x) * y / z) % 2**256`, where the intermediate calculation is performed with 512-bit precision. +- Pushes `r` on the stack. + +```python +# operations `*` and `//` are done in 512 bit precision +def muldiv(x, y, z): + if z == 0: + return (x * y) // (2**256) + else: + return ((x * y) // z) % (2**256) +``` + +The cost of the instruction is 8 gas (aka `mid`), the same as for `addmod` and `mulmod`. + +## Rationale + +### The special 0 case + +All the arithmetic instructions in EVM handle division or modulo 0 specially: the instructions return 0. We have decided to break consistency in order to provide a flexible opcode, which can be used to detect wrapping behaviour. + +Alternate options include: + +- Returning a flag for wrapping +- Returning two stack items, higher and lower order bits +- Compute the higher order 256 bits in EVM: + +```solidity +/// Returns `hi` such that `x × y = hi × 2**256 + mul(x, y)` +function hob(uint x, uint y) returns (uint hi) { + uint uint_max = type(uint).max; + assembly { + let lo := mul(x, y) + let mm := mulmod(x, y, uint_max) + hi := sub(sub(mm, lo), lt(mm, lo)) + } +} +``` + +While this feature is clever and useful, callers must be aware that unlike other EVM instructions, passing 0 will have a vastly different behaviour. + +### Argument ordering + +The order of arguments matches `addmod` and `mulmod`. + +## Backwards Compatibility + +This is a new instruction not present pior. + +## Test Cases + +``` +PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +MULDIV +--- +0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +``` + + +``` +PUSH 0x0000000000000000000000000000000000000000000000000000000000000000 +PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +MULDIV +--- +0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe +``` + +``` +PUSH 0x0000000000000000000000000000000000000000000000000de0b6b3a7640000 +PUSH 0x000000000000000000000000000000000000000000000000016345785d8a0000 +PUSH 0x00000000000000000000000000000000000000000000d3c21bcecceda1000000 +MULDIV +--- +0x00000000000000000000000000000000000000000000152d02c7e14af6800000 +``` + +## Security Considerations + +TBA + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5003.md b/EIPS/eip-5003.md new file mode 100644 index 0000000..d42d650 --- /dev/null +++ b/EIPS/eip-5003.md @@ -0,0 +1,88 @@ +--- +eip: 5003 +title: Insert Code into EOAs with AUTHUSURP +description: Allow migrating away from ECDSA by deploying code in place of an externally owned account. +author: Dan Finlay (@danfinlay), Sam Wilson (@SamWilsn) +discussions-to: https://ethereum-magicians.org/t/eip-5003-auth-usurp-publishing-code-at-an-eoa-address/8979 +status: Stagnant +type: Standards Track +category: Core +created: 2022-03-26 +requires: 3074, 3607 +--- + +## Abstract + +This EIP introduces a new opcode, `AUTHUSURP`, which deploys code at an [EIP-3074](./eip-3074.md) authorized address. For externally owned accounts (EOAs), together with [EIP-3607](./eip-3607.md), this effectively revokes the original signing key's authority. + +## Motivation + +EOAs currently hold a significant amount of user-controlled value on Ethereum blockchains, but are limited by the protocol in a variety of critical ways. These accounts do not support rotating keys for security, batching to save gas, or sponsored transactions to reduce the need to hold ether yourself. There are countless other benefits that come from having a contract account or account abstraction, like choosing one's own authentication algorithm, setting spending limits, enabling social recovery, allowing key rotation, arbitrarily and transitively delegating capabilities, and just about anything else we can imagine. + +New users have access to these benefits using smart contract wallets, and new contracts can adopt recent standards to enable app-layer account abstraction (like [EIP-4337](./eip-4337.md)), but these would neglect the vast majority of existing Ethereum users' accounts. These users exist today, and they also need a path to achieving their security goals. + +Those added benefits would mostly come along with EIP-3074 itself, but with one significant shortcoming: the original signing key has ultimate authority for the account. While an EOA could delegate its authority to some _additional_ contract, the key itself would linger, continuing to provide an attack vector, and a constantly horrifying question lingering: have I been leaked? In other words, EIP-3074 can only grant authority to additional actors, but never revoke it. + +Today's EOAs have no option to rotate their keys. A leaked private key (either through phishing, or accidental access) cannot be revoked. A prudent user concerned about their key security might migrate to a new secret recovery phrase but at best this requires a transaction per asset (making it extremely expensive), and at worst, some powers (like hard-coded owners in a smart contract) might not be transferable at all. + +We know that EOAs cannot provide ideal user experience or safety, and there is a desire in the community to change the norm to contract-based accounts, but if that transition is designed without regard for the vast majority of users today—for whom Ethereum has always meant EOAs—we will be continually struggling against the need to support both of these userbases. This EIP provides a path not to enshrine EOAs, but to provide a migration path off of them, once and for all. + +This proposal combines well with, but is distinct from, [EIP-3074](./eip-3074.md), which provides opcodes that could enable any externally owned account (EOA) to delegate its signing authority to an arbitrary smart contract. It allows an EOA to authorize a contract account to act on its behalf _without forgoing its own powers_, while this EIP provides a final migration path off the EOA's original signing key. + +## Specification + +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. + +### Conventions + + - **`top - N`** - the `N`th most recently pushed value on the EVM stack, where `top - 0` is the most recent. + - **invalid execution** - execution that is invalid and must exit the current execution frame immediately, consuming all remaining gas (in the same way as a stack underflow or invalid jump). + - **empty account** - account where its balance is 0, its nonce is 0, and it has no code. + +### `AUTHUSURP` (`0xf8`) + +A new opcode `AUTHUSURP` shall be created at `0xf8`. It shall take two stack elements and return one stack element. + +#### Input + +| Stack | Value | +| --------- | ------------ | +| `top - 0` | `offset` | +| `top - 1` | `length` | + +#### Output + +| Stack | Value | +| ---------- | --------- | +| `top - 0` | `address` | + +#### Behavior + +`AUTHUSURP` behaves identically to `CREATE` (`0xf0`), except as described below: + + - If `authorized` (as defined in EIP-3074) is unset, execution is invalid. + - If `authorized` points to an empty account, then `static_gas` remains 32,000. Otherwise, `static_gas` shall be 7,000. + - `AUTHUSURP` does not check the nonce of the `authorized` account. + - The initcode runs at the address `authorized`. + - If the initcode returns no bytes, its execution frame must be reverted, and `AUTHUSURP` returns zero. + - After executing the initcode, but before the returned code is deployed, if the account's code is non-empty, the initcode's execution frame must be reverted, and `AUTHUSURP` returns zero. + - The code is deployed into the account with the address `authorized`. + +## Rationale + +`AUTHUSURP` does not check the nonce of the `authorized` account because it must work with accounts that have previously sent transactions. + +When using `AUTHUSURP`, if the initcode were to deploy a zero-length contract, there would be no way to prevent using `AUTHUSURP` again later. + +The account's code must be checked immediately before deploying to catch the situation where the initcode attempts to `AUTHUSURP` at the same address. This is unnecessary with other deployment instructions because they increment and check the account's nonce. + +## Backwards Compatibility + +`AUTHUSURP` with EIP-3607 revokes the authority of the original ECDSA signature to send transactions from the account. This is completely new behavior, although it is somewhat similar to the `CREATE2` opcode. + +## Security Considerations + +Contracts using ECDSA signatures outside of transactions will not be aware that the usurped account is no longer controlled by a private key. This means that, for example, the private key will _always_ have access to the `permit` function on token contracts. This can—and should—be mitigated by modifying the `ecrecover` pre-compiled contract. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5005.md b/EIPS/eip-5005.md new file mode 100644 index 0000000..b37f5ff --- /dev/null +++ b/EIPS/eip-5005.md @@ -0,0 +1,233 @@ +--- +eip: 5005 +title: Zodiac Modular Accounts +description: Composable interoperable programmable accounts +author: Auryn Macmillan (@auryn-macmillan), Kei Kreutler (@keikreutler) +discussions-to: https://ethereum-magicians.org/t/eip-zodiac-a-composable-design-philosophy-for-daos/8963 +status: Review +type: Standards Track +category: ERC +created: 2022-04-14 +requires: 165 +--- + +## Abstract +This EIP standardizes interfaces for composable and interoperable tooling for programmable Ethereum accounts. These interfaces separate contract accounts ("avatars") from their authentication and execution logic ("guards" and "modules"). Avatars implement the `IAvatar` interface, and guards implement the `IGuard` interface. Modules may take any form. + +## Motivation +Currently, most programmable accounts (like DAO tools and frameworks) are built as monolithic systems where the authorization and execution logic are coupled, either within the same contract or in a tightly integrated system of contracts. This needlessly inhibits the flexibility of these tools and encourages platform lock-in via high switching costs. + +By using the this EIP standard to separate concerns (decoupling authentication and execution logic), users are able to: + +1. Enable flexible, module-based control of programmable accounts +2. Easily switch between tools and frameworks without unnecessary overhead. +3. Enable multiple control mechanism in parallel. +4. Enable cross-chain / cross-layer governance. +5. Progressively decentralize their governance as their project and community matures. + +## Specification +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. + +This EIP consists of four key concepts: + +- **Avatars** are programmable Ethereum accounts. Avatars are the address that holds balances, owns systems, executes transaction, is referenced externally, and ultimately represents your DAO. Avatars MUST implement the `IAvatar` interface. +- **Modules** are contracts enabled by an avatar that implement some execution logic. +- **Modifiers** are contracts that sit between modules and avatars to modify the module's behavior. For example, they might enforce a delay on all functions a module attempts to execute or limit the scope of transactions that can be initiated by the module. Modifiers MUST implement the `IAvatar` interface. +- **Guards** are contracts that MAY be enabled on modules or modifiers and implement pre- or post-checks on each transaction executed by those modules or modifiers. This allows avatars to do things like limit the scope of addresses and functions that a module or modifier can call or ensure a certain state is never changed by a module or modifier. Guards MUST expose the `IGuard` interface. Modules, modifiers, and avatars that wish to be guardable MUST inherit `Guardable`, MUST call `checkTransaction()` before triggering execution on their target, and MUST call `checkAfterExecution()` after execution is complete. + +```solidity +/// @title Avatar - A contract that manages modules that can execute transactions via this contract. + +pragma solidity >=0.7.0 <0.9.0; + +import "./Enum.sol"; + + +interface IAvatar { + event EnabledModule(address module); + event DisabledModule(address module); + event ExecutionFromModuleSuccess(address indexed module); + event ExecutionFromModuleFailure(address indexed module); + + /// @dev Enables a module on the avatar. + /// @notice Can only be called by the avatar. + /// @notice Modules should be stored as a linked list. + /// @notice Must emit EnabledModule(address module) if successful. + /// @param module Module to be enabled. + function enableModule(address module) external; + + /// @dev Disables a module on the avatar. + /// @notice Can only be called by the avatar. + /// @notice Must emit DisabledModule(address module) if successful. + /// @param prevModule Address that pointed to the module to be removed in the linked list + /// @param module Module to be removed. + function disableModule(address prevModule, address module) external; + + /// @dev Allows a Module to execute a transaction. + /// @notice Can only be called by an enabled module. + /// @notice Must emit ExecutionFromModuleSuccess(address module) if successful. + /// @notice Must emit ExecutionFromModuleFailure(address module) if unsuccessful. + /// @param to Destination address of module transaction. + /// @param value Ether value of module transaction. + /// @param data Data payload of module transaction. + /// @param operation Operation type of module transaction: 0 == call, 1 == delegate call. + function execTransactionFromModule( + address to, + uint256 value, + bytes memory data, + Enum.Operation operation + ) external returns (bool success); + + /// @dev Allows a Module to execute a transaction and return data + /// @notice Can only be called by an enabled module. + /// @notice Must emit ExecutionFromModuleSuccess(address module) if successful. + /// @notice Must emit ExecutionFromModuleFailure(address module) if unsuccessful. + /// @param to Destination address of module transaction. + /// @param value Ether value of module transaction. + /// @param data Data payload of module transaction. + /// @param operation Operation type of module transaction: 0 == call, 1 == delegate call. + function execTransactionFromModuleReturnData( + address to, + uint256 value, + bytes memory data, + Enum.Operation operation + ) external returns (bool success, bytes memory returnData); + + /// @dev Returns if an module is enabled + /// @return True if the module is enabled + function isModuleEnabled(address module) external view returns (bool); + + /// @dev Returns array of modules. + /// @param start Start of the page. + /// @param pageSize Maximum number of modules that should be returned. + /// @return array Array of modules. + /// @return next Start of the next page. + function getModulesPaginated(address start, uint256 pageSize) + external + view + returns (address[] memory array, address next); +} +``` + +```solidity +pragma solidity >=0.7.0 <0.9.0; + +import "./Enum.sol"; + +interface IGuard { + function checkTransaction( + address to, + uint256 value, + bytes memory data, + Enum.Operation operation, + uint256 safeTxGas, + uint256 baseGas, + uint256 gasPrice, + address gasToken, + address payable refundReceiver, + bytes memory signatures, + address msgSender + ) external; + + function checkAfterExecution(bytes32 txHash, bool success) external; +} + +``` + +```solidity +pragma solidity >=0.7.0 <0.9.0; + +import "./Enum.sol"; +import "./BaseGuard.sol"; + +/// @title Guardable - A contract that manages fallback calls made to this contract +contract Guardable { + address public guard; + + event ChangedGuard(address guard); + + /// `guard_` does not implement IERC165. + error NotIERC165Compliant(address guard_); + + /// @dev Set a guard that checks transactions before execution. + /// @param _guard The address of the guard to be used or the 0 address to disable the guard. + function setGuard(address _guard) external { + if (_guard != address(0)) { + if (!BaseGuard(_guard).supportsInterface(type(IGuard).interfaceId)) + revert NotIERC165Compliant(_guard); + } + guard = _guard; + emit ChangedGuard(guard); + } + + function getGuard() external view returns (address _guard) { + return guard; + } +} +``` + +```solidity +pragma solidity >=0.7.0 <0.9.0; + +import "./Enum.sol"; +import "./IERC165.sol"; +import "./IGuard.sol"; + +abstract contract BaseGuard is IERC165 { + function supportsInterface(bytes4 interfaceId) + external + pure + override + returns (bool) + { + return + interfaceId == type(IGuard).interfaceId || // 0xe6d7a83a + interfaceId == type(IERC165).interfaceId; // 0x01ffc9a7 + } + + /// @dev Module transactions only use the first four parameters: to, value, data, and operation. + /// Module.sol hardcodes the remaining parameters as 0 since they are not used for module transactions. + function checkTransaction( + address to, + uint256 value, + bytes memory data, + Enum.Operation operation, + uint256 safeTxGas, + uint256 baseGas, + uint256 gasPrice, + address gasToken, + address payable refundReceiver, + bytes memory signatures, + address msgSender + ) external virtual; + + function checkAfterExecution(bytes32 txHash, bool success) external virtual; +} +``` + +```solidity +pragma solidity >=0.7.0 <0.9.0; + +/// @title Enum - Collection of enums + +contract Enum { + + enum Operation {Call, DelegateCall} + +} +``` + +## Rationale +The interface defined in this standard is designed to be mostly compatible with most popular programmable accounts in use right now, to minimize the need for changes to existing tooling. + +## Backwards Compatibility +No backward compatibility issues are introduced by this standard. + +## Security Considerations +There are some considerations that module developers and users should take into account: +1. **Modules have absolute control:** Modules have absolute control over any avatar on which they are enabled, so any module implementation should be treated as security critical and users should be vary cautious about enabling new modules. ONLY ENABLE MODULES THAT YOU TRUST WITH THE FULL VALUE OF THE AVATAR. +2. **Race conditions:** A given avatar may have any number of modules enabled, each with unilateral control over the safe. In such cases, there may be race conditions between different modules and/or other control mechanisms. +3. **Don't brick your avatar:** There are no safeguards to stop you adding or removing modules. If you remove all of the modules that let you control an avatar, the avatar will cease to function and all funds will be stuck. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5006.md b/EIPS/eip-5006.md new file mode 100755 index 0000000..f735e39 --- /dev/null +++ b/EIPS/eip-5006.md @@ -0,0 +1,158 @@ +--- +eip: 5006 +title: Rental NFT, NFT User Extension +description: Add a user role with restricted permissions to ERC-1155 tokens +author: Lance (@LanceSnow), Anders (@0xanders), Shrug +discussions-to: https://ethereum-magicians.org/t/eip5006-erc-1155-usage-rights-extension/8941 +status: Final +type: Standards Track +category: ERC +created: 2022-04-12 +requires: 165, 1155 +--- + +## Abstract + +This standard is an extension of [ERC-1155](./eip-1155.md). It proposes an additional role (`user`) which can be granted to addresses that represent a `user` of the assets rather than an `owner`. + +## Motivation + +Like [ERC-721](./eip-721.md), [ERC-1155](./eip-1155.md) tokens may have utility of some kind. The people who “use” the token may be different than the people who own it (such as in a rental). Thus, it would be useful to have separate roles for the “owner” and the “user” so that the “user” would not be able to take actions that the owner could (for example, transferring ownership). + +## Specification + +The keywords "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. + +```solidity +// SPDX-License-Identifier: CC0-1.0 + +pragma solidity ^0.8.0; + +interface IERC5006 { + struct UserRecord { + uint256 tokenId; + address owner; + uint64 amount; + address user; + uint64 expiry; + } + + /** + * @dev Emitted when permission for `user` to use `amount` of `tokenId` token owned by `owner` + * until `expiry` are given. + */ + event CreateUserRecord( + uint256 recordId, + uint256 tokenId, + uint64 amount, + address owner, + address user, + uint64 expiry + ); + + /** + * @dev Emitted when record of `recordId` are deleted. + */ + event DeleteUserRecord(uint256 recordId); + + /** + * @dev Returns the usable amount of `tokenId` tokens by `account`. + */ + function usableBalanceOf(address account, uint256 tokenId) + external + view + returns (uint256); + + /** + * @dev Returns the amount of frozen tokens of token type `id` by `account`. + */ + function frozenBalanceOf(address account, uint256 tokenId) + external + view + returns (uint256); + + /** + * @dev Returns the `UserRecord` of `recordId`. + */ + function userRecordOf(uint256 recordId) + external + view + returns (UserRecord memory); + + /** + * @dev Gives permission to `user` to use `amount` of `tokenId` token owned by `owner` until `expiry`. + * + * Emits a {CreateUserRecord} event. + * + * Requirements: + * + * - If the caller is not `owner`, it must be have been approved to spend ``owner``'s tokens + * via {setApprovalForAll}. + * - `owner` must have a balance of tokens of type `id` of at least `amount`. + * - `user` cannot be the zero address. + * - `amount` must be greater than 0. + * - `expiry` must after the block timestamp. + */ + function createUserRecord( + address owner, + address user, + uint256 tokenId, + uint64 amount, + uint64 expiry + ) external returns (uint256); + + /** + * @dev Atomically delete `record` of `recordId` by the caller. + * + * Emits a {DeleteUserRecord} event. + * + * Requirements: + * + * - the caller must have allowance. + */ + function deleteUserRecord(uint256 recordId) external; +} + +``` + +The `supportsInterface` method MUST return `true` when called with `0xc26d96cc`. + +## Rationale + +This model is intended to facilitate easy implementation. The following are some problems that are solved by this standard: + +### Clear Rights Assignment + +With Dual “owner” and “user” roles, it becomes significantly easier to manage what lenders and borrowers can and cannot do with the NFT (in other words, their rights).  For example, for the right to transfer ownership, the project simply needs to check whether the address taking the action represents the owner or the user and prevent the transaction if it is the user.  Additionally, owners can control who the user is and it is easy for other projects to assign their own rights to either the owners or the users. + +### Easy Third-Party Integration + +In the spirit of permissionless interoperability, this standard makes it easier for third-party protocols to manage NFT usage rights without permission from the NFT issuer or the NFT application. Once a project has adopted the additional `user` role, any other project can directly interact with these features and implement their own type of transaction. For example, a PFP NFT using this standard can be integrated into both a rental platform where users can rent the NFT for 30 days AND, at the same time, a mortgage platform where users can use the NFT while eventually buying ownership of the NFT with installment payments. This would all be done without needing the permission of the original PFP project. + +## Backwards Compatibility + +As mentioned in the specifications section, this standard can be fully ERC compatible by adding an extension function set, and there are no conflicts between [ERC-5006](./eip-5006.md) and ERC-1155. + +In addition, new functions introduced in this standard have many similarities with the existing functions in ERC-1155. This allows developers to easily adopt the standard quickly. + +## Test Cases + +Test cases are included in [test.js](../assets/eip-5006/test/test.ts). + +Run in terminal: + +1. ```cd ../assets/eip-5006``` +1. ```npm install``` +1. ```npx hardhat test``` + +## Reference Implementation + +See [`ERC5006.sol`](../assets/eip-5006/contracts/ERC5006.sol). + +## Security Considerations + +This EIP standard can completely protect the rights of the owner, the owner can change the NFT user, the user can not transfer the NFT. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5007.md b/EIPS/eip-5007.md new file mode 100755 index 0000000..007d2a6 --- /dev/null +++ b/EIPS/eip-5007.md @@ -0,0 +1,142 @@ +--- +eip: 5007 +title: Time NFT, EIP-721 Time Extension +description: Add start time and end time to EIP-721 tokens. +author: Anders (@0xanders), Lance (@LanceSnow), Shrug +discussions-to: https://ethereum-magicians.org/t/eip-5007-eip-721-time-extension/8924 +status: Last Call +last-call-deadline: 2022-09-25 +type: Standards Track +category: ERC +created: 2022-04-13 +requires: 165, 721 +--- + +## Abstract + +This standard is an extension of [EIP-721](./eip-721.md). It proposes some additional functions (`startTime`, `endTime`) to help with on-chain time management. + +## Motivation + +Some NFTs have a defined usage period and cannot be used outside of that period. With traditional NFTs that do not include time information, if you want to mark a token as invalid or enable it at a specific time, you need to actively submit a transaction—a process both cumbersome and expensive. + +Some existing NFTs contain time functions, but their interfaces are not consistent, so it is difficult to develop third-party platforms for them. + +By introducing these functions (`startTime`, `endTime`), it is possible to enable and disable NFTs automatically on chain. + +## Specification + +The keywords "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. + +```solidity +/** + * @dev the EIP-165 identifier for this interface is 0x7a0cdf92. + */ +interface IERC5007 /* is IERC721 */ { + /** + * @dev Returns the start time of the NFT as a UNIX timestamp. + * + * Requirements: + * + * - `tokenId` must exist. + */ + function startTime(uint256 tokenId) external view returns (int64); + + /** + * @dev Returns the end time of the NFT as a UNIX timestamp. + * + * Requirements: + * + * - `tokenId` must exist. + */ + function endTime(uint256 tokenId) external view returns (int64); + +} +``` + +The **composable extension** is OPTIONAL for this standard. This allows your NFT to be minted from an existing NFT or to merge two NFTs into one NFT. + +```solidity +/** + * @dev the EIP-165 identifier for this interface is 0x620063db. + */ +interface IERC5007Composable /* is IERC5007 */ { + /** + * @dev Returns the ancestor token id of the NFT. + * + * Requirements: + * + * - `tokenId` must exist. + */ + function rootTokenId(uint256 tokenId) external view returns (uint256); + + /** + * @dev Mint a new token from an old token. + * The rootTokenId of the new token is the same as the rootTokenId of the old token + * + * Requirements: + * + * - `oldTokenId` must exist. + * - `newTokenId` must not exist. + * - `newTokenOwner` cannot be the zero address. + * - `newTokenStartTime` require(oldTokenStartTime < newTokenStartTime && newTokenStartTime <= oldTokenEndTime) + */ + function split( + uint256 oldTokenId, + uint256 newTokenId, + address newTokenOwner, + int64 newTokenStartTime + ) external; + + /** + * @dev Merge the first token and second token into the new token. + * + * Requirements: + * + * - `firstTokenId` must exist. + * - `secondTokenId` must exist. require((firstToken.endTime + 1) == secondToken.startTime) + * - `newTokenOwner` cannot be the zero address. + * - `newTokenId` must not exist. + */ + function merge( + uint256 firstTokenId, + uint256 secondTokenId, + address newTokenOwner, + uint256 newTokenId + ) external; +} +``` + +## Rationale + +### Time Data Type + +The max value of `int64` is 9,223,372,036,854,775,807. As a timestamp, 9,223,372,036,854,775,807 is about year 292,471,210,648. `uint256` is too big for C, C++, Java, Go, etc, and `int64` is natively supported by mainstream programming languages. + +## Backwards Compatibility + +This standard is fully EIP-721 compatible. + +## Test Cases + +Test cases are included in [test.js](../assets/eip-5007/test/test.js). + +Run in terminal: + +```shell +cd ../assets/eip-5007 +npm install +truffle test +``` + +## Reference Implementation + +See [`ERC5007.sol`](../assets/eip-5007/contracts/ERC5007.sol). + +## Security Considerations + +No security issues found. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5008.md b/EIPS/eip-5008.md new file mode 100755 index 0000000..f70f552 --- /dev/null +++ b/EIPS/eip-5008.md @@ -0,0 +1,69 @@ +--- +eip: 5008 +title: EIP-721 Nonce Extension +description: Add a `nonce` function to EIP-721. +author: Anders (@0xanders), Lance (@LanceSnow), Shrug +discussions-to: https://ethereum-magicians.org/t/eip5008-eip-721-nonce-and-metadata-update-extension/8925 +status: Stagnant +type: Standards Track +category: ERC +created: 2022-04-10 +requires: 165, 721 +--- + +## Abstract + +This standard is an extension of [EIP-721](./eip-721.md). It proposes adding a `nonce` function to EIP-721 tokens. + +## Motivation + +Some orders of NFT marketplaces have been attacked and the NFTs sold at a lower price than the current market floor price. This can happen when users transfer an NFT to another wallet and, later, back to the original wallet. This reactivates the order, which may list the token at a much lower price than the owner would have intended. + +This EIP proposes adding a `nonce` property to EIP-721 tokens, and the `nonce` will be changed when a token is transferred. If a `nonce` is added to an order, the order can be checked to avoid attacks. + +## Specification + +The keywords "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. + +```solidity +interface IERC5008 /* is IERC165 */ { + /// @notice Get the nonce of an NFT + /// Throws if `tokenId` is not a valid NFT + /// @param tokenId The id of the NFT + /// @return The nonce of the NFT + function nonce(uint256 tokenId) external view returns(uint256); +} +``` +The `nonce(uint256 tokenId)` function MUST be implemented as `view`. + +The `supportsInterface` method MUST return `true` when called with `0xce03fdab`. + +## Rationale + +At first `transferCount` was considered as function name, but there may some case to change the `nonce` besides transfer, such as important properties changed, then we changed `transferCount` to `nonce`. + +## Backwards Compatibility + +This standard is compatible with EIP-721. + +## Test Cases + +Test cases are included in [test.js](../assets/eip-5008/test/test.ts). + +Run: +```sh +cd ../assets/eip-5008 +npm install +npx hardhat test ./test/test.ts +``` + +## Reference Implementation + +See [`ERC5008.sol`](../assets/eip-5008/contracts/ERC5008.sol). + +## Security Considerations + +No security issues found. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5018.md b/EIPS/eip-5018.md new file mode 100644 index 0000000..8f1bc96 --- /dev/null +++ b/EIPS/eip-5018.md @@ -0,0 +1,161 @@ +--- +eip: 5018 +title: Filesystem-like Interface for Contracts +description: An interface to provide access to binary objects similar to filesystems. +author: Qi Zhou (@qizhou) +discussions-to: https://ethereum-magicians.org/t/eip-5018-directory-standard/8958 +status: Review +type: Standards Track +category: ERC +created: 2022-04-18 +--- + + +## Abstract + +The following standardizes an API for directories and files within smart contracts, similar to traditional filesystems. +This standard provides basic functionality to read/write binary objects of any size, as well as allow reading/writing chunks of the object if the object is too large to fit in a single transaction. + +## Motivation + +A standard interface allows any binary objects on EVM-based blockchain to be re-used by other dApps. + +With [EIP-4804](./eip-4804.md), we are able to locate a Web3 resource on blockchain using HTTP-style URIs. One application of Web3 resources are web contents that are referenced within a directory using relative paths such as HTML/SVG. This standard proposes a contract-based directory to simplify the mapping between local web contents and on-chain web contents. Further, with relative paths referenced in the web contents and EIP-4804, the users will have a consistent view of the web contents locally and on-chain. + +## Specification + +### Directory + +#### Methods + +##### write + +Writes binary `data` to the file `name` in the directory by an account with write permission. + +``` +function write(bytes memory name, bytes memory data) external payable +``` + +##### read + +Returns the binary `data` from the file `name` in the directory and existence of the file. + +``` +function read(bytes memory name) external view returns (bytes memory data, bool exist) +``` + +##### fallback read + +Returns the binary `data` from the file `prefixedName` (prefixed with `/`) in the directory. + +``` +fallback(bytes calldata prefixedName) external returns (bytes memory data) +``` + +##### size + +Returns the size of the `data` from the file `name` in the directory and the number of chunks of the data. + +``` +function size(bytes memory name) external view returns (uint256 size, uint256 chunks) +``` + +##### remove + +Removes the file `name` in the directory and returns the number of chunks removed (0 means the file does not exist) by an account with write permission. + +``` +function remove(bytes memory name) external returns (uint256 numOfChunksRemoved) +``` + +##### countChunks + +Returns the number of chunks of the file `name`. + +``` +function countChunks(bytes memory name) external view returns (uint256 numOfChunks); +``` + +##### writeChunk + +Writes a chunk of data to the file by an account with write permission. The write will fail if `chunkId > numOfChunks`, i.e., the write must append the file or replace the existing chunk. + +``` + function writeChunk(bytes memory name, uint256 chunkId, bytes memory chunkData) external payable; +``` + +##### readChunk + +Returns the chunk data of the file `name` and the existence of the chunk. + +``` +function readChunk(bytes memory name, uint256 chunkId) external view returns (bytes memory chunkData, bool exist); +``` + +##### chunkSize + +Returns the size of a chunk of the file `name` and the existence of the chunk. + +``` +function chunkSize(bytes memory name, uint256 chunkId) external view returns (uint256 chunkSize, bool exist); +``` + +##### removeChunk + +Removes a chunk of the file `name` and returns `false` if such chunk does not exist. The method should be called by an account with write permission. + +``` +function removeChunk(bytes memory name, uint256 chunkId) external returns (bool exist); +``` + +##### truncate + +Removes the chunks of the file `name` in the directory from the given `chunkId` and returns the number of chunks removed by an account with write permission. When `chunkId = 0`, the method is essentially the same as `remove()`. + +``` +function truncate(bytes memory name, uint256 chunkId) external returns (uint256 numOfChunksRemoved); +``` + +##### getChunkHash + +Returns the hash value of the chunk data. + +``` +function getChunkHash(bytes memory name, uint256 chunkId) external view returns (bytes32); +``` + +## Rationale + +One issue of uploading the web contents to the blockchain is that the web contents may be too large to fit into a single transaction. As a result, the standard provides chunk-based operations so that uploading a content can be split into several transactions. Meanwhile, the read operation can be done in a single transaction, i.e., with a single Web3 URL defined in EIP-4804. + +### Interactions Between Unchunked/Chunked Functions + +`read` method should return the concatenated chunked data written by `writeChunk` method. The following gives some examples of the interactions: + +- `read("hello.txt")` => "" (file is empty) +- `writeChunk("hello.txt", 0, "abc")` will succeed +- `read("hello.txt")` => "abc" +- `writeChunk("hello.txt", 1, "efg")` will succeed +- `read("hello.txt")` => "abcefg" +- `writeChunk("hello.txt", 0, "aaa")` will succeed (replace chunk 0's data) +- `read("hello.txt")` => "aaaefg" +- `writeChunk("hello.txt", 3, "hij")` will fail because the operation is not replacement or append. + +With `writeChunk` method, we allow writing a file with external data that exceeds the current calldata limit (e.g., 1.8MB now), and it is able to read the whole file in a single `read` method (which is friendly for large web objects such as HTML/SVG/PNG/JPG, etc). + +For `write` method, calling a `write` method will replace all data chunks of the file with `write` method data, and one implementation can be: + +1. `writeChunk(filename, chunkId=0, data_from_write)` to chunk 0 with the same `write` method data; and +2. `truncate(filename, chunkId=1)`, which will remove the rest chunks. + +## Backwards Compatibility + +No backwards compatibility issues were identified. + +## Security Considerations + +No security considerations were found. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5022.md b/EIPS/eip-5022.md new file mode 100644 index 0000000..5633cbf --- /dev/null +++ b/EIPS/eip-5022.md @@ -0,0 +1,67 @@ +--- +eip: 5022 +title: Increase price of SSTORE from zero to non-zero to 40k gas +author: Green (@greenlucid) +status: Stagnant +type: Standards Track +category: Core +created: 2022-04-20 +discussions-to: https://ethereum-magicians.org/t/eip-proposal-increase-cost-of-sstore-from-20k-to-x-when-creating-new-storage/7614 +--- + +### Abstract + +Increase the price of the SSTORE opcode from `20_000` gas to `40_000` gas when the original slot is zero and the resultant slot is non-zero. + +### Motivation + +The cost of creating a piece of new state increases as state is larger. However, the price for creating every new storage slot has not increased. +All resources are merged into the same pricing mechanism. If the price for creating new storage slots is fixed, then it needs to be manually changed. + +One of the main reasons for not increasing gas limit is the increase of state. In that regard, because the cost of creating storage is higher than its price, all the users of all the other opcodes are subsidizing the creation of state. If state creation was more precisely priced, raising gas limit would be more feasible, and would benefit the users. + +## Rationale + +### Why not also raise the cost of non-zero to non-zero? + +Rewriting storage does not affect state growth, which is the main issue this EIP is addressing. Rewriting storage may also be underpriced. +Increasing the price of state growth will, at least, incentivize developers to reuse storage instead. + +### Why not also increase the gas refund from setting non-zero to zero? + +More discussion is needed on this. + +### Why not a better state solution? + +Whereas solutions like state rent, or state expiry have been researched for a long time, they will not be ready on the short to medium term. So, it is desirable to patch pricing for the short term. Opcode repricing has been done before, so it should not impose a large development time investment for clients. + +### Why was that specific amount chosen? + +The current pricing was made off a naive approach of benchmarking opcodes in a laptop. Not only it did not consider the long term problem of having the same price for a resource that costs more over time, the benchmark itself was wrong. This price is closer to what the naive original benchmark should have been. It could go higher, but that may be too disruptive. + +### Is this too distruptive? + +This change will severely impact the gas cost of many applications. The network does not have to subsidize state growth at the expense of more expensive regular transactions, so even if it is too disruptive, it will increase the health of the network. + +### Specification + +| Constant | Value | +| - | - | +| `FORK_BLOCK` | TBD | +| `NEW_STORAGE_PRICE` | `40_000` + +For blocks where `block.number >= FORK_BLOCK`, a new gas schedule applies. Make `SSTORE_SET_GAS`, the price when a slot is set from zero to non-zero, equal `NEW_STORAGE_PRICE`. All other costs remain the same. + +### Backwards compatibility + +Contracts that depend on hardcoded gas costs will break if they create state. + +It is a gas schedule change, so transactions from an epoch before FORK_BLOCK should be treated with previous gas costs. + +## Implementation + +https://github.com/ethereum/go-ethereum/pull/24725 + +## Security considerations + +TODO \ No newline at end of file diff --git a/EIPS/eip-5023.md b/EIPS/eip-5023.md new file mode 100644 index 0000000..c3e36b5 --- /dev/null +++ b/EIPS/eip-5023.md @@ -0,0 +1,169 @@ +--- +eip: 5023 +title: Shareable Non-Fungible Token +description: An interface for creating value-holding tokens shareable by multiple owners +author: Jarno Marttila (@yaruno), Martin Moravek (@mmartinmo) +discussions-to: https://ethereum-magicians.org/t/new-nft-concept-shareable-nfts/8681 +status: Final +type: Standards Track +category: ERC +created: 2022-01-28 +requires: 165 +--- + +## Abstract + +This EIP standardizes an interface for non-fungible value-holding shareable tokens. Shareability is accomplished by minting copies of existing tokens for new recipients. Sharing and associated events allow the construction of a graph describing who has shared what to which party. + + +## Motivation + +NFT standards such as [EIP-721](./eip-721.md) and [EIP-1155](./eip-1155.md) have been developed to standardize scarce digital resources. However, many non-fungible digital resources need not be scarce. + +We have attempted to capture positive externalities in ecosystems with new types of incentive mechanisms that exhibit anti-rival logic, serve as an unit of accounting and function as medium of sharing. We envision that shareable tokens can work both as incentives but also as representations of items that are typically digital in their nature and gain more value as they are shared. + +These requirements have set us to define shareable NFTs and more specifically a variation of shareable NFTs called non-transferable shareable NFTs. These shareable NFTs can be “shared” in the same way digital goods can be shared, at an almost zero technical transaction cost. We have utilized them to capture anti-rival value in terms of accounting positive externalities in an economic system. + +Typical NFT standards such as EIP-721 and EIP-1155 do not define a sharing modality. Instead ERC standards define interfaces for typical rival use cases such as token minting and token transactions that the NFT contract implementations should fulfil. The ‘standard contract implementations' may extend the functionalities of these standards beyond the definition of interfaces. The shareable tokens that we have designed and developed in our experiments are designed to be token standard compatible at the interface level. However the implementation of token contracts may contain extended functionalities to match the requirements of the experiments such as the requirement of 'shareability'. In reflection to standard token definitions, shareability of a token could be thought of as re-mintability of an existing token to another party while retaining the original version of it. + +Sharing is an interesting concept as it can be thought and perceived in different ways. For example, when we talk about sharing we can think about it is as digital copying, giving a copy of a digital resource while retaining a version by ourselves. Sharing can also be fractional or sharing could be about giving rights to use a certain resource. The concept of shareability and the context of shareability can take different forms and one might use different types of implementatins for instances of shareable tokens. Hence we haven't restricted that the interface should require any specific token type. + +Shareable tokens can be made non-transferable at the contract implementaiton level. Doing so, makes them shareable non-transferable tokens. In the reference implementation we have distilled a general case from our use cases that defines a shareable non-transferable NFTs using the shareable NFT interface. + +We believe that the wider audience should benefit from an abstraction level higher definition for shareability, such as this interface implementation, that defines minimum amount of functions that would be implemented to satisfy the concept of shareability. + +## Specification + +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. + +```solidity +/// Note: the ERC-165 identifier for this interface is 0xded6338b +interface IERC5023 is IERC165 { + + /// @dev This emits when a token is shared, reminted and given to another wallet that isn't function caller + event Share(address indexed from, address indexed to, uint256 indexed tokenId, uint256 derivedFromtokenId); + + /// @dev Shares, remints an existing token, gives a newly minted token a fresh token id, keeps original token at function callers possession and transfers newly minted token to receiver which should be another address than function caller. + function share(address to, uint256 tokenIdToBeShared) external returns(uint256 newTokenId); + +} +``` + +The Share event is expected to be emitted when function method share is succesfully called and a new token on basis of a given token id is minted and transferred to a recipient. + +## Rationale + +Current NFT standards define transferable non-fungible tokens, but not shareable non-fungible tokens. To be able to create shareable NFTs we see that existing NFT contracts could be extended with an interface which defines the basic principles of sharing, namely the Event of sharing and the function method of sharing. Definition of how transferability of tokens should be handled is left to the contract implementor. In case transfering is left enable shareable tokens behave similarily to the existing tokens, except when they are shared, a version of token is retained. In case transfering is disabled, shareable tokens become shareable non-transferable tokens, where they can be minted and given or shared to other people, but they cannot be transferred away. + +Imagine that Bob works together with Alice on a project. Bob earns an unique NFT indicating that he has made effort to the project, but Bob feels that his accomplishments are not only out of his own accord. Bob wants to share his token with Alice to indicate that also Alice deserves recognition of having put effort on their project. Bob initiates token sharing by calling `Share` method on the contract which has his token and indicates which one of his tokens he wishes to share and to whom by passing address and token id parameters. A new token is minted for Alice and a `Share` event is initiated to communicate that it was Bob whom shared his token to Alice by logging addresses who shared a token id to whose address and which token id was this new token derived from. + +Over time, a tree-like structures can be formed from the Share event information. If Bob shared to Alice, and Alice shared further to Charlie and Alice also shared to David a rudimentary tree structure forms out from sharing activity. This share event data can be later on utilized to gain more information of share activities that the tokens represent. + +```text +B -> A -> C + \ + > D +``` + +These tree structures can be further aggregated and collapsed to network representations e.g. social graphs on basis of whom has shared to whom over a span of time. E.g. if Bob shared a token to Alice, and Alice has shared a different token to Charlie and Bob has shared a token to Charlie, connections form between all these parties through sharing activities. + +```text + B----A----C + \_______/ +``` + +## Backwards Compatibility + +This proposal is backwards compatible with EIP-721 and EIP-1155. + +## Reference Implementation + +Following reference implementation demonstrates a general use case of one of our pilots. In this case a shareable non-transferable token represents a contribution done to a community that the contract owner has decided to merit with a token. Contract owner can mint a merit token and give it to a person. This token can be further shared by the receiver to other parties for example to share the received merit to others that have participated or influenced his contribution. + +```solidity +// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.0; + +import "./IERC5023.sol"; +import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; +import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; +import "@openzeppelin/contracts/utils/Address.sol"; +import "@openzeppelin/contracts/utils/Context.sol"; +import "@openzeppelin/contracts/utils/Strings.sol"; +import "@openzeppelin/contracts/utils/introspection/ERC165.sol"; +import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol"; +import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; + +contract ShareableERC721 is ERC721URIStorage, Ownable, IERC5023 /* EIP165 */ { + + string baseURI; + + uint256 internal _currentIndex; + + constructor(string memory _name, string memory _symbol) ERC721(_name, _symbol) {} + + function mint( + address account, + uint256 tokenId + ) external onlyOwner { + _mint(account, tokenId); + } + + function setTokenURI( + uint256 tokenId, + string memory tokenURI + ) external { + _setTokenURI(tokenId, tokenURI); + } + + function setBaseURI(string memory baseURI_) external { + baseURI = baseURI_; + } + + function _baseURI() internal view override returns (string memory) { + return baseURI; + } + + function share(address to, uint256 tokenIdToBeShared) external returns(uint256 newTokenId) { + require(to != address(0), "ERC721: mint to the zero address"); + require(_exists(tokenIdToBeShared), "ShareableERC721: token to be shared must exist"); + + require(msg.sender == ownerOf(tokenIdToBeShared), "Method caller must be the owner of token"); + + string memory _tokenURI = tokenURI(tokenIdToBeShared); + _mint(to, _currentIndex); + _setTokenURI(_currentIndex, _tokenURI); + + emit Share(msg.sender, to, _currentIndex, tokenIdToBeShared); + + return _currentIndex; + } + + function transferFrom( + address from, + address to, + uint256 tokenId + ) public virtual override { + revert('In this reference implementation tokens are not transferrable'); + } + + function safeTransferFrom( + address from, + address to, + uint256 tokenId + ) public virtual override { + revert('In this reference implementation tokens are not transferrable'); + } +} + +``` + +## Security Considerations + +Reference implementation should not be used as is in production. +There are no other security considerations related directly to implementation of this standard. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5027.md b/EIPS/eip-5027.md new file mode 100644 index 0000000..8d0e920 --- /dev/null +++ b/EIPS/eip-5027.md @@ -0,0 +1,94 @@ +--- +eip: 5027 +title: Remove the limit on contract code size +description: Change the limit on contract size from 24576 to infinity +author: Qi Zhou (@qizhou) +discussions-to: https://ethereum-magicians.org/t/eip-5027-unlimit-contract-code-size/9010 +status: Draft +type: Standards Track +category: Core +created: 2022-04-21 +requires: 170, 2929, 2930 +--- + + +## Abstract + +Remove the limit on the contract code size, i.e., only limit the contract code size by block gas limit, with minimal changes to existing code and proper gas metering adjustment to avoid possible attacks. + + +## Motivation + +The motivation is to remove the limit on the code size so that users can deploy a large-code contract without worrying about splitting the contract into several sub-contracts. + +With the dramatic growth of dApplications, the functionalities of smart contracts are becoming more and more complicated, and thus, the sizes of newly developed contracts are steadily increasing. As a result, we are facing more and more issues with the 24576-bytes contract size limit. Although several techniques such as splitting a large contract into several sub-contracts can alleviate the issue, these techniques inevitably increase the burden of developing/deploying/maintaining smart contracts. + +The proposal implements a solution to remove the existing 24576-bytes limit of the code size. Further, the proposal aims to minimize the changes in the client implementation (e.g., Geth) with +- proper gas metering to avoid abusing the node resources for contract-related opcodes, i.e, `CODESIZE (0x38)/CODECOPY (0x39)/EXTCODESIZE (0x3B)/EXTCODECOPY (0x3C)/EXTCODEHASH (0x3F)/DELEGATECALL (0xF4)/CALL (0xF1)/CALLCODE (0xF2)/STATICCALL (0xFA)/CREATE (0xF0)/CREATE2 (0xF5)`; and +- no change to the existing structure of the Ethereum state. + + +## Specification + +### Parameters + +| Constant | Value | +| ------------------------- | ---------------- | +| `FORK_BLKNUM` | TBD | +| `CODE_SIZE_UNIT` | 24576 | +| `COLD_ACCOUNT_CODE_ACCESS_COST_PER_UNIT` | 2600 | +| `CREATE_DATA_GAS` | 200 | + +If `block.number >= FORK_BLKNUM`, the contract creation initialization can return data with any length, but the contract-related opcodes will take extra gas as defined below: + +- For `CODESIZE/CODECOPY/EXTCODESIZE/EXTCODEHASH`, the gas is unchanged. + +- For CREATE/CREATE2, if the newly created contract size > `CODE_SIZE_UNIT`, the opcodes will take extra write gas as + +`(CODE_SIZE - CODE_SIZE_UNIT) * CREATE_DATA_GAS`. + +- For `EXTCODECOPY/CALL/CALLCODE/DELEGATECALL/STATICCALL`, if the contract code size > `CODE_SIZE_UNIT`, then the opcodes will take extra gas as + +``` +(CODE_SIZE - 1) // CODE_SIZE_UNIT * COLD_ACCOUNT_CODE_ACCESS_COST_PER_UNIT +``` + +if the contract is not in `accessed_code_in_addresses` or `0` if the contract is in `accessed_code_in_addresses`, where `//` is the integer divide operator, and `accessed_code_in_addresses: Set[Address]` is a tranasction-context-wide set similar to `access_addressses` and `accessed_storage_keys`. + +When a transactoin execution begins, `accessed_code_in_addresses` will include `tx.sender`, `tx.to`, and all precompiles. + +When `CREATE/CREATE2/EXTCODECOPY/CALL/CALLCODE/DELEGATECALL/STATICCALL` is called, immediately add the address to `accessed_code_in_addresses`. + +## Rationale + +### Gas Metering +The goal is to measure the CPU/IO cost of the contract read/write operations reusing existing gas metering so that the resources will not be abused. + +- For code-size-related opcodes (`CODESIZE`/`EXTCODESIZE`), we would expect the client to implement a mapping from the hash of code to the size, so reading the code size of a large contract should still be O(1). + +- For `CODECOPY`, the data is already loaded in memory (as part of `CALL/CALLCODE/DELEGATECALL/STATICCALL`), so we do not charge extra gas. + +- For `EXTCODEHASH`, the value is already in the account, so we do not charge extra gas. + +- For `EXTCODECOPY/CALL/CALLCODE/DELEGATECALL/STATICCALL`, since it will read extra data from the database, we will additionally charge `COLD_ACCOUNT_CODE_ACCESS_COST_PER_UNIT` per extra `CODE_SIZE_UNIT`. + +- For `CREATE/CREATE2`, since it will create extra data to the database, we will additionally charge `CREATE_DATA_GAS` per extra bytes. + + +## Backwards Compatibility + +All existing contracts will not be impacted by the proposal. + +Only contracts deployed before [EIP-170](./eip-170.md) could possibly be longer than the current max code size, and the reference implementation was able to successfully import all blocks before that fork. + +## Reference Implementation + +The reference implementation on Geth is available at [0001-unlimit-code-size.patch](../assets/eip-5027/0001-unlimit-code-size.patch). + +## Security Considerations +TBD + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). + + diff --git a/EIPS/eip-5050.md b/EIPS/eip-5050.md new file mode 100644 index 0000000..aff711c --- /dev/null +++ b/EIPS/eip-5050.md @@ -0,0 +1,351 @@ +--- +eip: 5050 +title: Interactive NFTs with Modular Environments +description: Action messaging and discovery protocol for interactions on and between NFTs +author: Alexi (@alexi) +discussions-to: https://ethereum-magicians.org/t/eip-5050-nft-interaction-standard/9922 +status: Stagnant +type: Standards Track +category: ERC +created: 2021-4-18 +requires: 165, 173, 721, 1155, 1820, 4906 +--- + +## Abstract + +This standard defines a broadly applicable action messaging protocol for the transmission of user-initiated actions between tokens. Modular statefulness is achieved with optional state controller contracts (i.e. environments) that manage shared state, and provide arbitration and settlement of the action process. + +## Motivation + +Tokenized item standards such as [EIP-721](./eip-721.md) and [EIP-1155](./eip-1155.md) serve as the objects of the Ethereum computing environment. A growing number of projects are seeking to build interactivity and *"digital physics"* into NFTs, especially in the contexts of gaming and decentralized identity. A standard action messaging protocol will allow this physics layer to be developed in the same open, Ethereum-native way as the objects they operate on. + +The messaging protocol outlined defines how an action is initiated and transmitted between tokens and (optional) shared state environments. It is paired with a common interface for defining functionality that allows off-chain services to aggregate and query supported contracts for functionality and interoperability; creating a discoverable, human-readable network of interactive token contracts. Not only can contracts that implement this standard be automatically discovered by such services, their *policies for interaction* can be as well. This allows clients to easily discover compatible senders and receivers, and allowed actions. + +Aggregators can also parse action event logs to derive analytics on new action types, trending/popular/new interactive contracts, which token and state contract pairs users are likely to interact with, and other discovery tools to facilitate interaction. + +### Benefits + +1. Make interactive token contracts **discoverable and usable** by applications +2. Create a decentralized "digital physics" layer for gaming and other applications +3. Provide developers a simple solution with viable validity guarantees to make dynamic NFTs and other tokens +4. Allow for generalized action bridges to transmit actions between chains (enabling actions on L1 assets to be saved to L2s, L1 assets to interact with L2 assets, and L2 actions to be "rolled-up"/finalized on L1). + +## Specification + +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. + +Smart contracts implementing this EIP standard MUST implement the [EIP-165](./eip-165.md) supportsInterface function and MUST return the constant value `true` if the `IERC5050Sender` interface ID `0xc8c6c9f3` and/or the `IERC5050Receiver` interface ID `0x1a3f02f4` is passed through the `interfaceID` argument (depending on which interface(s) the contract implements). + +```solidity +pragma solidity ^0.8.0; + +/// @param _address The address of the interactive object +/// @param tokenId The token that is interacting (optional) +struct Object { + address _address; + uint256 _tokenId; +} + +/// @param selector The bytes4(keccack256()) encoding of the action string +/// @param user The address of the sender +/// @param from The initiating object +/// @param to The receiving object +/// @param state The state controller contract +/// @param data Additional data with no specified format +struct Action { + bytes4 selector; + address user; + Object from; + Object to; + address state; + bytes data; +} + +/// @title EIP-5050 Interactive NFTs with Modular Environments +interface IERC5050Sender { + /// @notice Send an action to the target address + /// @dev The action's `fromContract` is automatically set to `address(this)`, + /// and the `from` parameter is set to `msg.sender`. + /// @param action The action to send + function sendAction(Action memory action) external payable; + + /// @notice Check if an action is valid based on its hash and nonce + /// @dev When an action passes through all three possible contracts + /// (`fromContract`, `to`, and `state`) the `state` contract validates the + /// action with the initiating `fromContract` using a nonced action hash. + /// This hash is calculated and saved to storage on the `fromContract` before + /// action handling is initiated. The `state` contract calculates the hash + /// and verifies it and nonce with the `fromContract`. + /// @param _hash The hash to validate + /// @param _nonce The nonce to validate + function isValid(bytes32 _hash, uint256 _nonce) external returns (bool); + + /// @notice Retrieve list of actions that can be sent. + /// @dev Intended for use by off-chain applications to query compatible contracts, + /// and to advertise functionality in human-readable form. + function sendableActions() external view returns (string[] memory); + + /// @notice Change or reaffirm the approved address for an action + /// @dev The zero address indicates there is no approved address. + /// Throws unless `msg.sender` is the `_account`, or an authorized + /// operator of the `_account`. + /// @param _account The account of the account-action pair to approve + /// @param _action The action of the account-action pair to approve + /// @param _approved The new approved account-action controller + function approveForAction( + address _account, + bytes4 _action, + address _approved + ) external returns (bool); + + /// @notice Enable or disable approval for a third party ("operator") to conduct + /// all actions on behalf of `msg.sender` + /// @dev Emits the ApprovalForAll event. The contract MUST allow + /// an unbounded number of operators per owner. + /// @param _operator Address to add to the set of authorized operators + /// @param _approved True if the operator is approved, false to revoke approval + function setApprovalForAllActions(address _operator, bool _approved) + external; + + /// @notice Get the approved address for an account-action pair + /// @dev Throws if `_tokenId` is not a valid NFT. + /// @param _account The account of the account-action to find the approved address for + /// @param _action The action of the account-action to find the approved address for + /// @return The approved address for this account-action, or the zero address if + /// there is none + function getApprovedForAction(address _account, bytes4 _action) + external + view + returns (address); + + /// @notice Query if an address is an authorized operator for another address + /// @param _account The address on whose behalf actions are performed + /// @param _operator The address that acts on behalf of the account + /// @return True if `_operator` is an approved operator for `_account`, false otherwise + function isApprovedForAllActions(address _account, address _operator) + external + view + returns (bool); + + /// @dev This emits when an action is sent (`sendAction()`) + event SendAction( + bytes4 indexed name, + address _from, + address indexed _fromContract, + uint256 _tokenId, + address indexed _to, + uint256 _toTokenId, + address _state, + bytes _data + ); + + /// @dev This emits when the approved address for an account-action pair + /// is changed or reaffirmed. The zero address indicates there is no + /// approved address. + event ApprovalForAction( + address indexed _account, + bytes4 indexed _action, + address indexed _approved + ); + + /// @dev This emits when an operator is enabled or disabled for an account. + /// The operator can conduct all actions on behalf of the account. + event ApprovalForAllActions( + address indexed _account, + address indexed _operator, + bool _approved + ); +} + +interface IERC5050Receiver { + /// @notice Handle an action + /// @dev Both the `to` contract and `state` contract are called via + /// `onActionReceived()`. + /// @param action The action to handle + function onActionReceived(Action calldata action, uint256 _nonce) + external + payable; + + /// @notice Retrieve list of actions that can be received. + /// @dev Intended for use by off-chain applications to query compatible contracts, + /// and to advertise functionality in human-readable form. + function receivableActions() external view returns (string[] memory); + + /// @dev This emits when a valid action is received. + event ActionReceived( + bytes4 indexed name, + address _from, + address indexed _fromContract, + uint256 _tokenId, + address indexed _to, + uint256 _toTokenId, + address _state, + bytes _data + ); +} +``` + +### Action Naming + +Actions SHOULD use dot-separation for namespacing (e.g. `"spells.cast"` specifies the `"cast"` action with namespace `"spells"`), and arrow-separation for sequence specification (e.g. `"settle>build"` indicating `"settle"` must be received before `"build"`). + +### How State Contracts Work + +Actions do not require that a state contract be used. Actions can be transmitted from one token contract (`Object`) to another, or from a user to a single token contract. In these cases, the sending and receiving contracts each control their own state. + +State contracts allow arbitrary senders and receivers to share a user-specified state environment. Each `Object` MAY define its own action handling, which MAY include reading from the state contract during, but the action MUST be finalized by the state contract. This means the state contract serves as ground truth. + +The intended workflow is for state contracts to define stateful game environments, typically with a custom `IState` interface for use by other contracts. `Objects` register with state contracts to initialize their state. Then, users commit actions using a specific state contract to make things happen in the game. + +The modularity of state contracts allows multiple copies of the same or similar "game environment" to be created and swapped in or out by the client. There are many ways this modularity can be used: + +- Aggregator services can analyze action events to determine likely state contracts for a given sender/receiver +- Sender/receiver contracts can require a specific state contract +- Sender/receiver contracts can allow any state contract, but set a default. This is important for NFTs that change their render based on state. This default can also be configurable by the token holder. +- State contracts can be bridges to state contracts on another chain, allowing for L1-verification, L2-storage usage pattern (validate action with layer-1 assets, save on l2 where storage is cheaper). + +#### Example + +State Contract `FightGame` defines a fighting game environment. Token holders call `FightGame.register(contract, tokenId)` to randomly initialize their stats (strength/hp/etc.). An account which holds a registered token A of contract `Fighters`, calls `Fighters.sendAction(AttackAction)`, specifying token A from `Fighters` as the sender, token B from `Pacifists` contract as the receiver, and `FightGame` as the state contract. + +The action is passed to token B, which may handle the action in whatever way it wants before passing the action to the `FightGame` state contract. The state contract can verify the stored action hash with the `Fighters` contract to validate the action is authentic before updating the stats if the tokens, dealing damage to token B. + +Tokens A and B may update their metadata based on stats in the `FightGame` state contract, or based on their own stored data updated in response to sending/receiving actions. + +### Extensions + +#### Interactive + +Some contracts may have custom user interfaces that facilitate interaction. + +```solidity +pragma solidity ^0.8.0; + +/// @title EIP-5050 Interactive NFTs with Modular Environments +interface IERC5050Interactive { + function interfaceURI(bytes4 _action) external view returns (string); +} +``` + +#### Action Proxies + +Action proxies can be used to support backwards compatibility with non-upgradeable contracts, and potentially for cross-chain action bridging. + +They can be implemented using a modified version of [EIP-1820](./eip-1820.md#erc-1820-registry-smart-contract) that allows [EIP-173](./eip-173.md) contract owners to call `setManager()`. + +#### Controllable + +Users of this standard may want to allow trusted contracts to control the action process to provide security guarantees, and support action bridging. Controllers step through the action chain, calling each contract individually in sequence. + +Contracts that support Controllers SHOULD ignore require/revert statements related to action verification, and MUST NOT pass the action to the next contract in the chain. + +```solidity +pragma solidity ^0.8.0; + +/// @title EIP-5050 Action Controller +interface IControllable { + + /// @notice Enable or disable approval for a third party ("controller") to force + /// handling of a given action without performing EIP-5050 validity checks. + /// @dev Emits the ControllerApproval event. The contract MUST allow + /// an unbounded number of controllers per action. + /// @param _controller Address to add to the set of authorized controllers + /// @param _action Selector of the action for which the controller is approved / disapproved + /// @param _approved True if the controller is approved, false to revoke approval + function setControllerApproval(address _controller, bytes4 _action, bool _approved) + external; + + /// @notice Enable or disable approval for a third party ("controller") to force + /// action handling without performing EIP-5050 validity checks. + /// @dev Emits the ControllerApproval event. The contract MUST allow + /// an unbounded number of controllers per action. + /// @param _controller Address to add to the set of authorized controllers + /// @param _approved True if the controller is approved, false to revoke approval + function setControllerApprovalForAll(address _controller, bool _approved) + external; + + /// @notice Query if an address is an authorized controller for a given action. + /// @param _controller The trusted third party address that can force action handling + /// @param _action The action selector to query against + /// @return True if `_controller` is an approved operator for `_account`, false otherwise + function isApprovedController(address _controller, bytes4 _action) + external + view + returns (bool); + + /// @dev This emits when a controller is enabled or disabled for the given + /// action. The controller can force `action` handling on the emitting contract, + /// bypassing the standard EIP-5050 validity checks. + event ControllerApproval( + address indexed _controller, + bytes4 indexed _action, + bool _approved + ); + + /// @dev This emits when a controller is enabled or disabled for all actions. + /// Disabling all action approval for a controller does not override explicit action + /// action approvals. Controller's approved for all actions can force action handling + /// on the emitting contract for any action. + event ControllerApprovalForAll( + address indexed _controller, + bool _approved + ); +} +``` + +#### Metadata Update + +Interactive NFTs are likely to update their metadata in response to certain actions and developers MAY want to implement [EIP-4906](./eip-4906.md) event emitters. + +## Rationale + +The critical features of this interactive token standard are that it 1) creates a common way to define, advertise, and conduct object interaction, 2) enables optional, brokered statefulness with *useful* validity assurances at minimum gas overhead, 3) is easy for developers to implement, and 4) is easy for end-users to use. + +### Action Names & Selectors + +Actions are advertised using human-readable strings, and processed using function selectors (`bytes4(keccack256(action_key))`). Human-readable strings allow end-users to easily interpret functionality, while function selectors allow efficient comparison operations on arbitrarily long action keys. This scheme also allows for simple namespacing and sequence specification. + +Off-chain services can easily convert the strings to `bytes4` selector encoding when interacting with contracts implementing this EIP or parsing `SendAction` and `ActionReceived` event logs. + +### Validation + +Validation of the initiating contract via a hash of the action data was satisfactory to nearly everyone surveyed and was the most gas efficient verification solution explored. We recognize that this solution does not allow the receiving and state contracts to validate the initiating `user` account beyond using `tx.origin`, which is vulnerable to phishing attacks. + +We considered using a signed message to validate user-intiation, but this approach had two major drawbacks: + +1. **UX** users would be required to perform two steps to commit each action (sign the message, and send the transaction) +2. **Gas** performing signature verification is computationally expensive + +Most importantly, the consensus among the developers surveyed is that strict user validation is not necessary because the concern is only that malicious initiating contracts will phish users to commit actions *with* the malicious contract's assets. **This protocol treats the initiating contract's token as the prime mover, not the user.** Anyone can tweet at Bill Gates. Any token can send an action to another token. Which actions are accepted, and how they are handled is left up to the contracts. High-value actions can be reputation-gated via state contracts, or access-gated with allow/disallow-lists. [`Controllable`](#controllable) contracts can also be used via trusted controllers as an alternative to action chaining. + +*Alternatives considered: action transmitted as a signed message, action saved to reusable storage slot on initiating contract* + +### State Contracts + +Moving state logic into dedicated, parameterized contracts makes state an action primitive and prevents state management from being obscured within the contracts. Specifically, it allows users to decide which "environment" to commit the action in, and allows the initiating and receiving contracts to share state data without requiring them to communicate. + +The specifics of state contract interfaces are outside the scope of this standard, and are intended to be purpose-built for unique interactive environments. + +### Gas and Complexity (regarding action chaining) + +Action handling within each contract can be arbitrarily complex, and there is no way to eliminate the possibility that certain contract interactions will run out of gas. However, developers SHOULD make every effort to minimize gas usage in their action handler methods, and avoid the use of for-loops. + +*Alternatives considered: multi-request action chains that push-pull from one contract to the next.* + +## Backwards Compatibility + +Non-upgradeable, already deployed token contracts will not be compatible with this standard unless a proxy registry extension is used. + +## Reference Implementation + +A reference implementation is included in `../assets/eip-5050` with a simple stateless example [`ExampleToken2Token.sol`](../assets/eip-5050/ExampleToken2Token.sol), and a stateful example [`ExampleStateContract.sol`](../assets/eip-5050/ExampleStateContract.sol) + +## Security Considerations + +The core security consideration of this protocol is action validation. Actions are passed from one contract to another, meaning it is not possible for the receiving contract to natively verify that the caller of the initiating contract matches the `action.from` address. One of the most important contributions of this protocol is that it provides an alternative to using signed messages, which require users to perform two operations for every action committed. + +As discussed in [Validation](#validation), this is viable because the initiating contract / token is treated as the prime mover, not the user. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). \ No newline at end of file diff --git a/EIPS/eip-5058.md b/EIPS/eip-5058.md new file mode 100644 index 0000000..e6fe7b6 --- /dev/null +++ b/EIPS/eip-5058.md @@ -0,0 +1,206 @@ +--- +eip: 5058 +title: Lockable Non-Fungible Tokens +description: Lockable EIP-721 tokens +author: Tyler (@radiocaca), Alex (@gojazdev), John (@sfumato00) +discussions-to: https://ethereum-magicians.org/t/eip-5058-erc-721-lockable-standard/9201 +status: Draft +type: Standards Track +category: ERC +created: 2022-04-30 +requires: 20, 165, 721 +--- + +## Abstract + +We propose to extend the [EIP-721](./eip-721.md) standard with a secure locking mechanism. The NFT owners approve the operator to lock the NFT through `setLockApprovalForAll()` or `lockApprove()`. The approved operator locks the NFT through `lock()`. The locked NFTs cannot be transferred until the end of the locking period. An immediate use case is to allow NFTs to participate in smart contracts without leaving the wallets of their owners. + +## Motivation + +NFTs, enabled by [EIP-721](./eip-721.md), have exploded in demand. The total market value and the ecosystem continue to grow with more and more blue chip NFTs, which are approximately equivalent to popular intellectual properties in a conventional sense. Despite the vast success, something is left to be desired. Liquidity has always been one of the biggest challenges for NFTs. Several attempts have been made to tackle the liquidity challenge: NFTFi and BendDAO, to name a few. Utilizing the currently prevalent EIP-721 standard, these projects require participating NFTs to be transferred to the projects' contracts, which poses inconveniences and risks to the owners: + +1. Smart contract risks: NFTs can be lost or stolen due to bugs or vulnerabilities in the contracts. +2. Loss of utility: NFTs have utility values, such as profile pictures and bragging rights, which are lost when the NFTs are no longer seen under the owners' custody. +3. Missing Airdrops: The owners can no longer directly receive airdrops entitled to the NFTs. Considering the values and price fluctuation of some of the airdrops, either missing or not getting the airdrop on time can financially impact the owners. + +All of the above are bad UX, and we believe the EIP-721 standard can be improved by adopting a native locking mechanism: + +1. Instead of being transferred to a smart contract, an NFT remains in self-custody but locked. +2. While an NFT is locked, its transfer is prohibited. Other properties remain unaffected. +3. The owners can receive or claim airdrops themselves. + +The value of an NFT can be reflected in two aspects: collection value and utility value. Collection value needs to ensure that the holder's wallet retains ownership of the NFT forever. Utility value requires ensuring that the holder can verify their NFT ownership in other projects. Both of these aspects require that the NFT remain in its owner's wallet. + +The proposed standard allows the underlying NFT assets to be managed securely and conveniently by extending the EIP-721 standard to natively support common NFTFi use cases including locking, staking, lending, and crowdfunding. We believe the proposed standard will encourage NFT owners to participate more actively in NFTFi projects and, hence, improve the livelihood of the whole NFT ecosystem. + +## Specification + +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. + +Lockable EIP-721 **MUST** implement the `IERC5058` interfaces: + +```solidity +// SPDX-License-Identifier: CC0-1.0 + +pragma solidity ^0.8.8; + +/** + * @dev EIP-721 Non-Fungible Token Standard, optional lockable extension + * ERC721 Token that can be locked for a certain period and cannot be transferred. + * This is designed for a non-escrow staking contract that comes later to lock a user's NFT + * while still letting them keep it in their wallet. + * This extension can ensure the security of user tokens during the staking period. + * If the nft lending protocol is compatible with this extension, the trouble caused by the NFT + * airdrop can be avoided, because the airdrop is still in the user's wallet + */ +interface IERC5058 { + /** + * @dev Emitted when `tokenId` token is locked by `operator` from `from`. + */ + event Locked(address indexed operator, address indexed from, uint256 indexed tokenId, uint256 expired); + + /** + * @dev Emitted when `tokenId` token is unlocked by `operator` from `from`. + */ + event Unlocked(address indexed operator, address indexed from, uint256 indexed tokenId); + + /** + * @dev Emitted when `owner` enables `approved` to lock the `tokenId` token. + */ + event LockApproval(address indexed owner, address indexed approved, uint256 indexed tokenId); + + /** + * @dev Emitted when `owner` enables or disables (`approved`) `operator` to lock all of its tokens. + */ + event LockApprovalForAll(address indexed owner, address indexed operator, bool approved); + + /** + * @dev Returns the locker who is locking the `tokenId` token. + * + * Requirements: + * + * - `tokenId` must exist. + */ + function lockerOf(uint256 tokenId) external view returns (address locker); + + /** + * @dev Lock `tokenId` token until the block number is greater than `expired` to be unlocked. + * + * Requirements: + * + * - `tokenId` token must be owned by `owner`. + * - `expired` must be greater than block.number + * - If the caller is not `owner`, it must be approved to lock this token + * by either {lockApprove} or {setLockApprovalForAll}. + * + * Emits a {Locked} event. + */ + function lock(uint256 tokenId, uint256 expired) external; + + /** + * @dev Unlock `tokenId` token. + * + * Requirements: + * + * - `tokenId` token must be owned by `owner`. + * - the caller must be the operator who locks the token by {lock} + * + * Emits a {Unlocked} event. + */ + function unlock(uint256 tokenId) external; + + /** + * @dev Gives permission to `to` to lock `tokenId` token. + * + * Requirements: + * + * - The caller must own the token or be an approved lock operator. + * - `tokenId` must exist. + * + * Emits an {LockApproval} event. + */ + function lockApprove(address to, uint256 tokenId) external; + + /** + * @dev Approve or remove `operator` as an lock operator for the caller. + * Operators can call {lock} for any token owned by the caller. + * + * Requirements: + * + * - The `operator` cannot be the caller. + * + * Emits an {LockApprovalForAll} event. + */ + function setLockApprovalForAll(address operator, bool approved) external; + + /** + * @dev Returns the account lock approved for `tokenId` token. + * + * Requirements: + * + * - `tokenId` must exist. + */ + function getLockApproved(uint256 tokenId) external view returns (address operator); + + /** + * @dev Returns if the `operator` is allowed to lock all of the assets of `owner`. + * + * See {setLockApprovalForAll} + */ + function isLockApprovedForAll(address owner, address operator) external view returns (bool); + + /** + * @dev Returns if the `tokenId` token is locked. + */ + function isLocked(uint256 tokenId) external view returns (bool); + + /** + * @dev Returns the `tokenId` token lock expired time. + */ + function lockExpiredTime(uint256 tokenId) external view returns (uint256); +} +``` + +## Rationale + +### NFT lock approvals + +An NFT owner can give another trusted operator the right to lock his NFT through the approve functions. The `lockApprove()` function only approves for the specified NFT, whereas `setLockApprovalForAll()` approves for all NFTs of the collection under the wallet. When a user participates in an NFTFi project, the project contract calls `lock()` to lock the user's NFT. Locked NFTs cannot be transferred, but the NFTFi project contract can use the unlock function `unlock()` to unlock the NFT. + +### NFT lock/unlock + +Authorized project contracts have permission to lock NFT with the `lock` method. Locked NFTs cannot be transferred until the lock time expires. The project contract also has permission to unlock NFT in advance through the `unlock` function. Note that only the address of the locked NFT has permission to unlock that NFT. + +### NFT lock period + +When locking an NFT, one must specify the lock expiration block number, which must be greater than the current block number. When the current block number exceeds the expiration block number, the NFT is automatically released and can be transferred. + +### Bound NFT + +Bound NFT is an extension of this EIP, which implements the ability to mint a boundNFT during the NFT locking period. The boundNFT is identical to the locked NFT metadata and can be transferred. However, a boundNFT only exists during the NFT locking period and will be destroyed after the NFT is unlocked. +BoundNFT can be used to lend, as a staking credential for the contract. The credential can be locked in the contract, but also to the user. In NFT leasing, boundNFT can be rented to users because boundNFT is essentially equivalent to NFT. This consensus, if accepted by all projects, boundNFT will bring more creativity to NFT. + +### Bound NFT Factory + +Bound NFT Factory is a common boundNFT factory, similar to Uniswap's [EIP-20](./eip-20.md) pairs factory. It uses the create2 method to create a boundNFT contract address for any NFT deterministic. BoundNFT contract that has been created can only be controlled by the original NFT contract. + + +## Backwards Compatibility + +This standard is compatible with EIP-721. + +## Test Cases + +Test cases written using hardhat can be found [here](../assets/eip-5058/test/test.ts) + +## Reference Implementation + +You can find an implementation of this standard in the [assets](../assets/eip-5058/ERC5058.sol) folder. + +## Security Considerations + +After being locked, the NFT can not be transferred, so before authorizing locking rights to other project contracts, you must confirm that the project contract can unlock NFT. Otherwise there is a risk of NFT being permanently locked. It is recommended to give a reasonable locking period in use for projects. NFT can be automatically unlocked, which can reduce the risk to a certain extent. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5065.md b/EIPS/eip-5065.md new file mode 100644 index 0000000..9aab276 --- /dev/null +++ b/EIPS/eip-5065.md @@ -0,0 +1,53 @@ +--- +eip: 5065 +title: Instruction for transferring ether +description: Instruction for just transferring ether without transferring the flow of execution +author: Mudit Gupta (@maxsam4) +discussions-to: https://ethereum-magicians.org/t/eip-5065-instruction-for-transferring-ether/9107 +status: Stagnant +type: Standards Track +category: Core +created: 2022-04-30 +requires: 2929 +--- + +## Abstract +Add a new instruction that transfers ether to a destination address without handing over the flow of execution to it. It should work similarly to how `SELFDESTRUCT (0xFF)` transfers ether to the destination without making a call to it. + +## Motivation +From an architectural point of view, execution flow should never be handed over to an untrusted contract. Ethereum currently does not have any ideal way to transfer ether without transferring the flow of execution. People have come up with reentrancy guards and similar solutions to prevent some types of attacks but it's not an ideal solution. The only way to transfer ether from smart contracts without triggering a call is to create a dummy contract, send the precise amount of ether to it and then call `SELFDESTRUCT (0xFF)` from it. + +## Specification +Introduce a new instruction, `AIRDROP` (`0xFG`) that transfers ether to the destination without making a call to it. + +### Stack input +address: the account to send ether to. +value: value in wei to send to the account. + +### Gas + +The total gas cost should be the sum of a static cost + address_access_cost + value_to_empty_account_cost. + - Static cost: 6700 + - Dynamic cost: + 1. address_access_cost: If the target is not in `accessed_addresses`, charge `COLD_ACCOUNT_ACCESS_COST` gas, and add the address to `accessed_addresses`. Otherwise, charge `WARM_STORAGE_READ_COST` gas. Currently, `COLD_ACCOUNT_ACCESS_COST` is 2600 while `WARM_STORAGE_READ_COST` is 100. + 2. value_to_empty_account_cost: If value is not 0 and the address given points to an empty account, then value_to_empty_account_cost is the account creation gas cost which currently is 25000. An account is empty if its balance is 0, its nonce is 0 and it has no code. + + +## Rationale +This behavior is already possible by deploying a new contract that does `SELFDESTRUCT (0xFF)` but it is prohibitively expensive. In most scenarios, the contract author only wants to transfer ether rather than transferring control of the execution. ERC20 can be used as a case study for this where most users transfer funds without a post-transfer hook. + +This instruction allows contracts to safely pass ether to an untrusted address without worrying about reentrancy or other malicious things an untrusted contract can do on. + +The static gas cost is derived by subtracting the gas stipend (2300) from the positive_value_cost of `CALL (0xF1)` opcode which is currently set to 9000. + +## Backwards Compatibility +No known issues as this is a new instruction that does not affect any old instructions and does not break any valid assumptions since it make not anything impossible possible. + +## Test Cases +TODO + +## Security Considerations +No known security risks. + +## Copyright +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). diff --git a/EIPS/eip-5069.md b/EIPS/eip-5069.md new file mode 100644 index 0000000..3b81570 --- /dev/null +++ b/EIPS/eip-5069.md @@ -0,0 +1,44 @@ +--- +eip: 5069 +title: EIP Editor Handbook +description: Handy reference for EIP editors and those who want to become one +author: Pooja Ranjan (@poojaranjan), Gavin John (@Pandapip1) +discussions-to: https://ethereum-magicians.org/t/pr-5069-eip-editor-handbook/9137 +status: Living +type: Informational +created: 2022-05-02 +requires: 1 +--- + +## Abstract + +An Ethereum Improvement Proposal (EIP) is a design document providing information to the Ethereum community, or describing a new feature for Ethereum or its processes or environment. The EIP standardization process is a mechanism for proposing new features, for collecting community technical input on an issue, and for documenting the design decisions that have gone into Ethereum. Because improvement proposals are key components of Ethereum blockchain, it is important that they are well reviewed before reaching `Final` status. EIPs are stored in text files in a versioned repository which is monitored by the EIP editors. + +This EIP describes the recommended process for becoming an EIP editor. + +## Specification + +### Application and Onboarding Process + +Anyone having a good understanding of the EIP standardization and network upgrade process, intermediate level experience on the core and/or application side of the Ethereum blockchain, and willingness to contribute to the process management may apply to become an EIP editor. Potential EIP editors should have the following skills: + +- Good communication skills +- Ability to handle contentious discourse +- 1-5 spare hours per week + +The best available resource to understand the EIP process is [EIP-1](./eip-1.md). Anyone desirous of becoming an EIP editor MUST understand this document. Afterwards, participating in the EIP process by commenting on and suggesting improvements to PRs and issues will familliarize the procedure, and is recommended. The contributions of newer editors should be monitored by other EIP editors. + +Anyone meeting the above requirements may make a pull request adding themselves as an EIP editor and adding themselves to the editor list at `config/eip-editors.yml` and in [EIP-1](./eip-1.md). If every existing EIP editor approves, the author becomes a full EIP editor. This should notify the editor of relevant new proposals submitted in the EIPs repository, and they should review and merge those pull requests. + +### Special Merging Rules for this EIP + +This EIP MUST have the same rules regarding changes as [EIP-1](./eip-1.md). + +## Rationale + +- "6 months" was chosen as the cutoff for denoting `Stagnant` EIPs terminally-`Stagnant` arbitrarily. +- This EIP requires special merging rules for the same reason [EIP-1](./eip-1.md) does. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5081.md b/EIPS/eip-5081.md new file mode 100644 index 0000000..8907fba --- /dev/null +++ b/EIPS/eip-5081.md @@ -0,0 +1,91 @@ +--- +eip: 5081 +title: Expirable Trainsaction +description: This EIP adds a new transaction type of that includes expiration with a blocknum +author: Zainan Victor Zhou (@xinbenlv), Nick Johnson (@Arachnid), Konrad Feldmeier +discussions-to: https://ethereum-magicians.org/t/eip-5081-expirable-transaction/9208 +status: Draft +type: Standards Track +category: Core +created: 2022-05-06 +requires: 155, 1559, 2718, 2929, 2930 +--- + +## Abstract +This EIP adds a new transaction type of that includes expiration with a blocknum. + +## Motivation + +When a user sends a transaction `tx0` with a low gas price, sometimes it might not be high enough to be executed. +A common resolution is for the user to submit the transaction again with the same nonce and higher gas price. + +That previous `tx0` can theoretically be included in any time in the future unless a `tx` with the exact same nonce is already executed. + +When network is congested, gas price are high, for critical transactions user might try gas price that is much higher than an average day. +This cause the `tx0` choose might be very easy to executed in the average day. + +If user already uses a `tx1` with different nonce or from another account to execute the intended transaction, +there is currently no clean way to cancel it, +except for signing a new `tx0'` that shares the same nonce but with higher gas fee hoping that it will execute to *preempt*- than `tx0`. + +Given `tx0` was already high gas price, the current way of *preempting* `tx0` could be both unreliable and very costly. + +TODO(@xinbenlv): to include in the motivation: + +- Expiring transactions are transactions that have low time preference, but can easily become invalid in the future. For example, you may want to do a swap on an AMM but you don't want to pay a very high fee for it so you set the max fee to a low number. However, your transaction will almost certainly fail if it takes longer than a couple minutes to be mined. In this scenario, you would rather fail cheaply if your transaction doesn't get included quickly. + +- Similarly, there are situations where there is a limited window of availability of some asset and if your transaction doesn't mine within that period you know with certainty that it will fail. In these cases, it would be nice to be able to express that to the system and not waste unnecessary resources just to have the transaction fail. + +## Specification +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. + +### Parameters +- `FORK_BLKNUM` = `TBD` +- `CHAIN_ID` = `TBD` +- `TX_TYPE` = TBD, > 0x02 ([EIP-1559](./eip-1559.md)) + + +As of `FORK_BLOCK_NUMBER`, a new [EIP-2718](./eip-2718.md) transaction is introduced with `TransactionType` = `TX_TYPE(TBD)`. + +The intrinsic cost of the new transaction is inherited from [EIP-2930](./eip-2930.md), specifically `21000 + 16 * non-zero calldata bytes + 4 * zero calldata bytes + 1900 * access list storage key count + 2400 * access list address count`. + +The [EIP-2718](./eip-2718.md) `TransactionPayload` for this transaction is + +``` +rlp([chain_id, expire_by, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination, amount, data, access_list, signature_y_parity, signature_r, signature_s]) +``` + +The definition of `expire_by` is a block number the latest possible block to +execute this transaction. Any block with a block number `block_num > expire_by` MUST NOT execute this transaction. + +The definitions of all other fields share the same meaning with [EIP-1559](./eip-1559.md) + +The `signature_y_parity, signature_r, signature_s` elements of this transaction represent a secp256k1 signature over `keccak256(0x02 || rlp([chain_id, expire_by, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination, amount, data, access_list]))`. + +The [EIP-2718](./eip-2718.md) `ReceiptPayload` for this transaction is `rlp([status, cumulative_transaction_gas_used, logs_bloom, logs])`. + + +## Rationale +TODO + +## Backwards Compatibility +TODO + +## Security Considerations + +1. If `current_block_num` is available, client MUST drop and stop propagating/broadcasting any transactions that has a +`transacton_type == TX_TYPE` AND `current_block_num > expire_by` + +2. It is suggested but not required that a `currentBlockNum` SHOULD be made available to client. Any client doing PoW calculation on blocks expire tx or propagating such are essentially penalized for wasting of work, mitigating possible denial of service attack. + +3. It is suggested but not required that client SHOULD introduce a +`gossip_ttl` in unit of block_num as a safe net so that it only propagate +a tx if `current_block_num + gossip_ttl <= expire_by`. Backward compatibility: +for nodes that doesn't have `current_block_num` or `gossip_ttl` available, +they should be presume to be `0`. + +4. It is suggested by not required that any propagating client SHOULD properly deduct the `gossip_ttl` +based on the network environment it sees fit. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5094.md b/EIPS/eip-5094.md new file mode 100644 index 0000000..ed4fa85 --- /dev/null +++ b/EIPS/eip-5094.md @@ -0,0 +1,95 @@ +--- +eip: 5094 +title: URL Format for Ethereum Network Switching +description: A way of representing various network configurations as URLs. +author: Luc van Kampen (@lucemans), Jakob Helgesson (@svemat01), Joshua Hendrix (@thejoshuahendrix) +discussions-to: https://ethereum-magicians.org/t/5094-uri-format-for-ethereum-network-switching/9277 +status: Stagnant +type: Standards Track +category: ERC +created: 2022-05-13 +requires: 681, 831 +--- + +## Abstract + +This standard includes all needed information for adding a network to a wallet via URL, by including parameters such as `chainId`, `rpc_url`, `chain_name` and others, such that the network configuration is provided through the URL itself. + +## Motivation + +As observed with the use of [EIP-681](./eip-681.md) and its implementation in current mobile wallets, transactions can be made, approved, viewed, and used. However, if the wallet is instructed to perform a transaction on a chain they have not yet been configured before, the operation tends to fail. + +This is understandable, as the `chain_id` provided makes up only one part of what is required to connect to a network. This EIP aims to introduce a new type of URL for usage with deep-linking, QR, and more, to allow users to seamlessly add new networks to their (for ex. mobile) wallet to then be able to more easily partake in `pay-`, `tx-`, or other Ethereum URL interactions. + +As an extension to [EIP-831](./eip-831.md) and neighboring [EIP-681](./eip-681.md) and [EIP-2400](./eip-2400.md), this document aims to standardize the addition of new networks and switching thereof through the means of URLs. User convenience in this case is primary. + +Introduction of this EIP is meant to bridge to a safer RPC listing system to be introduced in the near future. + +## Specification + +### Syntax + +Network Switching URLs contain "ethereum" in their schema (protocol) part and are constructed as follows: + + network_add = erc831_part "add" "@" chain_id [ "/" ] "?" parameters + erc831_part = "ethereum:network-" + chain_id = 1*DIGIT + parameters = parameter *( "&" parameter ) + parameter = key "=" value + key = required_keys / optional_keys + required_keys = "rpc_url" / "chain_name" + optional_keys = "name" / "symbol" / "decimals" / "explorer_url" / "icon_url" + value = STRING / number + number = 1*DIGIT + +`STRING` is a URL-encoded Unicode string of arbitrary length, where delimiters and the +percentage symbol (`%`) are mandatorily hex-encoded with a `%` prefix. + +If the *key* in the parameter is `decimals` the *value* MUST be a `number`. + +### Semantics + +`chain_id` is mandatory and denotes the decimal chain ID, such that we have the identifier of the network we would like to add. + +`rpc_url` is represented as an array of RPC URLs. A minimum of 1 `rpc_url` MUST be present, in the format of `rpc_url=https%3A%2F%2Fpolygon-rpc.com`, or when multiple present `rpc_url=https%3A%2F%2Fpolygon-rpc.com&rpc_url=https%3A%2F%2Frpc-mainnet.matic.network`. + +`chain_name` is required to specify the name of the network to be added. + +`name` and `symbol` if provided, SHOULD be a human-readable string representing the native token. + +`decimals` if provided, MUST be a non-negative integer representing the decimal precision of the native token. + +`explorer_url` if provided, MUST specify one or more URLs pointing to block explorer web sites for the chain. + +`icon_url` if provided, MUST specify one or more URLs pointing to reasonably sized images that can be used to visually identify the chain. + +An example of adding a network with RPC endpoints `https://rpc-polygon.com` and `https://rpc-mainnet.matic.network`, the name `Polygon Mainnet`, token `Matic`, symbol `MATIC`, decimals `18`, explorer at `https://polygonscan.com/`, and Chain ID `137` would look as follows: + +```URL +ethereum:network-add@137/?chain_name=Polygon%20Mainnet&rpc_url=https%3A%2F%2Frpc-polygon.com&rpc_url=https%3A%2F%2Frpc-mainnet.matic.network&name=Matic&symbol=MATIC&decimals=18&explorer_url=https%3A%2F%2Fpolygonscan.com +``` + +## Rationale + +In furtherance of the Ethereum URL saga, network configuration is a needed addition to the possibility of Ethereum URLs. This would improve functionality for URLs, and offer non-mainnet users a way to connect without needing to configure their wallet by hand. + +The URL follows [EIP-831](./eip-831.md) with the `PREFIX` being `network` and the `PAYLOAD` being a composite of `add` and [EIP-681](./eip-681.md)-like `chain_id` and parameters. + +The choice for `PREFIX` being `network` is to allow further expansion and allow variants following the pattern `network-x`. + +An example URL for adding the Optimism Network + +```URL +ethereum:network-add@10/?chain_name=Optimistic%20Ethereum +&rpc_url=https%3A%2F%2Fmainnet.optimism.io&name=Ethereum&symbol=ETH&decimals=18&explorer_url=https%3A%2F%2Foptimistic.etherscan.io +``` + +The specification allows for a multitude of `rpc_url` and `explorer_url` to be specified. This is done such to overlap with parsing of the `TYPE` mentioned in [EIP-681](./eip-681.md). + +## Security Considerations + +URLs can be malformed to deceive users. Users SHOULD confirm source of URL before using any links. As well as checking source and transaction details before confirming any transactions. Applications SHOULD display network config, prior to network addition, such that users can confirm the validity of the network configuration being added. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5095.md b/EIPS/eip-5095.md new file mode 100644 index 0000000..a2f9899 --- /dev/null +++ b/EIPS/eip-5095.md @@ -0,0 +1,551 @@ +--- +eip: 5095 +title: Principal Token +description: Principal tokens (zero-coupon tokens) are redeemable for a single underlying EIP-20 token at a future timestamp. +author: Julian Traversa (@JTraversa), Robert Robbins (@robrobbins), Alberto Cuesta Cañada (@alcueca) +discussions-to: https://ethereum-magicians.org/t/eip-5095-principal-token-standard/9259 +status: Stagnant +type: Standards Track +category: ERC +created: 2022-05-01 +requires: 20, 2612 +--- + +## Abstract + +Principal tokens represent ownership of an underlying [EIP-20](./eip-20.md) token at a future timestamp. + +This specification is an extension on the [EIP-20](./eip-20.md) token that provides basic functionality for depositing +and withdrawing tokens and reading balances and the [EIP-2612](./eip-2612.md) specification that provides +[EIP-712](./eip-712.md) signature based approvals. + +## Motivation + +Principal tokens lack standardization which has led to a difficult to navigate development space and diverse implementation +schemes. + +The primary examples include yield tokenization platforms which strip future yield leaving a principal +token behind, as well as fixed-rate money-markets which utilize principal tokens as a medium +to lend/borrow. + +This inconsistency in implementation makes integration difficult at the application layer as well as +wallet layer which are key catalysts for the space's growth. +Developers are currently expected to implement individual adapters for each principal token, as well as adapters for +their pool contracts, and many times adapters for their custodial contracts as well, wasting significant developer resources. + +## Specification + +All Principal Tokens (PTs) MUST implement [EIP-20](./eip-20.md) to represent ownership of future underlying redemption. +If a PT is to be non-transferrable, it MAY revert on calls to `transfer` or `transferFrom`. +The [EIP-20](./eip-20.md) operations `balanceOf`, `transfer`, `totalSupply`, etc. operate on the Principal Token balance. + +All Principal Tokens MUST implement [EIP-20](./eip-20.md)'s optional metadata extensions. +The `name` and `symbol` functions SHOULD reflect the underlying token's `name` and `symbol` in some way, as well as the origination protocol, and in the case of yield tokenization protocols, the origination money-market. + +All Principal Tokens MAY implement [EIP-2612](./eip-2612.md) to improve the UX of approving PTs on various integrations. + +### Definitions: + +- underlying: The token that Principal Tokens are redeemable for at maturity. + Has units defined by the corresponding [EIP-20](./eip-20.md) contract. +- maturity: The timestamp (unix) at which a Principal Token matures. Principal Tokens become redeemable for underlying at or after this timestamp. +- fee: An amount of underlying or Principal Token charged to the user by the Principal Token. Fees can exist on redemption or post-maturity yield. +- slippage: Any difference between advertised redemption value and economic realities of PT redemption, which is not accounted by fees. + +### Methods + +#### `underlying` + +The address of the underlying token used by the Principal Token for accounting, and redeeming. + +MUST be an EIP-20 token contract. + +MUST _NOT_ revert. + +```yaml +- name: underlying + type: function + stateMutability: view + + inputs: [] + + outputs: + - name: underlyingAddress + type: address +``` + +#### `maturity` + +The unix timestamp (uint256) at or after which Principal Tokens can be redeemed for their underlying deposit. + +MUST _NOT_ revert. + +```yaml +- name: maturity + type: function + stateMutability: view + + inputs: [] + + outputs: + - name: timestamp + type: uint256 +``` + +#### `convertToUnderlying` + +The amount of underlying that would be exchanged for the amount of PTs provided, in an ideal scenario where all the conditions are met. + +Before maturity, the amount of underlying returned is as if the PTs would be at maturity. + +MUST NOT be inclusive of any fees that are charged against redemptions. + +MUST NOT show any variations depending on the caller. + +MUST NOT reflect slippage or other on-chain conditions, when performing the actual redemption. + +MUST NOT revert unless due to integer overflow caused by an unreasonably large input. + +MUST round down towards 0. + +This calculation MAY NOT reflect the "per-user" price-per-principal-token, and instead should reflect the "average-user's" price-per-principal-token, meaning what the average user should expect to see when exchanging to and from. + +```yaml +- name: convertToUnderlying + type: function + stateMutability: view + + inputs: + - name: principalAmount + type: uint256 + + outputs: + - name: underlyingAmount + type: uint256 +``` + +#### `convertToPrincipal` + +The amount of principal tokens that the principal token contract would request for redemption in order to provide the amount of underlying specified, in an ideal scenario where all the conditions are met. + +MUST NOT be inclusive of any fees. + +MUST NOT show any variations depending on the caller. + +MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. + +MUST NOT revert unless due to integer overflow caused by an unreasonably large input. + +MUST round down towards 0. + +This calculation MAY NOT reflect the "per-user" price-per-principal-token, and instead should reflect the "average-user's" price-per-principal-token, meaning what the average user should expect to see when redeeming. + +```yaml +- name: convertToPrincipal + type: function + stateMutability: view + + inputs: + - name: underlyingAmount + type: uint256 + + outputs: + - name: principalAmount + type: uint256 +``` + +#### `maxRedeem` + +Maximum amount of principal tokens that can be redeemed from the `holder` balance, through a `redeem` call. + +MUST return the maximum amount of principal tokens that could be transferred from `holder` through `redeem` and not cause a revert, which MUST NOT be higher than the actual maximum that would be accepted (it should underestimate if necessary). + +MUST factor in both global and user-specific limits, like if redemption is entirely disabled (even temporarily) it MUST return 0. + +MUST NOT revert. + +```yaml +- name: maxRedeem + type: function + stateMutability: view + + inputs: + - name: holder + type: address + + outputs: + - name: maxPrincipalAmount + type: uint256 +``` + +#### `previewRedeem` + +Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block, given current on-chain conditions. + +MUST return as close to and no more than the exact amount of underliyng that would be obtained in a `redeem` call in the same transaction. I.e. `redeem` should return the same or more `underlyingAmount` as `previewRedeem` if called in the same transaction. + +MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the redemption would be accepted, regardless if the user has enough principal tokens, etc. + +MUST be inclusive of redemption fees. Integrators should be aware of the existence of redemption fees. + +MUST NOT revert due to principal token contract specific user/global limits. MAY revert due to other conditions that would also cause `redeem` to revert. + +Note that any unfavorable discrepancy between `convertToUnderlying` and `previewRedeem` SHOULD be considered slippage in price-per-principal-token or some other type of condition. + +```yaml +- name: previewRedeem + type: function + stateMutability: view + + inputs: + - name: principalAmount + type: uint256 + + outputs: + - name: underlyingAmount + type: uint256 +``` + +#### `redeem` + +At or after maturity, burns exactly `principalAmount` of Principal Tokens from `from` and sends `underlyingAmount` of underlying tokens to `to`. + +Interfaces and other contracts MUST NOT expect fund custody to be present. While custodial redemption of Principal Tokens through the Principal Token contract is extremely useful for integrators, some protocols may find giving the Principal Token itself custody breaks their backwards compatibility. + +MUST emit the `Redeem` event. + +MUST support a redeem flow where the Principal Tokens are burned from `holder` directly where `holder` is `msg.sender` or `msg.sender` has EIP-20 approval over the principal tokens of `holder`. +MAY support an additional flow in which the principal tokens are transferred to the Principal Token contract before the `redeem` execution, and are accounted for during `redeem`. + +MUST revert if all of `principalAmount` cannot be redeemed (due to withdrawal limit being reached, slippage, the holder not having enough Principal Tokens, etc). + +Note that some implementations will require pre-requesting to the Principal Token before a withdrawal may be performed. Those methods should be performed separately. + +```yaml +- name: redeem + type: function + stateMutability: nonpayable + + inputs: + - name: principalAmount + type: uint256 + - name: to + type: address + - name: from + type: address + + outputs: + - name: underlyingAmount + type: uint256 +``` + +#### `maxWithdraw` + +Maximum amount of the underlying asset that can be redeemed from the `holder` principal token balance, through a `withdraw` call. + +MUST return the maximum amount of underlying tokens that could be redeemed from `holder` through `withdraw` and not cause a revert, which MUST NOT be higher than the actual maximum that would be accepted (it should underestimate if necessary). + +MUST factor in both global and user-specific limits, like if withdrawals are entirely disabled (even temporarily) it MUST return 0. + +MUST NOT revert. + +```yaml +- name: maxWithdraw + type: function + stateMutability: view + + inputs: + - name: holder + type: address + + outputs: + - name: maxUnderlyingAmount + type: uint256 +``` + +#### `previewWithdraw` + +Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block, given current on-chain conditions. + +MUST return as close to and no fewer than the exact amount of principal tokens that would be burned in a `withdraw` call in the same transaction. I.e. `withdraw` should return the same or fewer `principalAmount` as `previewWithdraw` if called in the same transaction. + +MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though the withdrawal would be accepted, regardless if the user has enough principal tokens, etc. + +MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. + +MUST NOT revert due to principal token contract specific user/global limits. MAY revert due to other conditions that would also cause `withdraw` to revert. + +Note that any unfavorable discrepancy between `convertToPrincipal` and `previewWithdraw` SHOULD be considered slippage in price-per-principal-token or some other type of condition. + +```yaml +- name: previewWithdraw + type: function + stateMutability: view + + inputs: + - name: underlyingAmount + type: uint256 + + outputs: + - name: principalAmount + type: uint256 +``` + +#### `withdraw` + +Burns `principalAmount` from `holder` and sends exactly `underlyingAmount` of underlying tokens to `receiver`. + +MUST emit the `Redeem` event. + +MUST support a withdraw flow where the principal tokens are burned from `holder` directly where `holder` is `msg.sender` or `msg.sender` has [EIP-20](./eip-20.md) approval over the principal tokens of `holder`. + MAY support an additional flow in which the principal tokens are transferred to the principal token contract before the `withdraw` execution, and are accounted for during `withdraw`. + +MUST revert if all of `underlyingAmount` cannot be withdrawn (due to withdrawal limit being reached, slippage, the holder not having enough principal tokens, etc). + +Note that some implementations will require pre-requesting to the principal token contract before a withdrawal may be performed. Those methods should be performed separately. + +```yaml +- name: withdraw + type: function + stateMutability: nonpayable + + inputs: + - name: underlyingAmount + type: uint256 + - name: receiver + type: address + - name: holder + type: address + + outputs: + - name: principalAmount + type: uint256 +``` + +### Events + +#### Redeem + +`from` has exchanged `principalAmount` of Principal Tokens for `underlyingAmount` of underlying, and transferred that underlying to `to`. + +MUST be emitted when Principal Tokens are burnt and underlying is withdrawn from the contract in the `EIP5095.redeem` method. + +```yaml +- name: Redeem + type: event + + inputs: + - name: from + indexed: true + type: address + - name: to + indexed: true + type: address + - name: amount + indexed: false + type: uint256 +``` + +## Rationale + +The Principal Token interface is designed to be optimized for integrators with a core minimal interface alongside optional interfaces to enable backwards compatibility. Details such as accounting and management of underlying are intentionally not specified, as Principal Tokens are expected to be treated as black boxes on-chain and inspected off-chain before use. + +[EIP-20](./eip-20.md) is enforced as implementation details such as token approval and balance calculation directly carry over. This standardization makes Principal Tokens immediately compatible with all [EIP-20](./eip-20.md) use cases in addition to EIP-5095. + +All principal tokens are redeemable upon maturity, with the only variance being whether further yield is generated post-maturity. Given the ubiquity of redemption, the presence of `redeem` allows integrators to purchase Principal Tokens on an open market, and them later redeem them for a fixed-yield solely knowing the address of the Principal Token itself. + +This EIP draws heavily on the design of [EIP-4626](./eip-4626.md) because technically Principal Tokens could be described as a subset of Yield Bearing Vaults, extended with a `maturity` variable and restrictions on the implementation. However, extending [EIP-4626](./eip-4626.md) would force PT implementations to include methods (namely, `mint` and `deposit`) that are not necessary to the business case that PTs solve. It can also be argued that partial redemptions (implemented via `withdraw`) are rare for PTs. + +PTs mature at a precise second, but given the reactive nature of smart contracts, there can't be an event marking maturity, because there is no guarantee of any activity at or after maturity. Emitting an event to notify of maturity in the first transaction after maturity would be imprecise and expensive. Instead, integrators are recommended to either use the first `Redeem` event, or to track themselves when each PT is expected to have matured. + +## Backwards Compatibility + +This EIP is fully backward compatible with the [EIP-20](./eip-20.md) specification and has no known compatibility issues with other standards. +For production implementations of Principal Tokens which do not use EIP-5095, wrapper adapters can be developed and used, or wrapped tokens can be implemented. + +## Reference Implementation + +``` +// SPDX-License-Identifier: MIT +pragma solidity 0.8.14; + +import {ERC20} from "yield-utils-v2/contracts/token/ERC20.sol"; +import {MinimalTransferHelper} from "yield-utils-v2/contracts/token/MinimalTransferHelper.sol"; + +contract ERC5095 is ERC20 { + using MinimalTransferHelper for ERC20; + + /* EVENTS + *****************************************************************************************************************/ + + event Redeem(address indexed from, address indexed to, uint256 underlyingAmount); + + /* MODIFIERS + *****************************************************************************************************************/ + + /// @notice A modifier that ensures the current block timestamp is at or after maturity. + modifier afterMaturity() virtual { + require(block.timestamp >= maturity, "BEFORE_MATURITY"); + _; + } + + /* IMMUTABLES + *****************************************************************************************************************/ + + ERC20 public immutable underlying; + uint256 public immutable maturity; + + /* CONSTRUCTOR + *****************************************************************************************************************/ + + constructor( + string memory name_, + string memory symbol_, + uint8 decimals_, + ERC20 underlying_, + uint256 maturity_ + ) ERC20(name_, symbol_, decimals_) { + underlying = underlying_; + maturity = maturity_; + } + + /* CORE FUNCTIONS + *****************************************************************************************************************/ + + /// @notice Burns an exact amount of principal tokens in exchange for an amount of underlying. + /// @dev This reverts if before maturity. + /// @param principalAmount The exact amount of principal tokens to be burned. + /// @param from The owner of the principal tokens to be redeemed. If not msg.sender then must have prior approval. + /// @param to The address to send the underlying tokens. + /// @return underlyingAmount The total amount of underlying tokens sent. + function redeem( + uint256 principalAmount, + address from, + address to + ) public virtual afterMaturity returns (uint256 underlyingAmount) { + _decreaseAllowance(from, principalAmount); + + // Check for rounding error since we round down in previewRedeem. + require((underlyingAmount = _previewRedeem(principalAmount)) != 0, "ZERO_ASSETS"); + + _burn(from, principalAmount); + + emit Redeem(from, to, principalAmount); + + _transferOut(to, underlyingAmount); + } + + /// @notice Burns a calculated amount of principal tokens in exchange for an exact amount of underlying. + /// @dev This reverts if before maturity. + /// @param underlyingAmount The exact amount of underlying tokens to be received. + /// @param from The owner of the principal tokens to be redeemed. If not msg.sender then must have prior approval. + /// @param to The address to send the underlying tokens. + /// @return principalAmount The total amount of underlying tokens redeemed. + function withdraw( + uint256 underlyingAmount, + address from, + address to + ) public virtual afterMaturity returns (uint256 principalAmount) { + principalAmount = _previewWithdraw(underlyingAmount); // No need to check for rounding error, previewWithdraw rounds up. + + _decreaseAllowance(from, principalAmount); + + _burn(from, principalAmount); + + emit Redeem(from, to, principalAmount); + + _transferOut(to, underlyingAmount); + } + + /// @notice An internal, overridable transfer function. + /// @dev Reverts on failed transfer. + /// @param to The recipient of the transfer. + /// @param amount The amount of the transfer. + function _transferOut(address to, uint256 amount) internal virtual { + underlying.safeTransfer(to, amount); + } + + /* ACCOUNTING FUNCTIONS + *****************************************************************************************************************/ + + /// @notice Calculates the amount of underlying tokens that would be exchanged for a given amount of principal tokens. + /// @dev Before maturity, it converts to underlying as if at maturity. + /// @param principalAmount The amount principal on which to calculate conversion. + /// @return underlyingAmount The total amount of underlying that would be received for the given principal amount.. + function convertToUnderlying(uint256 principalAmount) external view returns (uint256 underlyingAmount) { + return _convertToUnderlying(principalAmount); + } + + function _convertToUnderlying(uint256 principalAmount) internal view virtual returns (uint256 underlyingAmount) { + return principalAmount; + } + + /// @notice Converts a given amount of underlying tokens to principal exclusive of fees. + /// @dev Before maturity, it converts to principal as if at maturity. + /// @param underlyingAmount The total amount of underlying on which to calculate the conversion. + /// @return principalAmount The amount principal tokens required to provide the given amount of underlying. + function convertToPrincipal(uint256 underlyingAmount) external view returns (uint256 principalAmount) { + return _convertToPrincipal(underlyingAmount); + } + + function _convertToPrincipal(uint256 underlyingAmount) internal view virtual returns (uint256 principalAmount) { + return underlyingAmount; + } + + /// @notice Allows user to simulate redemption of a given amount of principal tokens, inclusive of fees and other + /// current block conditions. + /// @dev This reverts if before maturity. + /// @param principalAmount The amount of principal that would be redeemed. + /// @return underlyingAmount The amount of underlying that would be received. + function previewRedeem(uint256 principalAmount) external view afterMaturity returns (uint256 underlyingAmount) { + return _previewRedeem(principalAmount); + } + + function _previewRedeem(uint256 principalAmount) internal view virtual returns (uint256 underlyingAmount) { + return _convertToUnderlying(principalAmount); // should include fees/slippage + } + + /// @notice Calculates the maximum amount of principal tokens that an owner could redeem. + /// @dev This returns 0 if before maturity. + /// @param owner The address for which the redemption is being calculated. + /// @return maxPrincipalAmount The maximum amount of principal tokens that can be redeemed by the given owner. + function maxRedeem(address owner) public view returns (uint256 maxPrincipalAmount) { + return block.timestamp >= maturity ? _balanceOf[owner] : 0; + } + + /// @notice Allows user to simulate withdraw of a given amount of underlying tokens. + /// @dev This reverts if before maturity. + /// @param underlyingAmount The amount of underlying tokens that would be withdrawn. + /// @return principalAmount The amount of principal tokens that would be redeemed. + function previewWithdraw(uint256 underlyingAmount) external view afterMaturity returns (uint256 principalAmount) { + return _previewWithdraw(underlyingAmount); + } + + function _previewWithdraw(uint256 underlyingAmount) internal view virtual returns (uint256 principalAmount) { + return _convertToPrincipal(underlyingAmount); // should include fees/slippage + } + + /// @notice Calculates the maximum amount of underlying tokens that can be withdrawn by a given owner. + /// @dev This returns 0 if before maturity. + /// @param owner The address for which the withdraw is being calculated. + /// @return maxUnderlyingAmount The maximum amount of underlying tokens that can be withdrawn by a given owner. + function maxWithdraw(address owner) public view returns (uint256 maxUnderlyingAmount) { + return _previewWithdraw(maxRedeem(owner)); + } +} + +``` + +## Security Considerations + +Fully permissionless use cases could fall prey to malicious implementations which only conform to the interface in this EIP but not the specification, failing to implement proper custodial functionality but offering the ability to purchase Principal Tokens through secondary markets. + +It is recommended that all integrators review each implementation for potential ways of losing user deposits before integrating. + +The `convertToUnderlying` method is an estimate useful for display purposes, +and do _not_ have to confer the _exact_ amount of underlying assets their context suggests. + +As is common across many standards, it is strongly recommended to mirror the underlying token's `decimals` if at all possible, to eliminate possible sources of confusion and simplify integration across front-ends and for other off-chain users. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5114.md b/EIPS/eip-5114.md new file mode 100644 index 0000000..5c480d1 --- /dev/null +++ b/EIPS/eip-5114.md @@ -0,0 +1,97 @@ +--- +eip: 5114 +title: Soulbound Badge +description: A token that is attached to a "soul" at mint time and cannot be transferred after that. +author: Micah Zoltu (@MicahZoltu) +discussions-to: https://ethereum-magicians.org/t/eip-5114-soulbound-token/9417 +status: Review +type: Standards Track +category: ERC +created: 2022-05-30 +--- + + +## Abstract + +A soulbound badge is a token that, when minted, is bound to another Non-Fungible Token (NFT), and cannot be transferred/moved after that. + + +## Specification + +```solidity +interface IERC5114 { + // fired anytime a new instance of this badge is minted + // this event **MUST NOT** be fired twice for the same `badgeId` + event Mint(uint256 indexed badgeId, address indexed nftAddress, uint256 indexed nftTokenId); + + // returns the NFT that this badge is bound to. + // this function **MUST** throw if the badge hasn't been minted yet + // this function **MUST** always return the same result every time it is called after it has been minted + // this function **MUST** return the same value as found in the original `Mint` event for the badge + function ownerOf(uint256 badgeId) external view returns (address nftAddress, uint256 nftTokenId); + + // returns a URI with details about this badge collection + // the metadata returned by this is merged with the metadata return by `badgeUri(uint256)` + // the collectionUri **MUST** be immutable (e.g., ipfs:// and not http://) + // the collectionUri **MUST** be content addressable (e.g., ipfs:// and not http://) + // data from `badgeUri` takes precedence over data returned by this method + // any external links referenced by the content at `collectionUri` also **MUST** follow all of the above rules + function collectionUri() external pure returns (string collectionUri); + + // returns a censorship resistant URI with details about this badge instance + // the collectionUri **MUST** be immutable (e.g., ipfs:// and not http://) + // the collectionUri **MUST** be content addressable (e.g., ipfs:// and not http://) + // data from this takes precedence over data returned by `collectionUri` + // any external links referenced by the content at `badgeUri` also **MUST** follow all of the above rules + function badgeUri(uint256 badgeId) external view returns (string badgeUri); + + // returns a string that indicates the format of the `badgeUri` and `collectionUri` results (e.g., 'EIP-ABCD' or 'soulbound-schema-version-4') + function metadataFormat() external pure returns (string format); +} +``` + +Implementers of this standard **SHOULD** also depend on a standard for interface detection so callers can easily find out if a given contract implements this interface. + + +## Rationale + +### Immutability + +By requiring that badges can never move, we both guarantee non-separability and non-mergeability among collections of soulbound badges that are bound to a single NFT while simultaneously allowing users to aggressively cache results. + +### Content Addressable URIs Required + +Soulbound badges are meant to be permanent badges/indicators attached to a persona. +This means that not only can the user not transfer ownership, but the minter also cannot withdraw/transfer/change ownership as well. +This includes mutating or removing any remote content as a means of censoring or manipulating specific users. + +### No Specification for `badgeUri` Data Format + +The format of the data pointed to by `collectionUri()` and `badgeUri(uint256)`, and how to merge them, is intentionally left out of this standard in favor of separate standards that can be iterated on in the future. +The immutability constraints are the only thing defined by this to ensure that the spirit of this badge is maintained, regardless of the specifics of the data format. +The `metadataFormat` function can be used to inform a caller what type/format/version of data they should expect at the URIs, so the caller can parse the data directly without first having to deduce its format via inspection. + + +## Backwards Compatibility + +This is a new token type and is not meant to be backward compatible with any existing tokens other than existing viable souls (any asset that can be identified by `[address,id]`). + + +## Security Considerations + +Users of badges that claim to implement this EIP must be diligent in verifying they actually do. +A badge author can create a badge that, upon initial probing of the API surface, may appear to follow the rules when in reality it doesn't. +For example, the contract could allow transfers via some mechanism and simply not utilize them initially. + +It should also be made clear that soulbound badges are not bound to a human, they are bound to a persona. +A persona is any actor (which could be a group of humans) that collects multiple soulbound badges over time to build up a collection of badges. +This persona may transfer to another human, or to another group of humans, and anyone interacting with a persona should not assume that there is a single permanent human behind that persona. + +It is possible for a soulbound badge to be bound to another soulbound badge. +In theory, if all badges in the chain are created at the same time they could form a loop. +Software that tries to walk such a chain should take care to have an exit strategy if a loop is detected. + + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5115.md b/EIPS/eip-5115.md new file mode 100644 index 0000000..d930524 --- /dev/null +++ b/EIPS/eip-5115.md @@ -0,0 +1,349 @@ +--- +eip: 5115 +title: SY Token +description: Interface for wrapped yield-bearing tokens. +author: Vu Nguyen (@mrenoon), Long Vuong (@UncleGrandpa925), Anton Buenavista (@ayobuenavista) +discussions-to: https://ethereum-magicians.org/t/eip-5115-super-composable-yield-token-standard/9423 +status: Draft +type: Standards Track +category: ERC +created: 2022-05-30 +requires: 20 +--- + +## Abstract + +This standard proposes an API for wrapped yield-bearing tokens within smart contracts. It is an extension on the [EIP-20](./eip-20.md) token that provides basic functionality for transferring, depositing, withdrawing tokens, as well as reading balances. + +## Motivation + +Yield generating mechanisms are built in all shapes and sizes, necessitating a manual integration every time a protocol builds on top of another protocol’s yield generating mechanism. + +[EIP-4626](./eip-4626.md) tackled a significant part of this fragmentation by standardizing the interfaces for vaults, a major category among various yield generating mechanisms. + +In this EIP, we’re extending the coverage to include assets beyond EIP-4626’s reach, namely: + +- yield-bearing assets that have different input tokens used for minting vs accounting for the pool value. + - This category includes AMM liquidity tokens (which are yield-bearing assets that yield swap fees) since the value of the pool is measured in “liquidity units” (for example, $\sqrt k$ in UniswapV2, as defined in UniswapV2 whitepaper) which can’t be deposited in (as they are not tokens). + - This extends the flexibility in minting the yield-bearing assets. For example, there could be an ETH vault that wants to allow users to deposit cETH directly instead of ETH, for gas efficiency or UX reasons. +- Assets with reward tokens by default (e.g. COMP rewards for supplying in Compound). The reward tokens are expected to be sold to compound into the same asset. +- This EIP can be extended further to include the handling of rewards, such as the claiming of accrued multiple rewards tokens. + +While EIP-4626 is a well-designed and suitable standard for most vaults, there will inevitably be some yield generating mechanisms that do not fit into their category (LP tokens for instance). A more flexible standard is required to standardize the interaction with all types of yield generating mechanisms. + +Therefore, we are proposing Standardized Yield (SY), a flexible standard for wrapped yield-bearing tokens that could cover most mechanisms in DeFi. We foresee that: + +- EIP-4626 will still be a popular vault standard, that most vaults should adopt. +- SY tokens can wrap over most yield generating mechanisms in DeFi, including EIP-4626 vaults for projects built on top of yield-bearing tokens. +- Whoever needs the functionalities of SY could integrate with the existing SY tokens or write a new SY (to wrap over the target yield-bearing token). +- Reward handling can be extended from the SY token. + +### Use Cases + +This EIP is designed for flexibility, aiming to accommodate as many yield generating mechanisms as possible. Particularly, this standard aims to be generalized enough that it supports the following use cases and more: + +- Money market supply positions + - Lending DAI in Compound, getting DAI interests and COMP rewards + - Lending ETH in BenQi, getting ETH interests and QI + AVAX rewards + - Lending USDC in Aave, getting USDC interests and stkAAVE rewards +- AMM liquidity provision + - Provide ETH + USDC to ETHUSDC pool in SushiSwap, getting swap fees in more ETH+USDC + - Provide ETH + USDC to ETHUSDC pool in SushiSwap and stake it in Sushi Onsen, getting swap fees and SUSHI rewards + - Provide USDC+DAI+USDT to 3crv pool and stake it in Convex, getting 3crv swap fees and CRV + CVX rewards +- Vault positions + - Provide ETH into Yearn EIP-4626 vault, where the vault accrues yield from Yearn’s ETH strategy + - Provide DAI into Harvest and staking it, getting DAI interests and FARM rewards +- Liquid staking positions + - Holding stETH (in Lido), getting yields in more stETH +- Liquidity mining programs + - Provide USDC in Stargate, getting STG rewards + - Provide LOOKS in LooksRare, getting LOOKS yield and WETH rewards +- Rebasing tokens + - Stake OHM into sOHM/gOHM, getting OHM rebase yield + +The EIP hopes to minimize, if not possibly eliminate, the use of customized adapters in order to interact with many different forms of yield-bearing token mechanisms. + +## Specification + +### Generic Yield Generating Pool + +We will first introduce Generic Yield Generating Pool (GYGP), a model to describe most yield generating mechanisms in DeFi. In every yield generating mechanism, there is a pool of funds, whose value is measured in **assets**. There are a number of users who contribute liquidity to the pool, in exchange for **shares** of the pool, which represents units of ownership of the pool. Over time, the value (measured in **assets**) of the pool grows, such that each **share** is worth more **assets** over time. The pool could earn a number of **reward tokens** over time, which are distributed to the users according to some logic (for example, proportionally the number of **shares**). + +Here are the more concrete definitions of the terms: + +#### GYGP Definitions: + +- **asset**: Is a unit to measure the value of the pool. At time *t*, the pool has a total value of *TotalAsset(t)* **assets**. +- **shares**: Is a unit that represents ownership of the pool. At time *t*, there are *TotalShares(t)* **shares** in total. +- **reward tokens**: Over time, the pool earns $n_{rewards}$ types of reward tokens $(n_{rewards} \ge 0)$. At time *t*, $TotalRewards_i(t)$ is the amount of **reward token *i*** that has accumulated for the pool up until time *t*. +- **exchange rate**: At time *t*, the **exchange rate** *ExchangeRate(t)* is simply how many **assets** each **shares** is worth $ExchangeRate(t) = \frac{TotalAsset(t)}{TotalShares(t)}$ +- **users**: At time *t*, each user *u* has $shares_u(t)$ **shares** in the pool, which is worth $asset_u(t) = shares_u(t) \cdot ExchangeRate(t)$ **assets**. Until time *t*, user *u* is entitled to receive a total of $rewards_{u_i}(t)$ **reward token *i***. The sum of all users’ shares, assets and rewards should be the same as the total shares, assets and rewards of the whole pool. + +#### State changes: + +1. A user deposits $d_a$ **assets** into the pool at time $t$ ($d_a$ could be negative, which means a withdraw from the pool). $d_s = d_a / ExchangeRate(t)$ new **shares** will be created and given +to user (or removed and burned from the user when $d_a$ is negative). +2. The pool earns $d_a$ (or loses $−d_a$ if $d_a$ is negative) **assets** at time $t$. The **exchange rate** simply increases (or decreases if $d_a$ is negative) due to the additional assets. +3. The pool earns $d_r$ **reward token** $i$. Every user will receive a certain amount of **reward token** $i$. + +#### Examples of GYGPs in DeFi: + +| Yield generating mechanism | Asset | Shares | Reward tokens | Exchange rate | +| --- | --- | --- | --- | --- | +| Supply USDC in Compound | USDC | cUSDC | COMP | USDC value per cUSDC, increases with USDC supply interests | +| ETH liquid staking in Lido | stETH | wstETH | None | stETH value per wstETH, increases with ETH staking rewards | +| Stake LOOKS in LooksRare Compounder | LOOKS | shares (in contract) | WETH | LOOKS value per shares, increases with LOOKS rewards | +| Stake APE in $APE Compounder | sAPE | shares (in contract) | APE | sAPE value per shares, increases with APE rewards | +| Provide ETH+USDC liquidity on Sushiswap | ETHUSDC liquidity (a pool of x ETH + y USDC has sqrt(xy) ETHUSDC liquidity) | ETHUSDC Sushiswap LP (SLP) token | None | ETHUSDC liquidity value per ETHUSDC SLP, increases due to swap fees | +| Provide ETH+USDC liquidity on Sushiswap and stake into Onsen | ETHUSDC liquidity (a pool of x ETH + y USDC has sqrt(xy) ETHUSDC liquidity) | ETHUSDC Sushiswap LP (SLP) token | SUSHI | ETHUSDC liquidity value per ETHUSDC SLP, increases due to swap fees | +| Provide BAL+WETH liquidity in Balancer (80% BAL, 20% WETH) | BALWETH liquidity (a pool of x BAL + y WETH has x^0.8*y^0.2 BALWETH liquidity) | BALWETH Balancer LP token | None | BALWETH liquidity per BALWETH Balancer LP token, increases due to swap fees | +| Provide USDC+USDT+DAI liquidity in Curve | 3crv pool’s liquidity (amount of D per 3crv token) | 3crv token | CRV | 3crv pool’s liquidity per 3crv token, increases due to swap fees | +| Provide FRAX+USDC liquidity in Curve then stake LP in Convex | BALWETH liquidity (a pool of x BAL + y WETH has x^0.8*y^0.2 BALWETH liquidity) | BALWETH Balancer LP token | None | BALWETH liquidity per BALWETH Balancer LP token, increases due to swap fees | + + +### Standardized Yield Token Standard + +#### Overview: + +Standardized Yield (SY) is a token standard for any yield generating mechanism that conforms to the GYGP model. Each SY token represents **shares** in a GYGP and allows for interacting with the GYGP via a standard interface. + +All SY tokens: + +- **MUST** implement **`EIP-20`** to represent shares in the underlying GYGP. +- **MUST** implement EIP-20’s optional metadata extensions `name`, `symbol`, and `decimals`, which **SHOULD** reflect the underlying GYGP’s accounting asset’s `name`, `symbol`, and `decimals`. +- **MAY** implement [EIP-2612](./eip-2612.md) to improve the UX of approving SY tokens on various integrations. +- **MAY** revert on calls to `transfer` and `transferFrom` if a SY token is to be non-transferable. +- The EIP-20 operations `balanceOf`, `transfer`, `totalSupply`, etc. **SHOULD** operate on the GYGP “shares”, which represent a claim to ownership on a fraction of the GYGP’s underlying holdings. + +#### SY Definitions: + +On top of the definitions above for GYGPs, we need to define 2 more concepts: + +- **input tokens**: Are tokens that can be converted into assets to enter the pool. Each SY can accept several possible input tokens $tokens_{in_{i}}$ + +- **output tokens**: Are tokens that can be redeemed from assets when exiting the pool. Each SY can have several possible output tokens $tokens_{out_{i}}$ + +#### Interface + +```solidity +interface IStandardizedYield { + event Deposit( + address indexed caller, + address indexed receiver, + address indexed tokenIn, + uint256 amountDeposited, + uint256 amountSyOut + ); + + event Redeem( + address indexed caller, + address indexed receiver, + address indexed tokenOut, + uint256 amountSyToRedeem, + uint256 amountTokenOut + ); + + function deposit( + address receiver, + address tokenIn, + uint256 amountTokenToDeposit, + uint256 minSharesOut, + bool depositFromInternalBalance + ) external returns (uint256 amountSharesOut); + + function redeem( + address receiver, + uint256 amountSharesToRedeem, + address tokenOut, + uint256 minTokenOut, + bool burnFromInternalBalance + ) external returns (uint256 amountTokenOut); + + function exchangeRate() external view returns (uint256 res); + + function getTokensIn() external view returns (address[] memory res); + + function getTokensOut() external view returns (address[] memory res); + + function yieldToken() external view returns (address); + + function previewDeposit(address tokenIn, uint256 amountTokenToDeposit) + external + view + returns (uint256 amountSharesOut); + + function previewRedeem(address tokenOut, uint256 amountSharesToRedeem) + external + view + returns (uint256 amountTokenOut); + + function name() external view returns (string memory); + + function symbol() external view returns (string memory); + + function decimals() external view returns (uint8); +} +``` + +#### Methods + +```solidity +function deposit( + address receiver, + address tokenIn, + uint256 amountTokenToDeposit, + uint256 minSharesOut, + bool depositFromInternalBalance +) external returns (uint256 amountSharesOut); +``` + +This function will deposit *amountTokenToDeposit* of input token $i$ (*tokenIn*) to mint new SY shares. + +If *depositFromInternalBalance* is set to *false*, msg.sender will need to initially deposit *amountTokenToDeposit* of input token $i$ (*tokenIn*) into the SY contract, then this function will convert the *amountTokenToDeposit* of input token $i$ into $d_a$ worth of **asset** and deposit this amount into the pool for the *receiver*, who will receive *amountSharesOut* of SY tokens (**shares**). If *depositFromInternalBalance* is set to *true*, then *amountTokenToDeposit* of input token $i$ (*tokenIn*) will be taken from receiver directly (as msg.sender), and will be converted and shares returned to the receiver similarly to the first case. + +This function should revert if $amountSharesOut \lt minSharesOut$. + +- **MUST** emit the `Deposit` event. +- **MUST** support EIP-20’s `approve` / `transferFrom` flow where `tokenIn` are taken from receiver directly (as msg.sender) or if the msg.sender has EIP-20 approved allowance over the input token of the receiver. +- **MUST** revert if $amountSharesOut \lt minSharesOut$ (due to deposit limit being reached, slippage, or the user not approving enough `tokenIn` **to the SY contract, etc). +- **MAY** be payable if the `tokenIn` depositing asset is the chain's native currency (e.g. ETH). + +```solidity +function redeem( + address receiver, + uint256 amountSharesToRedeem, + address tokenOut, + uint256 minTokenOut, + bool burnFromInternalBalance +) external returns (uint256 amountTokenOut); +``` + +This function will redeem the $d_s$ shares, which is equivalent to $d_a = d_s \times ExchangeRate(t)$ assets, from the pool. The $d_a$ assets is converted into exactly *amountTokenOut* of output token $i$ (*tokenOut*). + +If *burnFromInternalBalance* is set to *false*, the user will need to initially deposit *amountSharesToRedeem* into the SY contract, then this function will burn the floating amount $d_s$ of SY tokens (**shares**) in the SY contract to redeem to output token $i$ (*tokenOut*). This pattern is similar to UniswapV2 which allows for more gas efficient ways to interact with the contract. If *burnFromInternalBalance* is set to *true*, then this function will burn *amountSharesToRedeem* $d_s$ of SY tokens directly from the user to redeem to output token $i$ (*tokenOut*). + +This function should revert if $amountTokenOut \lt minTokenOut$. + +- **MUST** emit the `Redeem` event. +- **MUST** support EIP-20’s `approve` / `transferFrom` flow where the shares are burned from receiver directly (as msg.sender) or if the msg.sender has EIP-20 approved allowance over the shares of the receiver. +- **MUST** revert if $amountTokenOut \lt minTokenOut$ (due to redeem limit being reached, slippage, or the user not approving enough `amountSharesToRedeem` to the SY contract, etc). + +```solidity +function exchangeRate() external view returns (uint256 res); +``` + +This method updates and returns the latest **exchange rate**, which is the **exchange rate** from SY token amount into asset amount, scaled by a fixed scaling factor of 1e18. + +- **MUST** return $ExchangeRate(t_{now})$ such that $ExchangeRate(t_{now}) \times syBalance / 1e18 = assetBalance$. +- **MUST NOT** include fees that are charged against the underlying yield token in the SY contract. + +```solidity +function getTokensIn() external view returns (address[] memory res); +``` + +This read-only method returns the list of all input tokens that can be used to deposit into the SY contract. + +- **MUST** return EIP-20 token addresses. +- **MUST** return at least one address. +- **MUST NOT** revert. + +```solidity +function getTokensOut() external view returns (address[] memory res); +``` + +This read-only method returns the list of all output tokens that can be converted into when exiting the SY contract. + +- **MUST** return EIP-20 token addresses. +- **MUST** return at least one address. +- **MUST NOT** revert. + +```solidity +function yieldToken() external view returns (address); +``` + +This read-only method returns the underlying yield-bearing token (representing a GYGP) address. + +- **MUST** return a token address that conforms to the EIP-20 interface, or zero address +- **MUST NOT** revert. +- **MUST** reflect the exact underlying yield-bearing token address if the SY token is a wrapped token. +- **MAY** return 0x or zero address if the SY token is natively implemented, and not from wrapping. + +```solidity +function previewDeposit(address tokenIn, uint256 amountTokenToDeposit) + external + view + returns (uint256 amountSharesOut); +``` + +This read-only method returns the amount of shares that a user would have received if they deposit *amountTokenToDeposit* of *tokenIn*. + +- **MUST** return less than or equal of *amountSharesOut* to the actual return value of the `deposit` method, and **SHOULD NOT** return greater than the actual return value of the `deposit` method. +- **MUST NOT** revert. + +```solidity +function previewRedeem(address tokenOut, uint256 amountSharesToRedeem) + external + view + returns (uint256 amountTokenOut); +``` + +This read-only method returns the amount of *tokenOut* that a user would have received if they redeem *amountSharesToRedeem* of *tokenOut*. + +- **MUST** return less than or equal of *amountTokenOut* to the actual return value of the `redeem` method, and **SHOULD NOT** return greater than the actual return value of the `redeem` method. +- **MUST NOT** revert. + +#### Events + +```solidity +event Deposit( + address indexed caller, + address indexed receiver, + address indexed tokenIn, + uint256 amountDeposited, + uint256 amountSyOut +); +``` + +`caller` has converted exact *tokenIn* tokens into SY (shares) and transferred those SY to `receiver`. + +- **MUST** be emitted when input tokens are deposited into the SY contract via `deposit` method. + +```solidity +event Redeem( + address indexed caller, + address indexed receiver, + address indexed tokenOut, + uint256 amountSyToRedeem, + uint256 amountTokenOut +); +``` + +`caller` has converted exact SY (shares) into input tokens and transferred those input tokens to `receiver`. + +- **MUST** be emitted when input tokens are redeemed from the SY contract via `redeem` method. + +**"SY" Word Choice:** + +"SY" (pronunciation: */sʌɪ/*), an abbreviation of Standardized Yield, was found to be appropriate to describe a broad universe of standardized composable yield-bearing digital assets. + +## Rationale + +[EIP-20](./eip-20.md) is enforced because implementation details such as transfer, token approvals, and balance calculation directly carry over to the SY tokens. This standardization makes the SY tokens immediately compatible with all EIP-20 use cases. + +[EIP-165](./eip-165.md) can optionally be implemented should you want integrations to detect the IStandardizedYield interface implementation. + +[EIP-2612](./eip-2612.md) can optionally be implemented in order to improve the UX of approving SY tokens on various integrations. + +## Backwards Compatibility + +This EIP is fully backwards compatible as its implementation extends the functionality of [EIP-20](./eip-20.md), however the optional metadata extensions, namely `name`, `decimals`, and `symbol` semantics MUST be implemented for all SY token implementations. + +## Security Considerations + +Malicious implementations which conform to the interface can put users at risk. It is recommended that all integrators (such as wallets, aggregators, or other smart contract protocols) review the implementation to avoid possible exploits and users losing funds. + +`yieldToken` must strongly reflect the address of the underlying wrapped yield-bearing token. For a native implementation wherein the SY token does not wrap a yield-bearing token, but natively represents a GYGP share, then the address returned MAY be a zero address. Otherwise, for wrapped tokens, you may introduce confusion on what the SY token represents, or may be deemed malicious. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5131.md b/EIPS/eip-5131.md new file mode 100644 index 0000000..54f0935 --- /dev/null +++ b/EIPS/eip-5131.md @@ -0,0 +1,343 @@ +--- +eip: 5131 +title: SAFE Authentication For ENS +description: Using ENS Text Records to facilitate safer and more convenient signing operations. +author: Wilkins Chung (@wwhchung), Jalil Wahdatehagh (@jwahdatehagh), Cry (@crydoteth), Sillytuna (@sillytuna), Cyberpnk (@CyberpnkWin) +discussions-to: https://ethereum-magicians.org/t/eip-5131-ens-subdomain-authentication/9458 +status: Stagnant +type: Standards Track +category: ERC +created: 2022-06-03 +requires: 137, 181, 634 +--- + +## Abstract +This EIP links one or more signing wallets via Ethereum Name Service Specification ([EIP-137](./eip-137.md)) to prove control and asset ownership of a main wallet. + +## Motivation +Proving ownership of an asset to a third party application in the Ethereum ecosystem is common. Users frequently sign payloads of data to authenticate themselves before gaining access to perform some operation. However, this method--akin to giving the third party root access to one's main wallet--is both insecure and inconvenient. + +***Examples:*** + 1. In order for you to edit your profile on OpenSea, you must sign a message with your wallet. + 2. In order to access NFT gated content, you must sign a message with the wallet containing the NFT in order to prove ownership. + 3. In order to gain access to an event, you must sign a message with the wallet containing a required NFT in order to prove ownership. + 4. In order to claim an airdrop, you must interact with the smart contract with the qualifying wallet. + 5. In order to prove ownership of an NFT, you must sign a payload with the wallet that owns that NFT. + +In all the above examples, one interacts with the dApp or smart contract using the wallet itself, which may be + - inconvenient (if it is controlled via a hardware wallet or a multi-sig) + - insecure (since the above operations are read-only, but you are signing/interacting via a wallet that has write access) + +Instead, one should be able to approve multiple wallets to authenticate on behalf of a given wallet. + +### Problems with existing methods and solutions +Unfortunately, we've seen many cases where users have accidentally signed a malicious payload. The result is almost always a significant loss of assets associated with the signing address. + +In addition to this, many users keep significant portions of their assets in 'cold storage'. With the increased security from 'cold storage' solutions, we usually see decreased accessibility because users naturally increase the barriers required to access these wallets. + +Some solutions propose dedicated registry smart contracts to create this link, or new protocols to be supported. This is problematic from an adoption standpoint, and there have not been any standards created for them. + +### Proposal: Use the Ethereum Name Service (EIP-137) +Rather than 're-invent the wheel', this proposal aims to use the widely adopted Ethereum Name Service in conjunction with the ENS Text Records feature ([EIP-634](./eip-634.md)) in order to achieve a safer and more convenient way to sign and authenticate, and provide 'read only' access to a main wallet via one or more secondary wallets. + +From there, the benefits are twofold. This EIP gives users increased security via outsourcing potentially malicious signing operations to wallets that are more accessible (hot wallets), while being able to maintain the intended security assumptions of wallets that are not frequently used for signing operations. + +#### Improving dApp Interaction Security +Many dApps requires one to prove control of a wallet to gain access. At the moment, this means that you must interact with the dApp using the wallet itself. This is a security issue, as malicious dApps or phishing sites can lead to the assets of the wallet being compromised by having them sign malicious payloads. + +However, this risk would be mitigated if one were to use a secondary wallet for these interactions. Malicious interactions would be isolated to the assets held in the secondary wallet, which can be set up to contain little to nothing of value. + +#### Improving Multiple Device Access Security +In order for a non-hardware wallet to be used on multiple devices, you must import the seed phrase to each device. Each time a seed phrase is entered on a new device, the risk of the wallet being compromised increases as you are increasing the surface area of devices that have knowledge of the seed phrase. + +Instead, each device can have its own unique wallet that is an authorized secondary wallet of the main wallet. If a device specific wallet was ever compromised or lost, you could simply remove the authorization to authenticate. + +Further, wallet authentication can be chained so that a secondary wallet could itself authorize one or many tertiary wallets, which then have signing rights for both the secondary address as well as the root main address. This, can allow teams to each have their own signer while the main wallet can easily invalidate an entire tree, just by revoking rights from the root stem. + +#### Improving Convenience +Many invididuals use hardware wallets for maximum security. However, this is often inconvenient, since many do not want to carry their hardware wallet with them at all times. + +Instead, if you approve a non-hardware wallet for authentication activities (such as a mobile device), you would be able to use most dApps without the need to have your hardware wallet on hand. + +## Specification +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. + +Let: + - `mainAddress` represent the wallet address we are trying to authenticate or prove asset ownership for. + - `mainENS` represent the reverse lookup ENS string for `mainAddress`. + - `authAddress` represent the address we want to use for signing in lieu of `mainAddress`. + - `authENS` represent the reverse lookup ENS string for `authAddress`. + - `authKey` represents a string in the format `[0-9A-Za-z]+`. + +Control of `mainAddress` and ownership of `mainAddress` assets by `authAddress` is proven if all the following conditions are met: + - `mainAddress` has an ENS resolver record and a reverse record set to `mainENS`. + - `authAddress` has an ENS resolver record and a reverse record set to `authENS`. + - `authENS` has an ENS TEXT record `eip5131:vault` in the format `:`. + - `mainENS` has an ENS TEXT record `eip5131:`. + +### Setting up one or many `authAddress` records on a single ENS domain +The `mainAddress` MUST have an ENS resolver record and reverse record configured. +In order to automatically discover the linked account, the `authAddress` SHOULD have an ENS resolver record and reverse record configured. + +1. Choose an unused ``. This can be any string in the format `[0-0A-Za-z]+`. +2. Set a TEXT record `eip5131:` on `mainENS`, with the value set to the desired `authAddress`. +3. Set a TEXT record `eip5131:vault` on `authENS`, with the value set to the `:mainAddress`. + +Currently this EIP does not enforce an upper-bound on the number of `authAddress` entries you can include. Users can repeat this process with as many address as they like. + +### Authenticating `mainAddress` via `authAddress` +Control of `mainAddress` and ownership of `mainAddress` assets is proven if any associated `authAddress` is the `msg.sender` or has signed the message. + +Practically, this would work by performing the following operations: +1. Get the resolver for `authENS` +2. Get the `eip5131:vault` TEXT record of `authENS` +3. Parse `:` to determine the `authKey` and `mainAddress`. +4. MUST get the reverse ENS record for `mainAddress` and verify that it matches ``. + - Otherwise one could set up other ENS nodes (with auths) that point to `mainAddress` and authenticate via those. +5. Get the `eip5131:` TEXT record of `mainENS` and ensure it matches `authAddress`. + +Note that this specification allows for both contract level and client/server side validation of signatures. It is not limited to smart contracts, which is why there is no proposed external interface definition. + +### Revocation of `authAddress` +To revoke permission of `authAddress`, delete the `eip5131:` TEXT record of `mainENS` or update it to point to a new `authAddress`. + +## Rationale + +### Usage of EIP-137 +The proposed specification makes use of EIP-137 rather than introduce another registry paradigm. The reason for this is due to the existing wide adoption of EIP-137 and ENS. + +However, the drawback to EIP-137 is that any linked `authAddress` must contain some ETH in order to set the `authENS` reverse record as well as the `eip5131:vault` TEXT record. This can be solved by a separate reverse lookup registry that enables `mainAddress` to set the reverse record and TEXT record with a message signed by `authAddress`. + +With the advent of L2s and ENS Layer 2 functionalities, off chain verification of linked addresses is possible even with domains managed across different chains. + +### One-to-Many Authentication Relationship +This proposed specification allows for a one (`mainAddress`) to many (`authAddress`) authentication relationship. i.e. one `mainAddress` can authorize many `authAddress` to authenticate, but an `authAddress` can only authenticate itself or a single `mainAddress`. + +The reason for this design choice is to allow for simplicity of authentication via client and smart contract code. You can determine which `mainAddress` the `authAddress` is signing for without any additional user input. + +Further, you can design UX without any user interaction necessary to 'pick' the interacting address by display assets owned by `authAddress` and `mainAddress` and use the appropriate address dependent on the asset the user is attempting to authenticate with. + +## Reference Implementation + +### Client/Server Side +In typescript, the validation function, using ethers.js would be as follows: +``` +export interface LinkedAddress { + ens: string, + address: string, +} + +export async function getLinkedAddress( + provider: ethers.providers.EnsProvider, address: string +): Promise { + const addressENS = await provider.lookupAddress(address); + if (!addressENS) return null; + + const vaultInfo = await (await provider.getResolver(addressENS))?.getText('eip5131:vault'); + if (!vaultInfo) return null; + + const vaultInfoArray = vaultInfo.split(':'); + if (vaultInfoArray.length !== 2) { + throw new Error('EIP5131: Authkey and vault address not configured correctly.'); + } + + const [ authKey, vaultAddress ] = vaultInfoArray; + + const vaultENS = await provider.lookupAddress(vaultAddress); + if (!vaultENS) { + throw new Error(`EIP5131: No ENS domain with reverse record set for vault.`); + }; + + const expectedSigningAddress = await ( + await provider.getResolver(vaultENS) + )?.getText(`eip5131:${authKey}`); + + if (expectedSigningAddress?.toLowerCase() !== address.toLowerCase()) { + throw new Error(`EIP5131: Authentication mismatch.`); + }; + + return { + ens: vaultENS, + address: vaultAddress + }; +} +``` + +### Contract side + +#### With a backend +If your application operates a secure backend server, you could run the client/server code above, then use the result in conjunction with specs like [EIP-1271](./eip-1271.md) : `Standard Signature Validation Method for Contracts` for a cheap and secure way to validate that the the message signer is indeed authenticated for the main address. + +#### Without a backend (JavaScript only) +Provided is a reference implementation for an internal function to verify that the message sender has an authentication link to the main address. + +``` +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +/// @author: manifold.xyz + +/** + * ENS Registry Interface + */ +interface ENS { + function resolver(bytes32 node) external view returns (address); +} + +/** + * ENS Resolver Interface + */ +interface Resolver { + function addr(bytes32 node) external view returns (address); + function name(bytes32 node) external view returns (string memory); + function text(bytes32 node, string calldata key) external view returns (string memory); +} + +/** + * Validate a signing address is associtaed with a linked address + */ +library LinkedAddress { + /** + * Validate that the message sender is an authentication address for mainAddress + * + * @param ensRegistry Address of ENS registry + * @param mainAddress The main address we want to authenticate for. + * @param mainENSNodeHash The main ENS Node Hash + * @param authKey The TEXT record of the authKey we are using for validation + * @param authENSNodeHash The auth ENS Node Hash + */ + function validateSender( + address ensRegistry, + address mainAddress, + bytes32 mainENSNodeHash, + string calldata authKey, + bytes32 authENSNodeHash + ) internal view returns (bool) { + return validate(ensRegistry, mainAddress, mainENSNodeHash, authKey, msg.sender, authENSNodeHash); + } + + /** + * Validate that the authAddress is an authentication address for mainAddress + * + * @param ensRegistry Address of ENS registry + * @param mainAddress The main address we want to authenticate for. + * @param mainENSNodeHash The main ENS Node Hash + * @param authAddress The address of the authentication wallet + * @param authENSNodeHash The auth ENS Node Hash + */ + function validate( + address ensRegistry, + address mainAddress, + bytes32 mainENSNodeHash, + string calldata authKey, + address authAddress, + bytes32 authENSNodeHash + ) internal view returns (bool) { + _verifyMainENS(ensRegistry, mainAddress, mainENSNodeHash, authKey, authAddress); + _verifyAuthENS(ensRegistry, mainAddress, authKey, authAddress, authENSNodeHash); + + return true; + } + + // ********************* + // Helper Functions + // ********************* + function _verifyMainENS( + address ensRegistry, + address mainAddress, + bytes32 mainENSNodeHash, + string calldata authKey, + address authAddress + ) private view { + // Check if the ENS nodes resolve correctly to the provided addresses + address mainResolver = ENS(ensRegistry).resolver(mainENSNodeHash); + require(mainResolver != address(0), "Main ENS not registered"); + require(mainAddress == Resolver(mainResolver).addr(mainENSNodeHash), "Main address is wrong"); + + // Verify the authKey TEXT record is set to authAddress by mainENS + string memory authText = Resolver(mainResolver).text(mainENSNodeHash, string(abi.encodePacked("eip5131:", authKey))); + require( + keccak256(bytes(authText)) == keccak256(bytes(_addressToString(authAddress))), + "Invalid auth address" + ); + } + + function _verifyAuthENS( + address ensRegistry, + address mainAddress, + string memory authKey, + address authAddress, + bytes32 authENSNodeHash + ) private view { + // Check if the ENS nodes resolve correctly to the provided addresses + address authResolver = ENS(ensRegistry).resolver(authENSNodeHash); + require(authResolver != address(0), "Auth ENS not registered"); + require(authAddress == Resolver(authResolver).addr(authENSNodeHash), "Auth address is wrong"); + + // Verify the TEXT record is appropriately set by authENS + string memory vaultText = Resolver(authResolver).text(authENSNodeHash, "eip5131:vault"); + require( + keccak256(abi.encodePacked(authKey, ":", _addressToString(mainAddress))) == + keccak256(bytes(vaultText)), + "Invalid auth text record" + ); + } + + bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; + + function sha3HexAddress(address addr) private pure returns (bytes32 ret) { + uint256 value = uint256(uint160(addr)); + bytes memory buffer = new bytes(40); + for (uint256 i = 39; i > 1; --i) { + buffer[i] = _HEX_SYMBOLS[value & 0xf]; + value >>= 4; + } + return keccak256(buffer); + } + + function _addressToString(address addr) private pure returns (string memory ptr) { + // solhint-disable-next-line no-inline-assembly + assembly { + ptr := mload(0x40) + + // Adjust mem ptr and keep 32 byte aligned + // 32 bytes to store string length; address is 42 bytes long + mstore(0x40, add(ptr, 96)) + + // Store (string length, '0', 'x') (42, 48, 120) + // Single write by offsetting across 32 byte boundary + ptr := add(ptr, 2) + mstore(ptr, 0x2a3078) + + // Write string backwards + for { + // end is at 'x', ptr is at lsb char + let end := add(ptr, 31) + ptr := add(ptr, 71) + } gt(ptr, end) { + ptr := sub(ptr, 1) + addr := shr(4, addr) + } { + let v := and(addr, 0xf) + // if > 9, use ascii 'a-f' (no conditional required) + v := add(v, mul(gt(v, 9), 39)) + // Add ascii for '0' + v := add(v, 48) + mstore8(ptr, v) + } + + // return ptr to point to length (32 + 2 for '0x' - 1) + ptr := sub(ptr, 33) + } + + return string(ptr); + } +} +``` + +## Security Considerations +The core purpose of this EIP is to enhance security and promote a safer way to authenticate wallet control and asset ownership when the main wallet is not needed and assets held by the main wallet do not need to be moved. Consider it a way to do 'read only' authentication. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5133.md b/EIPS/eip-5133.md new file mode 100644 index 0000000..ffc3e7f --- /dev/null +++ b/EIPS/eip-5133.md @@ -0,0 +1,59 @@ +--- +eip: 5133 +title: Delaying Difficulty Bomb to mid-September 2022 +description: Delays the difficulty bomb by a further 700000 blocks, to the middle of September 2022. +author: Tomasz Kajetan Stanczak (@tkstanczak), Eric Marti Haynes (@ericmartihaynes), Josh Klopfenstein (@joshklop), Abhimanyu Nag (@AbhiMan1601) +discussions-to: https://ethereum-magicians.org/t/eip-5133-delaying-difficulty-bomb-to-mid-september-2022/9622 +status: Final +type: Standards Track +category: Core +created: 2022-06-01 +--- + +## Abstract +Starting with `FORK_BLOCK_NUMBER` the client will calculate the difficulty based on a fake block number suggesting to the client that the difficulty bomb is adjusting 11,400,000 blocks later than the actual block number. + +## Motivation +To avoid network degradation due to a premature activation of the difficulty bomb. + +## Specification +#### Relax Difficulty with Fake Block Number +For the purposes of `calc_difficulty`, simply replace the use of `block.number`, as used in the exponential ice age component, with the formula: +```py +fake_block_number = max(0, block.number - 11_400_000) if block.number >= FORK_BLOCK_NUMBER else block.number +``` +## Rationale + +The following script predicts the bomb will go off at block 15530314, which is expected to be mined around mid-September. + +```python +import math +def predict_bomb_block(current_difficulty, diff_adjust_coeff, block_adjustment): + ''' + Predicts the block number at which the difficulty bomb will become noticeable. + + current_difficulty: the current difficulty + diff_adjust_coeff: intuitively, the percent increase in work that miners have to exert to find a PoW + block_adjustment: the number of blocks to delay the bomb by + ''' + return round(block_adjustment + 100000 * (2 + math.log2(diff_adjust_coeff * current_difficulty // 2048))) + +# current_difficulty = 13891609586928851 (Jun 01, 2022) +# diff_adjust_coeff = 0.1 (historically, the bomb is noticeable when the coefficient is >= 0.1) +# block_adjustment = 11400000 +print(predict_bomb_block(13891609586928851, 0.1, 11400000)) +``` + +Precise increases in block times are very difficult to predict (especially after the bomb is noticeable). +However, based on past manifestations of the bomb, we can anticipate 0.1s delays by mid-September and 0.6-1.2s delays by early October. + +## Backwards Compatibility +No known backward compatibility issues. + +## Security Considerations +Misjudging the effects of the difficulty can mean longer blocktimes than anticipated until a hardfork is released. Wild shifts in difficulty can affect this number severely. Also, gradual changes in blocktimes due to longer-term adjustments in difficulty can affect the timing of difficulty bomb epochs. This affects the usability of the network but unlikely to have security ramifications. + +In this specific instance, it is possible that the network hashrate drops considerably before The Merge, which could accelerate the timeline by which the bomb is felt in block times. The offset value chosen aims to take this into account. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5139.md b/EIPS/eip-5139.md new file mode 100644 index 0000000..c3bd497 --- /dev/null +++ b/EIPS/eip-5139.md @@ -0,0 +1,602 @@ +--- +eip: 5139 +title: Remote Procedure Call Provider Lists +description: Format for lists of RPC providers for Ethereum-like chains. +author: Sam Wilson (@SamWilsn) +discussions-to: https://ethereum-magicians.org/t/eip-5139-remote-procedure-call-provider-lists/9517 +status: Draft +type: Standards Track +category: ERC +created: 2022-06-06 +requires: 155, 1577 +--- + +## Abstract +This proposal specifies a JSON schema for describing lists of remote procedure call (RPC) providers for Ethereum-like chains, including their supported [EIP-155](./eip-155.md) `CHAIN_ID`. + +## Motivation +The recent explosion of alternate chains, scaling solutions, and other mostly Ethereum-compatible ledgers has brought with it many risks for users. It has become commonplace to blindly add new RPC providers using [EIP-3085](./eip-3085.md) without evaluating their trustworthiness. At best, these RPC providers may be accurate, but track requests; and at worst, they may provide misleading information and frontrun transactions. + +If users instead are provided with a comprehensive provider list built directly by their wallet, with the option of switching to whatever list they so choose, the risk of these malicious providers is mitigated significantly, without sacrificing functionality for advanced users. + +## Specification + +The keywords "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. + +### List Validation & Schema + +List consumers (like wallets) MUST validate lists against the provided schema. List consumers MUST NOT connect to RPC providers present only in an invalid list. + +Lists MUST conform to the following JSON Schema: + +```json +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + + "title": "Ethereum RPC Provider List", + "description": "Schema for lists of RPC providers compatible with Ethereum wallets.", + + "$defs": { + "VersionBase": { + "type": "object", + "description": "Version of a list, used to communicate changes.", + + "required": [ + "major", + "minor", + "patch" + ], + + "properties": { + "major": { + "type": "integer", + "description": "Major version of a list. Incremented when providers are removed from the list or when their chain ids change.", + "minimum": 0 + }, + + "minor": { + "type": "integer", + "description": "Minor version of a list. Incremented when providers are added to the list.", + "minimum": 0 + }, + + "patch": { + "type": "integer", + "description": "Patch version of a list. Incremented for any change not covered by major or minor versions, like bug fixes.", + "minimum": 0 + }, + + "preRelease": { + "type": "string", + "description": "Pre-release version of a list. Indicates that the version is unstable and might not satisfy the intended compatibility requirements as denoted by its major, minor, and patch versions.", + "pattern": "^[1-9A-Za-z][0-9A-Za-z]*(\\.[1-9A-Za-z][0-9A-Za-z]*)*$" + } + } + }, + + "Version": { + "type": "object", + "additionalProperties": false, + + "allOf": [ + { + "$ref": "#/$defs/VersionBase" + } + ], + + "properties": { + "major": true, + "minor": true, + "patch": true, + "preRelease": true, + "build": { + "type": "string", + "description": "Build metadata associated with a list.", + "pattern": "^[0-9A-Za-z-]+(\\.[0-9A-Za-z-])*$" + } + } + }, + + "VersionRange": { + "type": "object", + "additionalProperties": false, + + "properties": { + "major": true, + "minor": true, + "patch": true, + "preRelease": true, + "mode": true + }, + + "allOf": [ + { + "$ref": "#/$defs/VersionBase" + } + ], + + "oneOf": [ + { + "properties": { + "mode": { + "type": "string", + "enum": ["^", "="] + }, + "preRelease": false + } + }, + { + "required": [ + "preRelease", + "mode" + ], + + "properties": { + "mode": { + "type": "string", + "enum": ["="] + } + } + } + ] + }, + + "Logo": { + "type": "string", + "description": "A URI to a logo; suggest SVG or PNG of size 64x64", + "format": "uri" + }, + + "ProviderChain": { + "type": "object", + "description": "A single chain supported by a provider", + "additionalProperties": false, + "required": [ + "chainId", + "endpoints" + ], + "properties": { + "chainId": { + "type": "integer", + "description": "Chain ID of an Ethereum-compatible network", + "minimum": 1 + }, + "endpoints": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "type": "string", + "format": "uri" + } + } + } + }, + + "Provider": { + "type": "object", + "description": "Description of an RPC provider.", + "additionalProperties": false, + + "required": [ + "chains", + "name" + ], + + "properties": { + "name": { + "type": "string", + "description": "Name of the provider.", + "minLength": 1, + "maxLength": 40, + "pattern": "^[ \\w.'+\\-%/À-ÖØ-öø-ÿ:&\\[\\]\\(\\)]+$" + }, + "logo": { + "$ref": "#/$defs/Logo" + }, + "priority": { + "type": "integer", + "description": "Priority of this provider (where zero is the highest priority.)", + "minimum": 0 + }, + "chains": { + "type": "array", + "items": { + "$ref": "#/$defs/ProviderChain" + } + } + } + }, + + "Path": { + "description": "A JSON Pointer path.", + "type": "string" + }, + + "Patch": { + "items": { + "oneOf": [ + { + "additionalProperties": false, + "required": ["value", "op", "path"], + "properties": { + "path": { + "$ref": "#/$defs/Path" + }, + "op": { + "description": "The operation to perform.", + "type": "string", + "enum": ["add", "replace", "test"] + }, + "value": { + "description": "The value to add, replace or test." + } + } + }, + { + "additionalProperties": false, + "required": ["op", "path"], + "properties": { + "path": { + "$ref": "#/$defs/Path" + }, + "op": { + "description": "The operation to perform.", + "type": "string", + "enum": ["remove"] + } + } + }, + { + "additionalProperties": false, + "required": ["from", "op", "path"], + "properties": { + "path": { + "$ref": "#/$defs/Path" + }, + + "op": { + "description": "The operation to perform.", + "type": "string", + "enum": ["move", "copy"] + }, + "from": { + "$ref": "#/$defs/Path", + "description": "A JSON Pointer path pointing to the location to move/copy from." + } + } + } + ] + }, + "type": "array" + } + }, + + "type": "object", + "additionalProperties": false, + + "required": [ + "name", + "version", + "timestamp" + ], + + "properties": { + "name": { + "type": "string", + "description": "Name of the provider list", + "minLength": 1, + "maxLength": 40, + "pattern": "^[\\w ]+$" + }, + "logo": { + "$ref": "#/$defs/Logo" + }, + "version": { + "$ref": "#/$defs/Version" + }, + "timestamp": { + "type": "string", + "format": "date-time", + "description": "The timestamp of this list version; i.e. when this immutable version of the list was created" + }, + "extends": true, + "changes": true, + "providers": true + }, + + "oneOf": [ + { + "type": "object", + + "required": [ + "extends", + "changes" + ], + + "properties": { + "providers": false, + + "extends": { + "type": "object", + "additionalProperties": false, + + "required": [ + "version" + ], + + "properties": { + "uri": { + "type": "string", + "format": "uri", + "description": "Location of the list to extend, as a URI." + }, + "ens": { + "type": "string", + "description": "Location of the list to extend using EIP-1577." + }, + "version": { + "$ref": "#/$defs/VersionRange" + } + }, + + "oneOf": [ + { + "properties": { + "uri": false, + "ens": true + } + }, + { + "properties": { + "ens": false, + "uri": true + } + } + ] + }, + "changes": { + "$ref": "#/$defs/Patch" + } + } + }, + { + "type": "object", + + "required": [ + "providers" + ], + + "properties": { + "changes": false, + "extends": false, + "providers": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/Provider" + } + } + } + } + ] +} +``` + +For illustrative purposes, the following is an example list following the schema: + +```json +{ + "name": "Example Provider List", + "version": { + "major": 0, + "minor": 1, + "patch": 0, + "build": "XPSr.p.I.g.l" + }, + "timestamp": "2004-08-08T00:00:00.0Z", + "logo": "https://mylist.invalid/logo.png", + "providers": { + "some-key": { + "name": "Frustrata", + "chains": [ + { + "chainId": 1, + "endpoints": [ + "https://mainnet1.frustrata.invalid/", + "https://mainnet2.frustrana.invalid/" + ] + }, + { + "chainId": 3, + "endpoints": [ + "https://ropsten.frustrana.invalid/" + ] + } + ] + }, + "other-key": { + "name": "Sourceri", + "priority": 3, + "chains": [ + { + "chainId": 1, + "endpoints": [ + "https://mainnet.sourceri.invalid/" + ] + }, + { + "chainId": 42, + "endpoints": [ + "https://kovan.sourceri.invalid" + ] + } + ] + } + } +} +``` + +### Versioning + +List versioning MUST follow the [Semantic Versioning 2.0.0](../assets/eip-5139/semver.md) (SemVer) specification. + +The major version MUST be incremented for the following modifications: + + - Removing a provider. + - Changing a provider's key in the `providers` object. + - Removing the last `ProviderChain` for a chain id. + +The major version MAY be incremented for other modifications, as permitted by SemVer. + +If the major version is not incremented, the minor version MUST be incremented if any of the following modifications are made: + + - Adding a provider. + - Adding the first `ProviderChain` of a chain id. + +The minor version MAY be incremented for other modifications, as permitted by SemVer. + +If the major and minor versions are unchanged, the patch version MUST be incremented for any change. + +### Publishing + +Provider lists SHOULD be published to an Ethereum Name Service (ENS) name using [EIP-1577](./eip-1577.md)'s `contenthash` mechanism on mainnet. + +Provider lists MAY instead be published using HTTPS. Provider lists published in this way MUST allow reasonable access from other origins (generally by setting the header `Access-Control-Allow-Origin: *`.) + +### Priority + +Provider entries MAY contain a `priority` field. A `priority` value of zero SHALL indicate the highest priority, with increasing `priority` values indicating decreasing priority. Multiple providers MAY be assigned the same priority. All providers without a `priority` field SHALL have equal priority. Providers without a `priority` field SHALL always have a lower priority than any provider with a `priority` field. + +List consumers MAY use `priority` fields to choose when to connect to a provider, but MAY ignore it entirely. List consumers SHOULD explain to users how their implementation interprets `priority`. + +### List Subtypes + +Provider lists are subdivided into two categories: root lists, and extension lists. A root list contains a list of providers, while an extension list contains a set of modifications to apply to another list. + +#### Root Lists + +A root list has a top-level `providers` key. + +#### Extension Lists + +An extension list has top-level `extends` and `changes` keys. + +##### Specifying a Parent (`extends`) + +The `uri` and `ens` fields SHALL point to a source for the parent list. + +If present, the `uri` field MUST use a scheme specified in [Publishing](#publishing). + +If present, the `ens` field MUST specify an ENS name to be resolved using EIP-1577. + +The `version` field SHALL specify a range of compatible versions. List consumers MUST reject extension lists specifying an incompatible parent version. + +In the event of an incompatible version, list consumers MAY continue to use a previously saved parent list, but list consumers choosing to do so MUST display a prominent warning that the provider list is out of date. + +###### Default Mode + +If the `mode` field is omitted, a parent version SHALL be compatible if and only if the parent's version number matches the left-most non-zero portion in the major, minor, patch grouping. + +For example: + +```javascript +{ + "major": "1", + "minor": "2", + "patch": "3" +} +``` + +Is equivalent to: + +``` +>=1.2.3, <2.0.0 +``` + +And: + +```javascript +{ + "major": "0", + "minor": "2", + "patch": "3" +} +``` + +Is equivalent to: + +``` +>=0.2.3, <0.3.0 +``` + +###### Caret Mode (`^`) + +The `^` mode SHALL behave exactly as the default mode above. + +###### Exact Mode (`=`) + +In `=` mode, a parent version SHALL be compatible if and only if the parent's version number exactly matches the specified version. + +##### Specifying Changes (`changes`) + +The `changes` field SHALL be a JavaScript Object Notation (JSON) Patch document as specified in RFC 6902. + +JSON pointers within the `changes` field MUST be resolved relative to the `providers` field of the parent list. For example, see the following lists for a correctly formatted extension. + +###### Root List + +```json +TODO +``` + +###### Extension List + +```json +TODO +``` + +##### Applying Extension Lists + +List consumers MUST follow this algorithm to apply extension lists: + + 1. Is the current list an extension list? + * Yes: + 1. Ensure that this `from` has not been seen before. + 1. Retrieve the parent list. + 1. Verify that the parent list is valid according to the JSON schema. + 1. Ensure that the parent list is version compatible. + 1. Set the current list to the parent list and go to step 1. + * No: + 1. Go to step 2. + 1. Copy the current list into a variable `$output`. + 1. Does the current list have a child: + * Yes: + 1. Apply the child's `changes` to `providers` in `$output`. + 1. Verify that `$output` is valid according to the JSON schema. + 1. Set the current list to the child. + 1. Go to step 3. + * No: + 1. Replace the current list's `providers` with `providers` from `$output`. + 1. The current list is now the resolved list; return it. + + +List consumers SHOULD limit the number of extension lists to a reasonable number. + +## Rationale + +This specification has two layers (provider, then chain id) instead of a flatter structure so that wallets can choose to query multiple independent providers for the same query and compare the results. + +Each provider may specify multiple endpoints to implement load balancing or redundancy. + +List version identifiers conform to SemVer to roughly communicate the kinds of changes that each new version brings. If a new version adds functionality (eg. a new chain id), then users can expect the minor version to be incremented. Similarly, if the major version is not incremented, list subscribers can assume dapps that work in the current version will continue to work in the next one. + +## Security Considerations + +Ultimately it is up to the end user to decide on what list to subscribe to. Most users will not change from the default list maintained by their wallet. Since wallets already have access to private keys, giving them additional control over RPC providers seems like a small increase in risk. + +While list maintainers may be incentivized (possibly financially) to include or exclude particular providers, actually doing so may jeopardize the legitimacy of their lists. This standard facilitates swapping lists, so if such manipulation is revealed, users are free to swap to a new list with little effort. + +If the list chosen by the user is published using EIP-1577, the list consumer has to have access to ENS in some way. This creates a paradox: how do you query Ethereum without an RPC provider? This paradox creates an attack vector: whatever method the list consumer uses to fetch the list can track the user, and even more seriously, **can lie about the contents of the list**. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5143.md b/EIPS/eip-5143.md new file mode 100644 index 0000000..72afb4b --- /dev/null +++ b/EIPS/eip-5143.md @@ -0,0 +1,224 @@ +--- +eip: 5143 +title: Slippage Protection for Tokenized Vault +description: An extension of EIP-4626 supporting improved EOA interactions. +author: Hadrien Croubois (@amxx) +discussions-to: https://ethereum-magicians.org/t/eip-5143-slippage-protection-for-tokenized-vaults/9554 +status: Stagnant +type: Standards Track +category: ERC +created: 2022-06-09 +requires: 20, 4626 +--- + +## Abstract + +The following standard extends the [EIP-4626](./eip-4626.md) Tokenized Vault standard with functions dedicated to the safe interaction between EOAs and the vault when price is subject to slippage. + +## Motivation + +[EIP-4626](./eip-4626.md) security considerations section states that: +> "If implementors intend to support EOA account access directly, they should consider adding an additional function call for deposit/mint/withdraw/redeem with the means to accommodate slippage loss or unexpected deposit/withdrawal limits, since they have no other means to revert the transaction if the exact output amount is not achieved." + +Yet, EIP-4626 does not standardize the corresponding function signatures and behaviors. For improved interroperability, and better support by wallets, it is essential that this optional functions are also standardized. + +## Specification + +This ERC is an extension of EIP-4626. Any contract implementing it MUST also implement EIP-4626. + +### Methods + +#### deposit + +Overloaded version of ERC-4626's `deposit`. + +Mints `shares` Vault shares to `receiver` by depositing exactly `assets` of underlying tokens. + +MUST emit the `Deposit` event. + +MUST support [EIP-20](./eip-20.md) `approve` / `transferFrom` on `asset` as a deposit flow. +MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the `deposit` execution, and are accounted for during `deposit`. + +MUST revert if all of `assets` cannot be deposited (due to deposit limit being reached, slippage, the user not approving enough underlying tokens to the Vault contract, etc). +MUST revert if depositing `assets` underlying asset mints less then `minShares` shares. + +Note that most implementations will require pre-approval of the Vault with the Vault's underlying `asset` token. + +```yaml +- name: deposit + type: function + stateMutability: nonpayable + + inputs: + - name: assets + type: uint256 + - name: receiver + type: address + - name: minShares + type: uint256 + + outputs: + - name: shares + type: uint256 +``` + +#### mint + +Overloaded version of ERC-4626's `mint`. + +Mints exactly `shares` Vault shares to `receiver` by depositing `assets` of underlying tokens. + +MUST emit the `Deposit` event. + +MUST support ERC-20 `approve` / `transferFrom` on `asset` as a mint flow. +MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the `mint` execution, and are accounted for during `mint`. + +MUST revert if all of `shares` cannot be minted (due to deposit limit being reached, slippage, the user not approving enough underlying tokens to the Vault contract, etc). +MUST revert if minting `shares` shares cost more then `maxAssets` underlying tokens. + +Note that most implementations will require pre-approval of the Vault with the Vault's underlying `asset` token. + +```yaml +- name: mint + type: function + stateMutability: nonpayable + + inputs: + - name: shares + type: uint256 + - name: receiver + type: address + - name: maxAssets + type: uint256 + + outputs: + - name: assets + type: uint256 +``` + +#### withdraw + +Overloaded version of ERC-4626's `withdraw`. + +Burns `shares` from `owner` and sends exactly `assets` of underlying tokens to `receiver`. + +MUST emit the `Withdraw` event. + +MUST support a withdraw flow where the shares are burned from `owner` directly where `owner` is `msg.sender` or `msg.sender` has ERC-20 approval over the shares of `owner`. +MAY support an additional flow in which the shares are transferred to the Vault contract before the `withdraw` execution, and are accounted for during `withdraw`. + +MUST revert if all of `assets` cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner not having enough shares, etc). +MUST revert if withdrawing `assets` underlying tokens requires burning more then `maxShares` shares. + +Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed. Those methods should be performed separately. + +```yaml +- name: withdraw + type: function + stateMutability: nonpayable + + inputs: + - name: assets + type: uint256 + - name: receiver + type: address + - name: owner + type: address + - name: maxShares + type: uint256 + + outputs: + - name: shares + type: uint256 +``` + +#### redeem + +Overloaded version of ERC-4626's `redeem`. + +Burns exactly `shares` from `owner` and sends `assets` of underlying tokens to `receiver`. + +MUST emit the `Withdraw` event. + +MUST support a redeem flow where the shares are burned from `owner` directly where `owner` is `msg.sender` or `msg.sender` has ERC-20 approval over the shares of `owner`. +MAY support an additional flow in which the shares are transferred to the Vault contract before the `redeem` execution, and are accounted for during `redeem`. + +MUST revert if all of `shares` cannot be redeemed (due to withdrawal limit being reached, slippage, the owner not having enough shares, etc). +MUST revert if redeeming `shares` shares sends less than `minAssets` underlying tokens to `receiver`. + +Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed. Those methods should be performed separately. + +```yaml +- name: redeem + type: function + stateMutability: nonpayable + + inputs: + - name: shares + type: uint256 + - name: receiver + type: address + - name: owner + type: address + - name: minAssets + type: uint256 + + outputs: + - name: assets + type: uint256 +``` + +## Rationale + +This ERC's functions do not replace ERC-4626 equivalent mechanisms. They are additional (overloaded) methods designed to protect EOAs interacting with the vault. + +When smart contracts interact with an ERC-4626 vault, they can preview any operation using the dedicated functions before executing the operation. This can be done +atomically, with no risk of price change. This is not true of EOA, which will preview their operations on a UI, sign a transaction, and have it mined later. +Between the preview and the transaction being executed, the blockchain state might change, resulting in unexpected outcomes. In particular, frontrunning +make EOA's interractons with an ERC-4626 vault possibly risky. + +Other projects in the DeFi spaces, such as decentralized exchanges, already include similar mechanisms so a user can request its transaction reverts if the +resulting exchange rate is not considered good enough. + +Implementing This ERC on top of an ERC-4626 contract can be done very easily. It just requires calling the corresponding ERC-4626 function and adding a revert +check on the returned value. + +### Alternative approaches + +This ERC aims at solving the security concerns (describes in the motivation section) at the vault level. For completeness, we have to mention that these issues can also be addressed using a generic ERC-4626 router, similar to how Uniswap V2 & V3 use a router to provide good user workflows on top of the Uniswap pairs. The router approach is possibly more versatile and leaves more room for evolutions (the router can be replaced at any point) but it also leads to more expensive operations because the router needs to take temporary custody of the tokens going into the vault. + +## Reference Implementation + +Given an existing ERC-4626 implementation + +``` solidity +contract ERC5143 is ERC4626 { + function deposit(uint256 assets, address receiver, uint256 minShares) public virtual returns (uint256) { + uint256 shares = deposit(assets, receiver); + require(shares >= minShares, "ERC5143: deposit slippage protection"); + return shares; + } + function mint(uint256 shares, address receiver, uint256 maxAssets) public virtual returns (uint256) { + uint256 assets = mint(shares, receiver); + require(assets <= maxAssets, "ERC5143: mint slippage protection"); + return assets; + } + function withdraw(uint256 assets, address receiver, address owner, uint256 maxShares) public virtual returns (uint256) { + uint256 shares = withdraw(assets, receiver, owner); + require(shares <= maxShares, "ERC5143: withdraw slippage protection"); + return shares; + } + function redeem(uint256 shares, address receiver, address owner, uint256 minAssets) public virtual returns (uint256) { + uint256 assets = redeem(shares, receiver, owner); + require(assets >= minAssets, "ERC5143: redeem slippage protection"); + return assets; + } +} +``` +## Security Considerations + +This ERC addresses one of the security consideration raised by ERC-4626. Other considerations still apply. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5164.md b/EIPS/eip-5164.md new file mode 100644 index 0000000..5337a6d --- /dev/null +++ b/EIPS/eip-5164.md @@ -0,0 +1,329 @@ +--- +eip: 5164 +title: Cross-Chain Execution +description: Defines an interface that supports execution across EVM networks. +author: Brendan Asselstine (@asselstine), Pierrick Turelier (@PierrickGT), Chris Whinfrey (@cwhinfrey) +discussions-to: https://ethereum-magicians.org/t/eip-5164-cross-chain-execution/9658 +status: Review +type: Standards Track +category: ERC +created: 2022-06-14 +--- + +## Abstract + +This specification defines a cross-chain execution interface for EVM-based blockchains. Implementations of this specification will allow contracts on one chain to call contracts on another by sending a cross-chain message. + +The specification defines two components: the "Message Dispatcher" and the "Message Executor". The Message Dispatcher lives on the calling side, and the executor lives on the receiving side. When a message is sent, a Message Dispatcher will move the message through a transport layer to a Message Executor, where they are executed. Implementations of this specification must implement both components. + +## Motivation + +Many Ethereum protocols need to coordinate state changes across multiple EVM-based blockchains. These chains often have native or third-party bridges that allow Ethereum contracts to execute code. However, bridges have different APIs so bridge integrations are custom. Each one affords different properties; with varying degrees of security, speed, and control. Defining a simple, common specification will increase code re-use and allow us to use common bridge implementations. + +## Specification + +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. + +This specification allows contracts on one chain to send messages to contracts on another chain. There are two key interfaces that needs to be implemented: + +- `MessageDispatcher` +- `MessageExecutor` + +The `MessageDispatcher` lives on the origin chain and dispatches messages to the `MessageExecutor` for execution. The `MessageExecutor` lives on the destination chain and executes dispatched messages. + +There are also extensions of `MessageDispatcher`, each defining a method for initiating a message or message batch: + +- `SingleMessageDispatcher` +- `BatchMessageDispatcher` + +Alternatively, `MessageDispatcher`s may implement a custom interface for initiating messages. + +### MessageDispatcher + +The `MessageDispatcher` lives on the chain from which messages are sent. The Dispatcher's job is to broadcast messages through a transport layer to one or more `MessageExecutor` contracts. + +A unique `messageId` MUST be generated for each message or message batch. + +To ensure uniqueness, it is RECOMMENDED that a monotonically increasing nonce is used in the calculation of the `messageId`. + +#### MessageDispatcher Events + +**MessageDispatched** + +The `MessageDispatched` event MUST be emitted by the `MessageDispatcher` when an individual message is dispatched. + +```solidity +interface MessageDispatcher { + event MessageDispatched( + bytes32 indexed messageId, + address indexed from, + uint256 indexed toChainId, + address to, + bytes data, + ); +} +``` + +```yaml +- name: MessageDispatched + type: event + inputs: + - name: messageId + indexed: true + type: bytes32 + - name: from + indexed: true + type: address + - name: toChainId + indexed: true + type: uint256 + - name: to + type: address + - name: data + type: bytes +``` + +**MessageBatchDispatched** + +The `MessageBatchDispatched` event MUST be emitted by the `MessageDispatcher` when a batch of messages is dispatched. + +```solidity +struct Message { + address to; + bytes data; +} + +interface MessageDispatcher { + event MessageBatchDispatched( + bytes32 indexed messageId, + address indexed from, + uint256 indexed toChainId, + Message[] messages + ); +} +``` + +```yaml +- name: MessageBatchDispatched + type: event + inputs: + - name: messageId + indexed: true + type: bytes32 + - name: from + indexed: true + type: address + - name: toChainId + indexed: true + type: uint256 + - name: messages + type: Message[] +``` + +### SingleMessageDispatcher + +The `SingleMessageDispatcher` is an extension of `MessageDispatcher` that defines a method, `dispatchMessage`, for dispatching an individual message to be executed on the `toChainId`. + +#### SingleMessageDispatcher Methods + +**dispatchMessage** + +Will dispatch a message to be executed by the `MessageExecutor` on the destination chain specified by `toChainId`. + +`SingleMessageDispatcher`s MUST emit the `MessageDispatched` event when a message is dispatched. + +`SingleMessageDispatcher`s MUST revert if `toChainId` is not supported. + +`SingleMessageDispatcher`s MUST forward the message to a `MessageExecutor` on the `toChainId`. + +`SingleMessageDispatcher`s MUST use a unique `messageId` for each message. + +`SingleMessageDispatcher`s MUST return the `messageId` to allow the message sender to track the message. + +`SingleMessageDispatcher`s MAY require payment. + +```solidity +interface SingleMessageDispatcher is MessageDispatcher { + function dispatchMessage(uint256 toChainId, address to, bytes calldata data) external payable returns (bytes32 messageId); +} +``` + +```yaml +- name: dispatchMessage + type: function + stateMutability: payable + inputs: + - name: toChainId + type: uint256 + - name: to + type: address + - name: data + type: bytes + outputs: + - name: messageId + type: bytes32 +``` + +### BatchedMessageDispatcher + +The `BatchedMessageDispatcher` is an extension of `MessageDispatcher` that defines a method, `dispatchMessageBatch`, for dispatching a batch of messages to be executed on the `toChainId`. + +#### BatchedMessageDispatcher Methods + +**dispatchMessageBatch** + +Will dispatch a batch of messages to be executed by the `MessageExecutor` on the destination chain specified by `toChainId`. + +`BatchedMessageDispatcher`s MUST emit the `MessageBatchDispatched` event when a message batch is dispatched. + +`BatchedMessageDispatcher`s MUST revert if `toChainId` is not supported. + +`BatchedMessageDispatcher`s MUST forward the message batch to the `MessageExecutor` on the `toChainId`. + +`BatchedMessageDispatcher`s MUST use a unique `messageId` for each batch of messages. + +`BatchedMessageDispatcher`s MUST return the `messageId` to allow the message sender to track the batch of messages. + +`BatchedMessageDispatcher`s MAY require payment. + +```solidity +interface BatchedMessageDispatcher is MessageDispatcher { + function dispatchMessageBatch(uint256 toChainId, Message[] calldata messages) external payable returns (bytes32 messageId); +} +``` + +```yaml +- name: dispatchMessageBatch + type: function + stateMutability: payable + inputs: + - name: toChainId + type: uint256 + - name: messages + type: Message[] + outputs: + - name: messageId + type: bytes32 +``` + +### MessageExecutor + +The `MessageExecutor` executes dispatched messages and message batches. Developers must implement a `MessageExecutor` in order to execute messages on the receiving chain. + +The `MessageExecutor` will execute a messageId only once, but may execute messageIds in any order. This specification makes no ordering guarantees, because messages and message batches may travel non-sequentially through the transport layer. + +#### Execution + +`MessageExecutor`s SHOULD verify all message data with the bridge transport layer. + +`MessageExecutor`s MUST NOT successfully execute a message more than once. + +`MessageExecutor`s MUST revert the transaction when a message fails to be executed allowing the message to be retried at a later time. + +**Calldata** + +`MessageExecutor`s MUST append the ABI-packed (`messageId`, `fromChainId`, `from`) to the calldata for each message being executed. This allows the receiver of the message to verify the cross-chain sender and the chain that the message is coming from. + +```solidity +to.call(abi.encodePacked(data, messageId, fromChainId, from)); +``` + +```yaml +- name: calldata + type: bytes + inputs: + - name: data + type: bytes + - name: messageId + type: bytes32 + - name: fromChainId + type: uint256 + - name: from + type: address +``` + +#### Error handling + +**MessageAlreadyExecuted** + +`MessageExecutor`s MUST revert if a messageId has already been executed and SHOULD emit a `MessageIdAlreadyExecuted` custom error. + +```solidity +interface MessageExecutor { + error MessageIdAlreadyExecuted( + bytes32 messageId + ); +} +``` + +**MessageFailure** + +`MessageExecutor`s MUST revert if an individual message fails and SHOULD emit a `MessageFailure` custom error. + +```solidity +interface MessageExecutor { + error MessageFailure( + bytes32 messageId, + bytes errorData + ); +} +``` + +**MessageBatchFailure** + +`MessageExecutor`s MUST revert the entire batch if any message in a batch fails and SHOULD emit a `MessageBatchFailure` custom error. + +```solidity +interface MessageExecutor { + error MessageBatchFailure( + bytes32 messageId, + uint256 messageIndex, + bytes errorData + ); +} +``` + +#### MessageExecutor Events + +**MessageIdExecuted** + +`MessageIdExecuted` MUST be emitted once a message or message batch has been executed. + +```solidity +interface MessageExecutor { + event MessageIdExecuted( + uint256 indexed fromChainId, + bytes32 indexed messageId + ); +} +``` + +```yaml +- name: MessageIdExecuted + type: event + inputs: + - name: fromChainId + indexed: true + type: uint256 + - name: messageId + indexed: true + type: bytes32 +``` + +## Rationale + +The `MessageDispatcher` can be coupled to one or more `MessageExecutor`. It is up to bridges to decide how to couple the two. Users can easily bridge a message by calling `dispatchMessage` without being aware of the `MessageExecutor` address. Messages can also be traced by a client using the data logged by the `MessageIdExecuted` event. + +Some bridges may require payment in the native currency, so the `dispatchMessage` function is payable. + +## Backwards Compatibility + +This specification is compatible with existing governance systems as it offers simple cross-chain execution. + +## Security Considerations + +Bridge trust profiles are variable, so users must understand that bridge security depends on the implementation. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5169.md b/EIPS/eip-5169.md new file mode 100644 index 0000000..702126b --- /dev/null +++ b/EIPS/eip-5169.md @@ -0,0 +1,211 @@ +--- +eip: 5169 +title: Client Script URI for Token Contracts +description: Add a scriptURI to point to an executable script associated with the functionality of the token. +author: James (@JamesSmartCell), Weiwu (@weiwu-zhang) +discussions-to: https://ethereum-magicians.org/t/eip-5169-client-script-uri-for-token-contracts/9674 +status: Review +type: Standards Track +category: ERC +created: 2022-05-03 +requires: 20, 165, 721, 777, 1155 +--- + +## Abstract + +This EIP provides a contract interface adding a `scriptURI()` function for locating executable scripts associated with the token. + +## Motivation + +Often, smart contract authors want to provide some user functionality to their tokens through client scripts. The idea is made popular with function-rich NFTs. It's important that a token's contract is linked to its client script, since the client script may carry out trusted tasks such as creating transactions for the user. + +This EIP allows users to be sure they are using the correct script through the contract by providing a URI to an official script, made available with a call to the token contract itself (`scriptURI`). This URI can be any RFC 3986-compliant URI, such as a link to an IPFS multihash, GitHub gist, or a cloud storage provider. Each contract implementing this EIP implements a `scriptURI` function which returns the download URI to a client script. The script provides a client-side executable to the hosting token. Examples of such a script could be: + +- A 'miniDapp', which is a cut-down DApp tailored for a single token. +- A 'TokenScript' which provides TIPS from a browser wallet. +- A 'TokenScript' that allows users to interact with contract functions not normally provided by a wallet, eg 'mint' function. +- An extension that is downloadable to the hardware wallet with an extension framework, such as Ledger. +- JavaScript instructions to operate a smartlock, after owner receives authorization token in their wallet. + +### Overview + +With the discussion above in mind, we outline the solution proposed by this EIP. For this purpose, we consider the following variables: + +- `SCPrivKey`: The private signing key to administrate a smart contract implementing this EIP. Note that this doesn't have to be a new key especially added for this EIP. Most smart contracts made today already have an administration key to manage the tokens issued. It can be used to update the `scriptURI`. + +- `newScriptURI`: an array of URIs for different ways to find the client script. + +We can describe the life cycle of the `scriptURI` functionality: + +- Issuance + +1. The token issuer issues the tokens and a smart contract implementing this EIP, with the admin key for the smart contract being `SCPrivKey`. +2. The token issuer calls `setScriptURI` with the `scriptURI`. + +- Update `scriptURI` + +1. The token issuer stores the desired `script` at all the new URI locations and constructs a new `scriptURI` structure based on this. +2. The token issuer calls `setScriptURI` with the new `scriptURI` structure. + +## Specification + +The keywords “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. + +We define a scriptURI element using the `string[]`. +Based on this, we define the smart contract interface below: + +```solidity +interface IERC5169 { + /// @dev This event emits when the scriptURI is updated, + /// so wallets implementing this interface can update a cached script + event ScriptUpdate(string[] memory newScriptURI); + + /// @notice Get the scriptURI for the contract + /// @return The scriptURI + function scriptURI() external view returns(string[] memory); + + /// @notice Update the scriptURI + /// emits event ScriptUpdate(scriptURI memory newScriptURI); + function setScriptURI(string[] memory newScriptURI) external; +} +``` + +The interface MUST be implemented under the following constraints: + +- The smart contract implementing `IERC5169` MUST store variables `address owner` in its state. + +- The smart contract implementing `IERC5169` MUST set `owner=msg.sender` in its constructor. + +- The `ScriptUpdate(...)` event MUST be emitted when the ```setScriptURI``` function updates the `scriptURI`. + +- The `setScriptURI(...)` function MUST validate that `owner == msg.sender` *before* executing its logic and updating any state. + +- The `setScriptURI(...)` function MUST update its internal state such that `currentScriptURI = newScriptURI`. + +- The `scriptURI()` function MUST return the `currentScriptURI` state. + +- The `scriptURI()` function MAY be implemented as pure or view. + +- Any user of the script learned from `scriptURI` MUST validate the script is either at an immutable location, its URI contains its hash digest, or it implements the separate `Authenticity for Client Script` EIP, which asserts authenticity using signatures instead of a digest. + +## Rationale + +This method avoids the need for building secure and certified centralized hosting and allows scripts to be hosted anywhere: IPFS, GitHub or cloud storage. + +## Backwards Compatibility + +This standard is backwards-compatible with most existing token standards, including the following commonly-used ones: + +- [ERC-20](./eip-20.md) +- [ERC-721](./eip-721.md) +- [ERC-777](./eip-777.md) +- [ERC-1155](./eip-1155.md) + +## Test Cases + +### Test Contract + +```solidity + +import "@openzeppelin/contracts/access/Ownable.sol"; +import "./IERC5169.sol"; +contract ERC5169 is IERC5169, Ownable { + string[] private _scriptURI; + function scriptURI() external view override returns(string[] memory) { + return _scriptURI; + } + + function setScriptURI(string[] memory newScriptURI) external onlyOwner override { + _scriptURI = newScriptURI; + + emit ScriptUpdate(newScriptURI); + } +} + +``` + +### Test Cases + +```ts + +const { expect } = require('chai'); +const { BigNumber, Wallet } = require('ethers'); +const { ethers, network, getChainId } = require('hardhat'); + +describe('ERC5169', function () { + before(async function () { + this.ERC5169 = await ethers.getContractFactory('ERC5169'); + }); + + beforeEach(async function () { + // targetNFT + this.erc5169 = await this.ERC5169.deploy(); + }); + + it('Should set script URI', async function () { + const scriptURI = [ + 'uri1', 'uri2', 'uri3' + ]; + + await expect(this.erc5169.setScriptURI(scriptURI)) + .emit(this.erc5169, 'ScriptUpdate') + .withArgs(scriptURI); + + const currentScriptURI = await this.erc5169.scriptURI(); + + expect(currentScriptURI.toString()).to.be.equal(scriptURI.toString()); + }); + +``` + +## Reference Implementation + +An intuitive implementation is the STL office door token. This NFT is minted and transferred to STL employees. The TokenScript attached to the token contract via the `scriptURI()` function contains instructions on how to operate the door interface. This takes the form of: + +1. Query for challenge string (random message from IoT interface eg 'Apples-5E3FA1'). + +2. Receive and display challenge string on Token View, and request 'Sign Personal'. + +3. On obtaining the signature of the challenge string, send back to IoT device. + +4. IoT device will unlock door if ec-recovered address holds the NFT. + +With `scriptURI()` the experience is greatly enhanced as the flow for the user is: + +1. Receive NFT. + +2. Use authenticated NFT functionality in the wallet immediately. + +The project with contract, TokenScript and IoT firmware is in use by Smart Token Labs office door and numerous other installations. An example implementation contract: [ERC-5169 Contract Example](../assets/eip-5169/contract/ExampleContract.sol) and TokenScript: [ERC-5169 TokenScript Example](../assets/eip-5169/tokenscript/ExampleScript.xml). Links to the firmware and full sample can be found in the associated discussion linked in the header. +The associated TokenScript can be read from the contract using `scriptURI()`. + +### Script location + +While the most straightforward solution to facilitate specific script usage associated with NFTs, is clearly to store such a script on the smart contract. However, this has several disadvantages: + +1. The smart contract signing key is needed to make updates, causing the key to become more exposed, as it is used more often. + +2. Updates require smart contract interaction. If frequent updates are needed, smart contract calls can become an expensive hurdle. + +3. Storage fee. If the script is large, updates to the script will be costly. A client script is typically much larger than a smart contract. + +For these reasons, storing volatile data, such as token enhancing functionality, on an external resource makes sense. Such an external resource can be either be hosted centrally, such as through a cloud provider, or privately hosted through a private server, or decentralized hosted, such as the interplanetary filesystem. + +While centralized storage for a decentralized functionality goes against the ethos of web3, fully decentralized solutions may come with speed, price or space penalties. This EIP handles this by allowing the function `ScriptURI` to return multiple URIs, which could be a mix of centralized, individually hosted and decentralized locations. + +While this EIP does not dictate the format of the stored script, the script itself could contain pointers to multiple other scripts and data sources, allowing for advanced ways to expand token scripts, such as lazy loading. +The handling of integrity of such secondary data sources is left dependent on the format of the script. + +## Security Considerations + +**When a server is involved** + +When the client script does not purely rely on connection to a blockchain node, but also calls server APIs, the trustworthiness of the server API is called into question. This EIP does not provide any mechanism to assert the authenticity of the API access point. Instead, as long as the client script is trusted, it's assumed that it can call any server API in order to carry out token functions. This means the client script can mistrust a server API access point. + +**When the scriptURI doesn't contain integrity (hash) information** + +We separately authored `Authenticity for Client Script` EIP to guide on how to use digital signatures efficiently and concisely to ensure authenticity and integrity of scripts not stored at a URI which is a digest of the script itself. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5173.md b/EIPS/eip-5173.md new file mode 100644 index 0000000..7a1336b --- /dev/null +++ b/EIPS/eip-5173.md @@ -0,0 +1,419 @@ +--- +eip: 5173 +title: NFT Future Rewards (nFR) +description: A multigenerational reward mechanism that rewards‌ all ‌owners of non-fungible tokens (NFT). +author: Yale ReiSoleil (@longnshort), dRadiant (@dRadiant), D Wang, PhD +discussions-to: https://ethereum-magicians.org/t/non-fungible-future-rewards-token-standard/9203 +status: Draft +type: Standards Track +category: ERC +created: 2022-05-08 +requires: 165, 721 +--- + +## Abstract + +In this EIP, we propose the implementation of a Future Rewards (FR) extension which will enable owners of [EIP-721](./eip-721.md) tokens (NFTs) to participate in future price increases after they sell their tokens. + +Through the implementation of this nFR proposal, the creators, buyers and sellers create a giving circle in trading practice. A giving circle is formed when all participants work in a framework to build greater wealth through each other's success. One does not expect the same amount of return from the same person when they give someone a portion of their profits. There is no quid pro quo. Rather, they are confident that someone else in the same circle will give them the same benefits, with a smaller or greater monetary value, from other participants in the same circle later on. + +Owners of nFR compliant tokens can benefit in two ways from such a gift economic framework: + +1. An increase in price during their holding period; +2. They continue to receive Future Rewards (FRs) after the token is sold. + +The realized profits from the sale of nFR compliant tokens will be shared across the chain of historical ownership if the seller is not the original Minter and therefore not the very first seller. Through the NFT Future Rewards (nFR) framework, the same seller, as well as every other seller, will receive the same FR distributions. Everybody pays it forward, forming a giving circle. + +Giving circles are groups of people who work together to improve a situation that is typically much larger than it is at the moment. Some of the characteristics of a giving circle are community interdependence and delayed reciprocity. + +In a well-designed circle of giving, givers may be able to receive more than they give over time, so giving is not the only thing involved. As a result, the traditional model of platform versus user and user vs. user relationships has been fundamentally altered into one, shared objective: if others succeed, I succeed more. + + +## Motivation + +Not limited to NFT trading, it is common for an average trader to fall victim to spoofing, insider trading, front running, wash trading, and pump and dump, among a number of other techniques used by various actors. The current system guarantees that most traders will lose money because of their emotions, the constant oscillation between greed and fear. Under the current system, a trader has no advantage over many of the more sophisticated techniques used by various actors. + +Although this historical precedent has been followed in today's markets, just as crypto has revolutionized traditional trading, we now have the opportunity to transform this historic trail of unequal value distribution by tracking every transaction of every distinguishable token through the emergence of the non-fungible token standard. + +There needs to be a change in historical unfair trading practices so that: + +* With a success-based model, everyone is on the same page; +* A mutually beneficial economic rule benefits both buyers and sellers. + + +NFTs, in contrast to physical art and collectibles in the physical world, are not currently reflecting the contributions of their owners to their value. Since each [EIP-721](./eip-721.md) token can be tracked individually, and may be modified to record every change in the price of any specific NFT token, there is no reason that a Future Rewards program of this type should not be established. + +This nFR proposal establishes a standard interface for a profit sharing framework in all stages of the token's ownership history desired by all market participants. In a giving circle, art buyers/owners are compensated for their participation in the instrument’s trading price discovery process. + +We embrace and promote a new gift economic model, which is similar to the Copyleft and open-source spirit as opposed to traditional copyrights. The advancement of technology has enabled such implementation in trading for the first time. In the same way that open-source software has changed the software industry and society, we can also change the financial industry. + +As in trading, most traders lose money, but the proposed Future Rewards framework is designed to help average traders do better. + +Additionally, as we will explain later, it discourages any "under-the-table" deals that may circumvent the rules set forth by artists and marketplaces. + +### Is This Just a Ponzi Scheme? + +No, it is not. Ponzi schemes promise profits that are impossible to keep. + +As opposed to fixed-yield schemes, our proposal only distributes future profits when those profits are achieved rather than guaranteeing them. Should later holders fail to make a profit, future return shares will not be distributed. + +The early participants in price discovery will receive a share of profits as part of the FR implementation only and if a later owner has accumulated profits during their holdings of the token. + +## Specification + +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. + +The following is an extension of the [EIP-721](./eip-721.md) standard. + +[EIP-721](./eip-721.md)-compliant contracts MAY implement this EIP for rewards to provide a standard method of rewarding future buyers and previous owners with realized profits in the future. + +Implementers of this standard MUST have all of the following functions: + +```solidity + +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; + +/* + * + * @dev Interface for the Future Rewards Token Standard. + * + * A standardized way to receive future rewards for non-fungible tokens (NFTs.) + * + */ +interface IERC5173 is IERC165 { + + event FRClaimed(address indexed account, uint256 indexed amount); + + event FRDistributed(uint256 indexed tokenId, uint256 indexed soldPrice, uint256 indexed allocatedFR); + + function list(uint256 tokenId, uint256 salePrice) external; + + function unlist(uint256 tokenId) external; + + function buy(uint256 tokenId) payable external; + + function releaseFR(address payable account) external; + + function retrieveFRInfo(uint256 tokenId) external returns(uint8, uint256, uint256, uint256, uint256, address[] memory); + + function retrieveAllottedFR(address account) external returns(uint256); + + function retrieveListInfo(uint256 tokenId) external returns(uint256, address, bool); + +} + +``` + +An nFR contract MUST implement and update for each Token ID. The data in the `FRInfo` struct MAY either be stored wholly in a single mapping, or MAY be broken down into several mappings. The struct MUST either be exposed in a public mapping or mappings, or MUST have public functions that access the private data. This is for client-side data fetching and verification. + +```solidity + +struct FRInfo { + uint8 numGenerations; // Number of generations corresponding to that Token ID + uint256 percentOfProfit; // Percent of profit allocated for FR, scaled by 1e18 + uint256 successiveRatio; // The common ratio of successive in the geometric sequence, used for distribution calculation + uint256 lastSoldPrice; // Last sale price in ETH mantissa + uint256 ownerAmount; // Amount of owners the Token ID has seen + address[] addressesInFR; // The addresses currently in the FR cycle +} + +struct ListInfo { + uint256 salePrice; // ETH mantissa of the listed selling price + address lister; // Owner/Lister of the Token + bool isListed; // Boolean indicating whether the Token is listed or not +} + +``` + +Additionally, an nFR smart contract MUST store the corresponding `ListInfo` for each Token ID in a mapping. A method to retrieve a Token ID’s corresponding `ListInfo` MUST also be accessible publicly. + +An nFR smart contract MUST also store and update the amount of Ether allocated to a specific address using the `_allotedFR` mapping. The `_allottedFR` mapping MUST either be public or have a function to fetch the FR payment allotted to a specific address. + +### Percent Fixed Point + +The `allocatedFR` MUST be calculated using a percentage fixed point with a scaling factor of 1e18 (X/1e18) - such as "5e16" - for 5%. This is REQUIRED to maintain uniformity across the standard. The max and min values would be - 1e18 - 1. + +### Default FR Info + +A default `FRInfo` MUST be stored in order to be backward compatible with [EIP-721](./eip-721.md) mint functions. It MAY also have a function to update the `FRInfo`, assuming it has not been hard-coded. + +### EIP-721 Overrides + +An nFR-compliant smart contract MUST override the [EIP-721](./eip-721.md) `_mint`, `_transfer`, and `_burn` functions. When overriding the `_mint` function, a default FR model is REQUIRED to be established if the mint is to succeed when calling the [EIP-721](./eip-721.md) `_mint` function and not the nFR `_mint` function. It is also to update the owner amount and directly add the recipient address to the FR cycle. When overriding the `_transfer` function, the smart contract SHALL consider the NFT as sold for 0 ETH, and update the state accordingly after a successful transfer. This is to prevent FR circumvention. Additionally, the `_transfer` function SHALL prevent the caller from transferring the token to themselves, this can be done through a require statement that ensures the sender is not the recipient, otherwise, it’d be possible to fill up the FR sequence with one’s own address. Finally, when overriding the `_burn` function, the smart contract SHALL delete the `FRInfo` corresponding to that Token ID after a successful burn. + +Additionally, the [EIP-721](./eip-721.md) `_checkOnERC721Received` function MAY be explicitly called after mints and transfers if the smart contract aims to have safe transfers and mints. + +### Safe Transfers + +If the wallet/broker/auction application will accept safe transfers, then it MUST implement the [EIP-721](./eip-721.md) wallet interface. + +### Listing, Unlisting, and Buying + +The `list`, `unlist`, and `buy` functions MUST be implemented, as they provide the capability to sell a token. + +```solidity +function list(uint256 tokenId, uint256 salePrice) public virtual override { + //... +} + + +function unlist(uint256 tokenId) public virtual override { + //... +} + +function buy(uint256 tokenId) public virtual override payable { + //... +} + +``` + +The `list` function accepts a `tokenId` and a `salePrice` and updates the corresponding `ListInfo` for that given `tokenId` after ensuring that the `msg.sender` is either approved or the owner of the token. The function signifies that the token is listed and at what price it is listed for. + +The `unlist` function accepts a `tokenId` and it deletes the corresponding `ListInfo` after the owner verifications have been met. + +The `buy` function accepts a `tokenId` and MUST be payable. It MUST verify that the `msg.value` matches the token’s `salePrice` and that the token is listed, before proceeding and calling the FR `_transferFrom` function. This is to ensure the values are valid and will also allow for the necessary FR to be held in the contract. + + +### Future Rewards `_transferFrom` Function + +The FR `_transferFrom` function MUST be called by all nFR-supporting smart contracts, though the accommodations for non-nFR-supporting contracts MAY also be implemented to ensure backwards compatibility. + +```solidity + +function transferFrom(address from, address to, uint256 tokenId, uint256 soldPrice) public virtual override payable { + //... +} + +``` + +Based on the stored `lastSoldPrice`, the smart contract will determine whether the sale was profitable after calling the [EIP-721](./eip-721.md) transfer function and transferring the NFT. If it was not profitable, the smart contract SHALL update the last sold price for the corresponding Token ID, increment the owner amount, shift the generations, and transfer all of the `msg.value` to the `lister` depending on the implementation. Otherwise, if the transaction was profitable, the smart contract SHALL call the `_distributeFR` function, then update the `lastSoldPrice`, increment the owner amount, and finally shift generations. The `_distributeFR` function MUST return the difference between the allocated FR that is to be distributed amongst the `_addressesInFR` and the `msg.value` to the `lister`. Once the operations have completed, the function MUST clear the corresponding `ListInfo`. Similarly to the `_transfer` override, the FR `_transferFrom` SHALL ensure that the recipient is not the sender of the token. + +### Future Rewards Calculation + +Marketplaces that support this standard MAY implement various methods of calculating or transferring Future Rewards to the previous owners. + +```solidity + +function _calculateFR(uint256 totalProfit, uint256 buyerReward, uint256 successiveRatio, uint256 ownerAmount, uint256 windowSize) pure internal virtual returns(uint256[] memory) { + //... +} + +``` + +In this example (*Figure 1*), a seller is REQUIRED to share a portion of their net profit with 10 previous holders of the token. Future Rewards will also be paid to the same seller as the value of the token increases from up to 10 subsequent owners. + +When an owner loses money during their holding period, they MUST NOT be obligated to share Future Rewards distributions, since there is no profit to share. However, he SHALL still receive a share of Future Rewards distributions from future generations of owners, if they are profitable. + +![Figure 1: Geometric sequence distribution](../assets/eip-5173/Total_FR_Payout_Distribution-geo.png) + +*Figure 1: Geometric sequence distribution* + +The buyers/owners receive a portion ( r ) of the realized profit (P ) from an NFT transaction. The remaining proceeds go to the seller. + +As a result of defining a sliding window mechanism ( n ), we can determine which previous owners will receive distributions. The owners are arranged in a queue, starting with the earliest owner and ending with the owner immediately before the current owner (the Last Generation). The First Generation is the last of the next n generations. There is a fixed-size profit distribution window from the First Generation to the Last Generation. + +The profit distribution SHALL be only available to previous owners who fall within the window. + +In this example, there SHALL be a portion of the proceeds awarded to the Last Generation owner (the owner immediately prior to the current seller) based on the geometric sequence in which profits are distributed. The larger portion of the proceeds SHALL go to the Mid-Gen owners, the earlier the greater, until the last eligible owner is determined by the sliding window, the First Generation. Owners who purchase earlier SHALL receive a greater reward, with first-generation owners receiving the greatest reward. + +### Future Rewards Distribution + +![Figure 2: NFT Owners' Future Rewards (nFR)](../assets/eip-5173/nFR_Standard_Outline.jpeg) + +*Figure 2: NFT Owners' Future Rewards (nFR)* + +*Figure 2* illustrates an example of a five-generation Future Rewards Distribution program based on an owner's realized profit. + +```solidity + +function _distributeFR(uint256 tokenId, uint256 soldPrice) internal virtual { + //... + + emit FRDistributed(tokenId, soldPrice, allocatedFR); + } + +``` + +The `_distributeFR` function MUST be called in the FR `transferFrom` function if there is a profitable sale. The function SHALL calculate the difference between the current sale price and the `lastSoldPrice`, then it SHALL call the `_calculateFR` function to receive the proper distribution of FR. Then it SHALL distribute the FR accordingly, making order adjustments as necessary. Then, the contract SHALL calculate the total amount of FR that was distributed (`allocatedFR`), in order to return the difference of the `soldPrice` and `allocatedFR` to the `lister`. Finally, it SHALL emit the `FRDistributed` event. + +### Future Rewards Claiming + +The future Rewards payments SHOULD utilize a pull-payment model, similar to that demonstrated by OpenZeppelin with their PaymentSplitter contract. The event FRClaimed would be triggered after a successful claim has been made. + +```solidity + +function releaseFR(address payable account) public virtual override { + //... +} + +``` + +### Owner Generation Shifting + +The `_shiftGenerations` function MUST be called regardless of whether the sale was profitable or not. As a result, it will be called in the `_transfer` [EIP-721](./eip-721.md) override function and the FR `transferFrom` function. The function SHALL remove the oldest account from the corresponding `_addressesInFR` array. This calculation will take into account the current length of the array versus the total number of generations for a given token ID. + +## Rationale + +### Fixed Percentage to 10^18 + +Considering Fixed-Point Arithmetic is to be enforced, it is logical to have 1e18 represent 100% and 1e16 represent 1% for Fixed-Point operations. This method of handling percents is also commonly seen in many Solidity libraries for Fixed-Point operations. + +### Emitting Event for Payment + +Since each NFT contract is independent, and while a marketplace contract can emit events when an item is sold, choosing to emit an event for payment is important. As the royalty and FR recipient may not be aware of/watching for a secondary sale of their NFT, they would never know that they received a payment except that their ETH wallet has been increased randomly. + +The recipient of the secondary sale will therefore be able to verify that the payment has been received by calling the parent contract of the NFT being sold, as implemented in [EIP-2981](./eip-2981.md). + +### Number of Generations of All Owners ( n ) vs Number of Generations of Only Profitable Owners + +It is the number of generations of all owners, not just those who are profitable, that determines the number of owners from which the subsequent owners' profits will be shared, see *Figure 3*. As part of the effort to discourage "ownership hoarding," Future Rewards distributions will not be made to the current owner/purchaser if all the owners lose money holding the NFT. Further information can be found under Security Considerations. + +![Figure 3: Losing owners](../assets/eip-5173/Losing_owners.jpeg) + +*Figure 3: Losing owners* + +### Single vs Multigenerations + +In a single generation reward, the new buyer/owner receives a share of the next single generation's realized profit only. In a multigenerational reward system, buyers will have future rewards years after their purchase. The NFT should have a long-term growth potential and a substantial dividend payout would be possible in this case. + +We propose that the marketplace operator can choose between a single generational distribution system and a multigenerational distribution system. + +### Direct FR Payout by the Seller vs Smart Contract-managed Payout + +FR payouts directly derived from the sale proceeds are immediate and final. As part of the fraud detection detailed later in the Security Considerations section, we selected a method in which the smart contract calculates all the FR amounts for each generation of previous owners, and handles payout according to other criteria set by the marketplace, such as reduced or delayed payments for wallet addresses with low scores, or a series of consecutive orders detected using a time-heuristic analysis. + +### Equal vs Linear Reward Distributions +#### Equal FR Payout + +![Figure 4: Equal, linear reward distribution](../assets/eip-5173/Total_FR_Payout_Distribution-flat.png?raw=true) + +*Figure 4: Equal, linear reward distribution* + +FR distributions from the realization of profits by later owners are distributed equally to all eligible owners (*Figure 4*). The exponential reward curve, however, may be more desirable, as it gives a slightly larger share to the newest buyer. Additionally, this distribution gives the earliest generations the largest portions as their FR distributions near the end, so they receive higher rewards for their early involvement, but the distribution is not nearly as extreme as one based on arithmetic sequences (*Figure 5*). + +This system does not discriminate against any buyer because each buyer will go through the same distribution curve. + +#### Straight line arithmetic sequence FR payout + +![Figure 5: Arithmetic sequence distribution](../assets/eip-5173/Arithmetic_Sequence_FR_Payout_Distribution.png?raw=true) + +*Figure 5: Arithmetic sequence distribution* + +The profit is distributed according to the arithmetic sequence, which is 1, 2, 3, ... and so on. The first owner will receive 1 portion, the second owner will receive 2 portions, the third owner will receive 3 portions, etc. + +## Backwards Compatibility + +This proposal is fully compatible with current [EIP-721](./eip-721.md) standards and [EIP-2981](./eip-2981.md). It can also be easily adapted to work with [EIP-1155](./eip-1155.md). + +## Test Cases + +[This contract](../assets/eip-5173/Implementation/nFRImplementation.sol) contains the reference implementation for this proposal. + +[Here is a visualization of the test case](../assets/eip-5173/animate-1920x1080-1750-frames.gif?raw=true). + +## Reference Implementation + +This implementation uses OpenZeppelin contracts and the PRB Math library created by Paul R Berg for fixed-point arithmetic. It demonstrates the interface for the nFR standard, an nFR standard-compliant extension, and an [EIP-721](./eip-721.md) implementation using the extension. + +The code for the reference implementation is [here](../assets/eip-5173/Implementation/nFRImplementation.sol). + +### Distribution of NFT Royalties to Artists and Creators + +We agree that artists’ royalties should be uniform and on-chain. We support [EIP-2981](./eip-2981.md) NFT royalty Standard proposal. + +All platforms can support royalty rewards for the same NFT based on on-chain parameters and functions: + +- No profit, no profit sharing, no cost; +- The question of "who owned it" is often crucial to the provenance and value of a collectible; +- The previous owner should be re-compensated for their ownership; +- And the buyer/owner incentive in FR eliminates any motive to circumvent the royalty payout schemes; + +### Distribution of NFT Owners’ Future Rewards (FRs) + +#### Future Rewards calculation + +Any realized profits (P) when an NFT is sold are distributed among the buyers/owners. The previous owners will take a fixed portion of the profix (P), and this portion is called Future Rewards (FRs). The seller takes the rest of the profits. + +We define a sliding window mechanism to decide which previous owners will be involved in the profit distribution. Let's imagine the owners as a queue starting from the first hand owner to the current owner. The profit distribution window starts from the previous owner immediately to the current owner and extends towards the first owner, and the size of the windows is fixed. Only previous owners located inside the window will join the profit distribution. + +![Future Rewards calculation formula](../assets/eip-5173/nFR_distribution_formula.png?raw=true) + +In this equation: + +- P is the total profit, the difference between the selling price minus the buying price; +- r is buyer reward ratio of the total P; +- g is the common ratio of successive in the geometric sequence; +- n is the actual number of owners eligible and participating in the future rewards sharing. To calculate n, we have n = min(m, w), where m is the current number of owners for a token, and w is the window size of the profit distribution sliding window algorithm + +#### Converting into Code + +```solidity + +pragma solidity ^0.8.0; +//... + +/* Assumes usage of a Fixed Point Arithmetic library (prb-math) for both int256 and uint256, and OpenZeppelin Math utils for Math.min. */ +function _calculateFR(uint256 P, uint256 r, uint256 g, uint256 m, uint256 w) pure internal virtual returns(uint256[] memory) { + uint256 n = Math.min(m, w); + uint256[] memory FR = new uint256[](n); + + for (uint256 i = 1; i < n + 1; i++) { + uint256 pi = 0; + + if (successiveRatio != 1e18) { + int256 v1 = 1e18 - int256(g).powu(n); + int256 v2 = int256(g).powu(i - 1); + int256 v3 = int256(P).mul(int256(r)); + int256 v4 = v3.mul(1e18 - int256(g)); + pi = uint256(v4 * v2 / v1); + } else { + pi = P.mul(r).div(n); + } + + FR[i - 1] = pi; + } + + return FR; +} + +``` +The complete implementation code can be found [here](../assets/eip-5173/Implementation/nFRImplementation.sol). + +## Security Considerations + +### Payment Attacks + +As this EIP introduces royalty and realized profit rewards collection, distribution, and payouts to the EIP-721 standard, the attack vectors increase. As discussed by Andreas Freund regarding mitigations to phishing attacks, we recommend reentrancy protection for all payment functions to reduce the most significant attack vectors for payments and payouts. + +### Royalty Circumventing + +Many methods are being used to avoid paying royalties to creators under the current [EIP-721](./eip-721.md) standard. Through an under-the-table transaction, the new buyer's cost basis will be reduced to zero, increasing their FR liability to the full selling price. Everyone, either the buyer or seller, would pay a portion of the previous owner's net realized profits ( P x r ). Acting in his or her own interests, the buyer rejects any loyalty circumventing proposal. + +### FR Hoarding through Wash Sales + +Quantexa blog and beincrypto articles have reported widespread wash trading on all unregulated cryptocurrency trading platforms and NFT marketplaces. The use of wash trading by dishonest actors can lead to an unfair advantage, as well as inflated prices and money laundering. When a single entity becomes multiple generations of owners to accumulate more rewards in the future, the validity of the system is undermined. + +#### Wash trading by users +Using a different wallet address, an attacker can "sell" the NFT to themselves at a loss. It is possible to repeat this process n times in order to maximize their share of the subsequent FR distributions (*Figure 6*). A wallet ranking score can partially alleviate this problem. It is evident that a brand new wallet is a red flag, and the marketplace may withhold FR distribution from it if it has a short transaction history (i.e. fewer than a certain number of transactions). + +We do not want a large portion of future rewards to go to a small number of wash traders. Making such practices less profitable is one way to discourage wash trading and award hoarding. It can be partially mitigated, for example, by implementing a wallet-score and holding period-based incentive system. The rewards for both parties are reduced if a new wallet is used or if a holding period is less than a certain period. + +![Figure 6: Same owner using different wallets](../assets/eip-5173/Same_owner_using_different_wallets.jpeg) + +*Figure 6: Same owner using different wallets* + +#### Wash trading by the marketplace operator + +However, the biggest offender appears to be the marketplace, which engages heavily in wash trading, or simply does not care about it, according to Decrypt. The authors have personally experienced this phenomenon. A senior executive of a top-5 cryptocurrency exchange boasted during a mid-night drinking session in 2018, that they had "brushed" (wash-traded) certain newly listed tokens, which they called "marketmaking." The exchange is still ranked among the top five crypto exchanges today. + +Many of these companies engage in wash trading on their own or collude with certain users, and royalties and FR payments are reimbursed under the table. It is crucial that all exchanges have robust features to prevent self-trading. Users should be able to observe watchers transparently. Marketplaces should provide their customers with free access to an on-chain transaction monitoring service like Chainalysis Reactor. + +### Long/Cyclical FR-Entitled Owner Generations + +In most cases, malicious actors will create excessively long or cyclical Future Rewards Owner Generations that will result in applications that attempt to distribute FR or shift generations running out of gas and not functioning. Therefore, clients are responsible for verifying that the contract with which they interact has an appropriate number of generations, so that looping over will not deplete the gas. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5185.md b/EIPS/eip-5185.md new file mode 100644 index 0000000..000784d --- /dev/null +++ b/EIPS/eip-5185.md @@ -0,0 +1,232 @@ +--- +eip: 5185 +title: NFT Updatable Metadata Extension +description: An interface extension for ERC-721/ERC-1155 controlled metadata updates +author: Christophe Le Bars (@clbrge) +discussions-to: https://ethereum-magicians.org/t/erc-721-erc-1155-updatable-metadata-extension/9077 +status: Stagnant +type: Standards Track +category: ERC +requires: 721, 1155 +created: 2022-06-27 +--- + +## Abstract + +This specification defines a standard way to allow controlled NFTs' metadata updates along predefined formulas. Updates of the original metadata are restricted and defined by a set of recipes and the sequence and results of these recipes are deterministic and fully verifiable with on-chain metadata updates event. The proposal depends on and extends the [EIP-721](./eip-721.md) and [EIP-1155](./eip-1155.md). + +## Motivation + +Storing voluminous NFT metadata on-chain is often neither practical nor cost-efficient. + +Storing NFT metadata off-chain on distributed file systems like IPFS can answer some needs of verifiable correlation and permanence between an NFT tokenId and its metadata but updates come at the cost of being all or nothing (aka changing the `tokenURI`). Bespoke solutions can be easily developed for a specific NFT smart contract but a common specification is necessary for NFT marketplaces and third parties tools to understand and verify these metadata updates. + +This ERC allows the original JSON metadata to be modified step by step along a set of predefined JSON transformation formulas. Depending on NFT use-cases, the transformation formulas can be more or less restrictive. + +As examples, an NFT representing a house could only allow append-only updates to the list of successive owners, and a game using NFT characters could let some attributes change from time to time (e.g. health, experience, level, etc) while some other would be guaranteed to never change (e.g. physicals traits etc). + +This standard extension is compatible with NFTs bridged between Ethereum and L2 networks and allows efficient caching solutions. + +## Specification + +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. + +The **metadata updates extension** is OPTIONAL for [EIP-721](./eip-721.md) and [EIP-1155](./eip-1155.md) contracts. + +```solidity +/// @title ERC-721/ERC-1155 Updatable Metadata Extension +interface IERC5185UpdatableMetadata { + /// @notice A distinct Uniform Resource Identifier (URI) for a set of updates + /// @dev This event emits an URI (defined in RFC 3986) of a set of metadata updates. + /// The URI should point to a JSON file that conforms to the "NFT Metadata Updates JSON Schema" + /// Third-party platforms such as NFT marketplace can deterministically calculate the latest + /// metadata for all tokens using these events by applying them in sequence for each token. + event MetadataUpdates(string URI); +} +``` + +The original metadata SHOULD conform to the "ERC-5185 Updatable Metadata JSON Schema" which is a compatible extension of the "ERC-721 Metadata JSON Schema" defined in ERC-721. + +"ERC-5185 Updatable Metadata JSON Schema" : + +```json +{ + "title": "Asset Updatable Metadata", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Identifies the asset to which this NFT represents" + }, + "description": { + "type": "string", + "description": "Describes the asset to which this NFT represents" + }, + "image": { + "type": "string", + "description": "A URI pointing to a resource with mime type image/* representing the asset to which this NFT represents. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive." + }, + "updatable": { + "type": "object", + "required": ["engine", "recipes"], + "properties": { + "engine": { + "type": "string", + "description": "Non ambiguous transformation method/language (with version) to process updates along recipes defined below" + }, + "schema": { + "type": "object", + "description": "if present, a JSON Schema that all sequential post transformation updated metadata need to conform. If a transformed JSON does not conform, the update should be considered voided." + }, + "recipes": { + "type": "object", + "description": "A catalog of all possibles recipes identified by their keys", + "patternProperties": { + ".*": { + "type": "object", + "description": "The key of this object is used to select which recipe to apply for each update", + "required": ["eval"], + "properties": { + "eval": { + "type": "string", + "description": "The evaluation formula to transform the last JSON metadata using the engine above (can take arguments)" + } + } + } + } + } + } + } + } +} +``` + +"NFT Metadata Updates JSON Schema" : + +```json +{ + "title": "Metadata Updates JSON Schema", + "type": "object", + "properties": { + "updates": { + "type": "array", + "description": "A list of updates to apply sequentially to calculate updated metadata", + "items": { "$ref": "#/$defs/update" }, + "$defs": { + "update": { + "type": "object", + "required": ["tokenId", "recipeKey"], + "properties": { + "tokenId": { + "type": "string", + "description": "The tokenId for which the update recipe should apply" + }, + "recipeKey": { + "type": "string", + "description": "recipeKey to use to get the JSON transformation expression in current metadata" + }, + "args": { + "type": "string", + "description": "arguments to pass to the JSON transformation" + } + } + } + } + } + } +} +``` + +### Engines + +Only one engine is currently defined in this extension proposal. + +If the engine in the original metadata is "jsonata@1.8.*", updated metadata is calculated starting from original metadata and applying each update sequentially (all updates which are present in all the URIs emitted by the event `MetadataUpdates` for which tokenId matches). + +For each step, the next metadata is obtained by the javascript calculation (or compatible jsonata implementation in other language) : + +```js +const nextMetadata = jsonata(evalString).evaluate(previousMetadata, args) +``` + +With `evalString` is found with `recipeKey` in the original metadata recipes list. + +If the key is not present in the original metadata list, `previousMetadata` is kept as the valid updated metadata. + +If the evaluation throws any errors, `previousMetadata` is kept as the valid updated metadata. + +If a validation Schema JSON has been defined and the result JSON `nextMetadata` does not conform, that update is not valid and `previousMetadata` is kept as the valid updated metadata. + +## Rationale + +There have been numerous interesting uses of [EIP-721](./eip-721.md) and [EIP-1155](./eip-1155.md) smart contracts that associate for each token essential and significant metadata. While some projects (e.g. EtherOrcs) have experimented successfully to manage these metadata on-chain, that experimental solution will always be limited by the cost and speed of generating and storing JSON on-chain. Symmetrically, while storing the JSON metadata at URI endpoint controlled by traditional servers permit limitless updates the the metadata for each NFT, it is somehow defeating in many uses cases, the whole purpose of using a trustless blockchain to manage NFT: indeed users may want or demand more permanence and immutability from the metadata associated with their NFT. + +Most use cases have chosen intermediate solutions like IPFS or arweave to provide some permanence or partial/full immutability of metadata. This is a good solution when an NFT represents a static asset whose characteristics are by nature permanent and immutable (like in the art world) but less so with other use cases like gaming or NFT representing a deed or title. Distinguishable assets in a game often should be allowed to evolve and change over time in a controlled way and titles need to record real life changes. + +The advantages of this standard is precisely to allow these types of controlled transformations over time of each NFT metadata by applying sequential transformations starting with the original metadata and using formulas themselves defined in the original metadata. + +The original metadata for a given NFT is always defined as the JSON pointed by the result of `tokenURI` for [EIP-721](./eip-721.md) and function `uri` for [EIP-1155](./eip-1155.md). + +The on-chain log trace of updates guarantee that anyone can recalculate and verify independently the current updated metadata starting from the original metadata. The fact that the calculation is deterministic allows easy caching of intermediate transformations and the efficient processing of new updates using these caches. + +The number of updates defined by each event is to be determined by the smart contract logic and use case, but it can easily scale to thousands or millions of updates per event. The function(s) that should emit `MetadataUpdates` and the frequency of these on-chain updates is left at the discretion of this standard implementation. + +The proposal is extremely gas efficient, since gas costs are only proportional to the frequency of committing changes. Many changes for many tokens can be batched in one transaction for the cost of only one `emit`. + +## Reference Implementation + +### Transformation engines + +We have been experimenting with this generic Metadata update proposal using the JSONata transformation language. + +Here is a very simple example of a NFT metadata for an imaginary 'little monster' game : + +```json +{ + "name": "Monster 1", + "description": "Little monsters you can play with.", + "attributes": [ + { "trait_type": "Level", "value": 0 }, + { "trait_type": "Stamina", "value": 100 } + ], + "updatable": { + "engine": "jsonata@1.8.*", + "recipes": { + "levelUp": { + "eval": "$ ~> | attributes[trait_type='Level'] | {'value': value + 1} |" + }, + "updateDescription": { + "eval": "$ ~> | $ | {'description': $newDescription} |" + } + } + } +} + ``` + +This updatable metadata can only be updated to increment by one the trait attribute "Level". + +An example JSON updates metadata would be : +```json +{ + "updates": [ + {"tokenId":"1","action":"levelUp"}, + {"tokenId":"2","action":"levelUp"}, + {"tokenId":"1","action":"updateDescription","args":{"newDescription":"Now I'm a big monster"}}, + {"tokenId":"1","action":"levelUp"}, + {"tokenId":"3","action":"levelUp"} + ] +} + ``` + +## Security Considerations + +A malicious recipe in the original metadata might be constructed as a DDoS vector for third parties marketplaces and tools that calculate NFT updated JSON metadata. They are encouraged to properly encapsulate software in charge of these calculations and put limits for the engine updates processing. + +Smart contracts should be careful and conscious of using this extension and still allow the metadata URI to be updated in some contexts (by not having the same URI returned by `tokenURI` or `uri` for a given tokenId over time). They need to take into account if previous changes could have been already broadcasted for that NFT by the contract, if these changes are compatible with the new "original metadata" and what semantic they decide to associate by combining these two kinds of "updates". + +## Backwards Compatibility + +The proposal is fully compatible with both [EIP-721](./eip-721.md) and [EIP-1155](./eip-1155.md). Third-party applications that don't support this EIP will still be able to use the original metadata for each NFT. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5187.md b/EIPS/eip-5187.md new file mode 100644 index 0000000..bb1ff12 --- /dev/null +++ b/EIPS/eip-5187.md @@ -0,0 +1,148 @@ +--- +eip: 5187 +title: Extend EIP-1155 with rentable usage rights +description: Separate ownership and usage rights of EIP-1155 to allow users to use NFTs for an allotted time and return them to owners after expiration. +author: DerivStudio (@DerivStudio) +discussions-to: https://ethereum-magicians.org/t/eip-draft-extending-erc1155-with-rentable-usage-rights/9553/4 +status: Draft +type: Standards Track +category: ERC +created: 2022-04-17 +requires: 165, 1155 +--- + +## Abstract + +This standard is an extension of [EIP-1155](./eip-1155.md). It proposes to introduce separable, rentable, and transferable usage rights (in the form of NFT-IDs), enabling the property owner (the only NFT holder) to rent out the NFT to multiple users (ID holders) at the same time for different terms, and be withdrawn by smart contract upon expiration. + +The property owner always retains ownership and is able to transfer the NFT to others during the lease. + +The proposal also supports the sublease and renewal of the rental so that users can freely transfer the usage rights among each other and extend the lease term. Early return of NFTs can also be achieved by subletting the usage rights back to the property owners. + +## Motivation + +The well-accepted [EIP-721](./eip-721.md) and EIP-1155 standards focused on the ownership of unique assets, quite sensible in the time of NFTs being used primarily as arts and collectibles, or, you can say, as private property rights. +### First Step: "Expirable" NFTs +The advent of private ownership in the real world has promoted the vigorous development of the modern economy, and we believe that the usage right will be the first detachable right widely applied in the blockchain ecosystem. As NFTs are increasingly applied in rights, finance, games, and the Metaverse, the value of NFT is no longer simply the proof of ownership, but with limitless practice use scenarios. For example, artists may wish to rent out their artworks to media or audiences within specific periods, and game guilds may wish to rent out game items to new players to reduce their entry costs. + +The lease/rental of NFTs in the crypto space is not a new topic, but the implementation of leasing has long relied on over-collateralization, centralized custody, or pure trust, which significantly limits the boom of the leasing market. Therefore, a new type of "expirable" NFTs that can be automatically withdrawn upon expiration through smart contract is proposed, at the technical level, to eliminate those bottlenecks. Based on that, a new leasing model that is decentralized, collateral-free, and operated purely "on-chain" may disrupt the way people trade and use NFTs. Thus, this EIP proposal is here to create "expirable" NFTs compatible with EIP-1155. +### Then, Make Everything Transferable +The way we achieve leasing is to separate ownership and usage rights, and beyond that, we focus more on making them freely priced and traded after separation, which is impossible to happen in the traditional financial field. Imagine the below scenarios: i) as a landlord, you can sell your house in rental to others without affecting the tenancy, and your tenants will then pay rent to the new landlord; ii) as a tenant, you can sublet the house to others without the consent of the landlord, and even the one sublets can continue subletting the house until the lease term is close the last tenant can apply for a renewal of the lease. All of this can happen in the blockchain world, and that's the beauty of blockchain. Without permission, without trust, code is the law. + +Making ownership and usage rights transferable may further revolutionize the game rules in NFT's field, both in capital allocation and NFT development. Buying NFT ownership is more like investing in stocks, and the price is determined by market expectations of the project; renting the usage right is less speculative, so the price is easier to determine based on supply and demand. The ownership market and the usage-right market will function to meet the needs of target participants and achieve a balance that is conducive to the long-term and stable development of NFT projects. +Based on the above, we propose this EIP standard to complement the current EIP scopes and introduce those functions as new standards. + + +## Specification + +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. + +```solidity +pragma solidity ^0.8.0; + +/// Note: the ERC-165 identifier for this interface is 0x6938e358. + interface IRental /* is IERC165,IERC1155 */ { + /** + * @notice This emits when user rent NFT + * - `id` The id of the current token + * - `user` The address to rent the NFT usage rights + * - `amount` The amount of usage rights + * - `expire` The specified period of time to rent + **/ + event Rented(uint256 indexed id,address indexed user,uint256 amount,uint256 expire); + + /** + * MUST trigger on any successful call to `renew(address user,uint256 id)` + * - `id` The id of the current token + * - `user` The user of the NFT + * - `expire` The new specified period of time to rent + **/ + event Renew(uint256 indexed id,address indexed user,uint256 expire); + + /** + * MUST trigger on any successful call to `renew(address user,uint256 id,uint256 expire)` + * - `id` The id of the current token + * - `from` The current user of the NFT + * - `to` The new user + **/ + event Sublet(uint256 indexed id,address indexed from,address to); + + /** + * @notice This emits when the NFT owner takes back the usage rights from the tenant (the `user`) + * - id The id of the current token + * - user The address to rent the NFT's usage rights + * - amount Amount of usage rights + **/ + event TakeBack(uint256 indexed id, address indexed user, uint256 amount); + + /** + * @notice Function to rent out usage rights + * - from The address to approve + * - to The address to rent the NFT usage rights + * - id The id of the current token + * - amount The amount of usage rights + * - expire The specified period of time to rent + **/ + function safeRent(address from,address to,uint256 id,uint256 amount,uint256 expire) external; + + /** + * @notice Function to take back usage rights after the end of the tenancy + * - user The address to rent the NFT's usage rights + * - tokenId The id of the current token + **/ + function takeBack(address user,uint256 tokenId) external; + + /** + * @notice Return the NFT to the address of the NFT property right owner. + **/ + function propertyRightOf(uint256 id) external view returns (address); + + /** + * @notice Return the total supply amount of the current token + **/ + function totalSupply(uint256 id) external view returns (uint256); + + /** + * @notice Return expire The specified period of time to rent + **/ + function expireAt(uint256 id,address user) external view returns(uint256); + + /** + * extended rental period + * - `id` The id of the current token + * - `user` The user of the NFT + * - `expire` The new specified period of time to rent + **/ + function renew(address user,uint256 id,uint256 expire) external; + + /** + * transfer of usage right + * - `id` The id of the current token + * - `user` The user of the NFT + * - `expire` The new specified period of time to rent + **/ + function sublet(address to,uint256 id) external; +} + + +``` + +## Rationale + +Implementing the proposal to create rentable NFTs has two main benefits. + +One is that NFTs with multiple usage rights allow NFT property owners to perform the safeRent function and rent out usage rights to multiple users at the same time. For each usage right leased and expires, the property owner can perform the takeBack function to retrieve the usage right. + +Another benefit is that the transfer of usage rights can be quite flexible. The user can transfer the usage rights to other users by calling the Sublet function during the lease period, and can also extend the lease period of the usage rights by asking the property owner to perform the Renewal function. It is worth mentioning that if the user sublet the NFT to the property owner, it will realize the early return of NFT before the end of the lease period. + +## Backwards Compatibility + +As mentioned at the beginning, this is an extension of EIP-1155. Therefore, it is fully backward compatible with EIP-1155. + +## Security Considerations + +Needs discussion. + +## Copyright + +Disclaimer of copyright and related rights through [CC0](../LICENSE.md). diff --git a/EIPS/eip-5189.md b/EIPS/eip-5189.md new file mode 100644 index 0000000..786275f --- /dev/null +++ b/EIPS/eip-5189.md @@ -0,0 +1,225 @@ +--- +eip: 5189 +title: Account Abstraction via Endorsed Operations +description: An account abstraction proposal that avoids protocol changes while maintaining compatibility with existing smart contract wallets. +author: Agustín Aguilar (@agusx1211), Philippe Castonguay (@phabc) +discussions-to: https://ethereum-magicians.org/t/erc-account-abstraction-via-endorsed-operations/9799 +type: Standards Track +category: ERC +status: Stagnant +created: 2022-06-29 +--- + +## Abstract +This EIP proposes a form of account abstraction that ensures compatibility with existing smart contract wallets and provides flexibility for alternative designs while avoiding introducing changes to the consensus layer. Instead of defining a strict structure for meta-transactions, this proposal introduces the figure of `endorser` contracts. These smart contract instances are tasked with determining the quality of the submitted meta-transactions, thus safely helping bundlers determine if a meta-transaction should be kept in the mempool or not. Developers that intend to make their smart contract wallet compatible with this EIP must create and deploy an instance of an `endorser`; this instance must be seeded with a small amount of ETH to be burnt that incentivizes its good behavior. + +## Motivation +This account abstraction proposal aims to implement a generalized system for executing meta-transactions while maintaining the following goals: + +* **Achieve the primary goal of account abstraction:** allow users to use smart contract wallets containing arbitrary verification and execution logic instead of EOAs as their primary account. +* **Decentralization:** +* * Allow any bundler to participate in the process of including meta-transactions. +* * Work with all activity happening over a public mempool without having to concentrate transactions on centralized relayers. +* * Define structures that help maintain a healthy mempool without risking its participants from getting flooded with invalid or malicious payloads. +* * Avoid trust assumptions between bundlers, developers, and wallets. +* **Support existing smart contract wallet implementations:** Work with all the smart contract wallets already deployed and active while avoiding forcing each wallet instance to be manually upgraded. +* **Provide an unrestrictive framework:** Smart contract wallets are very different in design, limitations, and capabilities from one another; the proposal is designed to accommodate almost all possible variations. +* **No overhead:** Smart contract wallets already have a cost overhead compared to EOA alternatives, the proposal does not worsen the current situation. +* **Support other use cases:** +* * Privacy-preserving applications. +* * Atomic multi-operations (similar to EIP-3074). +* * Payment of transaction fees using ERC-20 tokens. +* * Scheduled execution of smart contracts without any user input. +* * Applications that require a generalistic relayer. + +## Specification +To avoid Ethereum consensus changes, we do not attempt to create new transaction types for account-abstracted transactions. Instead, meta-transactions are packed up in a struct called `Operation`, operations are structs composed by the following fields: + +| Field | Type | Description | +|-------------------|---------|--------------------------------------------------------------------------------------------------------| +| entrypoint | address | contract address that must be called with `callData` to execute the `operation`. | +| callData | bytes | data that must be passed to the `entrypoint` call to execute the `operation`. | +| gasLimit | uint64 | minimum gasLimit that must be passed when executing the `operation`. | +| endorser | address | address of the endorser contract that should be used to validate the `operation`. | +| endorserGasLimit | uint64 | amount of gas that should be passed to the endorser when validating the `operation`. | +| maxFeePerGas | uint256 | max amount of basefee that the `operation` execution is expected to pay, (similar to EIP-1559 `max_fee_per_gas`) | +| priorityFeePerGas | uint256 | fixed amount of fees that the `operation` execution is expected to pay to the bundler (similar to EIP-1559 `max_priority_fee_per_gas`). | + +These `Operation` objects can be sent to a dedicated operations mempool. A specialized class of actors called bundlers (either miners running special-purpose code, or just users that can relay transactions to miners) listen for operations on the mempool and execute these transactions. + +Transactions are executed by calling the `entrypoint` with the provided `callData`. The `entrypoint` can be any contract, but most commonly it will be the wallet contract itself, alternatively it can be an intermediary utility that deploys the wallet and then performs the transaction. + +#### Endorser functionality +Mempool participants need to be able to able to filter "good operations" (operations that pay the bundler the defined fee) from "bad operations" (operations that either miss payment or revert altogether). + +This categorization is facilitated by the `endorser`; the endorser must be a deployed smart contract that implements the following interface: + +```solidity +interface Endorser { + struct Dependency { + address addr; + bool balance; + bool code; + bool nonce; + bytes32[] slots; + } + + function isOperationReady( + address _entrypoint, + bytes calldata _data, + uint256 _gasLimit, + uint256 _maxFeePerGas, + uint256 _maxPriorityFeePerGas + ) external view returns ( + bool readiness, + Dependency[] memory dependencies + ); +} +``` + +It should also be registered in the `EndorserRegistry` with a minimum amount of burned ETH (Mempool operators are free to accept operations from endorsers without any burn, but they would increase their risk exposing themselves to denial of service attacks). + +When the `isOperationReady` method is called, the endorser must return this information: + +* **readiness:** when returning`true`, it means the transaction WILL be executed correctly and the bundler WILL be paid the offered gas fees (even if the underlying intent of the operation fails). +* **dependencies:** a comprehensive list of addresses and storage slots that must be monitored; any state change in these dependencies MUST trigger a re-evaluation of the operation's readiness. + +The information provided by the endorser helps the mempool operator maintain a pool of "good" meta-transactions that behave correctly; it DOES NOT guarantee that such transactions will be able to be executed correctly. Bundlers must always simulate the result of the execution before including a transaction in a block. + +#### Dependencies +| Field | Type | Description | +| -------- | -------- | -------- | +| addr | address | Contract address of the dependencies entry *(only one entry per address should be allowed)*. | +| balance | bool | `true` if the balance of `addr` should be considered a dependency of the `operation`. | +| code | bool | `true` if the code of `addr` should be considered a dependency of the `operation`. | +| nonce | bool | `true` if the nonce of `addr` should be considered a dependency of the `operation`. | +| slots | bytes32[] | List of all storage slots of `addr` that should be considered dependencies of `operation`. | + +The `endorser` does not need to include all accessed storage slots on the dependencies list, it only needs to include storage slots that after a change may also result in a change of readiness. + +> E.g. A wallet may pay fees using funds stored as WETH. During `isValidOperation()`, the endorser contract may call the `balanceOf` method of the `WETH` contract to determine if the wallet has enough `WETH` balance. Even though the ETH balance of the WETH contract and the code of the WETH contract are being accessed, the endorser only cares about the user's WETH balance for this operation and hence does not include these as dependencies. + +### Misbehavior detection +The `endorser` contracts may behave maliciously or erratically in the following ways: + +* (1) It may consider an operation `ready`, but when the operation is executed it transfers less than the agreed-upon fees to the bundler. +* (2) It may consider an operation `ready`, but when the operation is executed the top-level call fails. +* (3) It may change the status from `ready` to `not-ready` while none of the dependencies register any change. + +The bundler must always discard and re-evaluate the readiness status after a change on any of the dependencies of the `operation`, meaning that only operations considered `ready` are candidates for constructing the next block. + +If, when simulating the final inclusion of the operation, the bundler discovers that it does not result in correct payment (either because the transaction fails, or transferred amount is below the defined fee), then it should proceed to ban the `endorser` for one of the following reasons: + +1) The `endorser` returns `isOperationReady == true` even though the `operation` is not healthy to be included in a block. +2) The `operation` changed readiness status from `true` to `false` while all dependencies remained unchanged. + +After an `endorser` is banned, the mempool operator should drop all `operations` related to such endorser. + +> Notice: The mempool operator could call one last time `isOperationReady` to determine if the `endorser` should be banned because `(1)` or `(2)`, but this step is not strictly necessary since both scenarios lead to the `endoser` being banned. + +### Client behavior upon receiving an operation +When a client receives an `operation`, it must first run some basic sanity checks, namely that: + +* The `endorserGasLimit` is sufficiently low (<= `MAX_ENDORSER_GAS`). +* The endorser (i) is registered and has enough burn (>= `MIN_ENDORSER_BURN`), and (ii) it has not been internally flagged as banned. +* The `gasLimit` is at least the cost of a `CALL` with a non-zero value. +* The `maxFeePerGas` and `priorityPerGas` are above a configurable minimum value the client is willing to accept. +* If another operation exists in the mempool with the exact same dependency set AND the same endorser address, the `maxFeePerGas` and `priorityFeePerGas` of the newly received operation MUST be 12% higher than the one on the mempool to replace it. (Similar with how EOA with same nonce work) + +If the `operation` passes these checks, then the client MUST call `isOperationReady()` on the `endorser`. If the endorser considers the operation ready, then the client MUST add the operation to the mempool. Otherwise, the operation MUST discarded. + +The `endorser` result MUST be invalidated and its readiness be re-evaluated if any of the values of the provided dependencies change. If the operation readiness changes to `false`, the operation MUST be discarded. + +Before including the operation in a block, a last simulation MUST be performed, this time without calling the `endorser`, but by constructing the block and probing the result. All transactions in the block listed **before** the operation must be simulated and the endorser must be queried again there for readiness in-case some dependencies changed. + +If the operation fails during simulation, the endorser must be banned because (i) it returned a bad readiness state or (ii) it changed the operation readiness independently from the dependencies. + +Additional events that must invalidate the readiness are: + +* A transaction or operation modifies the same storage slots (as the dependencies) is queued before the given operation. + +#### Optional rules +Mempool clients could implement additional rules to further protect against maliciously constructed transactions. +* Limit the size of accepted dependencies to `MAX_OPERATION_DEPENDENCIES`, dropping operations that cross the boundary. +* Limit the number of times an operation may trigger a re-evaluation to `MAX_OPERATION_REEVALS`, dropping operations that cross the boundary. +* Limit the number of operations in the mempool that depend on the same dependency slots. + +If these rules are widely adopted, wallet developers should keep usage of dependencies to the lowest possible levels. + +#### Evaluation +To evaluate an `operation`, the client must call the `isOperationReady` method, with a `gasLimit` above or equal to `endorserGasLimit`. + +If the call fails, or the `endorser` returns `ready == false`, then the operation must be dropped from the mempool. + +If the call succeeds and returns `ready == true`, then the operation can be kept in the mempool and used when constructing the next block. The client must keep track of all fields returned as `dependencies`. If any of these register a change, then readiness should be reevaluated. + + +#### After operation inclusion +There is no limit in-place that defines that an operation can only be executed once. + +The bundler MUST NOT drop an `operation` after successfully including such operation in a block, the `operation` must remain in the mempool and a last `isOperationReady` call must be performed. + +If the `endorser` still returns `readiness == true` (after inclusion) then the operation SHOULD be treated as any other healthy operation, and thus it COULD be kept in the mempool. + +### Endorser registry +The endorser registry serves as a place to register the burn of each endorser, anyone can increase the burn of any endorser by calling the `addBurn` function. + +All burn is effectively locked forever; slashing can't be reliably proved on-chain without protocol alterations, so it remains a virtual event on which mempool operators will ignore the deposited ETH. + +#### Implementation +(EXAMPLE) + +```solidity +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.15; + + +contract EndorserRegistry { + event Burned( + address indexed _endorser, + address indexed _sender, + uint256 _new, + uint256 _total + ); + + mapping(address => uint256) public burn; + + function addBurn(address _endorser) external payable returns (uint256) { + uint256 total = burn[_endorser] + msg.value; + burn[_endorser] = total; + + emit Burned(_endorser, msg.sender, msg.value, total); + + return total; + } +} +``` + +## Rationale +The main challenge with a purely smart contract wallet-based account abstraction system is DoS safety: how can a bundler that includes an operation make sure that it will pay fees without executing the entire operation? + +Bundlers could execute the entire operation to determine if it is healthy or not, but this operation may be expensive and complex for the following reasons: + +* The bundler does not have a way to simulate the transaction with a reduced amount of gas; it has to use the whole `gasLimit`, exposing itself to a higher level of griefing. +* The bundler does not have a way to know if a change to the state will affect the operation or not, and thus it has to re-evaluate the operation after every single change. +* The bundler does not have a way to know if a change to the state will invalidate a large portion of the mempool. + +In this proposal, we add the `endorser` as a tool for the bundlers to validate arbitrary operations in a controlled manner, without the bundler having to know any of the inner workings of such operation. + +In effect, we move the responsibility from the wallet to the wallet developer; the developer must code, deploy and burn ETH for the `endorser`; this is a nearly ideal scenario because developers know how their wallet operations work, and thus they can build tools to evaluate these operations efficiently. + +Additionally, the specification is kept as simple as possible as enforcing a highly structured behavior and schema for smart contract wallet transactions may stagnate the adoption of more innovative types of wallets and the adoption of a shared standard among them. + +#### Differences with alternative proposals +1) This proposal does not require monitoring for forbidden opcodes or storage access boundaries. Wallets have complete freedom to use any EVM capabilities during validation and execution. +2) This proposal does not specify any replay protection logic since all existing smart contract wallets already have their own, and designs can vary among them. Nonces can be communicated to the bundler using a `dependency`. +3) This proposal does not specify a pre-deployment logic because it can be handled directly by the entrypoint. +4) This proposal does not require wallets to accept `execution` transactions from a trusted entrypoint contract, reducing overhead and allowing existing wallets to be compatible with the proposal. +5) This proposal does not distinguish between `execution` and `signature` payloads, this distinction remains implementation-specific. + + +## Backwards Compatibility +This EIP does not change he consensus layer, nor does impose changes on existing smart contract wallets, so there are no backwards compatibility issues. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5192.md b/EIPS/eip-5192.md new file mode 100755 index 0000000..30e5d14 --- /dev/null +++ b/EIPS/eip-5192.md @@ -0,0 +1,71 @@ +--- +eip: 5192 +title: Minimal Soulbound NFTs +description: Minimal interface for soulbinding EIP-721 NFTs +author: Tim Daubenschütz (@TimDaub), Anders (@0xanders) +discussions-to: https://ethereum-magicians.org/t/eip-5192-minimal-soulbound-nfts/9814 +status: Final +type: Standards Track +category: ERC +created: 2022-07-01 +requires: 165, 721 +--- + +## Abstract + +This standard is an extension of [EIP-721](./eip-721.md). It proposes a minimal interface to make tokens soulbound using the feature detection functionality of [EIP-165](./eip-165.md). A soulbound token is a non-fungible token bound to a single account. + +## Motivation + +The Ethereum community has expressed a need for non-transferrable, non-fungible, and socially-priced tokens similar to World of Warcraft’s soulbound items. But the lack of a token standard leads many developers to simply throw errors upon a user's invocation of transfer functionalities. Over the long term, this will lead to fragmentation and less composability. + +In this document, we outline a minimal addition to [EIP-721](./eip-721.md) that allows wallet implementers to check for a token contract's permanent (non-)transferability using [EIP-165](./eip-165.md). + +## Specification + +The keywords "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. + +### Contract Interface + +A token with a `uint256 tokenId` may be bound to a receiving account with `function locked(...)` returning `true`. In this case, all [EIP-721](./eip-721.md) functions of the contract that transfer the token from one account to another must throw. + +```solidity +// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.0; + +interface IERC5192 { + /// @notice Emitted when the locking status is changed to locked. + /// @dev If a token is minted and the status is locked, this event should be emitted. + /// @param tokenId The identifier for a token. + event Locked(uint256 tokenId); + + /// @notice Emitted when the locking status is changed to unlocked. + /// @dev If a token is minted and the status is unlocked, this event should be emitted. + /// @param tokenId The identifier for a token. + event Unlocked(uint256 tokenId); + + /// @notice Returns the locking status of an Soulbound Token + /// @dev SBTs assigned to zero address are considered invalid, and queries + /// about them do throw. + /// @param tokenId The identifier for an SBT. + function locked(uint256 tokenId) external view returns (bool); +} +``` + +To aid recognition that an [EIP-721](./eip-721.md) token implements "soulbinding" via this EIP upon calling [EIP-721](./eip-721.md)'s `function supportsInterface(bytes4 interfaceID) external view returns (bool)` with `interfaceID=0xb45a3c0e`, a contract implementing this EIP must return `true`. + +## Rationale + +The above model is the simplest possible path towards a canonical interface for Soulbound tokens. It reflects upon the numerous Soulbound token implementations that simply revert upon transfers. + +## Backwards Compatibility + +This proposal is fully backward compatible with [EIP-721](./eip-721.md). + +## Security Considerations + +There are no security considerations related directly to the implementation of this standard. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5202.md b/EIPS/eip-5202.md new file mode 100644 index 0000000..b7b3500 --- /dev/null +++ b/EIPS/eip-5202.md @@ -0,0 +1,105 @@ +--- +eip: 5202 +title: Blueprint contract format +description: Define a bytecode container format for indexing and utilizing blueprint contracts +author: Charles Cooper (@charles-cooper), Edward Amor (@skellet0r) +discussions-to: https://ethereum-magicians.org/t/erc-5202-standard-factory-contract-format/9851 +status: Review +type: Standards Track +category: ERC +created: 2022-06-23 +requires: 170 +--- + +## Abstract +Define a standard for "blueprint" contracts, or contracts which represent initcode that is stored on-chain. + +## Motivation +To decrease deployer contract size, a useful pattern is to store initcode on chain as a "blueprint" contract, and then use `EXTCODECOPY` to copy the initcode into memory, followed by a call to `CREATE` or `CREATE2`. However, this comes with the following problems: + +- It is hard for external tools and indexers to detect if a contract is a "regular" runtime contract or a "blueprint" contract. Heuristically searching for patterns in bytecode to determine if it is initcode poses maintenance and correctness problems. +- Storing initcode byte-for-byte on-chain is a correctness and security problem. Since the EVM does not have a native way to distinguish between executable code and other types of code, unless the initcode explicitly implements ACL rules, *anybody* can call such a "blueprint" contract and execute the initcode directly as ordinary runtime code. This is particularly problematic if the initcode stored by the blueprint contract has side effects such as writing to storage or calling external contracts. If the initcode stored by the blueprint contract executes a `SELFDESTRUCT` opcode, the blueprint contract could even be removed, preventing the correct operation of downstream deployer contracts that rely on the blueprint existing. For this reason, it would be good to prefix blueprint contracts with a special preamble to prevent execution. + +## Specification +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. + +A blueprint contract MUST use the preamble `0xFE71`. 6 bits are allocated to the version, and 2 bits to the length encoding. The first version begins at 0 (`0b000000`), and versions increment by 1. The value `0b11` for `` is reserved. In the case that the length bits are `0b11`, the third byte is considered a continuation byte (that is, the version requires multiple bytes to encode). The exact encoding of a multi-byte version is left to a future ERC. + +A blueprint contract MUST contain at least one byte of initcode. + +A blueprint contract MAY insert any bytes (data or code) between the version byte(s) and the initcode. If such variable length data is used, the preamble must be `0xFE71`. The `` represent a number between 0 and 2 (inclusive) describing how many bytes `` takes, and `` is the big-endian encoding of the number of bytes that `` takes. + +## Rationale +- To save gas and storage space, the preamble should be as minimal as possible. + +- It is considered "bad" behavior to try to CALL a blueprint contract directly, therefore the preamble starts with `INVALID (0xfe)` to end execution with an exceptional halting condition (rather than a "gentler" opcode like `STOP (0x00)`). + +- To help distinguish a blueprint contract from other contracts that may start with `0xFE`, a "magic" byte is used. The value `0x71` was arbitrarily chosen by taking the last byte of the keccak256 hash of the bytestring "blueprint" (i.e.: `keccak256(b"blueprint")[-1]`). + +- An empty initcode is disallowed by the spec to prevent what might be a common mistake. + +- Users may want to include arbitrary data or code in their preamble. To allow indexers to ignore these bytes, a variable length encoding is proposed. To allow the length to be only zero or one bytes (in the presumably common case that `len(data bytes)` is smaller than 256), two bits of the third byte are reserved to specify how many bytes the encoded length takes. + +- In case we need an upgrade path, version bits are included. While we do not expect to exhaust the version bits, in case we do, a continuation sequence is reserved. Since only two bytes are required for `` (as [EIP-170](./eip-170.md) restricts contract length to 24KB), a `` value of 3 would never be required to describe ``. For that reason, the special `` value of `0b11` is reserved as a continuation sequence marker. + +- The length of the initcode itself is not included by default in the preamble because it takes space, and it can be trivially determined using `EXTCODESIZE`. + +- The EOF ([EIP-3540](./eip-3540.md)) could provide another way of specifying blueprint contracts, by adding another section kind (3 - initcode). However, it is not yet in the EVM, and we would like to be able to standardize blueprint contracts today, without relying on EVM changes. If, at some future point, section kind 3 becomes part of the EOF spec, and the EOF becomes part of the EVM, this ERC will be considered to be obsolesced since the EOF validation spec provides much stronger guarantees than this ERC. + + +## Backwards Compatibility +Needs discussion + +## Reference Implementation + +```python +from typing import Optional, Tuple + +def parse_blueprint_preamble(bytecode: bytes) -> Tuple[int, Optional[bytes], bytes]: + """ + Given bytecode as a sequence of bytes, parse the blueprint preamble and + deconstruct the bytecode into: + the ERC version, preamble data and initcode. + Raises an exception if the bytecode is not a valid blueprint contract + according to this ERC. + arguments: + bytecode: a `bytes` object representing the bytecode + returns: + (version, + None if is 0, otherwise the bytes of the data section, + the bytes of the initcode, + ) + """ + if bytecode[:2] != b"\xFE\x71": + raise Exception("Not a blueprint!") + + erc_version = (bytecode[2] & 0b11111100) >> 2 + + n_length_bytes = bytecode[2] & 0b11 + if n_length_bytes == 0b11: + raise Exception("Reserved bits are set") + + data_length = int.from_bytes(bytecode[3:3 + n_length_bytes], byteorder="big") + + if n_length_bytes == 0: + preamble_data = None + else: + data_start = 3 + n_length_bytes + preamble_data = bytecode[data_start:data_start + data_length] + + initcode = bytecode[3 + n_length_bytes + data_length:] + + if len(initcode) == 0: + raise Exception("Empty initcode!") + + return erc_version, preamble_data, initcode +``` + +## Security Considerations + +There could be contracts on-chain already which happen to start with the same prefix as proposed in this ERC. However, this is not considered a serious risk, because the way it is envisioned that indexers will use this is to verify source code by compiling it and prepending the preamble. + +As of 2022-07-08, no contracts deployed on the Ethereum mainnet have a bytecode starting with `0xFE71`. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5216.md b/EIPS/eip-5216.md new file mode 100644 index 0000000..1e16fd4 --- /dev/null +++ b/EIPS/eip-5216.md @@ -0,0 +1,104 @@ +--- +eip: 5216 +title: EIP-1155 Approval By Amount Extension +description: Extension for EIP-1155 secure approvals +author: Iván Mañús (@ivanmmurciaua), Juan Carlos Cantó (@EscuelaCryptoES) +discussions-to: https://ethereum-magicians.org/t/eip-erc1155-approval-by-amount/9898 +status: Last Call +last-call-deadline: 2022-11-12 +type: Standards Track +category: ERC +created: 2022-07-11 +requires: 20, 165, 1155 +--- + +## Abstract + +This EIP defines standard functions for granular approval of [EIP-1155](./eip-1155.md) tokens by both `id` and `amount`. This EIP extends [EIP-1155](./eip-1155.md). + +## Motivation + +[EIP-1155](./eip-1155.md)'s popularity means that multi-token management transactions occur on a daily basis. Although it can be used as a more comprehensive alternative to [EIP-721](./eip-721.md), EIP-1155 is most commonly used as intended: creating multiple `id`s, each with multiple tokens. While many projects interface with these semi-fungible tokens, by far the most common interactions are with NFT marketplaces. + +Due to the nature of the blockchain, programming errors or malicious operators can cause permanent loss of funds. It is therefore essential that transactions are as trustless as possible. EIP-1155 uses the `setApprovalForAll` function, which approves ALL tokens with a specific `id`. This system has obvious minimum required trust flaws. This EIP combines ideas from [EIP-20](./eip-20.md) and [EIP-721](./eip-721.md) in order to create a trust mechanism where an owner can allow a third party, such as a marketplace, to approve a limited (instead of unlimited) number of tokens of one `id`. + +## Specification + +The keywords “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. + +Contracts using this EIP MUST implement the `IERC1155ApprovalByAmount` interface. + +### Interface implementation + +```solidity +/** + * @title ERC-1155 Approval By Amount Extension + * Note: the ERC-165 identifier for this interface is 0x1be07d74 + */ +interface IERC1155ApprovalByAmount is IERC1155 { + + /** + * @notice Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to + * `id` and with an amount: `amount`. + */ + event ApprovalByAmount(address indexed account, address indexed operator, uint256 id, uint256 amount); + + /** + * @notice Grants permission to `operator` to transfer the caller's tokens, according to `id`, and an amount: `amount`. + * Emits an {ApprovalByAmount} event. + * + * Requirements: + * - `operator` cannot be the caller. + */ + function approve(address operator, uint256 id, uint256 amount) external; + + /** + * @notice Returns the amount allocated to `operator` approved to transfer `account`'s tokens, according to `id`. + */ + function allowance(address account, address operator, uint256 id) external view returns (uint256); +} +``` + +The `approve(address operator, uint256 id, uint256 amount)` function MUST be either `public` or `external`. + +The `allowance(address account, address operator, uint256 id)` function MUST be either `public` or `external` and MUST be `view`. + +The `safeTrasferFrom` function (as defined by EIP-1155) MUST: + +- Not revert if the user has approved `msg.sender` with a sufficient `amount` +- Subtract the transferred amount of tokens from the approved amount if `msg.sender` is not approved with `setApprovalForAll` + +In addition, the `safeBatchTransferFrom` MUST: + +- Add an extra condition that checks if the `allowance` of all `ids` have the approved `amounts` (See `_checkApprovalForBatch` function reference implementation) + +The `ApprovalByAmount` event MUST be emitted when a certain number of tokens are approved. + +The `supportsInterface` method MUST return `true` when called with `0x1be07d74`. + +## Rationale + +The name "EIP-1155 Approval By Amount Extension" was chosen because it is a succinct description of this EIP. Users can approve their tokens by `id` and `amount` to `operator`s. + +By having a way to approve and revoke in a manner similar to [EIP-20](./eip-20.md), the trust level can be more directly managed by users: + +- Using the `approve` function, users can approve an operator to spend an `amount` of tokens for each `id`. +- Using the `allowance` function, users can see the approval that an operator has for each `id`. + +The [EIP-20](./eip-20.md) name patterns were used due to similarities with [EIP-20](./eip-20.md) approvals. + +## Backwards Compatibility + +This standard is compatible with [EIP-1155](./eip-1155.md). + +## Reference Implementation + +The reference implementation can be found [here](../assets/eip-5216/ERC1155ApprovalByAmount.sol). + +## Security Considerations + +Users of this EIP must thoroughly consider the amount of tokens they give permission to `operators`, and should revoke unused authorizations. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5218.md b/EIPS/eip-5218.md new file mode 100644 index 0000000..4b7323b --- /dev/null +++ b/EIPS/eip-5218.md @@ -0,0 +1,239 @@ +--- +eip: 5218 +title: NFT Rights Management +description: An interface for creating copyright licenses that transfer with an NFT. +author: James Grimmelmann (@grimmelm), Yan Ji (@iseriohn), Tyler Kell (@relyt29) +discussions-to: https://ethereum-magicians.org/t/eip-5218-nft-rights-management/9911 +status: Draft +type: Standards Track +category: ERC +created: 2022-07-11 +requires: 721 +--- + + + +## Abstract + +The following standard defines an API for managing NFT licenses. This standard provides basic functionality to create, transfer, and revoke licenses, and to determine the current licensing state of an NFT. The standard does not define the legal details of the license. Instead, it provides a structured framework for recording licensing details. + +We consider use cases of NFT creators who wish to give the NFT holder a copyright license to use a work associated with the NFT. The holder of an active license can issue sublicenses to others to carry out the rights granted under the license. The license can be transferred with the NFT, so do all the sublicenses. The license can optionally be revoked under conditions specified by the creator. + + +## Motivation + +The [ERC-721](./eip-721.md) standard defines an API to track and transfer ownership of an NFT. When an NFT is to represent some off-chain asset, however, we would need some legally effective mechanism to *tether* the on-chain asset (NFT) to the off-chain property. One important case of off-chain property is creative work such as an image or music file. Recently, most NFT projects involving creative works have used licenses to clarify what legal rights are granted to the NFT owner. But these licenses are almost always off-chain and the NFTs themselves do not indicate what licenses apply to them, leading to uncertainty about rights to use the work associated with the NFT. It is not a trivial task to avoid all the copyright vulnerabilities in NFTs, nor have existing EIPs addressed rights management of NFTs beyond the simple cases of direct ownership (see [ERC-721](./eip-721.md)) or rental (see [ERC-4907](./eip-4907.md)). + +This EIP attempts to provide a standard to facilitate rights management of NFTs in the world of Web3. In particular, [ERC-5218](./eip-5218.md) smart contracts allow all licenses to an NFT, including the *root license* issued to the NFT owner and *sublicenses* granted by a license holder, to be recorded and easily tracked with on-chain data. These licenses can consist of human-readable legal code, machine-readable summaries such as those written in CC REL, or both. An ERC-5218 smart contract points to a license by recording a URI, providing a reliable reference for users to learn what legal rights they are granted and for NFT creators and auditors to detect unauthorized infringing uses. + + + +## Specification + +The keywords “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. + +**Every ERC-5218 compliant contract *must* implement the `IERC5218` interface**: + +```solidity +pragma solidity ^0.8.0; + +/// @title ERC-5218: NFT Rights Management +interface IERC5218 is IERC721 { + + /// @dev This emits when a new license is created by any mechanism. + event CreateLicense(uint256 _licenseId, uint256 _tokenId, uint256 _parentLicenseId, address _licenseHolder, string _uri, address _revoker); + + /// @dev This emits when a license is revoked. Note that under some + /// license terms, the sublicenses may be `implicitly` revoked following the + /// revocation of some ancestral license. In that case, your smart contract + /// may only emit this event once for the ancestral license, and the revocation + /// of all its sublicenses can be implied without consuming additional gas. + event RevokeLicense(uint256 _licenseId); + + /// @dev This emits when the a license is transferred to a new holder. The + /// root license of an NFT should be transferred with the NFT in an ERC721 + /// `transfer` function call. + event TransferLicense(uint256 _licenseId, address _licenseHolder); + + /// @notice Check if a license is active. + /// @dev A non-existing or revoked license is inactive and this function must + /// return `false` upon it. Under some license terms, a license may become + /// inactive because some ancestral license has been revoked. In that case, + /// this function should return `false`. + /// @param _licenseId The identifier for the queried license + /// @return Whether the queried license is active + function isLicenseActive(uint256 _licenseId) external view returns (bool); + + /// @notice Retrieve the token identifier a license was issued upon. + /// @dev Throws unless the license is active. + /// @param _licenseId The identifier for the queried license + /// @return The token identifier the queried license was issued upon + function getLicenseTokenId(uint256 _licenseId) external view returns (uint256); + + /// @notice Retrieve the parent license identifier of a license. + /// @dev Throws unless the license is active. If a license doesn't have a + /// parent license, return a special identifier not referring to any license + /// (such as 0). + /// @param _licenseId The identifier for the queried license + /// @return The parent license identifier of the queried license + function getParentLicenseId(uint256 _licenseId) external view returns (uint256); + + /// @notice Retrieve the holder of a license. + /// @dev Throws unless the license is active. + /// @param _licenseId The identifier for the queried license + /// @return The holder address of the queried license + function getLicenseHolder(uint256 _licenseId) external view returns (address); + + /// @notice Retrieve the URI of a license. + /// @dev Throws unless the license is active. + /// @param _licenseId The identifier for the queried license + /// @return The URI of the queried license + function getLicenseURI(uint256 _licenseId) external view returns (string memory); + + /// @notice Retrieve the revoker address of a license. + /// @dev Throws unless the license is active. + /// @param _licenseId The identifier for the queried license + /// @return The revoker address of the queried license + function getLicenseRevoker(uint256 _licenseId) external view returns (address); + + /// @notice Retrieve the root license identifier of an NFT. + /// @dev Throws unless the queried NFT exists. If the NFT doesn't have a root + /// license tethered to it, return a special identifier not referring to any + /// license (such as 0). + /// @param _tokenId The identifier for the queried NFT + /// @return The root license identifier of the queried NFT + function getLicenseIdByTokenId(uint256 _tokenId) external view returns (uint256); + + /// @notice Create a new license. + /// @dev Throws unless the NFT `_tokenId` exists. Throws unless the parent + /// license `_parentLicenseId` is active, or `_parentLicenseId` is a special + /// identifier not referring to any license (such as 0) and the NFT + /// `_tokenId` doesn't have a root license tethered to it. Throws unless the + /// message sender is eligible to create the license, i.e., either the + /// license to be created is a root license and `msg.sender` is the NFT owner, + /// or the license to be created is a sublicense and `msg.sender` is the holder + /// of the parent license. + /// @param _tokenId The identifier for the NFT the license is issued upon + /// @param _parentLicenseId The identifier for the parent license + /// @param _licenseHolder The address of the license holder + /// @param _uri The URI of the license terms + /// @param _revoker The revoker address + /// @return The identifier of the created license + function createLicense(uint256 _tokenId, uint256 _parentLicenseId, address _licenseHolder, string memory _uri, address _revoker) external returns (uint256); + + /// @notice Revoke a license. + /// @dev Throws unless the license is active and the message sender is the + /// eligible revoker. This function should be used for revoking both root + /// licenses and sublicenses. Note that if a root license is revoked, the + /// NFT should be transferred back to its creator. + /// @param _licenseId The identifier for the queried license + function revokeLicense(uint256 _licenseId) external; + + /// @notice Transfer a sublicense. + /// @dev Throws unless the sublicense is active and `msg.sender` is the license + /// holder. Note that the root license of an NFT should be tethered to and + /// transferred with the NFT. Whenever an NFT is transferred by calling the + /// ERC721 `transfer` function, the holder of the root license should be + /// changed to the new NFT owner. + /// @param _licenseId The identifier for the queried license + /// @param _licenseHolder The new license holder + function transferSublicense(uint256 _licenseId, address _licenseHolder) external; +} +``` + +Licenses to an NFT in general have a tree structure as below: + +![The license tree](../assets/eip-5218/license-tree.png) + +There is one root license to the NFT itself, granting the NFT owner some rights to the linked work. The NFT owner (i.e., the root license holder) may create sublicenses, holders of which may also create sublicenses recursively. + +The full log of license creation, transfer, and revocation *must* be traceable via event logs. Therefore, all license creations and transfers *must* emit a corresponding log event. Revocation may differ a bit. An implementation of this EIP may emit a `Revoke` event only when a license is revoked in a function call, or for every revoked license, both are sufficient to trace the status of all licenses. The former costs less gas if revoking a license automatically revokes all sublicenses under it, while the latter is efficient in terms of interrogation of a license status. Implementers should make the tradeoffs depending on their license terms. + +The `revoker` of a license may be the licensor, the license holder, or a smart contract address which calls the `revokeLicense` function when some conditions are met. Implementers should be careful with the authorization, and may make the `revoker` smart contract forward compatible with transfers by not hardcoding the addresses of `licensor` or `licenseHolder`. + +The license `URI` may point to a JSON file that conforms to the "ERC-5218 Metadata JSON Schema" as below, which adopts the "three-layer" design of the Creative Commons Licenses: + +```json +{ + "title": "License Metadata", + "type": "object", + "properties": { + "legal-code": { + "type": "string", + "description": "The legal code of the license." + }, + "human-readable": { + "type": "string", + "description": "The human readable license deed." + }, + "machine-readable": { + "type": "string", + "description": "The machine readable code of the license that can be recognized by software, such as CC REL." + } + } +} +``` + +Note that this EIP doesn't include a function to update license URI so the license terms should be persistent by default. It is recommended to store the license metadata on a decentralized storage service such as IPFS or adopt the IPFS-style URI which encodes the hash of the metadata for integrity verification. On the other hand, license updatability, if necessary in certain scenarios, can be realized by revoking the original license and creating a new license, or adding a updating function, the eligibile caller of which must be carefully specified in the license and securely implemented in the smart contract. + +The `supportsInterface` method MUST return `true` when called with `0xac7b5ca9`. + +## Rationale + +This EIP aims to allow tracing all licenses to an NFT to facilitate right management. The ERC-721 standard only logs the property but not the legal rights tethered to NFTs. Even when logging the license via the optional ERC-721 Metadata extension, sublicenses are not traceable, which doesn't comply with the transparency goals of Web3. Some implementations attempt to get around this limitation by minting NFTs to represent a particular license, such as the BAYC #6068 Royalty-Free Usage License. This is not an ideal solution because the linking between different licenses to an NFT is ambiguous. An auditor has to investigate all NFTs in the blockchain and inspect the metadata which hasn't been standardized in terms of sublicense relationship. To avoid these problems, this EIP logs all licenses to an NFT in a tree data structure, which is compatible with ERC-721 and allows efficient traceability. + +This EIP attempts to tether NFTs with copyright licenses to the creative work by default and is not subject to the high legal threshold for copyright ownership transfers which require an explicit signature from the copyright owner. To transfer and track copyright ownership, one may possibly integrate ERC-5218 and [ERC-5289](./eip-5289.md) after careful scrutinizing and implement a smart contract that atomically (1) signs the legal contract via ERC-5289, and (2) transfers the NFT together with the copyright ownership via ERC-5218. Either both take place or both revert. + +## Backwards Compatibility + +This standard is compatible with the current ERC-721 standards: a contract can inherit from both ERC-721 and ERC-5218 at the same time. + +## Test Cases + +Test cases are available [here](../assets/eip-5218/contracts/test/Contract.t.sol). + +## Reference Implementation + +A reference implementation maintains the following data structures: + +```solidity + struct License { + bool active; // whether the license is active + uint256 tokenId; // the identifier of the NFT the license is tethered to + uint256 parentLicenseId; // the identifier of the parent license + address licenseHolder; // the license holder + string uri; // the license URI + address revoker; // the license revoker + } + mapping(uint256 => License) private _licenses; // maps from a license identifier to a license object + mapping(uint256 => uint256) private _licenseIds; // maps from an NFT to its root license identifier +``` + +Each NFT has a license tree and starting from each license, one can trace back to the root license via `parentLicenseId` along the path. + +In the reference implementation, once a license is revoked, all sublicenses under it are revoked. This is realized in a *lazy* manner for lower gas cost, i.e., assign `active=false` only for licenses that are explicitly revoked in a `revokeLicense` function call. Therefore, `isLicenseActive` returns `true` only if all its ancestral licenses haven't been revoked. + +For non-root licenses, the creation, transfer and revocation are straightforward: + +1. Only the holder of an active license can create sublicenses. +2. Only the holder of an active license can transfer it to a different license holder. +3. Only the revoker of an active license can revoke it. + +The root license must be compatible with `ERC-721`: + +1. When an NFT is minted, a license is granted to the NFT owner. +2. When an NFT is transferred, the license holder is changed to the new owner of the NFT. +3. When a root license is revoked, the NFT is returned to the NFT creator, and the NFT creator may later transfer it to a new owner with a new license. + +The complete implementation can be found [here](../assets/eip-5218/contracts/src/RightsManagement.sol). + +In addition, the [Token-Bound NFT License](../assets/eip-5218/ic3license/ic3license.pdf) is specifically designed to work with this interface and provides a reference to the language of NFT licenses. + +## Security Considerations + +Implementors of the `IERC5218` standard must consider thoroughly the permissions they give to `licenseHolder` and `revoker`. If the license is ever to be transferred to a different license holder, the `revoker` smart contract should not hardcode the `licenseHolder` address to avoid undesirable scenarios. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). + diff --git a/EIPS/eip-5219.md b/EIPS/eip-5219.md new file mode 100644 index 0000000..57567e5 --- /dev/null +++ b/EIPS/eip-5219.md @@ -0,0 +1,79 @@ +--- +eip: 5219 +title: Contract Resource Requests +description: Allows the requesting of resources from contracts +author: Gavin John (@Pandapip1) +discussions-to: https://ethereum-magicians.org/t/pr-5219-discussion-contract-rest/9907 +status: Final +type: Standards Track +category: ERC +created: 2022-07-10 +--- + +## Abstract + +This EIP standardizes an interface to make resource requests to smart contracts and to receive HTTP-like responses. + +## Motivation + +Ethereum is the most-established blockchain for building decentralized applications (referred to as `DApp`s). Due to this, the Ethereum DApp ecosystem is very diverse. However, one issue that plagues DApps is the fact that they are not fully decentralized. Specifically, to interface a "decentralized" application, one first needs to access a *centralized* website containing the DApp's front-end code, presenting a few issues. The following are some risks associated with using centralized websites to interface with decentralized applications: + +- Trust Minimization: An unnecessarily large number of entities need to be trusted +- Censorship: A centralized website is not resistant to being censored +- Permanence: The interface may not have a mechanism that permits it to be permanently stored +- Interoperability: Smart Contracts cannot directly interact with DApp interfaces + +## Specification + +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. + +### Name Resolution + +EIPs that propose a name resolution mechanism MAY reference this EIP and MAY recommend that clients support their mechanism. Clients MAY also support regular DNS, as defined in RFC 1034 and RFC 1035. + +### Separation of Concerns + +It is RECOMMENDED to separate the application logic from the front-end logic (the contract implementing the interface defined in [Contract Interface](#contract-interface)). + +### Contract Interface + +DApp contracts MUST implement the interface defined in the following file: [Contract Interface](../assets/eip-5219/IDecentralizedApp.sol). + +### Note to Implementers + +To save gas costs, it is recommended to use the `message/external-body` MIME-type, which allows you to point to data that the smart contract might not have access to. For example, the following response would tell a client to fetch the data off of IPFS: + +```yaml +statusCode: 200 +body: THIS IS NOT REALLY THE BODY! +headers: + - key: Content-type + value: message/external-body; access-type=URL; URL="ipfs://11148a173fd3e32c0fa78b90fe42d305f202244e2739" +``` + +## Rationale + +The `request` method was chosen to be readonly because all data should be sent to the contract from the parsed DApp. Here are some reasons why: + +- Submitting a transaction to send a request would be costly and would require waiting for the transaction to be mined, resulting in bad user experience. +- Complicated front-end logic should not be stored in the smart contract, as it would be costly to deploy and would be better run on the end-user's machine. +- Separation of Concerns: the front-end contract shouldn't have to worry about interacting with the back-end smart contract. +- Other EIPs can be used to request state changing operations in conjunction with a `307 Temporary Redirect` status code. + +Instead of mimicking a full HTTP request, a highly slimmed version was chosen for the following reasons: + +- The only particularly relevant HTTP method is `GET` +- Query parameters can be encoded in the resource. +- Request headers are, for the most part, unnecessary for `GET` requests. + +## Backwards Compatibility + +This EIP is backwards compatible with all standards listed in the [Name Resolution](#name-resolution) section. + +## Security Considerations + +The normal security considerations of accessing normal URLs apply here, such as potential privacy leakage by following `3XX` redirects. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5247.md b/EIPS/eip-5247.md new file mode 100644 index 0000000..bb7779d --- /dev/null +++ b/EIPS/eip-5247.md @@ -0,0 +1,147 @@ +--- +eip: 5247 +title: Smart Contract Executable Proposal Interface +description: An interface to create and execute proposals. +author: Zainan Victor Zhou (@xinbenlv) +discussions-to: https://ethereum-magicians.org/t/erc-5247-executable-proposal-standard/9938 +status: Draft +type: Standards Track +category: ERC +created: 2022-07-13 +--- + +## Abstract + +This EIP presents an interface for "smart contract executable proposals": proposals that are submitted to, recorded on, and possibly executed on-chain. Such proposals include a series of information about +function calls including the target contract address, ether value to be transmitted, gas limits and calldatas. + +## Motivation + +It is oftentimes necessary to separate the code that is to be executed from the actual execution of the code. + +A typical use case for this EIP is in a Decentralized Autonomous Organization (DAO). A proposer will create a smart proposal and advocate for it. Members will then choose whether or not to endorse the proposal and vote accordingly (see [EIP-1202](./eip-1202.md)). Finallym when consensus has been formed, the proposal is executed. + +A second typical use-case is that one could have someone who they trust, such as a delegator, trustee, or an attorney-in-fact, or any bilateral collaboration format, where a smart proposal will be first composed, discussed, approved in some way, and then put into execution. + +A third use-case is that a person could make an "offer" to a second person, potentially with conditions. The smart proposal can be presented as an offer and the second person can execute it if they choose to accept this proposal. + +## Specification + +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. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +interface IERC5247 { + event ProposalCreated( + address indexed proposer, + uint256 indexed proposalId, + address[] targets, + uint256[] values, + uint256[] gasLimits, + bytes[] calldatas, + bytes extraParams + ); + + event ProposalExecuted( + address indexed executor, + uint256 indexed proposalId, + bytes extraParams + ); + + function createProposal( + uint256 proposalId, + address[] calldata targets, + uint256[] calldata values, + uint256[] calldata gasLimits, + bytes[] calldata calldatas, + bytes calldata extraParams + ) external returns (uint256 registeredProposalId); + + function executeProposal(uint256 proposalId, bytes calldata extraParams) external; +} +``` + +## Rationale + +* Originally, this interface was part of part of [EIP-1202](./eip-1202.md). However, the proposal itself can potentially have many use cases outside of voting. It is possible that voting may not need to be upon a proposal in any particular format. Hence, we decide to *decouple the voting interface and proposal interface*. +* Arrays were used for `target`s, `value`s, `calldata`s instead of single variables, allowing a proposal to carry arbitrarily long multiple functional calls. +* `registeredProposalId` is returned in `createProposal` so the standard can support implementation to decide their own format of proposal id. + +## Test Cases + +A simple test case can be found as + +```ts + it("Should work for a simple case", async function () { + const { contract, erc721, owner } = await loadFixture(deployFixture); + const callData1 = erc721.interface.encodeFunctionData("mint", [owner.address, 1]); + const callData2 = erc721.interface.encodeFunctionData("mint", [owner.address, 2]); + await contract.connect(owner) + .createProposal( + 0, + [erc721.address, erc721.address], + [0,0], + [0,0], + [callData1, callData2], + []); + expect(await erc721.balanceOf(owner.address)).to.equal(0); + await contract.connect(owner).executeProposal(0, []); + expect(await erc721.balanceOf(owner.address)).to.equal(2); + }); +``` + +See [testProposalRegistry.ts](../assets/eip-5247/testProposalRegistry.ts) for the whole testset. + +## Reference Implementation + +A simple reference implementation can be found. + +```solidity + function createProposal( + uint256 proposalId, + address[] calldata targets, + uint256[] calldata values, + uint256[] calldata gasLimits, + bytes[] calldata calldatas, + bytes calldata extraParams + ) external returns (uint256 registeredProposalId) { + require(targets.length == values.length, "GeneralForwarder: targets and values length mismatch"); + require(targets.length == gasLimits.length, "GeneralForwarder: targets and gasLimits length mismatch"); + require(targets.length == calldatas.length, "GeneralForwarder: targets and calldatas length mismatch"); + registeredProposalId = proposalCount; + proposalCount++; + + proposals[registeredProposalId] = Proposal({ + by: msg.sender, + proposalId: proposalId, + targets: targets, + values: values, + calldatas: calldatas, + gasLimits: gasLimits + }); + emit ProposalCreated(msg.sender, proposalId, targets, values, gasLimits, calldatas, extraParams); + return registeredProposalId; + } + function executeProposal(uint256 proposalId, bytes calldata extraParams) external { + Proposal storage proposal = proposals[proposalId]; + address[] memory targets = proposal.targets; + string memory errorMessage = "Governor: call reverted without message"; + for (uint256 i = 0; i < targets.length; ++i) { + (bool success, bytes memory returndata) = proposal.targets[i].call{value: proposal.values[i]}(proposal.calldatas[i]); + Address.verifyCallResult(success, returndata, errorMessage); + } + emit ProposalExecuted(msg.sender, proposalId, extraParams); + } +``` + +See [ProposalRegistry.sol](../assets/eip-5247/ProposalRegistry.sol) for more information. + +## Security Considerations + +Needs discussion. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5252.md b/EIPS/eip-5252.md new file mode 100644 index 0000000..c4ef04e --- /dev/null +++ b/EIPS/eip-5252.md @@ -0,0 +1,247 @@ +--- +eip: 5252 +title: Account-bound Finance +description: An ERC-5114 extension that aids in preventing arbitrary loss of funds +author: Hyungsuk Kang (@hskang9), Viktor Pernjek (@smuxx) +discussions-to: https://ethereum-magicians.org/t/pr-5252-discussion-account-bound-finance/10027 +status: Review +type: Standards Track +category: ERC +created: 2022-06-29 +requires: 20, 721, 1155, 5114 +--- + +## Abstract + +This EIP proposes a form of smart contract design pattern and a new type of account abstraction on how one's finance should be managed, ensuring transparency of managing investments and protection with self-sovereignty even from its financial operators. This EIP enables greater self-sovereignty of one's assets using a personal finance contract for each individual. The seperation between an investor's funds and the operation fee is clearly specified in the personal smart contract, so investors can ensure safety from arbitrary loss of funds by the operating team's control. + +This EIP extends [ERC-5114](./eip-5114.md) to further enable transferring fund to other accounts for mobility between managing multiple wallets. + +## Motivation + +Decentralized finance (DeFi) faces a trust issue. Smart contracts are often proxies, with the actual logic of the contract hidden away in a separate logic contract. Many projects include a multi-signature "wallet" with unnecessarily-powerful permissions. And it is not possible to independently verify that stablecoins have enough real-world assets to continue maintaining their peg, creating a large loss of funds (such as happened in the official bankruptcy announcement of Celsius and UST de-pegging and anchor protocol failure). One should not trust exchanges or other third parties with one's own investments with the operators' clout in Web3.0. + +Smart contracts are best implemented as a promise between two parties written in code, but current DeFi contracts are often formed using less than 7 smart contracts to manage their whole investors' funds, and often have a trusted key that has full control. This is evidently an issue, as investors have to trust contract operators with their funds, meaning that users do not actually own their funds. + +The pattern with personal finance contract also offers more transparency than storing mixed fund financial data in the operating team's contract. With a personal finance contract, an account's activity is easier to track than one global smart contract's activity. The pattern introduces a Non-Fungiible Account-Bound Token (ABT) to store credentials from the personal finance contract. + +### Offchain-identity vs Soul-bound token on credentials + +This EIP provides a better alternative to off-chain identity solutions which take over the whole system because their backends eventually rely on the trust of the operator, not cryptographic proof (e.g. Proof-of-work, Proof-of-stake, etc). Off-chain identity as credentials are in direct opposition to the whole premise of crypto. Soulbound tokens are a better, verifiable credential, and data stored off-chain is only to store token metadata. + +## Specification + +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. + +The specification consists of two patterns for **Interaction** and **Governance**. + +### Interaction + +#### Interfaces + +The interaction pattern consists of 4 components for interaction; manager, factory, finance, account-bound token, and extension. + +Interaction contract pattern is defined with these contracts: + +- A soul-bound or account bound token contract to give access to interact with a financial contract with credentials +- A manager contract that interacts first contact with an investor +- A factory contract that creates a financial contract for each user +- A finance contract that can interact with the investor + +#### Requirements + +A soul-bound or account bound token contract is defined with these properties: + +1. It SHALL be non-fungible and MUST satisfy [ERC-721](./eip-721.md). +2. Credentials SHOULD be represented with its metadata with `tokenURI()` function. +3. It MUST only reference factory to verify its minting. +4. If it is transferrable, it is account-bound. If not, it is soul-bound. + +A manager contract is defined with these properties: + +1. It MUST be the only kind of contract which calls factory to create. +2. It SHOULD store all related configurations for financial parameters. + +A factory contract is defined with these properties: + +1. It SHALL clone the finance contract with uniform implementation. +2. It MUST be the only contract that can mint account-bound token. +3. It MUST keep an recent id of account bound token. + +A finance contract is defined with these properties: + +1. A finance contract MUST only be initialized once from factory contract in constructor. +2. Funds in the contract SHALL NOT be transferred to other contracts nor accounts unless sender who owns soul-bound or account bound token signs to do so. +3. Every state-changing function of the smart contract MUST only accept sender who owns soul-bound or account bound-token except global function(e.g. liquidation). +4. Global function SHOULD be commented as `/* global */` to clarify the function is can be accessed with anyone. +5. Each finance contract SHOULD be able to represent transaction that has happened only with those who had account-bound token. +6. If soul-bound token is used for access, the finance contract MUST be able to represent transaction that has happened only between whom had the private key and the finance contract. + +#### Contracts + +![Diagram](../assets/eip-5252/media/media.svg) + +
+Contract Diagram of [ERC-5252](eip-5252.md) +
+ +**`Manager`**: **`Manager`** contract acts as an entry point to interact with the investor. The contract also stores parameters for **`Finance`** contract. + +**`Factory`**: **`Factory`** contract manages contract bytecode to create for managing investor's fund and clones **`Finance`** contract on **`Manager`** contract's approval. It also mints account-bound tokens to interact with the `Finance` contract. + +**`Finance`**: **`Finance`** contract specifies all rules on managing an investor's fund. The contract is only accessible with an account that has an Account-bound token. When an investor deposits a fund to **`Manager`** contract, the contract sends the fund to **`Finance`** contract account after separating fees for operation. + +**`Account-bound token`**: **`Account-bound token`** contract in this EIP can bring the **`Finance`** contract's data and add metadata. For example, if there is a money market lending +**`Finance`** contract, its **`Account-bound token`** can show how much balance is in agreement using SVG. + +**`Extension`**: **`Extension`** contract is another contract that can utilize locked funds in **`Finance`** contract. The contract can access with **`Finance`** contract on operator's approval managed in **`Manager`** contract. Example use case of `Extension` can be a membership. + +**`Metadata`**: **`Metadata`** contract is the contract where it stores metadata related to account credentials. Credential related data are stored with specific key. Images are usually displayed as SVG, but offchain image is possible. + +--- + +### Governance + +The governance pattern consists of 2 components; influencer and governor. + +#### Interfaces + +#### Requirements + +An influencer contract is defined with these properties: + +1. The contract SHALL manage multiplier for votes. +2. The contract SHALL set a decimal to calculated normalized scores. +3. The contract SHALL set a function where governance can decide factor parameters. + +A governor contract is defined with these properties: + +1. The contract MUST satisfy Governor contract from OpenZeppelin. +2. The contract SHALL refer influencer contract for multiplier +3. The contract MUST limit transfer of account bound token once claimed for double vote prevention. + +#### From Token Governance To Contribution Based Governance + +| | Token Governance | Credential-based Governance | +| ----------- | ---------------------------- | ---------------------------------- | +| Enforcement | More tokens, more power | More contribution, More power | +| Incentives | More tokens, more incentives | More contribution, more incentives | +| Penalty | No penalty | Loss of power | +| Assignment | One who holds the token | One who has the most influence | + +
+Token Governance vs Credential Based Governance +
+ +Token governance is not sustainable in that it gives **more** power to "those who most want to rule". Any individual who gets more than 51% of the token supply can forcefully take control. + +New governance that considers contributions to the protocol is needed because: + +- **Rulers can be penalized on breaking the protocol** +- **Rulers can be more effectively incentivized on maintaining the protocol** + +The power should be given to "those who are most responsible". Instead of locked or owned tokens, voting power is determined with contributions marked in Account Bound Tokens (ABT). This EIP defines this form of voting power as **`Influence`**. + +#### Calculating Influence + +**`Influence`** is a multiplier on staked tokens that brings more voting power of a DAO to its contributors. To get **`Influence`**, a score is calculated on weighted contribution matrix. Then, the score is normalized to give a member's position in whole distribution. Finally, the multiplier is determined on the position in every community members. + +#### Calculating score + +The weights represent relative importance on each factor. The total importance is the total sum of the factors. More factors that can be normalized at the time of submitting proposal can be added by community. + +| | Description | +| --- | ----------------------------------------------------------------------------------------- | +| α | Contribution value per each **`Finance`** contract from current proposal | +| β | Time they maintained **`Finance`** per each contract from current timestamp of a proposal | + +```math +(score per each ABT) = α * (contribution value) + β * (time that abt was maintained from now) +``` + +#### Normalization + +Normalization is applied for data integrity on user's contribution in a DAO. +Normalized score can be calculated from the state of submitting a proposal + +```math +(Normalized score per each ABT) = α * (contribution value)/(total contribution value at submitting tx) + β * (time that abt was maintained)/(time passed from genesis to proposal creation) +``` + +and have a value between 0 and 1 (since α + β = 1). + +#### Multiplier + +The multiplier is determined linearly from base factor (b) and multiplier(m). + +The equation for influence is : + +```math +(influence) = m * (sum(normalized_score)) +``` + +#### Example + +For example, if a user has 3 **`Account-bound tokens`** with normalized score of each 1.0, 0.5, 0.3 and the locked token is 100, and multiplier is 0.5 and base factor is 1.5. Then the total influence is + +````math +0.5 * {(1.0 + 0.5 + 0.3) / 3} + 1.5 = 1.8 + + The total voting power would be + +```math +(voting power) = 1.8 * sqrt(100) = 18 +```` + +#### Stakers vs Enforcers + +| | Stakers | Enforcers | +| ------------ | --------------------------------- | --------------------------------------------------------------------------------------- | +| Role | stake governance token for voting | Contributed on the system, can make proposal to change rule, more voting power like 1.5 | +| Populations | many | small | +| Contribution | Less effect | More effect | +| Influence | sqrt(locked token) | Influence \* sqrt(locked token) | + +
+Stakers vs Enforcers +
+ +**Stakers**: Stakers are people who vote to enforcers' proposals and get dividend for staked tokens + +**Enforcers**: Enforcers are people who takes risk on managing protocol and contributes to the protocol by making a proposal and change to it. + +#### Contracts + +**`Influencer`**: An **`Influencer`** contract stores influence configurations and measures the contribution of a user from his activities done in a registered Account Bound Token contract. The contract puts a lock on that Account Bound Token until the proposal is finalized. + +**`Governor`**: **`Governor`** contract is compatible with the current governor contract in OpenZeppelin. For its special use case, it configures factors where the influencer manages and has access to changing parameters of **`Manager`** configs. Only the `Enforcer` can propose new parameters. + +## Rationale + +### Gas saving for end user + +The gas cost of using multiple contracts (as opposed to a single one) actually saves gas long-run if the clone factory pattern is applied. One contract storing users' states globally means each user is actually paying for the storage cost of other users after interacting with the contract. This, for example, means that MakerDAO's contract operating cost is sometimes over 0.1 ETH, limitimg users' minimum deposit for CDP in order to save gas costs. To solve inefficient n-times charging gas cost interaction for future users, one contract per user is used. + +#### Separation between investor's and operation fund + +The separation between an investor's funds and operation fee is clearly specified in the smart contract, so investors can ensure safety from arbitrary loss of funds by the operating team's control. + +## Backwards Compatibility + +This EIP has no known backward compatibility issues. + +## Reference Implementation + +[Reference implementation](../assets/eip-5252/README.md) is a simple deposit account contract as `Finance` contract and its contribution value α is measured with deposit amount with ETH. + +## Security Considerations + +- **`Factory`** contracts must ensure that each **`Finance`** contract is registered in the factory and check that **`Finance`** contracts are sending transactions related to their bounded owner. + +- Reentrancy attack guard should be applied or change state before delegatecall in each user function in **`Manager`** contract or **`Finance`** contract. Otherwise, **`Finance`** can be generated as double and ruin whole indices. + +- Once a user locks influence on a proposal's vote, an **`Account Bound Token`** cannot be transferred to another wallet. Otherwise, double influence can happen. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5267.md b/EIPS/eip-5267.md new file mode 100644 index 0000000..7201e79 --- /dev/null +++ b/EIPS/eip-5267.md @@ -0,0 +1,175 @@ +--- +eip: 5267 +title: Retrieval of EIP-712 domain +description: A way to describe and retrieve an EIP-712 domain to securely integrate EIP-712 signatures. +author: Francisco Giordano (@frangio) +discussions-to: https://ethereum-magicians.org/t/eip-5267-retrieval-of-eip-712-domain/9951 +status: Final +type: Standards Track +category: ERC +created: 2022-07-14 +requires: 155, 712, 2612 +--- + +## Abstract + +This EIP complements [EIP-712](./eip-712.md) by standardizing how contracts should publish the fields and values that describe their domain. This enables applications to retrieve this description and generate appropriate domain separators in a general way, and thus integrate EIP-712 signatures securely and scalably. + +## Motivation + +EIP-712 is a signature scheme for complex structured messages. In order to avoid replay attacks and mitigate phishing, the scheme includes a "domain separator" that makes the resulting signature unique to a specific domain (e.g., a specific contract) and allows user-agents to inform end users the details of what is being signed and how it may be used. A domain is defined by a data structure with fields from a predefined set, all of which are optional, or from extensions. Notably, EIP-712 does not specify any way for contracts to publish which of these fields they use or with what values. This has likely limited adoption of EIP-712, as it is not possible to develop general integrations, and instead applications find that they need to build custom support for each EIP-712 domain. A prime example of this is [EIP-2612](./eip-2612.md) (permit), which has not been widely adopted by applications even though it is understood to be a valuable improvement to the user experience. The present EIP defines an interface that can be used by applications to retrieve a definition of the domain that a contract uses to verify EIP-712 signatures. + +## Specification + +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. + +Compliant contracts MUST define `eip712Domain` exactly as declared below. All specified values MUST be returned even if they are not used, to ensure proper decoding on the client side. + +```solidity +function eip712Domain() external view returns ( + bytes1 fields, + string name, + string version, + uint256 chainId, + address verifyingContract, + bytes32 salt, + uint256[] extensions +); +``` + +The return values of this function MUST describe the domain separator that is used for verification of EIP-712 signatures in the contract. They describe both the form of the `EIP712Domain` struct (i.e., which of the optional fields and extensions are present) and the value of each field, as follows. + +- `fields`: A bit map where bit `i` is set to 1 if and only if domain field `i` is present (`0 ≤ i ≤ 4`). Bits are read from least significant to most significant, and fields are indexed in the order that is specified by EIP-712, identical to the order in which they are listed in the function type. +- `name`, `version`, `chainId`, `verifyingContract`, `salt`: The value of the corresponding field in `EIP712Domain`, if present according to `fields`. If the field is not present, the value is unspecified. The semantics of each field is defined in EIP-712. +- `extensions`: A list of EIP numbers, each of which MUST refer to an EIP that extends EIP-712 with new domain fields, along with a method to obtain the value for those fields, and potentially conditions for inclusion. The value of `fields` does not affect their inclusion. + +The return values of this function (equivalently, its EIP-712 domain) MAY change throughout the lifetime of a contract, but changes SHOULD NOT be frequent. The `chainId` field, if used, SHOULD change to mirror the [EIP-155](./eip-155.md) id of the underlying chain. Contracts MAY emit the event `EIP712DomainChanged` defined below to signal that the domain could have changed. + +```solidity +event EIP712DomainChanged(); +``` + +## Rationale + +A notable application of EIP-712 signatures is found in EIP-2612 (permit), which specifies a `DOMAIN_SEPARATOR` function that returns a `bytes32` value (the actual domain separator, i.e., the result of `hashStruct(eip712Domain)`). This value does not suffice for the purposes of integrating with EIP-712, as the RPC methods defined there receive an object describing the domain and not just the separator in hash form. Note that this is not a flaw of the RPC methods, it is indeed part of the security proposition that the domain should be validated and informed to the user as part of the signing process. On its own, a hash does not allow this to be implemented, given it is opaque. The present EIP fills this gap in both EIP-712 and EIP-2612. + +Extensions are described by their EIP numbers because EIP-712 states: "Future extensions to this standard can add new fields [...] new fields should be proposed through the EIP process." + +## Backwards Compatibility + +This is an optional extension to EIP-712 that does not introduce backwards compatibility issues. + +Upgradeable contracts that make use of EIP-712 signatures MAY be upgraded to implement this EIP. + +User-agents or applications that use this EIP SHOULD additionally support those contracts that due to their immutability cannot be upgraded to implement it. The simplest way to achieve this is to hardcode common domains based on contract address and chain id. However, it is also possible to implement a more general solution by guessing possible domains based on a few common patterns using the available information, and selecting the one whose hash matches a `DOMAIN_SEPARATOR` or `domainSeparator` function in the contract. + +## Reference Implementation + +### Solidity Example + +```solidity +pragma solidity 0.8.0; + +contract EIP712VerifyingContract { + function eip712Domain() external view returns ( + bytes1 fields, + string memory name, + string memory version, + uint256 chainId, + address verifyingContract, + bytes32 salt, + uint256[] memory extensions + ) { + return ( + hex"0d", // 01101 + "Example", + "", + block.chainid, + address(this), + bytes32(0), + new uint256[](0) + ); + } +} +``` + +This contract's domain only uses the fields `name`, `chainId`, and `verifyingContract`, therefore the `fields` value is `01101`, or `0d` in hexadecimal. + +Assuming this contract is on Ethereum mainnet and its address is 0x0000000000000000000000000000000000000001, the domain it describes is: + +```json5 +{ + name: "Example", + chainId: 1, + verifyingContract: "0x0000000000000000000000000000000000000001" +} +``` + +### JavaScript + +A domain object can be constructed based on the return values of an `eip712Domain()` invocation. + +```javascript +/** Retrieves the EIP-712 domain of a contract using EIP-5267 without extensions. */ +async function getDomain(contract) { + const { fields, name, version, chainId, verifyingContract, salt, extensions } = + await contract.eip712Domain(); + + if (extensions.length > 0) { + throw Error("Extensions not implemented"); + } + + return buildBasicDomain(fields, name, version, chainId, verifyingContract, salt); +} + +const fieldNames = ['name', 'version', 'chainId', 'verifyingContract', 'salt']; + +/** Builds a domain object without extensions based on the return values of `eip712Domain()`. */ +function buildBasicDomain(fields, name, version, chainId, verifyingContract, salt) { + const domain = { name, version, chainId, verifyingContract, salt }; + + for (const [i, field] of fieldNames.entries()) { + if (!(fields & (1 << i))) { + delete domain[field]; + } + } + + return domain; +} +``` + +#### Extensions + +Suppose EIP-XYZ defines a new field `subdomain` of type `bytes32` and a function `getSubdomain()` to retrieve its value. + +The function `getDomain` from above would be extended as follows. + +```javascript +/** Retrieves the EIP-712 domain of a contract using EIP-5267 with support for EIP-XYZ. */ +async function getDomain(contract) { + const { fields, name, version, chainId, verifyingContract, salt, extensions } = + await contract.eip712Domain(); + + const domain = buildBasicDomain(fields, name, version, chainId, verifyingContract, salt); + + for (const n of extensions) { + if (n === XYZ) { + domain.subdomain = await contract.getSubdomain(); + } else { + throw Error(`EIP-${n} extension not implemented`); + } + } + + return domain; +} +``` + +Additionally, the type of the `EIP712Domain` struct needs to be extended with the `subdomain` field. This is left out of scope of this reference implementation. + +## Security Considerations + +While this EIP allows a contract to specify a `verifyingContract` other than itself, as well as a `chainId` other than that of the current chain, user-agents and applications should in general validate that these do match the contract and chain before requesting any user signatures for the domain. This may not always be a valid assumption. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5269.md b/EIPS/eip-5269.md new file mode 100644 index 0000000..eb1b25e --- /dev/null +++ b/EIPS/eip-5269.md @@ -0,0 +1,276 @@ +--- +eip: 5269 +title: EIP/ERC Detection and Discovery +description: An interface to identify if major behavior or optional behavior specified in an ERC is supported for a given caller. +author: Zainan Victor Zhou (@xinbenlv) +discussions-to: https://ethereum-magicians.org/t/erc5269-human-readable-interface-detection/9957 +status: Review +type: Standards Track +category: ERC +created: 2022-07-15 +requires: 5750 +--- + +## Abstract + +An interface for better identification and detection of EIP/ERC by numbers. +It designates a field in which it's called `majorEIPIdentifier` which is normally known or referred to as "EIP number". For example, `ERC-721` aka [EIP-721](./eip-721.md) has a `majorEIPIdentifier = 721`. This EIP has a `majorEIPIdentifier = 5269`. + +Calling it a `majorEIPIdentifier` instead of `EIPNumber` makes it future-proof: anticipating there is a possibility where future EIP is not numbered or if we want to incorporate other types of standards. + +It also proposes a new concept of `minorEIPIdentifier` which is left for authors of +individual EIP to define. For example, EIP-721's author may define `ERC721Metadata` +interface as `minorEIPIdentifier= keccak256("ERC721Metadata")`. + +It also proposes an event to allow smart contracts to optionally declare the EIPs they support. + +## Motivation + +This EIP is created as a competing standard for [EIP-165](./eip-165.md). + +Here are the major differences between this EIP and [EIP-165](./eip-165.md). + +1. [EIP-165](./eip-165.md) uses the hash of a method's signature which declares the existence of one method or multiple methods, +therefore it requires at least one method to *exist* in the first place. In some cases, some EIP/ERCs interface does not have a method, such as some EIPs related to data format and signature schemes or the "Soul-Bound-ness" aka SBT which could just revert a transfer call without needing any specific method. +1. [EIP-165](./eip-165.md) doesn't provide query ability based on the caller. +The compliant contract of this EIP will respond to whether it supports certain EIP *based on* a given caller. + +Here is the motivation for this EIP given EIP-165 already exists: + +1. Using EIP/ERC numbers improves human readability as well as make it easier to work with named contract such as ENS. + +2. Instead of using an EIP-165 identifier, we have seen an increasing interest to use EIP/ERC numbers as the way to identify or specify an EIP/ERC. For example + +- [EIP-5267](./eip-5267.md) specifies `extensions` to be a list of EIP numbers. +- [EIP-600](./eip-600.md), and [EIP-601](./eip-601.md) specify an `EIP` number in the `m / purpose' / subpurpose' / EIP' / wallet'` path. +- [EIP-5568](./eip-5568.md) specifies `The instruction_id of an instruction defined by an EIP MUST be its EIP number unless there are exceptional circumstances (be reasonable)` +- [EIP-6120](./eip-6120.md) specifies `struct Token { uint eip; ..., }` where `uint eip` is an EIP number to identify EIPs. +- `EIP-867`(Stagnant) proposes to create `erpId: A string identifier for this ERP (likely the associated EIP number, e.g. “EIP-1234”).` + +3. Having an ERC/EIP number detection interface reduces the need for a lookup table in smart contract to +convert a function method or whole interface in any EIP/ERC in the bytes4 EIP-165 identifier into its respective EIP number and massively simplifies the way to specify EIP for behavior expansion. + +4. We also recognize a smart contract might have different behavior given different caller accounts. One of the most notable use cases is that when using Transparent Upgradable Pattern, a proxy contract gives an Admin account and Non-Admin account different treatment when they call. + +## Specification + +In the following description, we use EIP and ERC inter-exchangeably. This was because while most of the time the description applies to an ERC category of the Standards Track of EIP, the ERC number space is a subspace of EIP number space and we might sometimes encounter EIPs that aren't recognized as ERCs but has behavior that's worthy of a query. + +1. Any compliant smart contract MUST implement the following interface + +```solidity +// DRAFTv1 +pragma solidity ^0.8.9; + +interface IERC5269 { + event OnSupportEIP( + address indexed caller, // when emitted with `address(0x0)` means all callers. + uint256 indexed majorEIPIdentifier, + bytes32 indexed minorEIPIdentifier, // 0 means the entire EIP + bytes32 eipStatus, + bytes extraData + ); + + /// @dev The core method of EIP/ERC Interface Detection + /// @param caller, a `address` value of the address of a caller being queried whether the given EIP is supported. + /// @param majorEIPIdentifier, a `uint256` value and SHOULD BE the EIP number being queried. Unless superseded by future EIP, such EIP number SHOULD BE less or equal to (0, 2^32-1]. For a function call to `supportEIP`, any value outside of this range is deemed unspecified and open to implementation's choice or for future EIPs to specify. + /// @param minorEIPIdentifier, a `bytes32` value reserved for authors of individual EIP to specify. For example the author of [EIP-721](/EIPS/eip-721) MAY specify `keccak256("ERC721Metadata")` or `keccak256("ERC721Metadata.tokenURI")` as `minorEIPIdentifier` to be quired for support. Author could also use this minorEIPIdentifier to specify different versions, such as EIP-712 has its V1-V4 with different behavior. + /// @param extraData, a `bytes` for [EIP-5750](/EIPS/eip-5750) for future extensions. + /// @return eipStatus, a `bytes32` indicating the status of EIP the contract supports. + /// - For FINAL EIPs, it MUST return `keccak256("FINAL")`. + /// - For non-FINAL EIPs, it SHOULD return `keccak256("DRAFT")`. + /// During EIP procedure, EIP authors are allowed to specify their own + /// eipStatus other than `FINAL` or `DRAFT` at their discretion such as `keccak256("DRAFTv1")` + /// or `keccak256("DRAFT-option1")`and such value of eipStatus MUST be documented in the EIP body + function supportEIP( + address caller, + uint256 majorEIPIdentifier, + bytes32 minorEIPIdentifier, + bytes calldata extraData) + external view returns (bytes32 eipStatus); +} +``` + +In the following description, `EIP_5269_STATUS` is set to be `keccak256("DRAFTv1")`. + +In addition to the behavior specified in the comments of `IERC5269`: + +1. Any `minorEIPIdentifier=0` is reserved to be referring to the main behavior of the EIP being queried. +2. The Author of compliant EIP is RECOMMENDED to declare a list of `minorEIPIdentifier` for their optional interfaces, behaviors and value range for future extension. +3. When this EIP is FINAL, any compliant contract MUST return an `EIP_5269_STATUS` for the call of `supportEIP((any caller), 5269, 0, [])` + +*Note*: at the current snapshot, the `supportEIP((any caller), 5269, 0, [])` MUST return `EIP_5269_STATUS`. + +4. Any complying contract SHOULD emit an `OnSupportEIP(address(0), 5269, 0, EIP_5269_STATUS, [])` event upon construction or upgrade. +5. Any complying contract MAY declare for easy discovery any EIP main behavior or sub-behaviors by emitting an event of `OnSupportEIP` with relevant values and when the compliant contract changes whether the support an EIP or certain behavior for a certain caller or all callers. +6. For any `EIP-XXX` that is NOT in `Final` status, when querying the `supportEIP((any caller), xxx, (any minor identifier), [])`, it MUST NOT return `keccak256("FINAL")`. It is RECOMMENDED to return `0` in this case but other values of `eipStatus` is allowed. Caller MUST treat any returned value other than `keccak256("FINAL")` as non-final, and MUST treat 0 as strictly "not supported". +7. The function `supportEIP` MUST be mutability `view`, i.e. it MUST NOT mutate any global state of EVM. + +## Rationale + +1. When data type `uint256 majorEIPIdentifier`, there are other alternative options such as: + +- (1) using a hashed version of the EIP number, +- (2) use a raw number, or +- (3) use an EIP-165 identifier. + +The pros for (1) are that it automatically supports any evolvement of future EIP numbering/naming conventions. +But the cons are it's not backward readable: seeing a `hash(EIP-number)` one usually can't easily guess what their EIP number is. + +We choose the (2) in the rationale laid out in motivation. + +2. We have a `bytes32 minorEIPIdentifier` in our design decision. Alternatively, it could be (1) a number, forcing all EIP authors to define its numbering for sub-behaviors so we go with a `bytes32` and ask the EIP authors to use a hash for a string name for their sub-behaviors which they are already doing by coming up with interface name or method name in their specification. + +3. Alternatively, it's possible we add extra data as a return value or an array of all EIP being supported but we are unsure how much value this complexity brings and whether the extra overhead is justified. + +4. Compared to [EIP-165](./eip-165.md), we also add an additional input of `address caller`, given the increasing popularity of proxy patterns such as those enabled by [EIP-1967](./eip-1967.md). One may ask: why not simply use `msg.sender`? This is because we want to allow query them without transaction or a proxy contract to query whether interface ERC-`number` will be available to that particular sender. + +1. We reserve the input `majorEIPIdentifier` greater than or equals `2^32` in case we need to support other collections of standards which is not an ERC/EIP. + +## Test Cases + +```typescript + +describe("ERC5269", function () { + async function deployFixture() { + // ... + } + + describe("Deployment", function () { + // ... + it("Should emit proper OnSupportEIP events", async function () { + let { txDeployErc721 } = await loadFixture(deployFixture); + let events = txDeployErc721.events?.filter(event => event.event === 'OnSupportEIP'); + expect(events).to.have.lengthOf(4); + + let ev5269 = events!.filter( + (event) => event.args!.majorEIPIdentifier.eq(5269)); + expect(ev5269).to.have.lengthOf(1); + expect(ev5269[0].args!.caller).to.equal(BigNumber.from(0)); + expect(ev5269[0].args!.minorEIPIdentifier).to.equal(BigNumber.from(0)); + expect(ev5269[0].args!.eipStatus).to.equal(ethers.utils.id("DRAFTv1")); + + let ev721 = events!.filter( + (event) => event.args!.majorEIPIdentifier.eq(721)); + expect(ev721).to.have.lengthOf(3); + expect(ev721[0].args!.caller).to.equal(BigNumber.from(0)); + expect(ev721[0].args!.minorEIPIdentifier).to.equal(BigNumber.from(0)); + expect(ev721[0].args!.eipStatus).to.equal(ethers.utils.id("FINAL")); + + expect(ev721[1].args!.caller).to.equal(BigNumber.from(0)); + expect(ev721[1].args!.minorEIPIdentifier).to.equal(ethers.utils.id("ERC721Metadata")); + expect(ev721[1].args!.eipStatus).to.equal(ethers.utils.id("FINAL")); + + // ... + }); + + it("Should return proper eipStatus value when called supportEIP() for declared supported EIP/features", async function () { + let { erc721ForTesting, owner } = await loadFixture(deployFixture); + expect(await erc721ForTesting.supportEIP(owner.address, 5269, ethers.utils.hexZeroPad("0x00", 32), [])).to.equal(ethers.utils.id("DRAFTv1")); + expect(await erc721ForTesting.supportEIP(owner.address, 721, ethers.utils.hexZeroPad("0x00", 32), [])).to.equal(ethers.utils.id("FINAL")); + expect(await erc721ForTesting.supportEIP(owner.address, 721, ethers.utils.id("ERC721Metadata"), [])).to.equal(ethers.utils.id("FINAL")); + // ... + + expect(await erc721ForTesting.supportEIP(owner.address, 721, ethers.utils.id("WRONG FEATURE"), [])).to.equal(BigNumber.from(0)); + expect(await erc721ForTesting.supportEIP(owner.address, 9999, ethers.utils.hexZeroPad("0x00", 32), [])).to.equal(BigNumber.from(0)); + }); + + it("Should return zero as eipStatus value when called supportEIP() for non declared EIP/features", async function () { + let { erc721ForTesting, owner } = await loadFixture(deployFixture); + expect(await erc721ForTesting.supportEIP(owner.address, 721, ethers.utils.id("WRONG FEATURE"), [])).to.equal(BigNumber.from(0)); + expect(await erc721ForTesting.supportEIP(owner.address, 9999, ethers.utils.hexZeroPad("0x00", 32), [])).to.equal(BigNumber.from(0)); + }); + }); +}); +``` + +See [`TestERC5269.ts`](../assets/eip-5269/test/TestERC5269.ts). + +## Reference Implementation + +Here is a reference implementation for this EIP: + +```solidity +contract ERC5269 is IERC5269 { + bytes32 constant public EIP_STATUS = keccak256("DRAFTv1"); + constructor () { + emit OnSupportEIP(address(0x0), 5269, bytes32(0), EIP_STATUS, ""); + } + + function _supportEIP( + address /*caller*/, + uint256 majorEIPIdentifier, + bytes32 minorEIPIdentifier, + bytes calldata /*extraData*/) + internal virtual view returns (bytes32 eipStatus) { + if (majorEIPIdentifier == 5269) { + if (minorEIPIdentifier == bytes32(0)) { + return EIP_STATUS; + } + } + return bytes32(0); + } + + function supportEIP( + address caller, + uint256 majorEIPIdentifier, + bytes32 minorEIPIdentifier, + bytes calldata extraData) + external virtual view returns (bytes32 eipStatus) { + return _supportEIP(caller, majorEIPIdentifier, minorEIPIdentifier, extraData); + } +} +``` + +See [`ERC5269.sol`](../assets/eip-5269/contracts/ERC5269.sol). + +Here is an example where a contract of [EIP-721](./eip-721.md) also implement this EIP to make it easier +to detect and discover: + +```solidity +import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; +import "../ERC5269.sol"; +contract ERC721ForTesting is ERC721, ERC5269 { + + bytes32 constant public EIP_FINAL = keccak256("FINAL"); + constructor() ERC721("ERC721ForTesting", "E721FT") ERC5269() { + _mint(msg.sender, 0); + emit OnSupportEIP(address(0x0), 721, bytes32(0), EIP_FINAL, ""); + emit OnSupportEIP(address(0x0), 721, keccak256("ERC721Metadata"), EIP_FINAL, ""); + emit OnSupportEIP(address(0x0), 721, keccak256("ERC721Enumerable"), EIP_FINAL, ""); + } + + function supportEIP( + address caller, + uint256 majorEIPIdentifier, + bytes32 minorEIPIdentifier, + bytes calldata extraData) + external + override + view + returns (bytes32 eipStatus) { + if (majorEIPIdentifier == 721) { + if (minorEIPIdentifier == 0) { + return keccak256("FINAL"); + } else if (minorEIPIdentifier == keccak256("ERC721Metadata")) { + return keccak256("FINAL"); + } else if (minorEIPIdentifier == keccak256("ERC721Enumerable")) { + return keccak256("FINAL"); + } + } + return super._supportEIP(caller, majorEIPIdentifier, minorEIPIdentifier, extraData); + } +} + +``` + +See [`ERC721ForTesting.sol`](../assets/eip-5269/contracts/testing/ERC721ForTesting.sol). + +## Security Considerations + +Similar to [EIP-165](./eip-165.md) callers of the interface MUST assume the smart contract +declaring they support such EIP interfaces doesn't necessarily correctly support them. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5283.md b/EIPS/eip-5283.md new file mode 100644 index 0000000..19e3972 --- /dev/null +++ b/EIPS/eip-5283.md @@ -0,0 +1,108 @@ +--- +eip: 5283 +title: Semaphore for Reentrancy Protection +description: A Precompile-based parallelizable reentrancy protection using the call stack +author: Sergio D. Lerner (@SergioDemianLerner) +discussions-to: https://ethereum-magicians.org/t/eip-5283-a-semaphore-for-parallelizable-reentrancy-protection/10236 +status: Stagnant +type: Standards Track +category: Core +created: 2022-07-17 +requires: 20, 1283, 1352 +--- + +## Abstract + +This EIP proposes adding a precompiled contract that provides a semaphore function for creating a new type of reentrancy protection guard (RPG). This function aims to replace the typical RPG based on modifying a contract storage cell. The benefit is that the precompile-based RPG does not write to storage, and therefore it enables contracts to be forward-compatible with all designs that provide fine-grained (i.e. cell level) parallelization for the multi-threaded execution of EVM transactions. + +## Motivation + +The typical smart contract RPG uses a contract storage cell. The algorithm is simple: the code checks that a storage cell is 0 (or any other predefined constant) on entry, aborting if not, and then sets it to 1. After executing the required code, it resets the cell back to 0 before exiting. This is the algorithm implemented in OpenZeppelin's ReentrancyGuard. The algorithm results in a read-write pattern on the RPG's storage cell. This pattern prevents the parallelization of the execution of the smart contract for all known designs that try to provide fine-grained parallelization (detecting conflicts at the storage cell level). + +Several EVM-based blockchains have successfully tested designs for the parallelization of the EVM. The best results have been obtained with fine-grained parallelization where conflicts are detected by tracking writes and reads of individual storage cells. The designs based on tracking the use of accounts or contracts provide only minor benefits because most transactions use the same [EIP-20](./eip-20.md) contracts. + +To summarize, the only available RPG construction today is based on using a contract storage cell. This construction is clean but it is not forward-compatible with transaction execution parallelization. + +## Specification + +Starting from an activation block (TBD) a new precompiled contract `Semaphore` is created at address `0x0A`. When `Semaphore` is called, if the caller address is present more than once in the call stack, the contract behaves as if the first instruction had been a `REVERT`, therefore the CALL returns 0. Otherwise, it executes no code and returns 1. The gas cost of the contract execution is set to 100, which is consumed independently of the call result. + +## Rationale + +The address `0x0A` is the next one available within the range defined by [EIP-1352](./eip-1352). + +### Sample usage + +```solidity +pragma solidity ^0.8.0; + +abstract contract ReentrancyGuard2 { + + uint8 constant SemaphoreAddress = 0x0A; + /** + * @dev Prevents a contract from calling itself, directly or indirectly. + * Calling a `nonReentrant` function from another `nonReentrant` + * function is supported. + */ + modifier nonReentrant() { + _nonReentrantBefore(); + _; + } + + function _nonReentrantBefore() private { + assembly { + if iszero(staticcall(1000,SemaphoreAddress, 0, 0, 0, 0)) { + revert(0, 0) + } + } + } +} +``` + +### Parallellizable storage-based RPGs + +The only way to paralellize prexistent contracts that are using the storage RPG construction is that the VM automatically detects that a storage variable is used for the RPG, and proves that is works as required. This requires static code analysys. This is difficult to implement in consensus for two reasons. First, the CPU cost of detection and/or proving may be high. Second, some contract functions may not be protected by the RPG, meaning that some execution paths do not alter the RPG, which may complicate proving. Therefore this proposal aims to protect future contracts and let them be parallelizable, rather than to paralellize already deployed ones. + +### Alternatives + +There are alternative designs to implement RPGs on the EVM: + +1. Transient storage opcodes (`TLOAD`/`TSTORE`) provide contract state that is kept between calls in the same transaction but it is not committed to the world state afterward. These opcodes also enable fine-grained parallelization. +2. An opcode `SSTORE_COUNT` that retrieves the number of `SSTORE` instructions executed. It enables also fine-grained execution parallelization, but `SSTORE_COUNT` is much more complex to use correctly as it returns the number `SSTORE` opcodes executed, not the number of reentrant calls. Reentrancy must be deducted from this value. +3. A new `LOCKCALL` opcode that works similar to `STATICALL` but only blocks storage writes in the caller contract. This results in cheaper RPG, but it doesn't allow some contract functions to be free of the RPG. + +All these alternative proposals have the downside that they create new opcodes, and this is discouraged if the same functionality can be implemented with the same gas cost using precompiles. A new opcode requires modifying compilers, debuggers and static analysis tools. + +### Gas cost + +A gas cost of 100 represents a worst-case resource consumption, which occurs when the stack is almost full (approximately 400 addresses) and it is fully scanned. As the stack is always present in RAM, the scanning is fast. + +Note: Once code is implemented in geth, it can be benchmarked and the cost can be re-evaluated, as it may result to be lower in practice. As a precompile call currently costs 700 gas, the cost of stack scanning has a low impact on the total cost of the precompile call (800 gas in total). + +The storage-based RPG currently costs 200 gas (because of the savings introduced in [EIP-1283](./eip-1283.md). Using the `Semaphore` precompile as a reentrancy check would currently cost 800 gas (a single call from one of the function modifiers). While this cost is higher than the traditional RPG cost and therefore discourages its use, it is still much lower than the pre-EIP-1283 cost. If a reduction in precompile call cost is implemented, then the cost of using the `Semaphore` precompile will be reduced to approximately 140 gas, below the current 200 gas consumed by a storage-based RPG. To encourage to use of the precompile-based RPG, it is suggested that this EIP is implemented together with a reduction in precompile calls cost. + +## Backwards Compatibility + +This change requires a hard fork and therefore all full nodes must be updated. + +## Test Cases + +```solidity +contract Test is ReentrancyGuard2 { + function second() external nonReentrant { + } + function first() external nonReentrant { + this.second(); + } +} +``` + +A call to `second()` directly from a transaction does not revert, but a call to `first()` does revert. + +## Security Considerations + +Needs discussion. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5289.md b/EIPS/eip-5289.md new file mode 100644 index 0000000..5c2b156 --- /dev/null +++ b/EIPS/eip-5289.md @@ -0,0 +1,93 @@ +--- +eip: 5289 +title: Ethereum Notary Interface +description: Allows Smart Contracts to be Legally Binding Off-Chain +author: Gavin John (@Pandapip1) +discussions-to: https://ethereum-magicians.org/t/pr-5289-discussion-notary-interface/9980 +status: Review +type: Standards Track +category: ERC +created: 2022-07-16 +requires: 165, 5568 +--- + +## Abstract + +Currently, the real-world applications of smart contracts are limited by the fact that they aren't legally binding. This EIP proposes a standard that allows smart contracts to be legally binding by providing IPFS links to legal documents and ensuring that the users of the smart contract have privity with the relevant legal documents. + +## Motivation + +NFTs have oftentimes been branded as a way to hold and prove copyright of a specific work. However, this, in practice, has almost never been the case. Most of the time, NFTs have no legally-binding meaning, and in the rare cases that do, the NFT simply provides a limited license for the initial holder to use the work (but cannot provide any license for any future holders). + +## Specification + +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. + +### Legal Contract Library Interface + +```solidity +/// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.0; + +import "./IERC165.sol"; + +interface IERC5289Library is IERC165 { + /// @notice Emitted when signDocument is called + event DocumentSigned(address indexed signer, uint16 indexed documentId); + + /// @notice An immutable link to the legal document (RECOMMENDED to be hosted on IPFS). This MUST use a common file format, such as PDF, HTML, TeX, or Markdown. + function legalDocument(uint16 documentId) external view returns (string memory); + + /// @notice Returns whether or not the given user signed the document. + function documentSigned(address user, uint16 documentId) external view returns (bool signed); + + /// @notice Returns when the the given user signed the document. + /// @dev If the user has not signed the document, the timestamp may be anything. + function documentSignedAt(address user, uint16 documentId) external view returns (uint64 timestamp); + + /// @notice Sign a document + /// @dev This MUST be validated by the smart contract. This MUST emit DocumentSigned or throw. + function signDocument(address signer, uint16 documentId) external; +} +``` + +### Requesting a Signature + +To request that certain documents be signed, revert with an [ERC-5568](./eip-5568.md) signal. The format of the `instruction_data` is an ABI-encoded `(address, uint16)` pair, where the address is the address of the library, and the `uint16` is the `documentId` of the document: + +```solidity +throw WalletSignal24(0, 5289, abi.encode(0xcbd99eb81b2d8ca256bb6a5b0ef7db86489778a7, 12345)); +``` + +### Signing a Document + +When a signature is requested, wallets MUST call `legalDocument`, display the resulting document to the user, and prompt them to either sign the document or cancel: + +![image](../assets/eip-5289/example-popup.png) + +If the user agrees, the wallet MUST call `signDocument`. + +## Rationale + +- `uint64` was chosen for the timestamp return type as 64-bit time registers are standard. +- `uint16` was chosen for the document ID as 65536 documents are likely sufficient for any use case, and the contract can always be re-deployed. +- `signDocument` doesn't take an ECDSA signature for future compatibility with account abstraction. In addition, future extensions can supply this functionality. +- IPFS is mandatory because the authenticity of the signed document can be proven. + +## Backwards Compatibility + +No backwards compatibility issues found. + +## Reference Implementation + +### Legal Contract Library + +See [`IERC5289Library`](../assets/eip-5289/interfaces/IERC5289Library.sol), [`ERC5289Library`](../assets/eip-5289/ERC5289Library.sol). + +## Security Considerations + +Users can claim that their private key was stolen and used to fraudulently "sign" contracts. As such, **documents must only be permissive in nature, not restrictive.** For example, a document granting a license to use the image attached to an NFT would be acceptable, as there is no reason for the signer to plausibly deny signing the document. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5298.md b/EIPS/eip-5298.md new file mode 100644 index 0000000..5f88509 --- /dev/null +++ b/EIPS/eip-5298.md @@ -0,0 +1,153 @@ +--- +eip: 5298 +title: ENS Trust to hold NFTs under ENS name +description: An interface for a smart contract acting as a "trust" that holds tokens by ENS name. +author: Zainan Victor Zhou (@xinbenlv) +discussions-to: https://ethereum-magicians.org/t/erc-eip-5198-ens-as-token-holder/10374 +status: Review +type: Standards Track +category: ERC +created: 2022-07-12 +requires: 137, 721, 1155 +--- + +## Abstract + +This EIP standardizes an interface for smart contracts to hold of [EIP-721](./eip-721.md) and [EIP-1155](./eip-1155.md) tokens on behalf of ENS domains. + +## Motivation + +Currently, if someone wants to receive a token, they have to set up a wallet address. This EIP decouples NFT ownership from wallet addresses. + +## Specification + +1. Compliant contracts MUST implement `ERC721TokenReceiver`, as defined in [EIP-721](./eip-721.md). +2. Compliant contracts implement the following interface: + +```solidity +interface IERC_ENS_TRUST is ERC721Receiver, ERC1155Receiver { + function claimTo(address to, bytes32 ensNode, address operator, uint256 tokenId) payable external; +} +``` + +3. `claimTo` MUST check if `msg.sender` is the owner of the ENS node identified by `bytes32 ensNode` (and/or approved by the domain in implementation-specific ways). The compliant contract then MUST make a call to the `safeTransferFrom` function of [EIP-721](./eip-712.md) or [EIP-1155](./eip-1155.md). + +4. Any `ensNode` is allowed. + +## Rationale + +1. ENS was chosen because it is a well-established scoped ownership namespace. +This is nonetheless compatible with other scoped ownership namespaces. + +2. We didn't expose getters or setters for ensRoot because it is outside of the scope of this EIP. + +## Backwards Compatibility + +No backward compatibility issues were found. + +## Test Cases + +```ts +import { loadFixture } from "@nomicfoundation/hardhat-network-helpers"; +import { expect } from "chai"; +import { ethers } from "hardhat"; + +describe("FirstENSBankAndTrust", function () { + + describe("Receive and Claim Token", function () { + + it("Should ACCEPT/REJECT claimTo based on if ENS owner is msg.sender", async function () { + ... + // Steps of testing: + // mint to charlie + // charlie send to ENSTrust and recorded under bob.xinbenlvethsf.eth + // bob try to claimTo alice, first time it should be rejected + // bob then set the ENS record + // bob claim to alice, second time it should be accepted + + // mint to charlie + await erc721ForTesting.mint(charlie.address, fakeTokenId); + + // charlie send to ENSTrust and recorded under bob.xinbenlvethsf.eth + await erc721ForTesting.connect(charlie)["safeTransferFrom(address,address,uint256,bytes)"]( + charlie.address, firstENSBankAndTrust.address, + fakeTokenId, + fakeReceiverENSNamehash + ); + + // bob try to claimTo alice, first time it should be rejected + await expect(firstENSBankAndTrust.connect(bob).claimTo( + alice.address, + fakeReceiverENSNamehash, + firstENSBankAndTrust.address, + fakeTokenId + )) + .to.be.rejectedWith("ENSTokenHolder: node not owned by sender"); + + // bob then set the ENS record + await ensForTesting.setOwner( + fakeReceiverENSNamehash, bob.address + ); + + // bob claim to alice, second time it should be accepted + await expect(firstENSBankAndTrust.connect(bob).claimTo( + alice.address, + fakeReceiverENSNamehash, + erc721ForTesting.address, + fakeTokenId + )); + }); + }); +}); +``` + +## Reference Implementation + +```solidity +pragma solidity ^0.8.9; + +contract FirstENSBankAndTrust is IERC721Receiver, Ownable { + function getENS() public view returns (ENS) { + return ENS(ensAddress); + } + + function setENS(address newENSAddress) public onlyOwner { + ensAddress = newENSAddress; + } + + // @dev This function is called by the owner of the token to approve the transfer of the token + // @param data MUST BE the ENS node of the intended token receiver this ENSHoldingServiceForNFT is holding on behalf of. + function onERC721Received( + address operator, + address /*from*/, + uint256 tokenId, + bytes calldata data + ) external override returns (bytes4) { + require(data.length == 32, "ENSTokenHolder: last data field must be ENS node."); + // --- START WARNING --- + // DO NOT USE THIS IN PROD + // this is just a demo purpose of using extraData for node information + // In prod, you should use a struct to store the data. struct should clearly identify the data is for ENS + // rather than anything else. + bytes32 ensNode = bytes32(data[0:32]); + // --- END OF WARNING --- + + addToHolding(ensNode, operator, tokenId); // conduct the book keeping + return ERC721_RECEIVER_MAGICWORD; + } + + function claimTo(address to, bytes32 ensNode, address tokenContract uint256 tokenId) public { + require(getENS().owner(ensNode) == msg.sender, "ENSTokenHolder: node not owned by sender"); + removeFromHolding(ensNode, tokenContract, tokenId); + IERC721(tokenContract).safeTransferFrom(address(this), to, tokenId); + } +} +``` + +## Security Considerations + +Needs discussion. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5313.md b/EIPS/eip-5313.md new file mode 100644 index 0000000..7c7e53f --- /dev/null +++ b/EIPS/eip-5313.md @@ -0,0 +1,61 @@ +--- +eip: 5313 +title: Light Contract Ownership +description: An interface for identifying ownership of contracts +author: William Entriken (@fulldecent) +discussions-to: https://ethereum-magicians.org/t/eip-5313-light-contract-ownership/10052 +status: Final +type: Standards Track +category: ERC +created: 2022-07-22 +requires: 165, 173 +--- + +## Abstract + +This specification defines the minimum interface required to identify an account that controls a contract. + +## Motivation + +This is a slimmed-down alternative to [EIP-173](./eip-173.md). + +## Specification + +The key word “MUST” in this document is to be interpreted as described in RFC 2119. + +Every contract compliant with this EIP MUST implement the `EIP5313` interface. + +```solidity +// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.15; + +/// @title EIP-5313 Light Contract Ownership Standard +interface EIP5313 { + /// @notice Get the address of the owner + /// @return The address of the owner + function owner() view external returns(address); +} +``` + +## Rationale + +Key factors influencing the standard: + +- Minimize the number of functions in the interface +- Backwards compatibility with existing contracts + +This standard can be (and has been) extended by other standards to add additional ownership functionality. The smaller scope of this specification allows more and more straightforward ownership implementations, see limitations explained in EIP-173 under "other schemes that were considered". + +Implementing [EIP-165](./eip-165.md) could be a valuable addition to this interface specification. However, this EIP is being written to codify existing protocols that connect contracts (often NFTs), with third-party websites (often a well-known NFT marketplace). + +## Backwards Compatibility + +Every contract that implements EIP-173 already implements this specification. + +## Security Considerations + +Because this specification does not extend EIP-165, calling this EIP's `owner` function cannot result in complete certainty that the result is indeed the owner. For example, another function with the same function signature may return some value that is then interpreted to be the true owner. If this EIP is used solely to identify if an account is the owner of a contract, then the impact of this risk is minimized. But if the interrogator is, for example, sending a valuable NFT to the identified owner of any contract on the network, then the risk is heightened. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5334.md b/EIPS/eip-5334.md new file mode 100644 index 0000000..e0c2139 --- /dev/null +++ b/EIPS/eip-5334.md @@ -0,0 +1,115 @@ +--- +eip: 5334 +title: EIP-721 User And Expires And Level Extension +description: Add a time-limited role with restricted permissions to EIP-721 tokens. +author: Yan (@yan253319066) +discussions-to: https://ethereum-magicians.org/t/erc-721-user-and-expires-and-level-extension/10097 +status: Draft +type: Standards Track +category: ERC +created: 2022-07-25 +requires: 165, 721 +--- + +## Abstract + +An [EIP-721](./eip-721.md) extension that adds an additional role (`user`) which can be granted to addresses, and a time where the role is automatically revoked (`expires`) and (`level`) . The `user` role represents permission to "use" the NFT, but not the ability to transfer it or set users. + +## Motivation + +Some NFTs have certain utilities. For example, virtual land can be "used" to build scenes, and NFTs representing game assets can be "used" in-game. In some cases, the owner and user may not always be the same. There may be an owner of the NFT that rents it out to a “user”. The actions that a “user” should be able to take with an NFT would be different from the “owner” (for instance, “users” usually shouldn’t be able to sell ownership of the NFT).  In these situations, it makes sense to have separate roles that identify whether an address represents an “owner” or a “user” and manage permissions to perform actions accordingly. + +Some projects already use this design scheme under different names such as “operator” or “controller” but as it becomes more and more prevalent, we need a unified standard to facilitate collaboration amongst all applications. + +Furthermore, applications of this model (such as renting) often demand that user addresses have only temporary access to using the NFT. Normally, this means the owner needs to submit two on-chain transactions, one to list a new address as the new user role at the start of the duration and one to reclaim the user role at the end. This is inefficient in both labor and gas and so an “expires” and “level” function is introduced that would facilitate the automatic end of a usage term without the need of a second transaction. + +Here are some of the problems that are solved by this standard: + +### Clear Rights Assignment + +With Dual “owner” and “user” roles, it becomes significantly easier to manage what lenders and borrowers can and cannot do with the NFT (in other words, their rights). Additionally, owners can control who the user is and it’s easy for other projects to assign their own rights to either the owners or the users. + +### Simple On-chain Time Management + +Once a rental period is over, the user role needs to be reset and the “user” has to lose access to the right to use the NFT. This is usually accomplished with a second on-chain transaction but that is gas inefficient and can lead to complications because it’s imprecise. With the `expires` function, there is no need for another transaction because the “user” is invalidated automatically after the duration is over. + +### Easy Third-Party Integration + +In the spirit of permission less interoperability, this standard makes it easier for third-party protocols to manage NFT usage rights without permission from the NFT issuer or the NFT application. Once a project has adopted the additional `user` role and `expires` and `level`, any other project can directly interact with these features and implement their own type of transaction. For example, a PFP NFT using this standard can be integrated into both a rental platform where users can rent the NFT for 30 days AND, at the same time, a mortgage platform where users can use the NFT while eventually buying ownership of the NFT with installment payments. This would all be done without needing the permission of the original PFP project. + +## Specification + +The keywords "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. + +### Contract Interface +Solidity Interface with NatSpec & OpenZeppelin v4 Interfaces (also available at [`IERC5334.sol`](../assets/eip-5334/IERC5334.sol)): + +```solidity +interface IERC5334 { + + // Logged when the user of a NFT, expires, or level is changed + /// @notice Emitted when the `user` of an NFT or the `expires` of the `user` is changed or the user `level` is changed + /// The zero address for user indicates that there is no user address + event UpdateUser(uint256 indexed tokenId, address indexed user, uint64 expires, uint8 level); + + /// @notice set the user and expires and level of a NFT + /// @dev The zero address indicates there is no user + /// Throws if `tokenId` is not valid NFT + /// @param user The new user of the NFT + /// @param expires UNIX timestamp, The new user could use the NFT before expires + /// @param level user level + function setUser(uint256 tokenId, address user, uint64 expires, uint8 level) external; + + /// @notice Get the user address of an NFT + /// @dev The zero address indicates that there is no user or the user is expired + /// @param tokenId The NFT to get the user address for + /// @return The user address for this NFT + function userOf(uint256 tokenId) external view returns(address); + + /// @notice Get the user expires of an NFT + /// @dev The zero value indicates that there is no user + /// @param tokenId The NFT to get the user expires for + /// @return The user expires for this NFT + function userExpires(uint256 tokenId) external view returns(uint256); + + /// @notice Get the user level of an NFT + /// @dev The zero value indicates that there is no user + /// @param tokenId The NFT to get the user level for + /// @return The user level for this NFT + function userLevel(uint256 tokenId) external view returns(uint256); +} +``` + +The `userOf(uint256 tokenId)` function MAY be implemented as `pure` or `view`. + +The `userExpires(uint256 tokenId)` function MAY be implemented as `pure` or `view`. + +The `userLevel(uint256 tokenId)` function MAY be implemented as `pure` or `view`. + +The `setUser(uint256 tokenId, address user, uint64 expires)` function MAY be implemented as `public` or `external`. + +The `UpdateUser` event MUST be emitted when a user address is changed or the user expires is changed or the user level is changed. + + + +## Rationale + +TBD + +## Backwards Compatibility + +As mentioned in the specifications section, this standard can be fully EIP-721 compatible by adding an extension function set. + +In addition, new functions introduced in this standard have many similarities with the existing functions in EIP-721. This allows developers to easily adopt the standard quickly. + +## Reference Implementation +A reference implementation of this standard can be found in the assets folder. + + +## Security Considerations + +This EIP standard can completely protect the rights of the owner, the owner can change the NFT user and expires and level at any time. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). + diff --git a/EIPS/eip-5345.md b/EIPS/eip-5345.md new file mode 100644 index 0000000..68bc9ff --- /dev/null +++ b/EIPS/eip-5345.md @@ -0,0 +1,126 @@ +--- +eip: 5345 +title: Silent Signing Extension for JSON-RPC +description: Temporary transaction signing without user interaction +author: Stanley Wu (@fruit37), Mücahit Büyükyılmaz (@anndro), Muhammed Emin Aydın (@muhammedea) +discussions-to: https://ethereum-magicians.org/t/walletconnect-silent-signing-extension/10137 +status: Stagnant +type: Standards Track +category: Interface +created: 2022-07-26 +--- + +## Abstract + +Mobile applications supporting lots of transactions might become a source of bad user experience due to uncontrolled switching between the wallet's and application's UI. By this proposal, we would like to introduce the means to sign and send wallet transactions without the need for user participation. This feature can be implemented by providing user consent for a specific time duration. We call the feature Silent Signing. + +## Motivation + +Some blockchain applications interact with a blockchain much more frequently than others. It is especially true for gaming applications having their own sidechains. Interrupting the gaming process and switching to the wallet to perform a transaction drastically affect the user experience. + +## Specification + +To remedy the situation, we'd like to introduce new RPC methods for the ethereum JSON-RPC. Those methods help enable wallets to implement the Silent Signing feature. + +### Silent Signing User Flow + +The Silent Signing process has the following structure: + +1. First, the application requests the wallet to use Silent Signing via the RPC's `wallet_requestSilentSign` method. +2. Second, the wallet prompts the user to confirm enabling the Silent Singing functionality for a specific time duration. +3. If the user does not confirm Silent Signing or the RPC method is not allowed, the application will continue using the regular methods. +4. If the user confirms Silent Signing, then each subsequent transaction will be sent using the `wallet_silentSendTransaction` method for the time duration specified. + +### Implementation + +The implementation introduces new RPC methods and flow for application and wallet side. + +### New RPC Methods + +#### `wallet_requestSilentSign` + +This RPC method opens the wallet and prompts the user to enable automatic signing for a specific time duration. This function grants the application to call the following methods until the timestamp expires. Standard methods like `eth_signTrancaction` remain untouched. + +```shell +Parameters + Object: request object + until: NUMBER - unix timesptamp, the end time the permission will be valid + chainId: NUMBER - the chain id that the contract located in + contractAddress: ADDRESS - address of the contract to be allowed + allowedFunctions: STRING ARRAY - allowed function signatures + Ex: ["equip(address,uint256)", "unequip(address,uint256)"] + description: STRING - extra description that can be shown to user by the wallet + +Returns + DATA, 20 Bytes: permissionSecret - a secret key for silent-signing requests (randomly generated) +``` + +#### `wallet_silentSignTransaction` + +This RPC method creates a transaction and sends its data to the wallet for signing. The wallet signs the data in the background, interfering with no processes the user is involved in. Afterward, the application sends the signed transaction to the blockchain using Nethereum's or other libraries' `sendRawTransaction` method. + +```shell +Parameters + DATA, 20 Bytes: permissionSecret - secret key obtained from `wallet_requestSilentSign` method + Object - The transaction object + from: DATA, 20 Bytes - The address the transaction is sent from. + to: DATA, 20 Bytes - (optional when creating new contract) The address the transaction is directed to. + gas: QUANTITY - (optional, default: 90000) Integer of the gas provided for the transaction execution. It will return unused gas. + gasPrice: QUANTITY - (optional, default: To-Be-Determined) Integer of the gasPrice used for each paid gas, in Wei. + value: QUANTITY - (optional) Integer of the value sent with this transaction, in Wei. + data: DATA - The compiled code of a contract OR the hash of the invoked method signature and encoded parameters. + nonce: QUANTITY - (optional) Integer of a nonce. This allows to overwrite your own pending transactions that use the same nonce. + +Returns + DATA, The signed transaction object. +``` + +#### `wallet_silentSendTransaction` + +This RPC method creates a transaction and sends it to the blockchain without interfering with the process the user is involved in. + +```shell +Parameters + DATA, 20 Bytes: permissionSecret - secret key obtained from `wallet_requestSilentSign` method + Object - The transaction object + from: DATA, 20 Bytes - The address the transaction is sent from. + to: DATA, 20 Bytes - (optional when creating new contract) The address the transaction is directed to. + gas: QUANTITY - (optional, default: 90000) Integer of the gas provided for the transaction execution. It will return unused gas. + gasPrice: QUANTITY - (optional, default: To-Be-Determined) Integer of the gasPrice used for each paid gas. + value: QUANTITY - (optional) Integer of the value sent with this transaction. + data: DATA - The compiled code of a contract OR the hash of the invoked method signature and encoded parameters. + nonce: QUANTITY - (optional) Integer of a nonce. This allows to overwrite your own pending transactions that use the same nonce. + +Returns + DATA, 32 Bytes - the transaction hash, or the zero hash if the transaction is not yet available. +``` + +### Application and Wallet Communication + +Sending RPC requests between application and wallet can be as usual. For example browser extension wallets can use these new methods easily. Even hardware wallets can implement this too. But for mobile wallets extra communication techniques should be considered. Because mobile wallets can be inactive when it is not in use. + +Mobile wallets mostly use Walletconnect protocol. The application closed or active in the background can't connect to the Bridge server via WebSocket. Therefore, we have to trigger the wallet to connect to the Bridge and to start waiting for requests. For this purpose, push notifications are to be used. That means that only the wallets supporting push notifications can implement the feature. + +![](../assets/eip-5345/walletconnect-flow.png) + +Whenever the wallet receives a push notification, it connects to the Bridge server and gets access to the pending requests. If there are `wallet_silenSignTransaction` or `wallet_silentSendTransaction` silent signing requests pending and the interaction with the requesting client has been confirmed for this particular time duration, then the wallet executes the request without interfering with the ongoing user activity. + +## Rationale + +Games and Metaverse applications imply lots of cases when the user interacts with the wallet, switching to it and approving transactions. This switching aspect might interfere with gaming per se and create a bad user experience. That is why such applications can benefit if the wallets can support the Silent Signing functionality allowing transactions to be signed with no user interaction. + +## Backwards Compatibility + +These new RPC methods don't interfere with the current ones, and for mobile wallets the push notifications API is currently a part of the `WalletConnect` specification. Implementing the proposal's functionality changes nothing for other applications and wallets. + +## Security Considerations + +The proposed feature aims to improve the user experience and can only be enabled with user consent. Users might freely choose to use the application as usual. + +Silent Signing permission has restrictions that makes it more secure. +* Permission granted only for a specified time duration +* Permission granted only for specific contract in a specific chain and restricted to specified functions. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5375.md b/EIPS/eip-5375.md new file mode 100644 index 0000000..3eaa551 --- /dev/null +++ b/EIPS/eip-5375.md @@ -0,0 +1,304 @@ +--- +eip: 5375 +title: NFT Author Information and Consent +description: An extension of EIP-721 for NFT authorship and author consent. +author: Samuele Marro (@samuelemarro), Luca Donno (@lucadonnoh) +discussions-to: https://ethereum-magicians.org/t/eip-5375-nft-authorship/10182 +status: Final +type: Standards Track +category: ERC +created: 2022-07-30 +requires: 55, 155, 712, 721, 1155 +--- + +## Abstract + +This EIP standardizes a JSON format for storing off-chain information about NFT authors. Specifically, it adds a new field which provides a list of author names, addresses, and proofs of _authorship consent_: proofs that the authors have agreed to be named as authors. Note that a proof of authorship _consent_ is not a proof of authorship: an address can consent without having authored the NFT. + +## Motivation + +There is currently no standard to identify authors of an NFT, and existing techniques have issues: + +- Using the mint `tx.origin` or `msg.sender` + - Assumes that the minter and the author are the same + - Does not support multiple authors +- Using the first Transfer event for a given ID + - Contract/minter can claim that someone else is the author without their consent + - Does not support multiple authors +- Using a custom method/custom JSON field + - Requires per-contract support by NFT platforms + - Contract/minter can claim that someone else is the author without their consent + +The first practice is the most common. However, there are several situations where the minter and the author might not be the same, such as: + +- NFTs minted by a contract +- Lazy minting +- NFTs minted by an intermediary (which can be particularly useful when the author is not tech-savvy and/or the minting process is convoluted) + +This document thus defines a standard which allows the minter to provide authorship information, while also preventing authorship claims without the author's consent. + +## Specification + +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. + +All addresses used in this standard MUST follow the casing rules described in [EIP-55](./eip-55.md). + +### Definitions + +- **Authors**: creators of an NFT +- **Minter**: entity responsible for the actual minting transaction; the minter and the authors MAY be the same +- **Verifier**: entity that wants to verify the authorship of an NFT (e.g. a user or an NFT marketplace) +- **Author Consent Proof (ACP)**: a signed message that proves that the signer agrees to be considered the author of the NFT + +### Authorship Support + +The standard introduces a new JSON field, named `authorInfo`. It provides a REQUIRED interface for authorship claiming, as well as an OPTIONAL interface for author consent proofs. + +`authorInfo` is a top-level field of the NFT metadata. Specifically: + +- If a contract supports the metadata extension for [EIP-721](./eip-721.md), the JSON document pointed by `tokenURI(uint256 _tokenId)` MUST include the top-level field `authorInfo` +- If a contract supports the metadata extension for [EIP-1155](./eip-1155.md), the JSON document pointed by `uri(uint256 _id)` MUST include a top-level field `authorInfo` + +The JSON schema of `authorInfo` (named `ERC5375AuthorInfoSchema`) is defined as follows: + +```json +{ + "type": "object", + "properties": { + "consentInfo": { + "type": "object", + "description": "Helper fields for consent verification", + "properties": { + "chainId": { + "type": "integer", + "description": "EIP-155 chain id" + }, + "id": { + "type": "string", + "description": "NFT id" + }, + "contractAddress": { + "type": "string", + "description": "0x-prefixed address of the smart contract" + } + } + }, + "authors": { + "type": "array", + "items": "ERC5375AuthorSchema" + } + }, + "required": [ "authors" ] +} +``` + +Note that `authors` MAY be an empty array. + +`ERC5375AuthorSchema` is defined as follows: + +```json +{ + "type": "object", + "properties": { + "address": { + "type": "string", + "description": "0x-prefixed address of the author" + }, + "consent": { + "type": "ERC5375AuthorConsentSchema", + "description": "Author consent information" + } + }, + "required": [ "address" ] +} +``` + +Moreover, if the `consent` field is present, the `consentInfo` field of `authorInfo` MUST be present. + +`ERC5375AuthorConsentSchema` is defined as follows: + +```json +{ + "type": "object", + "properties": { + "consentData": { + "type": "object", + "properties": { + "version": { + "type": "string", + "description": "NFT authorship consent schema version" + }, + "issuer": { + "type": "string", + "description": "0x-prefixed address of the author" + }, + "metadataFields": { + "type": "object" + } + }, + "required": ["version", "issuer", "metadataFields"] + }, + "publicKey": { + "type": "string", + "description": "EVM public key of the author" + }, + "signature": { + "type": "string", + "description": "EIP-712 signature of the consent message" + } + }, + "required": ["consentData", "publicKey", "signature"] +} +``` + +where `metadataFields` is an object containing the JSON top-level fields (excluding `authorInfo`) that the author will certify. Note that the keys of `metadataFields` MAY be a (potentially empty) subset of the set of fields. + +`consentData` MAY support additional fields as defined by other EIPs. `consentData` MUST contain all the information (which is not already present in other fields) required to verify the validity of an authorship consent proof. + +### Author Consent + +Consent is obtained by signing an [EIP-712](./eip-712.md) compatible message. Specifically, the structure is defined as follows: + +```solidity +struct Author { + address subject; + uint256 tokenId; + string metadata; +} +``` + +where `subject` is the address of the NFT contract, `tokenId` is the id of the NFT and `metadata` is the JSON encoding of the fields listed in `metadataFields`. `metadata`: + +- MUST contain exactly the same fields as the ones listed in `metadataFields`, in the same order +- MUST escape all non-ASCII characters. If the escaped character contains hexadecimal letters, they MUST be uppercase +- MUST not contain any whitespace that is not part of a field name or value + +For example, if the top-level JSON fields are: + +```json +{ + "name": "The Holy Hand Grenade of Antioch", + "description": "Throw in the general direction of your favorite rabbit, et voilà", + "damage": 500, + "authors": [...], + ... +} +``` + +and the content of `metadataFields` is `["name", "description"]`, the content of `metadata` is: + +```json +{ + "name": "The Holy Hand Grenade of Antioch", + "description": "Throw in the general direction of your favorite rabbit, et voil\u00E0" +} +``` + +Similarly to `consentData`, this structure MAY support additional fields as defined by other EIPs. + +The domain separator structure is + +```solidity +struct EIP712Domain { + string name; + string version; + uint256 chainId; +} +``` + +where `name` and `version` are the same fields described in `consentData` + +This structure MAY support additional fields as defined by other EIPs. + +### Author Consent Verification + +Verification is performed using EIP-712 on an author-by-author basis. Specifically, given a JSON document D1, a consent proof is valid if all of the following statements are true: + +- D1 has a top-level `authorInfo` field that matches `ERC5375AuthorInfoSchema` +- `consent` exists and matches `ERC5375AuthorConsentSchema`; +- If calling `tokenURI` (for EIP-721) or `uri` (for EIP-1155) returns the URI of a JSON document D2, all the top-level fields listed in `metadataFields` MUST exist and have the same value; +- The EIP-712 signature in `signature` (computed using the fields specified in the JSON document) is valid; + +Verifiers MUST NOT assume that an NFT with a valid consent proof from address X means that X is the actual author. On the other hand, verifiers MAY assume that if an NFT does not provide a valid consent proof for address X, then X is not the actual author. + +## Rationale + +### Why provide only an author consent proof? + +Adding support for full authorship proofs (i.e. Alice is the author and no one else is the author) requires a protocol to prove that someone is the only author of an NFT. +In other words, we need to answer the question: "Given an NFT Y and a user X claiming to be the author, is X the original author of Y?". + +For the sake of the argument, assume that there exists a protocol that, given an NFT Y, can determine the original author of Y. Even if such method existed, an attacker could slightly modify Y, thus obtaining a new NFT Y', and rightfully claim to be the author of Y', despite the fact that it is not an original work. Real-world examples include changing some pixels of an image or replacing some words of a text with synonyms. +Preventing this behavior would require a general formal definition of when two NFTs are semantically equivalent. Even if defining such a concept were possible, it would still be beyond the scope of this EIP. + +Note that this issue is also present when using the minter's address as a proxy for the author. + +### Why off-chain? + +There are three reasons: + +- Adding off-chain support does not require modifications to existing smart contracts; +- Off-chain storage is usually much cheaper than on-chain storage, thus reducing the implementation barrier; +- While there may be some use cases for full on-chain authorship proofs (e.g. a marketplace providing special features for authors), there are limited applications for on-chain author consent, due to the fact that it is mostly used by users to determine the subjective value of an NFT. + +### Why repeat id, chainId and contractAddress? + +In many cases, this data can be derived from contextual information. However, requiring their inclusion in the JSON document ensures that author consent can be verified using only the JSON document. + +### Why not implement a revocation system? + +Authorship is usually final: either someone created an NFT or they didn't. Moreover, a revocation system would impose additional implementation requirements on smart contracts and increase the complexity of verification. Smart contracts MAY implement a revocation system, such as the one defined in other EIPs. + +#### Why escape non-ASCII characters in the signature message? + +EIP-712 is designed with the possibility of on-chain verification in mind; while on-chain verification is not a priority for this EIP, non-ASCII characters are escaped due to the high complexity of dealing with non-ASCII strings in smart contracts. + +### Usability Improvements for Authors + +Since the author only needs to sign an EIP-712 message, this protocol allows minters to handle the technical aspects of minting while still preserving the secrecy of the author's wallet. Specifically, the author only needs to: + +- Obtain an EVM wallet; +- Learn how to read and sign a EIP-712 message (which can often be simplified by using a Dapp) + +without needing to: + +- Obtain the chain's native token (e.g. through trading or bridging); +- Sign a transaction; +- Understand the pricing mechanism of transactions; +- Verify if a transaction has been included in a block + +This reduces the technical barrier for authors, thus increasing the usability of NFTs, without requiring authors to hand over their keys to a tech-savvy intermediary. + +### Limitations of Address-Based Consent + +The standard defines a protocol to verify that a certain _address_ provided consent. However, it does not guarantee that the address corresponds to the expected author (such as the one provided in the `name` field). Proving a link between an address and the entity behind it is beyond the scope of this document. + +## Backwards Compatibility + +No backward compatibility issues were found. + +## Security Considerations + +### Attacks + +A potential attack that exploits this EIP involves tricking authors into signing authorship consent messages against their wishes. For this reason, authors MUST verify that all signature fields match the required ones. + +A more subtle approach involves not adding important fields to `metadataFields`. By doing so, the author signature might be valid even if the minter changes critical information. + +### Deprecated Features + +`ERC5375AuthorInfoSchema` also originally included a field to specify a human-readable name for the author (without any kind of verification). This was scrapped due to the high risk of author spoofing, i.e.: + +- Alice mints an NFT using Bob's name and Alice's address +- Charlie does not check the address and instead relies on the provided name +- Charlie buys Alice's NFT while believing that it was created by Bob + +For this reason, smart contract developers SHOULD NOT add support for unverifiable information to the JSON document. We believe that the most secure way to provide complex authorship information (e.g. the name of the author) is to prove that the information is associated with the _author's address_, instead of with the NFT itself. + +### Replay Attack Resistance + +The chain id, the contract address and the token id uniquely identify an NFT; for this reason, there is no need to implement additional replay attack countermeasures (e.g. a nonce system). + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5380.md b/EIPS/eip-5380.md new file mode 100644 index 0000000..320784e --- /dev/null +++ b/EIPS/eip-5380.md @@ -0,0 +1,176 @@ +--- +eip: 5380 +title: ERC-721 Entitlement Extension +description: Allows token owners to grant the ability for others to use specific properties of those tokens +author: Gavin John (@Pandapip1), Tim Daubenschütz (@TimDaub) +discussions-to: https://ethereum-magicians.org/t/pr-5380-eip-4907-alternative-design/10190 +status: Final +type: Standards Track +category: ERC +created: 2022-03-11 +requires: 165, 721, 1046 +--- + +## Abstract + +This EIP proposes a new interface that allows [ERC-721](./eip-721.md) token owners to grant limited usage of those tokens to other addresses. + +## Motivation + +There are many scenarios in which it makes sense for the owner of a token to grant certain properties to another address. One use case is renting tokens. If the token in question represents a trading card in an on-chain TCG (trading card game), one might want to be able to use that card in the game without having to actually buy it. Therefore, the owner might grant the renter the "property" of it being able to be played in the TCG. However, this property should only be able to be assigned to one person at a time, otherwise a contract could simply "rent" the card to everybody. If the token represents usage rights instead, the property of being allowed to use the associated media does not need such a restriction, and there is no reason that the property should be as scarce as the token. + +## Specification + +The keywords "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. + +### Base + +Compliant entitlement contracts MUST implement the following Solidity interface: + +```solidity +/// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.0; + +interface ERC5380Entitlement is ERC165 { + /// @notice Emitted when the amount of entitlement a user has changes. If user is the zero address, then the user is the owner + event EntitlementChanged(address indexed user, address indexed contract, uint256 indexed tokenId); + + /// @notice Set the user associated with the given ERC-721 token as long as the owner is msg.sender. + /// @dev SHOULD NOT revert if the owner is not msg.sender. + /// @param user The user to grant the entitlement to + /// @param contract The property to grant + /// @param tokenId The tokenId to grant the properties of + function entitle(address user, address contract, uint256 tokenId) external; + + /// @notice Get the maximum number of users that can receive this entitlement + /// @param contract The contract to query + /// @param tokenId The tokenId to query + function maxEntitlements(address contract, uint256 tokenId) external view (uint256 max); + + /// @notice Get the user associated with the given contract and tokenId. + /// @dev Defaults to maxEntitlements(contract, tokenId) assigned to contract.ownerOf(tokenId) + /// @param user The user to query + /// @param contract The contract to query + /// @param tokenId The tokenId to query + function entitlementOf(address user, address contract, uint256 tokenId) external view returns (uint256 amt); +} +``` + +`supportsInterface` MUST return true when called with `ERC5380Entitlement`'s interface ID. + +### Enumerable Extension + +This OPTIONAL Solidity interface is RECOMMENDED. + +```solidity +/// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.0; + +interface ERC5380EntitlementEnumerable is ERC5380Entitlement { // Also implicitly supports ERC-165 + /// @notice Enumerate tokens with nonzero entitlement assigned to a user + /// @dev Throws if the index is out of bounds or if user == address(0) + /// @param user The user to query + /// @param index A counter + function entitlementOfUserByIndex(address user, uint256 index) external view returns (address contract, uint256 tokenId); +} +``` + +`supportsInterface` MUST return true when called with `ERC5380EntitlementEnumerable`'s interface ID. + +### Metadata Extension + +This OPTIONAL Solidity interface is RECOMMENDED. + +This extension uses [ERC-1046](./eip-1046.md) for `tokenURI` compatibility. + +```solidity +/// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.0; + +interface ERC5380EntitlementMetadata is ERC5380Entitlement { // Also implicitly supports ERC-165 + /// @notice ERC-1046 token URI + /// @dev See ERC-1046 and the metadata schema below + function tokenURI() external view returns (string); +} +``` + +`supportsInterface` MUST return true when called with `ERC5380EntitlementMetadata`'s interface ID. + +#### Interoperability Metadata Extension + +ERC-1046's `InteroperabilityMetadata` is extended with the following TypeScript interface: + +```typescript +/** + * ERC-5380's extension to ERC-1046's Interoperability metadata. + */ +interface ERC5380InteroperabilityMetadata is InteroperabilityMetadata { + /** + * This MUST be true if this is ERC-5380 Token Metadata, otherwise, this MUST be omitted. + * Setting this to true indicates to wallets that the address should be treated as an ERC-5380 entitlement. + **/ + erc5380?: boolean | undefined; +} +``` + +#### `tokenURI` Metadata Schema + +The resolved `tokenURI` data MUST conform to the following TypeScript interface: + +```typescript +/** + * ERC-5380 Asset Metadata + * Can be extended + */ +interface ERC5380TokenMetadata { + /** + * Interoperabiliy, to differentiate between different types of tokens and their corresponding URIs. + **/ + interop: ERC5380InteroperabilityMetadata; + + /** + * The name of the ERC-5380 token. + */ + name?: string; + + /** + * The symbol of the ERC-5380 token. + */ + symbol?: string; + + /** + * Provides a short one-paragraph description of the ERC-5380 token, without any markup or newlines. + */ + description?: string; + + /** + * One or more URIs each pointing to a resource with mime type `image/*` that represents this token. + * If an image is a bitmap, it SHOULD have a width between 320 and 1080 pixels + * Images SHOULD have an aspect ratio between 1.91:1 and 4:5 inclusive. + */ + images?: string[]; + + /** + * One or more URIs each pointing to a resource with mime type `image/*` that represent an icon for this token. + * If an image is a bitmap, it SHOULD have a width between 320 and 1080 pixels, and MUST have a height equal to its width + * Images MUST have an aspect ratio of 1:1, and use a transparent background + */ + icons?: string[]; +} +``` + +## Rationale + +[ERC-20](./eip-20.md) and [ERC-1155](./eip-1155.md) are unsupported as partial ownership is much more complex to track than boolean ownership. + +## Backwards Compatibility + +No backward compatibility issues were found. + +## Security Considerations + +The security considerations of [ERC-721](./eip-721.md) and [ERC-1046](./eip-1046.md) apply. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5409.md b/EIPS/eip-5409.md new file mode 100644 index 0000000..a276e69 --- /dev/null +++ b/EIPS/eip-5409.md @@ -0,0 +1,61 @@ +--- +eip: 5409 +title: EIP-1155 Non-Fungible Token extension +description: Allow EIP-1155 to represent Non-Fungible Tokens (tokens who have a unique owner) +author: Ronan Sandford (@wighawag) +discussions-to: https://ethereum-magicians.org/t/eip-5409-non-fungible-token-extension-for-eip-1155/10240 +status: Stagnant +type: Standards Track +category: ERC +created: 2022-07-23 +requires: 165, 721, 1155 +--- + +## Abstract + +This standard is an extension of [EIP-1155](./eip-1155.md). It proposes an additional function, `ownerOf`, which allows EIP-1155 tokens to support Non-Fungibility (unique owners). By implementing this extra function, EIP-1155 tokens can benefit from [EIP-721](./eip-721.md)'s core functionality without implementing the (less efficient) EIP-721 specification in the same contract. + +## Motivation + +Currently, EIP-1155 does not allow an external caller to detect whether a token is truly unique (can have only one owner) or fungible. This is because EIP-1155 do not expose a mechanism to detect whether a token will have its supply remain to be "1". Furthermore, it does not let an external caller retrieve the owner directly on-chain. + +The EIP-1155 specification does mention the use of split id to represent non-fungible tokens, but this requires a pre-established convention that is not part of the standard, and is not as simple as EIP-721's `ownerOf`. + +The ability to get the owner of a token enables novel use-cases, including the ability for the owner to associate data with it. + +## Specification + +The keywords "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. + +### Contract Interface + +```solidity +interface IERC1155OwnerOf { + + /// @notice Find the owner of an NFT + /// @dev The zero address indicates that there is no owner: either the token does not exist or it is not an NFT (supply potentially bigger than 1) + /// @param tokenId The identifier for an NFT + /// @return The address of the owner of the NFT + function ownerOf(uint256 tokenId) external view returns (address); +} +``` + +The `ownerOf(uint256 tokenId)` function MAY be implemented as `pure` or `view`. + +The `supportsInterface` method MUST return `true` when called with `0x6352211e`. + +## Rationale + +`ownerOf` does not throw when a token does not exist (or does not have an owner). This simplifies the handling of such a case. Since it would be a security risk to assume all EIP-721 implementation would throw, it should not break compatibility with contract handling EIP-721 when dealing with this EIP-1155 extension. + +## Backwards Compatibility + +This EIP is fully backward compatible with EIP-1155. + +## Security Considerations + +Needs discussion. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5437.md b/EIPS/eip-5437.md new file mode 100644 index 0000000..5d1058c --- /dev/null +++ b/EIPS/eip-5437.md @@ -0,0 +1,154 @@ +--- +eip: 5437 +title: Security Contact Interface +description: An interface for security notice using asymmetric encryption +author: Zainan Zhou (@xinbenlv) +discussions-to: https://ethereum-magicians.org/t/erc-interface-for-security-contract/10303 +status: Draft +type: Standards Track +category: ERC +created: 2022-08-09 +requires: 165 +--- + +## Abstract +An interface for security notice using asymmetric encryption. The interface exposes a asymmetric encryption key and a destination of delivery. + +## Motivation +Currently there is no consistent way to specify an official channel for security researchers to report security issues to smart contract maintainers. + +## Specification +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. + +```solidity +interface IEIP5437 { + + /// REQUIRED + function getSecurityContact(uint8 type, bytes memory data) public view + returns ( + uint8 type, + bytes memory publicKey, + bytes memory extraData + ); + + /// OPTIONAL + // TODO consider remove if not needed before finalized + function setSecurityContact( + uint8 type, + bytes memory publicKey, + bytes memory extraData) public; + event SecurityContactChanged(uint8 type, bytes memory publicKeyForEncryption, bytes memory extraData); + + /// OPTIONAL + function securityNotify(uint8 type, bytes memory data) public payable; + /// OPTIONAL + event OnSecurityNotification(uint8 type, bytes memory sourceData, uint256 value); + + /// OPTIONAL + // TODO consider to make it a separate EIP + function bountyPolicy(uint256 id) public view returns(string, bytes memory extraData); +} +``` + +1. Compliant interfaces MUST implement the `getSecurityContact` method. + +`type` is a one byte data with valid range of `[0x10, 0x7f]`. The ranges of `[0x00, 0x0f]` and `[0x80, 0xff]` are reserved for future extension. + +The `type` indicates the format of the `publicKey` and `extraData` in the following way + +------------------------------------------------------------------------------------------------ +| Type | Encryption scheme | extraData | +-------|-------------------------------------|-------------------------------------------------- +| 0x10 | GnuPG - RSA/3072 | Email address(es) encoded in format of RFC 2822 | +------------------------------------------------------------------------------------------------ + +A new version of this table can be proposed by future EIPs by specifying a new `type` number. + +2. The `publicKey` returned from `getSecurityContact` MUST follow the encryption scheme specified +in the table above. + +The following is an example of a `publicKey` using `RSA/3072` generated via GnuPG in an RFC 20 ASCII-encoding of the public key string: + +```text +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQGNBGLzM2YBDADnCxAW/A0idvKNeQ6s/iYUeIIE+2mWmHcBGqLi0zrfz7pKWI+D +m6Hek51sg2c7ZlswPEp8KqANrj/CV1stXHF+KAZtYeFiAqpIZl1wtB6QgKYWGsJf +sXjBU3duLzLut2yvTfbEZsWAvrEaDjlXywdpboorHvfTE2vOvI6iGcjdh7PW7W7g +IGzlL6ukLGG7y9FUO2dSMjCR/tWMLCupnDDLN2cUHnfEnHZ34FMd61NxcHLC7cIk +P8xkFt8GCxURniTjqI5HAB8bGfR34kflVpr2+iKD5e+vQxcWK7vB443nruVf8osn +udDF8Z6mgl7bKBbGyYH58QsVlmZ8g3E4YaMKjpwOzEK3V2R8Yh4ETdr670ZCRrIz +QWVkibGgmQ3J/9RYps5Hfqpj4wV60Bsh1xUIJEIAs3ubMt7Z5JYFeze7VlXGlwot +P+SnAfKzlZT4CDEl2LEEDrbpnpOEdp0x9hYsEaXTxBGSpTDaxP2MyhW3u6pYeehG +oD0UVTLjWgU+6akAEQEAAbQjc29tZXJlYWxuYW1lIDxncGcubG9jYWwuZ2VuQHp6 +bi5pbT6JAdQEEwEIAD4WIQTDk/9jzRZ+lU2cY8rSVJNbud1lrQUCYvMzZgIbAwUJ +EswDAAULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRDSVJNbud1lraulDACqFbQg +e9hfoK17UcPVz/u4ZnwmFd9zFAWSYkGqrK9XMvz0R8pr7Y3Dp5hfvaptqID/lHhA +2oPEZ1ViIYDBcqG9WoWjCOYNoIosEAczrvf8YtUC2MHI+5DdYHtST74jDLuWMw3U +AbBXHds3KcRY5/j01kqqi4uwsMBCYyH3Jl3IwjKgy0KDBbuQakvaHPmNnt81ayvZ +ucdsNB9n/JMDxUWNCcySR+cllW4mk68pdiuK5qw0JMaoUjHFoWsgMTbFSlAV/lre +qu8MnrLSs5iPvvaJ3uDOuYROB2FsbvWxayfAAVS1iZf2vQFBJPnDwDdYoPNYMjLp +s2SfU02MVRGp3wanbtvM52uP42SLLNjBqUvJV03/QwfxCRejgAJOBn+iaOxP9NOe +qfQdKzYPbA9FohdkL9991n21XBZcZzAgF9RyU9IZAPAnwZyex1zfzJsUp/HrjhP8 +Ljs8MIcjIlmpLk66TmJte4dN5eML1bpohmfMX8k0ILESLSUhxEg1JBNYIDK5AY0E +YvMzZgEMALnIkONpqCkV+yaP8Tb8TBjmM+3TioJQROViINUQZh6lZM3/M+DPxAWZ +r0MIh1a3+o+ThlZ70tlS67w3Sjd62sWAFzALzW4F+gTqjBTh6LURDqDV8OXUrggA +SKK222aDP+Fr21h/TtPLeyDvcgm8Xvi4Cy7Jmf5CfT5jDio7a+FyFBNlTFSVqzLM +TgFOkUFBg8kJKvDjWIrS2fcTkELwZ8+IlQ52YbrXwbDar843x1fRmsY+x9nnuGuP +RYn1U4Jbptu2pEkG5q94jzUzTkGZHCzBJY7a8mtvS0mLqIE0Se1p+HFLY76Rma/F +HB6J4JNOTzBZ0/1FVvUOcMkjuZ2dX81qoCZ8NP6eafzKvNYZrGa5NJnjWO1ag5jQ +D8qHuOwxs8Fy9evmkwAVl51evLFNT532I4LK0zHSbF8MccZjpEFMSKwalKJn02Ml +yTd+ljYLf8SKMOLVps8kc4VyMR1lz0PwSpKDFOmkC1LRURpM7UTtCK+/RFg1OLyQ +SKBmdI37KQARAQABiQG8BBgBCAAmFiEEw5P/Y80WfpVNnGPK0lSTW7ndZa0FAmLz +M2YCGwwFCRLMAwAACgkQ0lSTW7ndZa2oFgv8DAxHtRZchTvjxtdLhQEUSHt80JCQ +zgHd7OUI9EU3K+oDj9AKtKZF1fqMlQoOskgBsLy/xpWwyhatv2ONLtHSjYDkZ7qs +jsXshqpuvJ3X00Yn9PXG1Z1jKl7rzy2/0DnQ8aFP+gktfu2Oat4uIu4YSqRsVW/Z +sbdTsW3T4E6Uf0qUKDf49mK3Y2nhTwY0YZqJnuQkSuUvpuM5a/4zSoaIRz+vSNjX +MoXUIK/f8UnWABPm90OCptTMTzXCC1UXEHTNm6iBJThFiq3GeLZH+GnIola5KLO1 ++YbsFEchLfLZ27pWGfIbyppvsuQmrHef+J3g6sXybOWDHVYr3Za1fzxQVIbwoIEe +ndKG0bu7ZAi2b/c8uH/wHT5IvtfzHLeSTjDqG8UyLTnaDxHQZIE9JIzWSQ1DSoNC +YrU7CQtL+/HRpiGFHfClaXln8VWkjnUvp+Fg1ZPtE1t/SKddZ7m29Hd9nzUc0OQW +MOA+HDqgA3a9kWbQKSloORq4unft1eu/FCra +=O6Bf +-----END PGP PUBLIC KEY BLOCK----- +``` + +3. IF `setSecurityContact` is implemented and a call to it has succeeded in setting a new security contact, an event `SecurityContactChanged` MUST be emitted with the identical passed-in-parameters of `setSecurityContact` + +4. It's also RECOMMENDED that an on-chain security notify method `securityNotify` +to implemented to receive security notice onchain. If it's implemented and a call +has succeeded, it MUST emit an `OnSecurityNotification` with identical pass-in-parameter data. + +5. Compliant interfaces MUST implement [EIP-165](./eip-165.md). + + + +6. It's recommended to set a bounty policy via `bountyPolicy` method. The `id = 0` is preserved for a full overview, while other digits are used for different individual bounty policies. The returned +string will be URI to content of bounty policies. +No particular format of bounty policy is specified. + +## Rationale +1. For simplicity, this EIP specifies a simple GPG scheme with a given encryption scheme and uses email addresses as a contact method. It's possible that future EIPs will specify new encryption schemes or delivery methods. +2. This EIP adds an optional method, `setSecurityContact`, to set the security contact, because it might change due to circumstances such as the expiration of the cryptographic keys. +3. This EIP explicitly marks `securityNotify` as `payable`, in order to allow implementers to set a staking amount to report a security vulnerability. +4. This EIP allows for future expansion by adding the `bountyPolicy` the `extraData` fields. Additional values of these fields may be added in future EIPs. + +## Backwards Compatibility +Currently, existing solutions such as OpenZeppelin use plaintext in source code + +```solidity +/// @custom:security-contact some-user@some-domain.com +``` + +It's recommend that new versions of smart contracts adopt this EIP in addition to the legacy `@custom:security-contact` approach. + +## Security Considerations + +Implementors should properly follow security practices required by the encryption scheme to ensure the security of the chosen communication channel. Some best practices are as follows: + +1. Keep security contact information up-to-date; +2. Rotate encryption keys in the period recommended by best practice; +3. Regularly monitor the channel to receive notices in a timely manner. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5450.md b/EIPS/eip-5450.md new file mode 100644 index 0000000..d419ce1 --- /dev/null +++ b/EIPS/eip-5450.md @@ -0,0 +1,129 @@ +--- +eip: 5450 +title: EOF - Stack Validation +description: Deploy-time validation of stack usage for EOF functions. +author: Andrei Maiboroda (@gumb0), Paweł Bylica (@chfast), Alex Beregszaszi (@axic), Danno Ferrin (@shemnon) +discussions-to: https://ethereum-magicians.org/t/eip-5450-eof-stack-validation/10410 +status: Review +type: Standards Track +category: Core +created: 2022-08-12 +requires: 3540, 3670, 4200, 4750 +--- + +## Abstract + +Introduce extended validation of EOF code sections to guarantee that neither stack underflow nor overflow can happen during execution of validated contracts. + +## Motivation + +The current EVM performs a number of validity checks for each executed instruction, such as checking +for instruction being defined, stack overflow and underflow, and enough amount of gas remaining. + +This EIP minimizes the number of such checks required at run-time +by verifying that no exceptional conditions can happen +and preventing the execution and deployment of any invalid code. + +The operand stack validation provides several benefits: + +- removes run-time stack underflow check for all instructions, +- removes run-time stack overflow check for all instruction except `CALLF`, +- ensures that an execution terminates with one of the terminating instructions, +- prevents the deployment of code with unreachable instructions, which discourages the use of code sections for data storage. + +It also has some disadvantages: + +- adds constraints to the code structure (similar to JVM, CPython bytecode, WebAssembly and others); however, these constraints can be lifted in a backward-compatible manner if they are shown to be user-unfriendly, +- adds second validation pass; however, validation's computational and space complexity remains linear. + +The guarantees created by these validation rules also improve the feasabiliy of Ahead-Of-Time and Just-In-Time compilation of EVM code. Single pass transpilation passes can be safely executed with the code validation and advanced stack/register handling can be applied with the stack height validaitons. While not as impactful to a mainnet validator node that is bound mostly by storage state sizes, these can significantly speed up witness validation and other non-mainnet use cases. + +## Specification + +### Code validation + +*Remark:* We rely on the notions of *operand stack* and *type section* as defined by [EIP-4750](./eip-4750.md). + +Each code section is validated independently. + +#### Instructions validation + +In the first validation phase defined in [EIP-3670](./eip-3670.md) (and extended by [EIP-4200](./eip-4200.md) and [EIP-4750](./eip-4750.md)) instructions are inspected independently to check if their opcodes and immediate values are valid. + +#### Operand stack validation + +In the second validation phase control-flow analysis is performed on the code. + +*Operand stack height* here refers to the number of stack values accessible by this function, i.e. it does not take into account values of caller functions' frames (but does include this function's inputs). Note that validation procedure does not require actual operand stack implementation, but only to keep track of its height. + +*Terminating instructions* refers to the instructions either: + +- ending function execution: `RETF`, or +- ending whole EVM execution: `STOP`, `RETURN`, `REVERT`, `INVALID`. + +For each reachable instruction in the code the operand stack height is recorded. +The first instruction has the recorded stack height equal to the number of inputs to the function type matching the code (`type[code_section_index].inputs`). + +The FIFO queue *worklist* is used for tracking "to be inspected" instructions. Initially, it contains the first instruction. + +While *worklist* is not empty, dequeue an instruction and: + +1. Determine the effect the instruction has on the operand stack: + 1. **Check** if the recorded stack height satisfies the instruction requirements. Specifically: + - for `CALLF` instruction the recorded stack height must be at least the number of inputs of the called function according to its type defined in the type section. + - for `RETF` instruction the recorded stack height must be exactly the number of outputs of the function matching the code. + 2. Compute new stack height after the instruction execution. +2. Determine the list of successor instructions that can follow the current instructions: + 1. The next instruction for all instructions other than terminating instructions and unconditional jump. + 2. All targets of a conditional or unconditional jump. +3. For each successor instruction: + 1. **Check** if the instruction is present in the code (i.e. execution must not "fall off" the code). + 2. If the instruction does not have stack height recorded (visited for the first time): + 1. Record the instruction stack height as the value computed in 1.2. + 2. Add the instruction to the *worklist*. + 3. Otherwise, **check** if the recorded stack height equals the value computed in 1.2. + +When *worklist* is empty: + +1. **Check** if all instruction were reached by the analysis. +2. Determine the function maximum operand stack height: + 1. Compute the maximum stack height as the maximum of all recorded stack heights. + 2. **Check** if the maximum stack height does not exceed the limit of 1023 (`0x3FF`). + 3. **Check** if the maximum stack height matches the corresponding code section's `max_stack_height` within the type section. + +The computational and space complexity of this pass is *O(len(code))*. Each instruction is visited at most once. + +### Execution + +Given the deploy-time validation guarantees, an EVM implementation is not required anymore to have run-time stack underflow nor overflow checks for each executed instruction. The exception is the `CALLF` performing operand stack overflow check for the entire called function. + +## Rationale + +### Stack overflow check only in CALLF + +In this EIP, we provide a more efficient variant of the EVM where stack overflow check is performed only in `CALLF` instruction using the called function's `max_stack_height` information. This decreases flexibility of an EVM program because `max_stack_height` corresponds to the worst-case control-flow path in the function. + +### Unreachable code + +The operand stack validation algorithm rejects any code having any unreachable instructions. This check can be performed very cheaply. It prevents deploying degenerated code. Moreover, it enables combining instruction validation and operand stack validation into single pass. + +### Clean stack upon termination + +It is currently required that the operand stack is empty (in the current function context) after the `RETF` instruction. +Otherwise, the `RETF` semantic would be more complicated. For `n` function outputs and `s` the stack height at `RETF` the EVM must erase `s-n` non-top stack items and move the `n` stack items to the place of erased ones. Cost of such operation may be relatively cheap but is not constant. +However, lifting the requirement and modifying the `RETF` semantic as described above is backward +compatible and can be easily introduced in the future. + +## Backwards Compatibility + +This change requires a "network upgrade", since it modifies consensus rules. + +It poses no risk to backwards compatibility, as it is introduced only for EOF1 contracts, for which deploying undefined instructions is not allowed, therefore there are no existing contracts using these instructions. The new instructions are not introduced for legacy bytecode (code which is not EOF formatted). + +## Security Considerations + +Needs discussion. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5453.md b/EIPS/eip-5453.md new file mode 100644 index 0000000..322cc3b --- /dev/null +++ b/EIPS/eip-5453.md @@ -0,0 +1,345 @@ +--- +eip: 5453 +title: Endorsement - Permit for Any Functions +description: A general protocol for approving function calls in the same transaction rely on EIP-5750. +author: Zainan Victor Zhou (@xinbenlv) +discussions-to: https://ethereum-magicians.org/t/erc-5453-endorsement-standard/10355 +status: Draft +type: Standards Track +category: ERC +created: 2022-08-12 +requires: 165, 712, 1271, 5750 +--- + +## Abstract + +This EIP establish a general protocol for permitting approving function calls in the same transaction rely on [EIP-5750](./eip-5750.md). +Unlike a few prior art ([EIP-2612](./eip-2612.md) for [EIP-20](./eip-20.md), [EIP-4494](./eip-4494.md) for [EIP-721](./eip-721.md) that +usually only permit for a single behavior (`transfer` for EIP-20 and `safeTransferFrom` for EIP-721) and a single approver in two transactions (first a `permit(...)` TX, then a `transfer`-like TX), this EIP provides a way to permit arbitrary behaviors and aggregating multiple approvals from arbitrary number of approvers in the same transaction, allowing for Multi-Sig or Threshold Signing behavior. + + +## Motivation + +1. Support permit(approval) alongside a function call. +2. Support a second approval from another user. +3. Support pay-for-by another user +4. Support multi-sig +5. Support persons acting in concert by endorsements +6. Support accumulated voting +7. Support off-line signatures + + + +## Specification + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. + +### Interfaces + +The interfaces and structure referenced here are as followed + + + +```solidity +pragma solidity ^0.8.9; + +struct ValidityBound { + bytes32 functionParamStructHash; + uint256 validSince; + uint256 validBy; + uint256 nonce; +} + +struct SingleEndorsementData { + address endorserAddress; // 32 + bytes sig; // dynamic = 65 +} + +struct GeneralExtensionDataStruct { + bytes32 erc5453MagicWord; + uint256 erc5453Type; + uint256 nonce; + uint256 validSince; + uint256 validBy; + bytes endorsementPayload; +} + +interface IERC5453EndorsementCore { + function eip5453Nonce(address endorser) external view returns (uint256); + function isEligibleEndorser(address endorser) external view returns (bool); +} + +interface IERC5453EndorsementDigest { + function computeValidityDigest( + bytes32 _functionParamStructHash, + uint256 _validSince, + uint256 _validBy, + uint256 _nonce + ) external view returns (bytes32); + + function computeFunctionParamHash( + string memory _functionName, + bytes memory _functionParamPacked + ) external view returns (bytes32); +} + +interface IERC5453EndorsementDataTypeA { + function computeExtensionDataTypeA( + uint256 nonce, + uint256 validSince, + uint256 validBy, + address endorserAddress, + bytes calldata sig + ) external view returns (bytes memory); +} + + +interface IERC5453EndorsementDataTypeB { + function computeExtensionDataTypeB( + uint256 nonce, + uint256 validSince, + uint256 validBy, + address[] calldata endorserAddress, + bytes[] calldata sigs + ) external view returns (bytes memory); +} +``` + +See [`IERC5453.sol`](../assets/eip-5453/IERC5453.sol). + +### Behavior specification + +As specified in [EIP-5750 General Extensibility for Method Behaviors](./eip-5750.md), any compliant method that has an `bytes extraData` as its +last method designated for extending behaviors can conform to [EIP-5453](./eip-5453.md) as the way to indicate a permit from certain user. + +1. Any compliant method of this EIP MUST be a [EIP-5750](./eip-5750.md) compliant method. +2. Caller MUST pass in the last parameter `bytes extraData` conforming a solidity memory encoded layout bytes of `GeneralExtensonDataStruct` specified in _Section Interfaces_. The following descriptions are based on when decoding `bytes extraData` into a `GeneralExtensonDataStruct` +3. In the `GeneralExtensonDataStruct`-decoded `extraData`, caller MUST set the value of `GeneralExtensonDataStruct.erc5453MagicWord` to be the `keccak256("ERC5453-ENDORSEMENT")`. +4. Caller MUST set the value of `GeneralExtensonDataStruct.erc5453Type` to be one of the supported values. + +```solidity +uint256 constant ERC5453_TYPE_A = 1; +uint256 constant ERC5453_TYPE_B = 2; +``` + +5. When the value of `GeneralExtensonDataStruct.erc5453Type` is set to be `ERC5453_TYPE_A`, `GeneralExtensonDataStruct.endorsementPayload` MUST be abi encoded bytes of a `SingleEndorsementData`. +6. When the value of `GeneralExtensonDataStruct.erc5453Type` is set to be `ERC5453_TYPE_B`, `GeneralExtensonDataStruct.endorsementPayload` MUST be abi encoded bytes of `SingleEndorsementData[]` (a dynamic array). + +7. Each `SingleEndorsementData` MUST have a `address endorserAddress;` and a 65-bytes `bytes sig` signature. + +8. Each `bytes sig` MUST be an ECDSA (secp256k1) signature using private key of signer whose corresponding address is `endorserAddress` signing `validityDigest` which is the a hashTypeDataV4 of [EIP-712](./eip-712.md) of hashStruct of `ValidityBound` data structure as followed: + +```solidity +bytes32 validityDigest = + eip712HashTypedDataV4( + keccak256( + abi.encode( + keccak256( + "ValidityBound(bytes32 functionParamStructHash,uint256 validSince,uint256 validBy,uint256 nonce)" + ), + functionParamStructHash, + _validSince, + _validBy, + _nonce + ) + ) + ); +``` + + +9. The `functionParamStructHash` MUST be computed as followed + +```solidity + bytes32 functionParamStructHash = keccak256( + abi.encodePacked( + keccak256(bytes(_functionStructure)), + _functionParamPacked + ) + ); + return functionParamStructHash; +``` + +whereas + +- `_functionStructure` MUST be computed as `function methodName(type1 param1, type2 param2, ...)`. +- `_functionParamPacked` MUST be computed as `enc(param1) || enco(param2) ...` + +10. Upon validating that `endorserAddress == ecrecover(validityDigest, signature)` or `EIP1271(endorserAddress).isValidSignature(validityDigest, signature) == ERC1271.MAGICVALUE`, the single endorsement MUST be deemed valid. +11. Compliant method MAY choose to impose a threshold for a number of endorsements needs to be valid in the same `ERC5453_TYPE_B` kind of `endorsementPayload`. + +12. The `validSince` and `validBy` are both inclusive. Implementer MAY choose to use blocknumber or timestamp. Implementor SHOULD find away to indicate whether `validSince` and `validBy` is blocknumber or timestamp. + +## Rationale + +1. We chose to have both `ERC5453_TYPE_A`(single-endorsement) and `ERC5453_TYPE_B`(multiple-endorsements, same nonce for entire contract) so we +could balance a wider range of use cases. E.g. the same use cases of EIP-2612 and EIP-4494 can be supported by `ERC5453_TYPE_A`. And threshold approvals can be done via `ERC5453_TYPE_B`. More complicated approval types can also be extended by defining new `ERC5453_TYPE_?` + +2. We chose to include both `validSince` and `validBy` to allow maximum flexibility in expiration. This can be also be supported by EVM natively at if adopted [EIP-5081](./eip-5081.md) but EIP-5081 will not be adopted anytime soon, we choose to add these two numbers in our protocol to allow +smart contract level support. + +## Backwards Compatibility + +The design assumes a `bytes calldata extraData` to maximize the flexibility of future extensions. This assumption is compatible with [EIP-721](eip-721.md), [EIP-1155](eip-1155.md) and many other ERC-track EIPs. Those that aren't, such as [EIP-20](./eip-20.md), can also be updated to support it, such as using a wrapper contract or proxy upgrade. + +## Reference Implementation + +In addition to the specified algorithm for validating endorser signatures, we also present the following reference implementations. + +```solidity +pragma solidity ^0.8.9; + +import "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol"; +import "@openzeppelin/contracts/utils/cryptography/EIP712.sol"; + +import "./IERC5453.sol"; + +abstract contract AERC5453Endorsible is EIP712, + IERC5453EndorsementCore, IERC5453EndorsementDigest, IERC5453EndorsementDataTypeA, IERC5453EndorsementDataTypeB { + // ... + + function _validate( + bytes32 msgDigest, + SingleEndorsementData memory endersement + ) internal virtual { + require( + endersement.sig.length == 65, + "AERC5453Endorsible: wrong signature length" + ); + require( + SignatureChecker.isValidSignatureNow( + endersement.endorserAddress, + msgDigest, + endersement.sig + ), + "AERC5453Endorsible: invalid signature" + ); + } + // ... + + modifier onlyEndorsed( + bytes32 _functionParamStructHash, + bytes calldata _extensionData + ) { + require(_isEndorsed(_functionParamStructHash, _extensionData)); + _; + } + + function computeExtensionDataTypeB( + uint256 nonce, + uint256 validSince, + uint256 validBy, + address[] calldata endorserAddress, + bytes[] calldata sigs + ) external pure override returns (bytes memory) { + require(endorserAddress.length == sigs.length); + SingleEndorsementData[] + memory endorsements = new SingleEndorsementData[]( + endorserAddress.length + ); + for (uint256 i = 0; i < endorserAddress.length; ++i) { + endorsements[i] = SingleEndorsementData( + endorserAddress[i], + sigs[i] + ); + } + return + abi.encode( + GeneralExtensionDataStruct( + MAGIC_WORLD, + ERC5453_TYPE_B, + nonce, + validSince, + validBy, + abi.encode(endorsements) + ) + ); + } +} + +``` + +See [`AERC5453.sol`](../assets/eip-5453/AERC5453.sol) + +### Reference Implementation of `EndorsableERC721` + +Here is a reference implementation of `EndorsableERC721` that achieves similar behavior of [EIP-4494](./eip-4494.md). + +```solidity +pragma solidity ^0.8.9; + +contract EndorsableERC721 is ERC721, AERC5453Endorsible { + //... + + function mint( + address _to, + uint256 _tokenId, + bytes calldata _extraData + ) + external + onlyEndorsed( + _computeFunctionParamHash( + "function mint(address _to,uint256 _tokenId)", + abi.encode(_to, _tokenId) + ), + _extraData + ) + { + _mint(_to, _tokenId); + } +} +``` + +See [`EndorsableERC721.sol`](../assets/eip-5453/EndorsableERC721.sol) + +### Reference Implementation of `ThresholdMultiSigForwarder` + +Here is a reference implementation of ThresholdMultiSigForwarder that achieves similar behavior of multi-sig threshold approval +remote contract call like a Gnosis-Safe wallet. + +```solidity +pragma solidity ^0.8.9; + +contract ThresholdMultiSigForwarder is AERC5453Endorsible { + //... + function forward( + address _dest, + uint256 _value, + uint256 _gasLimit, + bytes calldata _calldata, + bytes calldata _extraData + ) + external + onlyEndorsed( + _computeFunctionParamHash( + "function forward(address _dest,uint256 _value,uint256 _gasLimit,bytes calldata _calldata)", + abi.encode(_dest, _value, _gasLimit, keccak256(_calldata)) + ), + _extraData + ) + { + string memory errorMessage = "Fail to call remote contract"; + (bool success, bytes memory returndata) = _dest.call{value: _value}( + _calldata + ); + Address.verifyCallResult(success, returndata, errorMessage); + } + +} + +``` + +See [`ThresholdMultiSigForwarder.sol`](../assets/eip-5453/ThresholdMultiSigForwarder.sol) + +## Security Considerations + +### Replay Attacks + +A replay attack is a type of attack on cryptography authentication. In a narrow sense, it usually refers to a type of attack that circumvents the cryptographically signature verification by reusing an existing signature for a message being signed again. Any implementations relying on this EIP must realize that all smart endorsements described here are cryptographic signatures that are _public_ and can be obtained by anyone. They must foresee the possibility of a replay of the transactions not only at the exact deployment of the same smart contract, but also other deployments of similar smart contracts, or of a version of the same contract on another `chainId`, or any other similar attack surfaces. The `nonce`, `validSince`, and `validBy` fields are meant to restrict the surface of attack but might not fully eliminate the risk of all such attacks, e.g. see the [Phishing](#phishing) section. + +### Phishing + +It's worth pointing out a special form of replay attack by phishing. An adversary can design another smart contract in a way that the user be tricked into signing a smart endorsement for a seemingly legitimate purpose, but the data-to-designed matches the target application + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5478.md b/EIPS/eip-5478.md new file mode 100644 index 0000000..e190681 --- /dev/null +++ b/EIPS/eip-5478.md @@ -0,0 +1,49 @@ +--- +eip: 5478 +title: CREATE2COPY Opcode +description: Reducing the gas cost of contract creation with existing code +author: Qi Zhou (@qizhou) +discussions-to: https://ethereum-magicians.org/t/eip-5478-reducing-the-gas-cost-of-contract-creation-with-existing-code/10419 +status: Stagnant +type: Standards Track +category: Core +created: 2022-08-17 +requires: 1014, 2929 +--- + +## Abstract + +Adding a new opcode, `CREATE2COPY`, that is identical to `CREATE2` but with potentially much lower gas cost by accepting an additional argument `existing_contract_address` that already stored the code of the new contract. + +## Motivation + +This EIP aims to reduce the smart contract creation cost of account abstraction (AA) contracts that have identical code. + +The major cost of creating an AA contract is the contract creation cost, especially data gas. For example, creating an AA contract with 10,000 bytes will consume 2,000,000 data gas. Considering the code for each user's AA contract is the same, `CREATE2COPY` can reduce the data gas cost to 2600 (cold account) or even 100 (warm account) if the contract code already exists in the local storage. + +## Specification + +### Parameters + +| Constant | Value | +| ---------------------------- | ---------------- | +| `FORK_BLKNUM` | TBD | +| `CREATE_DATA_GAS_PER_BYTE` | 200 | +| `COLD_ACCOUNT_ACCESS_COST` | 2600 | +| `WARM_ACCOUNT_ACCESS_COST` | 100 | + +If `block.number >= FORK_BLKNUM`, a new opcode is added (`CREATE2COPY`) at `0xf6`, which takes 5 stack arguments: `endowment`, `memory_start`, `memory_length`, `salt`, `existing_contract_address`. `CREATE2COPY` behaves identically to `CREATE2` (`0xf5` as defined in [EIP-1014](./eip-1014.md)), except that the code hash of the creating contract MUST be the same as that of `existing_contract_address`. + +`CREATE2COPY` has the same `gas` schema as `CREATE2`, but replacing the data gas from `CREATE_DATA_GAS_PER_BYTE * CONTRACT_BYTES` to the gas cost of `EXTCODEHASH` opcode, which is `COLD_ACCOUNT_ACCESS_COST` if the `existing_contract_address` is first-time accessed in the transaction or `WARM_ACCOUNT_ACCESS_COST` if `existing_contract_address` is already in the access list according to [EIP-2929](./eip-2929.md). + +If the code of the contract returned from the init code differs from that of `existing_contract_address`, the creation fails with the error "mismatched contract creation code with existing code", and will burn all gas for the contract creation. +## Rationale + +TBD +## Security Considerations + +Needs discussion. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5484.md b/EIPS/eip-5484.md new file mode 100644 index 0000000..a885f52 --- /dev/null +++ b/EIPS/eip-5484.md @@ -0,0 +1,116 @@ +--- +eip: 5484 +title: Consensual Soulbound Tokens +description: Interface for special NFTs with immutable ownership and pre-determined immutable burn authorization +author: Buzz Cai (@buzzcai) +discussions-to: https://ethereum-magicians.org/t/eip-5484-consensual-soulbound-tokens/10424 +status: Final +type: Standards Track +category: ERC +created: 2022-08-17 +requires: 165, 721 +--- + + +## Abstract + +This EIP defines an interface extending [EIP-721](./eip-721.md) to create soulbound tokens. Before issuance, both parties (the issuer and the receiver), have to agree on who has the authorization to burn this token. Burn authorization is immutable after declaration. After its issuance, a soulbound token can't be transferred, but can be burned based on a predetermined immutable burn authorization. + +## Motivation + +The idea of soulbound tokens has gathered significant attention since its publishing. Without a standard interface, however, soulbound tokens are incompatible. It is hard to develop universal services targeting at soulbound tokens without minimal consensus on the implementation of the tokens. + +This EIP envisions soulbound tokens as specialized NFTs that will play the roles of credentials, credit records, loan histories, memberships, and many more. In order to provide the flexibility in these scenarios, soulbound tokens must have an application-specific burn authorization and a way to distinguish themselves from regular EIP-721 tokens. + +## Specification + +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. + +- The token MUST implement the following interfaces: + + 1. [EIP-165](./eip-165.md)’s `ERC165` (`0x01ffc9a7`) + 1. [EIP-721](./eip-721.md)’s `ERC721` (`0x80ac58cd`) + +- `burnAuth` SHALL be presented to receiver before issuance. +- `burnAuth` SHALL be Immutable after issuance. +- `burnAuth` SHALL be the sole factor that determines which party has the rights to burn token. +- The issuer SHALL present token metadata to the receiver and acquire receiver's signature before issuance. +- The issuer SHALL NOT change metadata after issuance. + +/// Note: the EIP-165 identifier for this interface is 0x0489b56f + +### Contract Interface + +```solidity +// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.0; + +interface IERC5484 { + /// A guideline to standardlize burn-authorization's number coding + enum BurnAuth { + IssuerOnly, + OwnerOnly, + Both, + Neither + } + + /// @notice Emitted when a soulbound token is issued. + /// @dev This emit is an add-on to nft's transfer emit in order to distinguish sbt + /// from vanilla nft while providing backward compatibility. + /// @param from The issuer + /// @param to The receiver + /// @param tokenId The id of the issued token + event Issued ( + address indexed from, + address indexed to, + uint256 indexed tokenId, + BurnAuth burnAuth + ); + + /// @notice provides burn authorization of the token id. + /// @dev unassigned tokenIds are invalid, and queries do throw + /// @param tokenId The identifier for a token. + function burnAuth(uint256 tokenId) external view returns (BurnAuth); +} +``` + +## Rationale + +### Soulbound Token (SBTs) as an extension to EIP-721 + +We believe that soulbound token serves as a specialized subset of the existing EIP-721 tokens. The advantage of such design is seamless compatibility of soulbound token with existing NFT services. Service providers can treat SBTs like NFTs and do not need to make drastic changes to their existing codebase. + +### Non-Transferable + +One problem with current soulbound token implementations that extend from [EIP-721](./eip-721.md) is that all transfer implementations throw errors. A much cleaner approach would be for transfer functions to still throw, but also enable third parties to check beforehand if the contract implements the soulbound interface to avoid calling transfer. + +### Burn Authorization + +We want maximum freedom when it comes to interface usage. A flexible and predetermined rule to burn is crucial. Here are some sample scenarios for different burn authorizations: + +- `IssuerOnly`: Loan record +- `ReceiverOnly`: Paid membership +- `Both`: Credentials +- `Neither`: Credit history + +Burn authorization is tied to specific tokens and immutable after issuance. It is therefore important to inform the receiver and gain receiver's consent before the token is issued. + +### Issued Event + +On issuing, an `Issued` event will be emitted alongside [EIP-721](./eip-721.md)'s `Transfer` event. This design keeps backward compatibility while giving clear signals to thrid-parties that this is a soulBound token issuance event. + +### Key Rotations + +A concern Ethereum users have is that soulbound tokens having immutable ownership discourage key rotations. This is a valid concern. Having a burnable soulbound token, however, makes key rotations achievable. The owner of the soulbound token, when in need of key rotations, can inform the issuer of the token. Then the party with burn authorization can burn the token while the issuer can issue a replica to the new address. + +## Backwards Compatibility + +This proposal is fully backward compatible with [EIP-721](./eip-721.md) + +## Security Considerations + +There are no security considerations related directly to the implementation of this standard. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5485.md b/EIPS/eip-5485.md new file mode 100644 index 0000000..c5b1f98 --- /dev/null +++ b/EIPS/eip-5485.md @@ -0,0 +1,95 @@ +--- +eip: 5485 +title: Legitimacy, Jurisdiction and Sovereignty +description: An interface for identifying the legitimacy, jurisdiction and sovereignty. +author: Zainan Victor Zhou (@xinbenlv) +discussions-to: https://ethereum-magicians.org/t/erc-5485-interface-for-legitimacy-jurisdiction-and-sovereignty/10425 +status: Draft +type: Standards Track +category: ERC +created: 2022-08-17 +requires: 165, 5247 +--- + +## Abstract + +Provide a way for compliant smart contracts to declare their legitimacy lineage, jurisdiction they observe, and sovereignty if they choose to not fall onto any jurisdiction. + +## Motivation + +Today, smart contracts have no standard way to specify their legitimacy lineage, jurisdiction, or sovereignty relationship. The introduction of such a standard, supports better integration with today's legal and regulative scenarios: + +1. it supports a regulative body to allow or deny interoperability with smart contracts. +2. it also allows DAOs to clearly declare "self-sovereignty" by announcing via this interface by saying they do not assert legitimacy from any source other than themselves. + +A real-world example is that ContractA represents an **A company registered in a country**, ContractB represents a **The Secretary of State of the country**, and ContractC represents the **Supreme Court of the Country**. + +Another real example is a contract that declares "self-sovereignty" that doesn't follow any jurisdiction. + +This interface supports both cases, providing a way to allow smart contracts to determine if they want to allow/prohibit interaction based on sovereignty. + +For example, a country might want to require any digital money service's all smart contracts to observe their [ERC-5485](./eip-5485.md) jurisdiction before they are allowed to operate money in their (real world) legal jurisdiction. + +Another real world use-case is that in some jurisdiction e.g. in United States, if an token issuer choose to issue a token, +they can try to petition SEC to recognize their token as registered security, if approved, will gain legitimacy from SEC. +Should they choose to petition Commodity Futures Trading Commission (CFTC) to recognize them as a commodity, if approved, will +gain legitimacy from CFTC. + +On the other hand, a DAO with a strong decentralized ideology might choose to only inter-operate with EOA or "self-sovereign" smart contracts to avoid being affected by any country. + +## Specification + +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. + +1. Compliant contract MUSTS implement the following interface. + +```solidity +interface IERC5485 { + /// A function that returns the source of legitimacy in an address + /// + /// @returns address of EOA or contract that grants legitimacy status to this contract. + /// See Specification #2 and #3 for more details. + function sourceOfLegitimacy() public view returns (address); + + /// A function that returns the sovereign jurisdiction in an address + /// + /// @returns address of EOA or contract that *ultimately* grants legitimacy status (root of legitimacy lineage). + /// See Specification #4 and #4 for more details. + function jurisdiction() public view returns (address); +} +``` + +2. Suppose a compliant `ContractA` is deployed at address `0x` and gains its legitimacy from another contract or External Owned Account (EOA) at `0x`. In this case, it MUST return the `0x` when being queried for `sourceOfLegitimacy()`. For example. If `ContractA` gains legitimacy from `ContractB`, `ContractB` gains legitimacy from `ContractC` but `ContractC` doesn't gain legitimacy from anyone, ContractC is considered the "jurisdiction" that both `ContractB` and `ContractA` observe. Both `ContractA` and `ContractB` MUST return the address of `ContractC` as the result of jurisdiction. + +3. A contract that matches ANY of the following cases is considered a "sovereign contract": + +- Case 1: it does not implement this EIP +- Case 2: it implements this EIP but return its *own address*. +- Case 3: it implements this EIP but return a ZERO address. + +4. Any compliant contract MUST return its legitimacy sources chain up to the sovereign contract when queried by `jurisdiction()`. + +5. If ContractA gains legitimacy from ContractB, ContractA MUST observe the ContractB's order to ANY of the following +- Order Type 1: execute a `selfdestruct` of ContractA + +6. If ContractA observes jurisdiction of ContractC (a sovereignty), ContractA MUST observe ContractC's order to ANY the following +- Order Type 1: execute a `selfdestruct` of ContractA +- Order Type 2: execute ANY smart contract executable proposals as specified in [ERC-5247](./eip-5247.md) + +TODO determine whether to address "add/remove legitimacy" in this EIP or leave it as a future EIP + +## Rationale + +Needs discussion. + +## Backwards Compatibility + +Needs discussion. + +## Security Considerations + +Needs discussion. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5489.md b/EIPS/eip-5489.md new file mode 100644 index 0000000..aec096b --- /dev/null +++ b/EIPS/eip-5489.md @@ -0,0 +1,172 @@ +--- +eip: 5489 +title: NFT Hyperlink Extension +description: NFT Hyperlink Extension embeds hyperlinks onto NFTs, allowing users to click any hNFT and be transported to any url set by the owner. +author: IronMan_CH (@coderfengyun) +discussions-to: https://ethereum-magicians.org/t/eip-5489-nft-hyperlink-extension/10431 +status: Final +type: Standards Track +category: ERC +created: 2022-08-16 +requires: 165, 721 +--- + +## Abstract + +This EIP proposes a new extension for NFTs (non-fungible token, aka [EIP-721](./eip-721.md)): nft-hyperlink-extention (hNFT), embedding NFTs with hyperlinks, referred to as “hNFTs”. As owners of hNFTs, users may authorize a URL slot to a specific address which can be either an externally-owned account (EOA) or a contract address and hNFT owners are entitled to revoke that authorization at any time. The address which has slot authorization can manage the URL of that slot. + + +## Motivation + +As NFTs attract more attention, they have the potential to become the primary medium of Web3. Currently, end users can’t attach rich texts, videos, or images to NFTs, and there’s no way to render these rich-content attachments. Many industries eagerly look forward to this kind of rich-content attachment ability. Attaching, editing, and displaying highly customized information can usefully be standardized. + +This EIP uses hyperlinks as the aforementioned form of “highly customized attachment on NFT”, and also specifies how to attach, edit, and display these attachments on NFTs. + +## Specification + +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. + +### Interface + +#### `IERC5489` + +```solidity +// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.0; + +interface IERC5489 { + /** + * @dev this event emits when the slot on `tokenId` is authorzized to `slotManagerAddr` + */ + event SlotAuthorizationCreated(uint256 indexed tokenId, address indexed slotManagerAddr); + + /** + * @dev this event emits when the authorization on slot `slotManagerAddr` of token `tokenId` is revoked. + * So, the corresponding DApp can handle this to stop on-going incentives or rights + */ + event SlotAuthorizationRevoked(uint256 indexed tokenId, address indexed slotManagerAddr); + + /** + * @dev this event emits when the uri on slot `slotManagerAddr` of token `tokenId` has been updated to `uri`. + */ + event SlotUriUpdated(uint256 indexed tokenId, address indexed slotManagerAddr, string uri); + + /** + * @dev + * Authorize a hyperlink slot on `tokenId` to address `slotManagerAddr`. + * Indeed slot is an entry in a map whose key is address `slotManagerAddr`. + * Only the address `slotManagerAddr` can manage the specific slot. + * This method will emit SlotAuthorizationCreated event + */ + function authorizeSlotTo(uint256 tokenId, address slotManagerAddr) external; + + /** + * @dev + * Revoke the authorization of the slot indicated by `slotManagerAddr` on token `tokenId` + * This method will emit SlotAuthorizationRevoked event + */ + function revokeAuthorization(uint256 tokenId, address slotManagerAddr) external; + + /** + * @dev + * Revoke all authorizations of slot on token `tokenId` + * This method will emit SlotAuthorizationRevoked event for each slot + */ + function revokeAllAuthorizations(uint256 tokenId) external; + + /** + * @dev + * Set uri for a slot on a token, which is indicated by `tokenId` and `slotManagerAddr` + * Only the address with authorization through {authorizeSlotTo} can manipulate this slot. + * This method will emit SlotUriUpdated event + */ + function setSlotUri( + uint256 tokenId, + string calldata newUri + ) external; + + /** + * @dev Throws if `tokenId` is not a valid NFT. URIs are defined in RFC 3986. + * The URI MUST point to a JSON file that conforms to the "EIP5489 Metadata JSON schema". + * + * returns the latest uri of an slot on a token, which is indicated by `tokenId`, `slotManagerAddr` + */ + function getSlotUri(uint256 tokenId, address slotManagerAddr) + external + view + returns (string memory); +} +``` + +The `authorizeSlotTo(uint256 tokenId, address slotManagerAddr)` function MAY be implemented as public or external. + +The `revokeAuthorization(uint256 tokenId, address slotManagerAddr)` function MAY be implemented as public or external. + +The `revokeAllAuthorizations(uint256 tokenId)` function MAY be implemented as public or external. + +The `setSlotUri(uint256 tokenId, string calldata newUri)` function MAY be implemented as public or external. + +The `getSlotUri(uint256 tokenId, address slotManagerAddr)` function MAY be implemented as pure or view. + +The `SlotAuthorizationCreated` event MUST be emitted when a slot is authorized to an address. + +The `SlotAuthorizationRevoked` event MUST be emitted when a slot authorization is revoked. + +The `SlotUriUpdated` event MUSt be emitted when a slot's URI is changed. + +The `supportInterface` method MUST return true when called with `0x8f65987b`. + +### Authentication + +The `authorizeSlotTo`, `revokeAuthorization`, and `revokeAllAuthorizations` functions are authenticated if and only if the message sender is the owner of the token. + +### Metadata JSON schema + +```json +{ + "title": "AD Metadata", + "type": "object", + "properties": { + "icon": { + "type": "string", + "description": "A URI pointing to a resource with mime type image/* representing the slot's occupier. Consider making any images at a width between 48 and 1080 pixels and aspect ration between 1.91:1 and 4:5 inclusive. Suggest to show this as an thumbnail of the target resource" + }, + "description": { + "type": "string", + "description": "A paragraph which briefly introduce what is the target resource" + }, + "target": { + "type": "string", + "description": "A URI pointing to target resource, sugguest to follow 30X status code to support more redirections, the mime type and content rely on user's setting" + } + } +} +``` + +## Rationale + +### Extends NFT with hyperlinks + +URIs are used to represent the value of slots to ensure enough flexibility to deal with different use cases. + +### Authorize slot to address + +We use addresses to represent the key of slots to ensure enough flexibility to deal with all use cases. + +## Backwards Compatibility + +As mentioned in the specifications section, this standard can be fully EIP-721 compatible by adding an extension function set. + +In addition, new functions introduced in this standard have many similarities with the existing functions in EIP-721. This allows developers to easily adopt the standard quickly. + +## Reference Implementation + +You can find an implementation of this standard in [`ERC5489.sol`](../assets/eip-5489/contracts/ERC5489.sol). + +## Security Considerations + +No security considerations were found. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5496.md b/EIPS/eip-5496.md new file mode 100644 index 0000000..ea43674 --- /dev/null +++ b/EIPS/eip-5496.md @@ -0,0 +1,254 @@ +--- +eip: 5496 +title: Multi-privilege Management NFT Extension +description: Create shareable multi-privilege NFTs for EIP-721 +author: Jeremy Z (@wnft) +discussions-to: https://ethereum-magicians.org/t/eip-5496-multi-privilege-management-extension-for-erc-721/10427 +status: Last Call +last-call-deadline: 2022-11-29 +type: Standards Track +category: ERC +created: 2022-07-30 +requires: 721 +--- + + +## Abstract + +This EIP defines an interface extending [EIP-721](./eip-721.md) to provide shareable multi-privileges for NFTs. Privileges may be on-chain (voting rights, permission to claim an airdrop) or off-chain (a coupon for an online store, a discount at a local restaurant, access to VIP lounges in airports). Each NFT may contain many privileges, and the holder of a privilege can verifiably transfer that privilege to others. Privileges may be non-shareable or shareable. Shareable privileges can be cloned, with the provider able to adjust the details according to the spreading path. Expiration periods can also be set for each privilege. + +## Motivation + +This standard aims to efficiently manage privileges attached to NFTs in real-time. Many NFTs have functions other than just being used as profile pictures or art collections, they may have real utilities in different scenarios. For example, a fashion store may give a discount for its own NFT holders; a DAO member NFT holder can vote for the proposal of how to use their treasury; a dApp may create an airdrop event to attract a certain group of people like some blue chip NFT holders to claim; the grocery store can issue its membership card on chain (as an NFT) and give certain privileges when the members shop at grocery stores, etc. There are cases when people who own NFTs do not necessarily want to use their privileges. By providing additional data recording different privileges a NFT collection has and interfaces to manage them, users can transfer or sell privileges without losing their ownership of the NFT. + +[EIP-721](./eip-721.md) only records the ownership and its transfer, the privileges of an NFT are not recorded on-chain. This extension would allow merchants/projects to give out a certain privilege to a specified group of people, and owners of the privileges can manage each one of the privileges independently. This facilitates a great possibility for NFTs to have real usefulness. + +For example, an airline company issues a series of [EIP-721](./eip-721.md)/[EIP-1155](./eip-1155.md) tokens to Crypto Punk holders to give them privileges, in order to attract them to join their club. However, since these tokens are not bound to the original NFT, if the original NFT is transferred, these privileges remain in the hands of the original holders, and the new holders cannot enjoy the privileges automatically. +So, we propose a set of interfaces that can bind the privileges to the underlying NFT, while allowing users to manage the privileges independently. + +## Specification + +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. + +Every contract complying with this standard MUST implement the `IERC5496` interface. The **shareable multi-privilege extension** is OPTIONAL for EIP-721 contracts. + +```solidity +/// @title multi-privilege extension for EIP-721 +/// Note: the EIP-165 identifier for this interface is 0x076e1bbb +interface IERC5496{ + /// @notice Emitted when `owner` changes the `privilege holder` of a NFT. + event PrivilegeAssigned(uint256 tokenId, uint256 privilegeId, address user, uint256 expires); + /// @notice Emitted when `contract owner` changes the `total privilege` of the collection + event PrivilegeTotalChanged(uint256 newTotal, uint256 oldTotal); + + /// @notice set the privilege holder of a NFT. + /// @dev expires should be less than 30 days + /// Throws if `msg.sender` is not approved or owner of the tokenId. + /// @param tokenId The NFT to set privilege for + /// @param privilegeId The privilege to set + /// @param user The privilege holder to set + /// @param expires For how long the privilege holder can have + function setPrivilege(uint256 tokenId, uint256 privilegeId, address user, uint256 expires) external; + + /// @notice Return the expiry timestamp of a privilege + /// @param tokenId The identifier of the queried NFT + /// @param privilegeId The identifier of the queried privilege + /// @return Whether a user has a certain privilege + function privilegeExpires(uint256 tokenId, uint256 privilegeId) external view returns(uint256); + + /// @notice Check if a user has a certain privilege + /// @param tokenId The identifier of the queried NFT + /// @param privilegeId The identifier of the queried privilege + /// @param user The address of the queried user + /// @return Whether a user has a certain privilege + function hasPrivilege(uint256 tokenId, uint256 privilegeId, address user) external view returns(bool); +} +``` + +Every contract implementing this standard SHOULD set a maximum privilege number before setting any privilege, the `privilegeId` MUST NOT be greater than the maximum privilege number. + +The `PrivilegeAssigned` event MUST be emitted when `setPrivilege` is called. + +The `PrivilegeTotalChanged` event MUST be emitted when the `total privilege` of the collection is changed. + +The `supportsInterface` method MUST return `true` when called with `0x076e1bbb`. + +```solidity +/// @title Cloneable extension - Optional for EIP-721 +interface IERC721Cloneable { + /// @notice Emitted when set the `privilege ` of a NFT cloneable. + event PrivilegeCloned(uint tokenId, uint privId, address from, address to); + + /// @notice set a certain privilege cloneable + /// @param tokenId The identifier of the queried NFT + /// @param privilegeId The identifier of the queried privilege + /// @param referrer The address of the referrer + /// @return Whether the operation is successful or not + function clonePrivilege(uint tokenId, uint privId, address referrer) external returns (bool); +} +``` + +The `PrivilegeCloned` event MUST be emitted when `clonePrivilege` is called. + +For Compliant contract, it is RECOMMENDED to use [EIP-1271](./eip-1271.md) to validate the signatures. + +## Rationale + +### Shareable Privileges + +The number of privilege holders is limited by the number of NFTs if privileges are non-shareable. A shareable privilege means the original privilege holder can copy the privilege and give it to others, not transferring his/her own privilege to them. This mechanism greatly enhances the spread of privileges as well as the adoption of NFTs. + +### Expire Date Type + +The expiry timestamp of a privilege is a timestamp and stored in `uint256` typed variables. + +### Beneficiary of Referrer + +For example, a local pizza shop offers a 30% off Coupon and the owner of the shop encourages their consumers to share the coupon with friends, then the friends can get the coupon. Let's say Tom gets 30% off Coupon from the shop and he shares the coupon with Alice. Alice gets the coupon too and Alice's referrer is Tom. For some certain cases, Tom may get more rewards from the shop. This will help the merchants in spreading the promotion among consumers. + +### Proposal: NFT Transfer + +If the owner of the NFT transfers ownership to another user, there is no impact on "privileges". But errors may occur if the owner tries to withdraw the original [EIP-721](./eip-721.md) token from the wrapped NFT through `unwrap()` if any available privileges are still ongoing. We protect the rights of holders of the privileges to check the last expiration date of the privilege. + +```solidity +function unwrap(uint256 tokenId, address to) external { + require(getBlockTimestamp() >= privilegeBook[tokenId].lastExpiresAt, "privilege not yet expired"); + + require(ownerOf(tokenId) == msg.sender, "not owner"); + + _burn(tokenId); + + IERC721(nft).transferFrom(address(this), to, tokenId); + + emit Unwrap(nft, tokenId, msg.sender, to); +} +``` + +## Backwards Compatibility + +This EIP is compatible with any kind of NFTs that follow the EIP-721 standard. It only adds more functions and data structures without interfering with the original [EIP-721](./eip-721.md) standard. + +## Test Cases + +Test cases are implemented with the reference implementation. + +### Test Code + +[test.js](../assets/eip-5496/test/test.js) + +Run in terminal: + +```shell +truffle test ./test/test.js +``` + +[testCloneable.js](../assets/eip-5496/test/testCloneable.js) + +Run in terminal: + +```shell +truffle test ./test/testCloneable.js +``` + +## Reference Implementation + +```solidity +// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; +import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +import "./IERC5496.sol"; + +contract ERC5496 is ERC721, IERC5496 { + struct PrivilegeRecord { + address user; + uint256 expiresAt; + } + struct PrivilegeStorage { + uint lastExpiresAt; + // privId => PrivilegeRecord + mapping(uint => PrivilegeRecord) privilegeEntry; + } + + uint public privilegeTotal; + // tokenId => PrivilegeStorage + mapping(uint => PrivilegeStorage) public privilegeBook; + mapping(address => mapping(address => bool)) private privilegeDelegator; + + constructor(string memory name_, string memory symbol_) + ERC721(name_,symbol_) + { + + } + + function setPrivilege( + uint tokenId, + uint privId, + address user, + uint64 expires + ) external virtual { + require((hasPrivilege(tokenId, privId, ownerOf(tokenId)) && _isApprovedOrOwner(msg.sender, tokenId)) || _isDelegatorOrHolder(msg.sender, tokenId, privId), "ERC721: transfer caller is not owner nor approved"); + require(expires < block.timestamp + 30 days, "expire time invalid"); + require(privId < privilegeTotal, "invalid privilege id"); + privilegeBook[tokenId].privilegeEntry[privId].user = user; + if (_isApprovedOrOwner(msg.sender, tokenId)) { + privilegeBook[tokenId].privilegeEntry[privId].expiresAt = expires; + if (privilegeBook[tokenId].lastExpiresAt < expires) { + privilegeBook[tokenId].lastExpiresAt = expires; + } + } + emit PrivilegeAssigned(tokenId, privId, user, uint64(privilegeBook[tokenId].privilegeEntry[privId].expiresAt)); + } + + function hasPrivilege( + uint256 tokenId, + uint256 privId, + address user + ) public virtual view returns(bool) { + if (privilegeBook[tokenId].privilegeEntry[privId].expiresAt >= block.timestamp){ + return privilegeBook[tokenId].privilegeEntry[privId].user == user; + } + return ownerOf(tokenId) == user; + } + + function privilegeExpires( + uint256 tokenId, + uint256 privId + ) public virtual view returns(uint256){ + return privilegeBook[tokenId].privilegeEntry[privId].expiresAt; + } + + function _setPrivilegeTotal( + uint total + ) internal { + emit PrivilegeTotalChanged(total, privilegeTotal); + privilegeTotal = total; + } + + function getPrivilegeInfo(uint tokenId, uint privId) external view returns(address user, uint256 expiresAt) { + return (privilegeBook[tokenId].privilegeEntry[privId].user, privilegeBook[tokenId].privilegeEntry[privId].expiresAt); + } + + function setDelegator(address delegator, bool enabled) external { + privilegeDelegator[msg.sender][delegator] = enabled; + } + + function _isDelegatorOrHolder(address delegator, uint256 tokenId, uint privId) internal virtual view returns (bool) { + address holder = privilegeBook[tokenId].privilegeEntry[privId].user; + return (delegator == holder || isApprovedForAll(holder, delegator) || privilegeDelegator[holder][delegator]); + } + + function supportsInterface(bytes4 interfaceId) public override virtual view returns (bool) { + return interfaceId == type(IERC5496).interfaceId || super.supportsInterface(interfaceId); + } +} +``` + +## Security Considerations + +Implementations must thoroughly consider who has the permission to set or clone privileges. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-55.md b/EIPS/eip-55.md new file mode 100644 index 0000000..325a70f --- /dev/null +++ b/EIPS/eip-55.md @@ -0,0 +1,119 @@ +--- +eip: 55 +title: Mixed-case checksum address encoding +author: Vitalik Buterin , Alex Van de Sande +discussions-to: https://github.com/ethereum/eips/issues/55 +type: Standards Track +category: ERC +status: Final +created: 2016-01-14 +--- + +# Specification + +Code: + +``` python +import eth_utils + + +def checksum_encode(addr): # Takes a 20-byte binary address as input + hex_addr = addr.hex() + checksummed_buffer = "" + + # Treat the hex address as ascii/utf-8 for keccak256 hashing + hashed_address = eth_utils.keccak(text=hex_addr).hex() + + # Iterate over each character in the hex address + for nibble_index, character in enumerate(hex_addr): + + if character in "0123456789": + # We can't upper-case the decimal digits + checksummed_buffer += character + elif character in "abcdef": + # Check if the corresponding hex digit (nibble) in the hash is 8 or higher + hashed_address_nibble = int(hashed_address[nibble_index], 16) + if hashed_address_nibble > 7: + checksummed_buffer += character.upper() + else: + checksummed_buffer += character + else: + raise eth_utils.ValidationError( + f"Unrecognized hex character {character!r} at position {nibble_index}" + ) + + return "0x" + checksummed_buffer + + +def test(addr_str): + addr_bytes = eth_utils.to_bytes(hexstr=addr_str) + checksum_encoded = checksum_encode(addr_bytes) + assert checksum_encoded == addr_str, f"{checksum_encoded} != expected {addr_str}" + + +test("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed") +test("0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359") +test("0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB") +test("0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb") + +``` + +In English, convert the address to hex, but if the `i`th digit is a letter (ie. it's one of `abcdef`) print it in uppercase if the `4*i`th bit of the hash of the lowercase hexadecimal address is 1 otherwise print it in lowercase. + +# Rationale + +Benefits: +- Backwards compatible with many hex parsers that accept mixed case, allowing it to be easily introduced over time +- Keeps the length at 40 characters +- On average there will be 15 check bits per address, and the net probability that a randomly generated address if mistyped will accidentally pass a check is 0.0247%. This is a ~50x improvement over ICAP, but not as good as a 4-byte check code. + +# Implementation + +In javascript: + +```js +const createKeccakHash = require('keccak') + +function toChecksumAddress (address) { + address = address.toLowerCase().replace('0x', '') + var hash = createKeccakHash('keccak256').update(address).digest('hex') + var ret = '0x' + + for (var i = 0; i < address.length; i++) { + if (parseInt(hash[i], 16) >= 8) { + ret += address[i].toUpperCase() + } else { + ret += address[i] + } + } + + return ret +} +``` + +``` +> toChecksumAddress('0xfb6916095ca1df60bb79ce92ce3ea74c37c5d359') +'0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359' +``` + +Note that the input to the Keccak256 hash is the lowercase hexadecimal string (i.e. the hex address encoded as ASCII): + +``` + var hash = createKeccakHash('keccak256').update(Buffer.from(address.toLowerCase(), 'ascii')).digest() +``` + +# Test Cases + +``` +# All caps +0x52908400098527886E0F7030069857D2E4169EE7 +0x8617E340B3D01FA5F11F306F4090FD50E238070D +# All Lower +0xde709f2102306220921060314715629080e2fb77 +0x27b1fdb04752bbc536007a920d24acb045561c26 +# Normal +0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed +0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359 +0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB +0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb +``` diff --git a/EIPS/eip-5501.md b/EIPS/eip-5501.md new file mode 100644 index 0000000..3d34905 --- /dev/null +++ b/EIPS/eip-5501.md @@ -0,0 +1,251 @@ +--- +eip: 5501 +title: Rental & Delegation NFT - EIP-721 Extension +description: Adds a conditional time-limited user role to EIP-721. This role can be delegated or borrowed. +author: Jan Smrža (@smrza), David Rábel (@rabeles11), Tomáš Janča , Jan Bureš (@JohnyX89), DOBBYLABS (@DOBBYLABS) +discussions-to: https://ethereum-magicians.org/t/eip-tbd-rental-delegation-nft-erc-721-extension/10441 +status: Draft +type: Standards Track +category: ERC +created: 2022-08-18 +requires: 165, 721, 4400, 4907 +--- + +## Abstract +The following standard proposes an additional `user` role for [EIP-721](./eip-721.md). This role grants the permission to use the NFT with no ability to transfer or set users. It has an expiry and a flag if the token is borrowed or not. `Owner` can delegate the NFT for usage to hot wallets or lend the NFT. If the token is borrowed, not even the owner can change the user until the status expires or both parties agree to terminate. This way, it is possible to keep both roles active at the same time. + +## Motivation +Collectibles, gaming assets, metaverse, event tickets, music, video, domains, real item representation are several among many NFT use cases. With [EIP-721](./eip-721.md) only the owner can reap the benefits. However, with most of the utilities it would be beneficial to distinguish between the token owner and its user. For instance music or movies could be rented. Metaverse lands could be delegated for usage. + +The two reasons why to set the user are: + +* **delegation** - Assign user to your hot wallet to interact with applications securely. In this case, the owner can change the user at any time. +* **renting** - This use case comes with additional requirements. It is needed to terminate the loan once the established lending period is over. This is provided by `expires` of the user. It is also necessary to protect the borrower against resetting their status by the owner. Thus, `isBorrowed` check must be implemented to disable the option to set the user before the contract expires. + +The most common use cases for having an additional user role are: + +* **delegation** - For security reasons. +* **gaming** - Would you like to try a game (or particular gaming assets) but are you unsure whether or not you will like it? Rent assets first. +* **guilds** - Keep the owner of the NFTs as the multisig wallet and set the user to a hot wallet with shared private keys among your guild members. +* **events** - Distinguish between `ownerOf` and `userOf`. Each role has a different access. +* **social** - Differentiate between roles for different rooms. For example owner has read + write access while userOf has read access only. + +This proposal is a follow up on [EIP-4400](./eip-4400.md) and [EIP-4907](./eip-4907.md) and introduces additional upgrades for lending and borrowing which include: + +* **NFT stays in owner's wallet during rental period** +* **Listing and sale of NFT without termination of the rent** +* **Claiming owner benefits during rental period** + +Building the standard with additional isBorrowed check now allows to create rental marketplaces which can set the user of NFT without the necessary staking mechanism. With current standards if a token is not staked during the rental period, the owner can simply terminate the loan by setting the user repeatedly. This is taken care of by disabling the function if the token is borrowed which in turn is providing the owner additional benefits. They can keep the token tied to their wallet, meaning they can still receive airdrops, claim free mints based on token ownership or otherwise use the NFT provided by third-party services for owners. They can also keep the NFT listed for sale. Receiving airdrops or free mints was previously possible but the owner was completely reliant on the implementation of rental marketplaces and their discretion. + +Decentralized applications can now differentiate between ownerOf and userOf while both statuses can coexist. + +## Specification +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. + +**Every compliant contract MUST implement the `IERC5501` interface. This extension is OPTIONAL for [EIP-721](./eip-721.md) contracts.** + +```solidity +/** + * @title IERC5501: Rental & Delegation NFT - EIP-721 Extension + * @notice the EIP-165 identifier for this interface is 0xf808ec37. + */ +interface IERC5501 /* is IERC721 */ { + /** + * @dev Emitted when the user of an NFT is modified. + */ + event UpdateUser(uint256 indexed _tokenId, address indexed _user, uint64 _expires, bool _isBorrowed); + + /** + * @notice Set the user info of an NFT. + * @dev User address cannot be zero address. + * Only approved operator or NFT owner can set the user. + * If NFT is borrowed, the user info cannot be changed until user status expires. + * @param _tokenId uint256 ID of the token to set user info for + * @param _user address of the new user + * @param _expires Unix timestamp when user info expires + * @param _isBorrowed flag whether or not the NFT is borrowed + */ + function setUser(uint256 _tokenId, address _user, uint64 _expires, bool _isBorrowed) external; + + /** + * @notice Get the user address of an NFT. + * @dev Reverts if user is not set. + * @param _tokenId uint256 ID of the token to get the user address for + * @return address user address for this NFT + */ + function userOf(uint256 _tokenId) external view returns (address); + + /** + * @notice Get the user expires of an NFT. + * @param _tokenId uint256 ID of the token to get the user expires for + * @return uint64 user expires for this NFT + */ + function userExpires(uint256 _tokenId) external view returns (uint64); + + /** + * @notice Get the user isBorrowed of an NFT. + * @param _tokenId uint256 ID of the token to get the user isBorrowed for + * @return bool user isBorrowed for this NFT + */ + function userIsBorrowed(uint256 _tokenId) external view returns (bool); +} +``` + +Every contract implementing the `IERC5501` interface is free to define the permissions of a `user`. However, user MUST NOT be considered an `owner`. They MUST NOT be able to execute transfers and approvals. Furthermore, `setUser` MUST be blocked from executing if `userIsBorrowed` returns `true` and `userExpires` is larger than or equal to `block.timestamp`. + +The `UpdateUser` event MUST be emitted when a `user` is changed. +The `setUser(uint256 _tokenId, address _user, uint64 _expires, bool _isBorrowed)` function SHOULD `revert` unless the `msg.sender` is the `owner` or an approved operator. It MUST revert if a token is borrowed and status has not expired yet. It MAY be `public` or `external`. +The `userOf(uint256 _tokenId)` function SHOULD revert if `user` is not set or expired. +The `userExpires(uint256 _tokenId)` function returns a timestamp when user status expires. +The `userIsBorrowed(uint256 _tokenId)` function returns whether NFT is borrowed or not. +The `supportsInterface` function MUST return `true` when called with `0xf808ec37`. +On every `transfer`, the `user` MUST be reset if the token is not borrowed. If the token is borrowed the `user` MUST stay the same. + +**The Balance extension is OPTIONAL. This gives the option to query the number of tokens a `user` has.** + +```solidity +/** + * @title IERC5501Balance + * Extension for ERC5501 which adds userBalanceOf to query how many tokens address is userOf. + * @notice the EIP-165 identifier for this interface is 0x0cb22289. + */ +interface IERC5501Balance /* is IERC5501 */{ + /** + * @notice Count of all NFTs assigned to a user. + * @dev Reverts if user is zero address. + * @param _user an address for which to query the balance + * @return uint256 the number of NFTs the user has + */ + function userBalanceOf(address _user) external view returns (uint256); +} +``` + +The `userBalanceOf(address _user)` function SHOULD `revert` for zero address. + +**The Enumerable extension is OPTIONAL. This allows to iterate over user balance.** + +```solidity +/** + * @title IERC5501Enumerable + * This extension for ERC5501 adds the option to iterate over user tokens. + * @notice the EIP-165 identifier for this interface is 0x1d350ef8. + */ +interface IERC5501Enumerable /* is IERC5501Balance, IERC5501 */ { + /** + * @notice Enumerate NFTs assigned to a user. + * @dev Reverts if user is zero address or _index >= userBalanceOf(_owner). + * @param _user an address to iterate over its tokens + * @return uint256 the token ID for given index assigned to _user + */ + function tokenOfUserByIndex(address _user, uint256 _index) external view returns (uint256); +} +``` + +The `tokenOfUserByIndex(address _user, uint256 _index)` function SHOULD `revert` for zero address and `throw` if the index is larger than or equal to `user` balance. + +**The Terminable extension is OPTIONAL. This allows terminating the rent early if both parties agree.** + +```solidity +/** + * @title IERC5501Terminable + * This extension for ERC5501 adds the option to terminate borrowing if both parties agree. + * @notice the EIP-165 identifier for this interface is 0x6a26417e. + */ +interface IERC5501Terminable /* is IERC5501 */ { + /** + * @dev Emitted when one party from borrowing contract approves termination of agreement. + * @param _isLender true for lender, false for borrower + */ + event AgreeToTerminateBorrow(uint256 indexed _tokenId, address indexed _party, bool _isLender); + + /** + * @dev Emitted when agreements to terminate borrow are reset. + */ + event ResetTerminationAgreements(uint256 indexed _tokenId); + + /** + * @dev Emitted when borrow of token ID is terminated. + */ + event TerminateBorrow(uint256 indexed _tokenId, address indexed _lender, address indexed _borrower, address _caller); + + /** + * @notice Agree to terminate a borrowing. + * @dev Lender must be ownerOf token ID. Borrower must be userOf token ID. + * If lender and borrower are the same, set termination agreement for both at once. + * @param _tokenId uint256 ID of the token to set termination info for + */ + function setBorrowTermination(uint256 _tokenId) external; + + /** + * @notice Get if it is possible to terminate a borrow agreement. + * @param _tokenId uint256 ID of the token to get termination info for + * @return bool, bool first indicates lender agrees, second indicates borrower agrees + */ + function getBorrowTermination(uint256 _tokenId) external view returns (bool, bool); + + /** + * @notice Terminate a borrow if both parties agreed. + * @dev Both parties must have agreed, otherwise revert. + * @param _tokenId uint256 ID of the token to terminate borrow of + */ + function terminateBorrow(uint256 _tokenId) external; +} +``` + +The `AgreeToTerminateBorrow` event MUST be emitted when either the lender or borrower agrees to terminate the rent. +The `ResetTerminationAgreements` event MUST be emitted when a token is borrowed and transferred or `setUser` and `terminateBorrow` functions are called. +The `TerminateBorrow` event MUST be emitted when the rent is terminated. +The `setBorrowTermination(uint256 _tokenId)`. It MUST set an agreement from either party whichever calls the function. If the lender and borrower are the same address, it MUST assign an agreement for both parties at once. +The `getBorrowTermination(uint256 _tokenId)` returns if agreements from both parties are `true` or `false`. +The `terminateBorrow(uint256 _tokenId)` function MAY be called by anyone. It MUST `revert` if both agreements to terminate are not `true`. This function SHOULD change the `isBorrowed` flag from `true` to `false`. +On every `transfer`, the termination agreements from either party MUST be reset if the token is borrowed. + +## Rationale +The main factors influencing this standard are: + +* **[EIP-4400](./eip-4400.md) and [EIP-4907](./eip-4907.md)** +* **Allow lending and borrowing without the necessary stake or overcollateralization while owner retains ownership** +* **Leave the delegation option available** +* **Keep the number of functions in the interfaces to a minimum while achieving desired functionality** +* **Modularize additional extensions to let developers choose what they need for their project** + +### Name +The name for the additional role has been chosen to fit the purpose and to keep compatibility with EIP-4907. + +### Ownership retention +Many collections offer their owners airdrops or free minting of various tokens. This is essentially broken if the owner is lending a token by staking it into a contract (unless the contract is implementing a way to claim at least airdropped tokens). Applications can also provide different access and benefits to owner and user roles in their ecosystem. + +### Balance and Enumerable extensions +These have been chosen as OPTIONAL extensions due to the complexity of implementation based on the fact that balance is less once user status expires and there is no immediate on-chain transaction to evaluate that. In both `userBalanceOf` and `tokenOfUserByIndex` functions there must be a way to determine whether or not user status has expired. + +### Terminable extension +If the owner mistakenly sets a user with borrow status and expires to a large value they would essentially be blocked from setting the user ever again. The problem is addressed by this extension if both parties agree to terminate the user status. + +### Security +Once applications adopt the user role, it is possible to delegate ownership to hot wallet and interact with them with no fear of connecting to malicious websites. + +## Backwards Compatibility +This standard is compatible with current [EIP-721](./eip-721.md) by adding an extension function set. The new functions introduced are similar to existing functions in EIP-721 which guarantees easy adoption by developers and applications. This standard also shares similarities to [EIP-4907](./eip-4907.md) considering user role and its expiry which means applications will be able to determine the user if either of the standards is used. + +## Test Cases +Test cases can be found in the reference implementation: +* [Main contract](../assets/eip-5501/test/ERC5501Test.ts) +* [Balance extension](../assets/eip-5501/test/ERC5501BalanceTest.ts) +* [Enumerable extension](../assets/eip-5501/test/ERC5501EnumerableTest.ts) +* [Terminable extension](../assets/eip-5501/test/ERC5501TerminableTest.ts) +* [Scenario combined of all extensions](../assets/eip-5501/test/ERC5501CombinedTest.ts) + +## Reference Implementation +The reference implementation is available here: +* [Main contract](../assets/eip-5501/contracts/ERC5501.sol) +* [Balance extension](../assets/eip-5501/contracts/ERC5501Balance.sol) +* [Enumerable extension](../assets/eip-5501/contracts/ERC5501Enumerable.sol) +* [Terminable extension](../assets/eip-5501/contracts/ERC5501Terminable.sol) +* [Solution combined of all extensions](../assets/eip-5501/contracts/ERC5501Combined.sol) + +## Security Considerations +Developers implementing this standard and applications must consider all the permissions they give to users and owners. Since owner and user are both active roles at the same time, double-spending problem must be avoided. Balance extension must be implemented in such a way which will not cause any gas problems. Marketplaces should let users know if a token listed for sale is borrowed or not. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5505.md b/EIPS/eip-5505.md new file mode 100644 index 0000000..a4a5a08 --- /dev/null +++ b/EIPS/eip-5505.md @@ -0,0 +1,77 @@ +--- +eip: 5505 +title: EIP-1155 asset backed NFT extension +description: Extends EIP-1155 to support crucial operations for asset-backed NFTs +author: liszechung (@liszechung) +discussions-to: https://ethereum-magicians.org/t/eip-draft-erc1155-asset-backed-nft-extension/10437 +status: Stagnant +type: Standards Track +category: ERC +created: 2022-08-18 +requires: 1155 +--- + +## Abstract +To propose an extension of smart contract interfaces for asset-backed, fractionalized projects using the [EIP-1155](./eip-1155.md) standard such that total acquisition will become possible. This proposal focuses on physical asset, where total acquisition should be able to happen. + +## Motivation +Fractionalized, asset backed NFTs face difficulty when someone wants to acquire the whole asset. For example, if someone wants to bring home a fractionalized asset, he needs to buy all NFT pieces so he will become the 100% owner. However he could not do so as it is publicly visible that someone is trying to perform a total acquisition in an open environment like Ethereum. Sellers will take advantage to set unreasonable high prices which hinders the acquisition. Or in other cases, NFTs are owned by wallets with lost keys, such that the ownership will never be a complete one. We need a way to enable potential total acquisition. + +## Specification +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. + +[EIP-1155](./eip-1155.md) compliant contracts MAY implement this EIP for adding functionalities to support total acquisition. + +```solidity +//set the percentage required for any acquirer to trigger a forced sale +//set also the payment token to settle for the acquisition + +function setForcedSaleRequirement( + uint128 requiredBP, + address erc20Token +) public onlyOwner + +//set the unit price to acquire the remaining NFTs (100% - requiredBP) +//suggest to use a Time Weighted Average Price for a certain period before reaching the requiredBP +//emit ForcedSaleSet + +function setForcedSaleTWAP( + uint256 amount +) public onlyOwner + +//acquirer deposit remainingQTY*TWAP +//emit ForcedSaleFinished +//after this point, the acquirer is the new owner of the whole asset + +function execForcedSale ( + uint256 amount +) public external payable + +//burn ALL NFTs and collect funds +//emit ForcedSaleClaimed + +function claimForcedSale() +public + +event ForcedSaleSet( + bool isSet +) +event ForceSaleClaimed( + uint256 qtyBurned, + uint256 amountClaimed, + address claimer +) +``` + + +## Rationale +Native ETH is supported by via Wrapped Ether [EIP-20](./eip-20.md). +After forcedSale is set, the remaining NFTs metadata should be updated to reflect the NFTs are at most valued at the previously set TWAP price. + +## Security Considerations +The major security risks considered include +- The execution of the forcedSale is only executed by the contract owner, after a governance proposal. If there is any governance attack, the forcedSale TWAP price might be manipulated on a specific timing. The governance structure for using this extension should consider adding a **council** to safeguard the fairness of the forcedSale. +- Payment tokens are deposited into the contract account when forcedSale is executed. These tokens will then await the minority holders to withdraw on burning the NFT. There might be a potential security risk. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5507.md b/EIPS/eip-5507.md new file mode 100644 index 0000000..444b486 --- /dev/null +++ b/EIPS/eip-5507.md @@ -0,0 +1,332 @@ +--- +eip: 5507 +title: Refundable Tokens +description: Adds refund functionality to ERC-20, ERC-721, and ERC-1155 tokens +author: elie222 (@elie222), Gavin John (@Pandapip1) +discussions-to: https://ethereum-magicians.org/t/eip-5507-refundable-nfts/10451 +status: Review +type: Standards Track +category: ERC +created: 2022-08-19 +requires: 20, 165, 721, 1155 +--- + +## Abstract + +This ERC adds refund functionality for initial token offerings to [ERC-20](./eip-20.md), [ERC-721](./eip-721.md), and [ERC-1155](./eip-1155.md). Funds are held in escrow until a predetermined time before they are claimable. Until that predetermined time passes, users can receive a refund for tokens they have purchased. + +## Motivation + +The NFT and token spaces lack accountability. For the health of the ecosystem as a whole, better mechanisms to prevent rugpulls from happening are needed. Offering refunds provides greater protection for buyers and increases legitimacy for creators. + +A standard interface for this particular use case allows for certain benefits: + +- Greater Compliance with EU "Distance Selling Regulations," which require a 14-day refund period for goods (such as tokens) purchased online +- Interoperability with various NFT-related applications, such as portfolio browsers, and marketplaces + - NFT marketplaces could place a badge indicating that the NFT is still refundable on listings, and offer to refund NFTs instead of listing them on the marketplace + - DExes could offer to refund tokens if doing so would give a higher yield +- Better wallet confirmation dialogs + - Wallets can better inform the user of the action that is being taken (tokens being refunded), similar to how transfers often have their own unique dialog + - DAOs can better display the functionality of smart proposals that include refunding tokens + +## Specification + +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. + +All implementations MUST use and follow the directions of [ERC-165](./eip-165.md). + +### ERC-20 Refund Extension + +```solidity +// SPDX-License-Identifier: CC0-1.0 + +pragma solidity ^0.8.17; + +import "ERC20.sol"; +import "ERC165.sol"; + +/// @notice Refundable ERC-20 tokens +/// @dev The ERC-165 identifier of this interface is `0xf0ca2917` +interface ERC20Refund is ERC20, ERC165 { + /// @notice Emitted when a token is refunded + /// @dev Emitted by `refund` + /// @param _from The account whose assets are refunded + /// @param _amount The amount of token (in terms of the indivisible unit) that was refunded + event Refund( + address indexed _from, + uint256 indexed _amount + ); + + /// @notice Emitted when a token is refunded + /// @dev Emitted by `refundFrom` + /// @param _sender The account that sent the refund + /// @param _from The account whose assets are refunded + /// @param _amount The amount of token (in terms of the indivisible unit) that was refunded + event RefundFrom( + address indexed _sender, + address indexed _from, + uint256 indexed _amount + ); + + /// @notice As long as the refund is active, refunds the user + /// @dev Make sure to check that the user has the token, and be aware of potential re-entrancy vectors + /// @param amount The `amount` to refund + function refund(uint256 amount) external; + + /// @notice As long as the refund is active and the sender has sufficient approval, refund the tokens and send the ether to the sender + /// @dev Make sure to check that the user has the token, and be aware of potential re-entrancy vectors + /// The ether goes to msg.sender. + /// @param from The user from which to refund the assets + /// @param amount The `amount` to refund + function refundFrom(address from, uint256 amount) external; + + /// @notice Gets the refund price + /// @return _wei The amount of ether (in wei) that would be refunded for a single token unit (10**decimals indivisible units) + function refundOf() external view returns (uint256 _wei); + + /// @notice Gets the first block for which the refund is not active + /// @return block The first block where the token cannot be refunded + function refundDeadlineOf() external view returns (uint256 block); +} +``` + +### ERC-721 Refund Extension + +```solidity +// SPDX-License-Identifier: CC0-1.0 + +pragma solidity ^0.8.17; + +import "ERC721.sol"; +import "ERC165.sol"; + +/// @notice Refundable ERC-721 tokens +/// @dev The ERC-165 identifier of this interface is `0xe97f3c83` +interface ERC721Refund is ERC721 /* , ERC165 */ { + /// @notice Emitted when a token is refunded + /// @dev Emitted by `refund` + /// @param _from The account whose assets are refunded + /// @param _tokenId The `tokenId` that was refunded + event Refund( + address indexed _from, + uint256 indexed _tokenId + ); + + /// @notice Emitted when a token is refunded + /// @dev Emitted by `refundFrom` + /// @param _sender The account that sent the refund + /// @param _from The account whose assets are refunded + /// @param _tokenId The `tokenId` that was refunded + event RefundFrom( + address indexed _sender, + address indexed _from, + uint256 indexed _tokenId + ); + + /// @notice As long as the refund is active for the given `tokenId`, refunds the user + /// @dev Make sure to check that the user has the token, and be aware of potential re-entrancy vectors + /// @param tokenId The `tokenId` to refund + function refund(uint256 tokenId) external; + + /// @notice As long as the refund is active and the sender has sufficient approval, refund the token and send the ether to the sender + /// @dev Make sure to check that the user has the token, and be aware of potential re-entrancy vectors + /// The ether goes to msg.sender. + /// @param from The user from which to refund the token + /// @param tokenId The `tokenId` to refund + function refundFrom(address from, uint256 tokenId) external; + + /// @notice Gets the refund price of the specific `tokenId` + /// @param tokenId The `tokenId` to query + /// @return _wei The amount of ether (in wei) that would be refunded + function refundOf(uint256 tokenId) external view returns (uint256 _wei); + + /// @notice Gets the first block for which the refund is not active for a given `tokenId` + /// @param tokenId The `tokenId` to query + /// @return block The first block where token cannot be refunded + function refundDeadlineOf(uint256 tokenId) external view returns (uint256 block); +} +``` + +#### Optional ERC-721 Batch Refund Extension + +```solidity +// SPDX-License-Identifier: CC0-1.0; + +import "ERC721Refund.sol"; + +/// @notice Batch Refundable ERC-721 tokens +/// @dev The ERC-165 identifier of this interface is `` +contract ERC721BatchRefund is ERC721Refund { + /// @notice Emitted when one or more tokens are batch refunded + /// @dev Emitted by `refundBatch` + /// @param _from The account whose assets are refunded + /// @param _tokenId The `tokenIds` that were refunded + event RefundBatch( + address indexed _from, + uint256[] _tokenIds // This may or may not be indexed + ); + + /// @notice Emitted when one or more tokens are batch refunded + /// @dev Emitted by `refundFromBatch` + /// @param _sender The account that sent the refund + /// @param _from The account whose assets are refunded + /// @param _tokenId The `tokenId` that was refunded + event RefundFromBatch( + address indexed _sender, + address indexed _from, + uint256 indexed _tokenId + ); + + /// @notice As long as the refund is active for the given `tokenIds`, refunds the user + /// @dev Make sure to check that the user has the tokens, and be aware of potential re-entrancy vectors + /// These must either succeed or fail together; there are no partial refunds. + /// @param tokenIds The `tokenId`s to refund + function refundBatch(uint256[] tokenIds) external; + + /// @notice As long as the refund is active for the given `tokenIds` and the sender has sufficient approval, refund the tokens and send the ether to the sender + /// @dev Make sure to check that the user has the tokens, and be aware of potential re-entrancy vectors + /// The ether goes to msg.sender. + /// These must either succeed or fail together; there are no partial refunds. + /// @param from The user from which to refund the token + /// @param tokenIds The `tokenId`s to refund + function refundFromBatch(address from, uint256[] tokenIds) external; +} +``` + +### ERC-1155 Refund Extension + +```solidity +// SPDX-License-Identifier: CC0-1.0 + +pragma solidity ^0.8.17; + +import "ERC1155.sol"; +import "ERC165.sol"; + +/// @notice Refundable ERC-1155 tokens +/// @dev The ERC-165 identifier of this interface is `0x94029f5c` +interface ERC1155Refund is ERC1155 /* , ERC165 */ { + /// @notice Emitted when a token is refunded + /// @dev Emitted by `refund` + /// @param _from The account that requested a refund + /// @param _tokenId The `tokenId` that was refunded + /// @param _amount The amount of `tokenId` that was refunded + event Refund( + address indexed _from, + uint256 indexed _tokenId, + uint256 _amount + ); + + /// @notice Emitted when a token is refunded + /// @dev Emitted by `refundFrom` + /// @param _sender The account that sent the refund + /// @param _from The account whose assets are refunded + /// @param _tokenId The `tokenId` that was refunded + /// @param _amount The amount of `tokenId` that was refunded + event RefundFrom( + address indexed _sender, + address indexed _from, + uint256 indexed _tokenId + ); + + /// @notice As long as the refund is active for the given `tokenId`, refunds the user + /// @dev Make sure to check that the user has enough tokens, and be aware of potential re-entrancy vectors + /// @param tokenId The `tokenId` to refund + /// @param amount The amount of `tokenId` to refund + function refund(uint256 tokenId, uint256 amount) external; + + /// @notice As long as the refund is active and the sender has sufficient approval, refund the tokens and send the ether to the sender + /// @dev Make sure to check that the user has enough tokens, and be aware of potential re-entrancy vectors + /// The ether goes to msg.sender. + /// @param from The user from which to refund the token + /// @param tokenId The `tokenId` to refund + /// @param amount The amount of `tokenId` to refund + function refundFrom(address from, uint256 tokenId, uint256 amount) external; + + /// @notice Gets the refund price of the specific `tokenId` + /// @param tokenId The `tokenId` to query + /// @return _wei The amount of ether (in wei) that would be refunded for a single token + function refundOf(uint256 tokenId) external view returns (uint256 _wei); + + /// @notice Gets the first block for which the refund is not active for a given `tokenId` + /// @param tokenId The `tokenId` to query + /// @return block The first block where the token cannot be refunded + function refundDeadlineOf(uint256 tokenId) external view returns (uint256 block); +} +``` + +#### Optional ERC-1155 Batch Refund Extension + +```solidity +// SPDX-License-Identifier: CC0-1.0; + +import "ERC1155Refund.sol"; + +/// @notice Batch Refundable ERC-1155 tokens +/// @dev The ERC-165 identifier of this interface is `` +contract ERC1155BatchRefund is ERC1155Refund { + /// @notice Emitted when one or more tokens are batch refunded + /// @dev Emitted by `refundBatch` + /// @param _from The account that requested a refund + /// @param _tokenIds The `tokenIds` that were refunded + /// @param _amounts The amount of each `tokenId` that was refunded + event RefundBatch( + address indexed _from, + uint256[] _tokenIds, // This may or may not be indexed + uint256[] _amounts + ); + + /// @notice Emitted when one or more tokens are batch refunded + /// @dev Emitted by `refundFromBatch` + /// @param _sender The account that sent the refund + /// @param _from The account whose assets are refunded + /// @param _tokenIds The `tokenIds` that was refunded + /// @param _amounts The amount of each `tokenId` that was refunded + event RefundFromBatch( + address indexed _sender, + address indexed _from, + uint256[] _tokenId, // This may or may not be indexed + uint256[] _amounts + ); + + /// @notice As long as the refund is active for the given `tokenIds`, refunds the user + /// @dev Make sure to check that the user has enough tokens, and be aware of potential re-entrancy vectors + /// These must either succeed or fail together; there are no partial refunds. + /// @param tokenIds The `tokenId`s to refund + /// @param amounts The amount of each `tokenId` to refund + function refundBatch(uint256[] tokenIds, uint256[] amounts) external; + + /// @notice As long as the refund is active for the given `tokenIds` and the sender has sufficient approval, refund the tokens and send the ether to the sender + /// @dev Make sure to check that the user has the tokens, and be aware of potential re-entrancy vectors + /// The ether goes to msg.sender. + /// These must either succeed or fail together; there are no partial refunds. + /// @param from The user from which to refund the token + /// @param tokenIds The `tokenId`s to refund + /// @param amounts The amount of each `tokenId` to refund + function refundFromBatch(address from, uint256[] tokenIds, uint256[] amounts external; +} +``` + +## Rationale + +`refundDeadlineOf` uses blocks instead of timestamps, as timestamps are less reliable than block numbers. + +The function names of `refund`, `refundOf`, and `refundDeadlineOf` were chosen to fit the naming style of ERC-20, ERC-721, and ERC-1155. + +[ERC-165](./eip-165.md) is required as introspection by DApps would be made significantly harder if it were not. + +Custom ERC-20 tokens are not supported, as it needlessly increases complexity, and the `refundFrom` function allows for this functionality when combined with a DEx. + +Batch refunds are optional, as account abstraction would make atomic operations like these significantly easier. However, they might still reduce gas costs if properly implemented. + +## Backwards Compatibility + +No backward compatibility issues were found. + +## Security Considerations + +There is a potential re-entrancy risk with the `refund` function. Make sure to perform the ether transfer **after** the tokens are destroyed (i.e. obey the checks, effects, interactions pattern). + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5516.md b/EIPS/eip-5516.md new file mode 100644 index 0000000..bc9c8e7 --- /dev/null +++ b/EIPS/eip-5516.md @@ -0,0 +1,186 @@ +--- +eip: 5516 +title: Soulbound Multi-owner Tokens +description: An interface for non-transferable, Multi-owner NFTs binding to Ethereum accounts +author: Lucas Martín Grasso Ramos (@LucasGrasso), Matias Arazi (@MatiArazi) +discussions-to: https://ethereum-magicians.org/t/EIP-5516-soulbound-multi-token-standard/10485 +status: Review +type: Standards Track +category: ERC +created: 2022-08-19 +requires: 165, 1155 +--- + +## Abstract +This EIP proposes a standard interface for non-fungible double signature Soulbound multi-tokens. Previous account-bound token standards face the issue of users losing their account keys or having them rotated, thereby losing their tokens in the process. This EIP provides a solution to this issue that allows for the recycling of SBTs. + +## Motivation +This EIP was inspired by the main characteristics of the [EIP-1155](./eip-1155.md) token and by articles in which benefits and potential use cases of Soulbound/Accountbound Tokens (SBTs) were presented. +This design also allows for batch token transfers, saving on transaction costs. Trading of multiple tokens can be built on top of this standard and it removes the need to approve individual token contracts separately. It is also easy to describe and mix multiple fungible or non-fungible token types in a single contract. + +### Characteristics +- The NFT will be non-transferable after the initial transfer +- Partially compatible with [EIP-1155](./eip-1155.md) +- Double Signature +- Multi-Token +- Multi-Owner +- Semi-Fungible + +### Applications +- Academic Degrees +- Code audits +- POAPs (Proof of Attendance Protocol NFTs) + +## Specification +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. + +**Smart contracts implementing this EIP MUST implement all of the functions in the `EIP-5516` interface.** + +**Smart contracts implementing this EIP MUST implement the [EIP-165](./eip-165.md) `supportsInterface` function and and MUST return the constant value `true` if `0x8314f22b` is passed through the `interfaceID` argument. They also MUST implement the [EIP-1155](./eip-1155.md) Interface and MUST return the constant value `true` if `0xd9b67a26` is passed through the `interfaceID` argument. Furthermore, they MUST implement the [EIP-1155](./eip-1155.md) Metadata interface, and MUST return the constant value `true` if `0x0e89341c` is passed through the `interfaceID` argument.** + +_See [EIP-1155](./eip-1155.md#specification)_ + +```solidity +// SPDX-License-Identifier: CC0-1.0 + +pragma solidity ^0.8.4; + +/** + @title Soulbound, Multi-Token standard. + @notice Interface of the EIP-5516 + Note: The ERC-165 identifier for this interface is 0x8314f22b. + */ + +interface IERC5516 { + /** + * @dev Emitted when `account` claims or rejects pending tokens under `ids[]`. + */ + event TokenClaimed( + address indexed operator, + address indexed account, + bool[] actions, + uint256[] ids + ); + + /** + * @dev Emitted when `from` transfers token under `id` to every address at `to[]`. + */ + event TransferMulti( + address indexed operator, + address indexed from, + address[] to, + uint256 amount, + uint256 id + ); + + /** + * @dev Get tokens owned by a given address. + */ + function tokensFrom(address from) external view returns (uint256[] memory); + + /** + * @dev Get tokens awaiting to be claimed by a given address. + */ + function pendingFrom(address from) external view returns (uint256[] memory); + + /** + * @dev Claims or Reject pending `id`. + * + * Requirements: + * - `account` must have a pending token under `id` at the moment of call. + * - `account` must not own a token under `id` at the moment of call. + * + * Emits a {TokenClaimed} event. + * + */ + function claimOrReject( + address account, + uint256 id, + bool action + ) external; + + /** + * @dev Claims or Reject pending tokens under `ids[]`. + * + * Requirements for each `id` `action` pair: + * - `account` must have a pending token under `id` at the moment of call. + * - `account` must not own a token under `id` at the moment of call. + * + * Emits a {TokenClaimed} event. + * + */ + function claimOrRejectBatch( + address account, + uint256[] memory ids, + bool[] memory actions + ) external; + + /** + * @dev Transfers `id` token from `from` to every address at `to[]`. + * + * Requirements: + * + * - `from` MUST be the creator(minter) of `id`. + * - All addresses in `to[]` MUST be non-zero. + * - All addresses in `to[]` MUST have the token `id` under `_pendings`. + * - All addresses in `to[]` MUST not own a token type under `id`. + * + * Emits a {TransfersMulti} event. + * + */ + function batchTransfer( + address from, + address[] memory to, + uint256 id, + uint256 amount, + bytes memory data + ) external; + +} + +``` + +## Rationale + +### SBT as an extension of EIP-1155 +We believe that Soulbound Tokens serve as a specialized subset of existing [EIP-1155](./eip-1155.md) tokens. The advantage of such a design is the seamless compatibility of SBTs with existing NFT services. Service providers can treat SBTs like NFTs and do not need to make drastic changes to their existing codebase. + +Making the standard mostly compatible with [EIP-1155](./eip-1155.md) also allows for SBTs to bind to multiple addresses and to Smart Contracts. + +### Double-Signature +The Double-Signature functionality was implemented to prevent the receipt of unwanted tokens. It symbolizes a handshake between the token receiver and sender, implying that **both** parties agree on the token transfer. + +### Metadata. +The [EIP-1155](./eip-1155.md#metadata) Metadata Interface was implemented for further compatibility with [EIP-1155](./eip-1155.md). + +### Guaranteed log trace +> As the Ethereum ecosystem continues to grow, many DApps are relying on traditional databases and explorer API services to retrieve and categorize data. The EIP-1155 standard guarantees that event logs emitted by the smart contract will provide enough data to create an accurate record of all current token balances. A database or explorer may listen to events and be able to provide indexed and categorized searches of every EIP-1155 token in the contract. + +_Quoted from [EIP-1155](./eip-1155.md#guaranteed-log-trace)_ + +This EIP extends this concept to the Double Signature functionality: The `{TokenClaimed}` event logs all the necessary information of a `ClaimOrReject(...)` or `ClaimOrRejectBatch(...)` function call, storing relevant information about the actions performed by the user. This also applies to the `batchTransfer(...)` function: It emits the `{TransferMulti}` event and logs necessary data. + +### Exception handling +Given the non-transferability property of SBTs, if a user's keys to an account get compromised or rotated, such user may lose the ability to associate themselves with the token. + +**Given the multi-owner characteristic of [EIP-1155](./eip-1155.md) compliant interfaces and contracts, SBTs will be able to bind to multiple accounts, providing a potential solution to the issue.** + +Multi-owner SBTs can also be issued to a contract account that implements a multi-signature functionality (As recommended in [EIP-4973](./eip-4973.md#exception-handling)); this can be achieved via the [EIP-1155](./eip-1155.md#erc-1155-token-receiver) Token Receiver interface. + +### Multi-token +The multi-token functionality permits the implementation of multiple token types in the same contract. Furthermore, all emitted tokens are stored in the same contract, preventing redundant bytecode from being deployed to the blockchain. It also facilitates transfer to token issuers, since all issued tokens are stored and can be accessed under the same contract address. + +### The `batchTransfer` function +This EIP supports transfers to multiple recipients. This eases token transfer to a large number of addresses, making it more gas-efficient and user-friendly. + +## Backwards Compatibility +This proposal is only partially compatible with EIP-1155, because it makes tokens non-transferable after the first transfer. + +## Reference Implementation +You can find an implementation of this standard in [../assets/EIP-5516](../assets/eip-5516/ERC5516.sol). + +## Security Considerations +Needs discussion. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5521.md b/EIPS/eip-5521.md new file mode 100644 index 0000000..38c7740 --- /dev/null +++ b/EIPS/eip-5521.md @@ -0,0 +1,305 @@ +--- +eip: 5521 +title: Referable NFT +description: An ERC-721 extension to construct reference relationships among NFTs +author: Saber Yu (@OniReimu), Qin Wang , Shange Fu , Shiping Chen , Sherry Xu , Jiangshan Yu +discussions-to: https://ethereum-magicians.org/t/eip-x-erc-721-referable-nft/10310 +status: Draft +type: Standards Track +category: ERC +created: 2022-08-10 +requires: 165, 721 +--- + +## Abstract + +This standard is an extension of [ERC-721](./eip-721.md). It proposes two referrable indicators, referring and referred, and a time-based indicator `createdTimestamp`. The relationship between each NFT forms a Directed acyclic graph (DAG). The standard allows users to query, track and analyze their relationships. + +## Motivation + +Many scenarios require inheritance, reference, and extension of NFTs. For instance, an artist may develop his NFT work based on a previous NFT, or a DJ may remix his record by referring to two pop songs, etc. Proposing a referable solution for existing NFTs and enabling efficient queries on cross-references make much sense. + +By adding the `referring` indicator, users can mint new NFTs (e.g., C, D, E) by referring to existing NFTs (e.g., A, B), while `referred` enables the referred NFTs (A, B) to be aware that who has quoted it (e.g., A ← D; C ← E; B ← E, and A ← E). The `createdTimestamp` is an indicator used to show the creation time of NFTs (A, B, C, D, E). + +## Specification + +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. + +`Relationship`: a structure that contains `referring`, `referred`, `createdTimestamp`, and other customized attributes such as `mapping (uint256 => address) privityOfAgreement` recording the ownerships of referred NFTs at the time the rNFTs were being created. +`referring`: an out-degree indicator, used to show the users this NFT refers to; +`referred`: an in-degree indicator, used to show the users who have refereed this NFT; +`createdTimestamp`: a time-based indicator, used to compare the timestamp of mint. + +`safeMint`: mint a new rNFT; +`setNode`: set the referring list of an rNFT and update the referred list of each one in the referring list; +`setNodeReferring`: set the referring list of an rNFT; +`setNodeReferred`: set the referred list of the given rNFTs; +`setNodeReferredExternal`: set the referred list of the given rNFTs sourced from other contracts; +`referringOf`: Get the referring list of an rNFT; +`referredOf`: Get the referred list of an rNFT. + +## Rationale + +This standard is intended to establish the referable DAG for queries on cross-relationship and accordingly provide the simplest functions. It provides advantages as follows. + +*Clear ownership inheritance*: This standard extends the static NFT into a virtually extensible NFT network. Artists do not have to create work isolated from others. The ownership inheritance avoids reinventing the same wheel. + +*Incentive Compatibility*: This standard clarifies the referable relationship across different NFTs, helping to integrate multiple up-layer incentive models for both original NFT owners and new creators. + +*Easy Integration*: This standard makes it easier for the existing token standards or third-party protocols. For instance, the rNFT can be collaborating with the Top-down composible NFT (cf. [ERC-998](./eip-998.md) to build a finer-grained reference relationship, where the `Relationship` structure and the interface `IERC_rNFT` can be seamlessly stored and updated when invoking the `mint` function). Another example is that the rNFT can be applied to rentable scenarios (cf. [ERC-5006](./eip-5006.md) to build a hierarchical rental market, where multiple users can rent the same NFT during the same time or one user can rent multiple NFTs during the same duration). + +*Scalable Interoperability* From March 26th 2023, this standard has been stepping forward by enabling cross-contract references, giving a scalable adoption for the broader public with stronger interoperability. + +## Backwards Compatibility + +This standard can be fully [ERC-721](./eip-721.md) compatible by adding an extension function set. + +## Test Cases + +Truffle and Openzeppelin are required to run the following in a test network. + +```node + +truffle develop +rNFT = await ERC_rNFT.new("ERC_5521","ERC_5521") +rNFT.safeMint(1,[],[]) +rNFT.referredOf(1) +rNFT.referringOf(1) + +rNFT.safeMint(2,[rNFT.address],[1]) +rNFT.referredOf(2) +rNFT.referringOf(2) + +rNFT.safeMint(3,[rNFT.address],[1,2]) +rNFT.referredOf(2) +rNFT.referredOf(3) +rNFT.referringOf(3) + +``` + +## Reference Implementation + +```solidity + +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +interface IERC_5521 { + + /// Logged when a node in the rNFT gets referred and changed + /// @notice Emitted when the `node` (i.e., an rNFT) is changed + event UpdateNode(uint256 indexed tokenId, + address indexed owner, + address[] _address_referringList, + uint256[][] _tokenIds_referringList, + address[] _address_referredList, + uint256[][] _tokenIds_referredList + ); + + /// @notice set the referred list of an rNFT associated with different contract addresses and update the referring list of each one in the referred list + /// @param tokenIds array of rNFTs, recommended to check duplication at the caller's end + function setNode(uint256 tokenId, address[] memory addresses, uint256[][] memory tokenIds) external; + + /// @notice Get the referring list of an rNFT + /// @param tokenId The considered rNFT, _address The corresponding contract address + /// @return The referring mapping of an rNFT + function referringOf(address _address, uint256 tokenId) external view returns(address[] memory, uint256[][] memory); + + /// @notice Get the referred list of an rNFT + /// @param tokenId The considered rNFT, _address The corresponding contract address + /// @return The referred mapping of an rNFT + function referredOf(address _address, uint256 tokenId) external view returns(address[] memory, uint256[][] memory); +} + +interface TargetContract { + function setNodeReferredExternal(address successor, uint256 tokenId, uint256[] memory _tokenIds) external; + function referringOf(address _address, uint256 tokenId) external view returns(address[] memory, uint256[][] memory); + function referredOf(address _address, uint256 tokenId) external view returns(address[] memory, uint256[][] memory); +} + +``` + +```solidity + +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; +import "./IERC_5521.sol"; + +contract ERC_5521 is ERC721, IERC_5521, TargetContract { + + struct Relationship { + mapping (address => uint256[]) referring; + mapping (address => uint256[]) referred; + uint256 createdTimestamp; // unix timestamp when the rNFT is being created + } + + mapping (uint256 => Relationship) internal _relationship; + address contractOwner = address(0); + + mapping (uint256 => address[]) private referringKeys; + mapping (uint256 => address[]) private referredKeys; + + constructor(string memory name_, string memory symbol_) ERC721(name_, symbol_) { + contractOwner = msg.sender; + } + + function safeMint(uint256 tokenId, address[] memory addresses, uint256[][] memory _tokenIds) public { + // require(msg.sender == contractOwner, "ERC_rNFT: Only contract owner can mint"); + _safeMint(msg.sender, tokenId); + setNode(tokenId, addresses, _tokenIds); + } + + /// @notice set the referred list of an rNFT associated with different contract addresses and update the referring list of each one in the referred list + /// @param tokenIds array of rNFTs, recommended to check duplication at the caller's end + function setNode(uint256 tokenId, address[] memory addresses, uint256[][] memory tokenIds) public virtual override { + require( + addresses.length == tokenIds.length, + "Addresses and TokenID arrays must have the same length" + ); + for (uint i = 0; i < tokenIds.length; i++) { + if (contractOwner != msg.sender && tokenIds[i].length == 0) { revert("ERC_5521: the referring list cannot be empty"); } + } + setNodeReferring(addresses, tokenId, tokenIds); + setNodeReferred(addresses, tokenId, tokenIds); + } + + /// @notice set the referring list of an rNFT associated with different contract addresses + /// @param _tokenIds array of rNFTs associated with addresses, recommended to check duplication at the caller's end + function setNodeReferring(address[] memory addresses, uint256 tokenId, uint256[][] memory _tokenIds) private { + require(_isApprovedOrOwner(msg.sender, tokenId), "ERC_5521: transfer caller is not owner nor approved"); + + Relationship storage relationship = _relationship[tokenId]; + + for (uint i = 0; i < addresses.length; i++) { + if (relationship.referring[addresses[i]].length == 0) { referringKeys[tokenId].push(addresses[i]); } // Add the address if it's a new entry + relationship.referring[addresses[i]] = _tokenIds[i]; + } + + relationship.createdTimestamp = block.timestamp; + emitEvents(tokenId, msg.sender); + } + + /// @notice set the referred list of an rNFT associated with different contract addresses + /// @param _tokenIds array of rNFTs associated with addresses, recommended to check duplication at the caller's end + function setNodeReferred(address[] memory addresses, uint256 tokenId, uint256[][] memory _tokenIds) private { + for (uint i = 0; i < addresses.length; i++) { + if (_relationship[tokenId].referred[addresses[i]].length == 0) { referredKeys[tokenId].push(addresses[i]); } // Add the address if it's a new entry + if (addresses[i] == address(this)) { + for (uint j = 0; j < _tokenIds[i].length; j++) { + Relationship storage relationship = _relationship[_tokenIds[i][j]]; + + require(tokenId != _tokenIds[i][j], "ERC_5521: self-reference not allowed"); + if (relationship.createdTimestamp >= block.timestamp) { revert("ERC_5521: the referred rNFT needs to be a predecessor"); } // Make sure the reference complies with the timing sequence + + relationship.referred[address(this)].push(tokenId); + emitEvents(_tokenIds[i][j], ownerOf(_tokenIds[i][j])); + } + } else { + TargetContract targetContractInstance = TargetContract(addresses[i]); + targetContractInstance.setNodeReferredExternal(addresses[i], tokenId, _tokenIds[i]); + } + } + } + + /// @notice set the referred list of an rNFT associated with different contract addresses + /// @param _tokenIds array of rNFTs associated with addresses, recommended to check duplication at the caller's end + function setNodeReferredExternal(address _address, uint256 tokenId, uint256[] memory _tokenIds) external { + for (uint i = 0; i < _tokenIds.length; i++) { + Relationship storage relationship = _relationship[_tokenIds[i]]; + + require(_address == address(this), "ERC_5521: this must be an external contract address"); + if (relationship.createdTimestamp >= block.timestamp) { revert("ERC_5521: the referred rNFT needs to be a predecessor"); } // Make sure the reference complies with the timing sequence + + relationship.referred[_address].push(tokenId); + emitEvents(_tokenIds[i], ownerOf(_tokenIds[i])); + } + } + + /// @notice Get the referring list of an rNFT + /// @param tokenId The considered rNFT, _address The corresponding contract address + /// @return The referring mapping of an rNFT + function referringOf(address _address, uint256 tokenId) external view virtual override(IERC_5521, TargetContract) returns (address[] memory, uint256[][] memory) { + address[] memory _referringKeys; + uint256[][] memory _referringValues; + + if (_address == address(this)) { + require(_exists(tokenId), "ERC_5521: token ID not existed"); + (_referringKeys, _referringValues) = convertMap(tokenId, true); + } else { + TargetContract targetContractInstance = TargetContract(_address); + (_referringKeys, _referringValues) = targetContractInstance.referringOf(_address, tokenId); + } + return (_referringKeys, _referringValues); + } + + /// @notice Get the referred list of an rNFT + /// @param tokenId The considered rNFT, _address The corresponding contract address + /// @return The referred mapping of an rNFT + function referredOf(address _address, uint256 tokenId) external view virtual override(IERC_5521, TargetContract) returns (address[] memory, uint256[][] memory) { + address[] memory _referredKeys; + uint256[][] memory _referredValues; + + if (_address == address(this)) { + require(_exists(tokenId), "ERC_5521: token ID not existed"); + (_referredKeys, _referredValues) = convertMap(tokenId, false); + } else { + TargetContract targetContractInstance = TargetContract(_address); + (_referredKeys, _referredValues) = targetContractInstance.referredOf(_address, tokenId); + } + return (_referredKeys, _referredValues); + } + + /// @dev See {IERC165-supportsInterface}. + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + return interfaceId == type(IERC_5521).interfaceId + || interfaceId == type(TargetContract).interfaceId + || super.supportsInterface(interfaceId); + } + + // @notice Emit an event of UpdateNode + function emitEvents(uint256 tokenId, address sender) private { + (address[] memory _referringKeys, uint256[][] memory _referringValues) = convertMap(tokenId, true); + (address[] memory _referredKeys, uint256[][] memory _referredValues) = convertMap(tokenId, false); + + emit UpdateNode(tokenId, sender, _referringKeys, _referringValues, _referredKeys, _referredValues); + } + + // @notice Convert a specific `local` token mapping to a key array and a value array + function convertMap(uint256 tokenId, bool isReferring) private view returns (address[] memory, uint256[][] memory) { + Relationship storage relationship = _relationship[tokenId]; + + address[] memory returnKeys; + uint256[][] memory returnValues; + + if (isReferring) { + returnKeys = referringKeys[tokenId]; + returnValues = new uint256[][](returnKeys.length); + for (uint i = 0; i < returnKeys.length; i++) { + returnValues[i] = relationship.referring[returnKeys[i]]; + } + } else { + returnKeys = referredKeys[tokenId]; + returnValues = new uint256[][](returnKeys.length); + for (uint i = 0; i < returnKeys.length; i++) { + returnValues[i] = relationship.referred[returnKeys[i]]; + } + } + return (returnKeys, returnValues); + } +} + +``` + +## Security Considerations + +The `createdTimestamp` only covers the block-level timestamp (based on block headers), which does not support fine-grained comparisons such as transaction-level. + +The change of ownership has nothing to do with the reference relationship. Normally, the distribution of profits complies to the aggreement when the NFT was being created regardless of the change of ownership unless specified in the agreement. + +In the context of collaborating with [ERC-998](./eip-998.md), referring a token will not refer its descendants by default. In the case that only a specific child token gets referred, it means the privity of contract will involve nobody other than the owner of this specific child token. Alternatively, a chain-of-reference all the way from the root token to a specific very bottom child token (from root to leaf) can be constructured and recorded in the `referring` to explicitly define the distribution of profits. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5528.md b/EIPS/eip-5528.md new file mode 100644 index 0000000..cb822cc --- /dev/null +++ b/EIPS/eip-5528.md @@ -0,0 +1,266 @@ +--- +eip: 5528 +title: Refundable Fungible Token +description: Allows refunds for EIP-20 tokens by escrow smart contract +author: StartfundInc (@StartfundInc) +discussions-to: https://ethereum-magicians.org/t/eip-5528-refundable-token-standard/10494 +status: Final +type: Standards Track +category: ERC +created: 2022-08-16 +requires: 20 +--- + +## Abstract + +This standard is an extension of [EIP-20](./eip-20.md). This specification defines a type of escrow service with the following flow: + +- The seller issues tokens. +- The seller creates an escrow smart contract with detailed escrow information like contract addresses, lock period, exchange rate, additional escrow success conditions, etc. +- The seller funds seller tokens to the *Escrow Contract*. +- Buyers fund buyer tokens which are pre-defined in the *Escrow Contract*. +- When the escrow status meets success, the seller can withdraw buyer tokens, and buyers can withdraw seller tokens based on exchange rates. +- Buyers can withdraw (or refund) their funded token if the escrow process is failed or is in the middle of the escrow process. + +## Motivation + +Because of the pseudonymous nature of cryptocurrencies, there is no automatic recourse to recover funds that have already been paid. + +In traditional finance, trusted escrow services solve this problem. In the world of decentralized cryptocurrency, however, it is possible to implement an escrow service without a third-party arbitrator. This standard defines an interface for smart contracts to act as an escrow service with a function where tokens are sent back to the original wallet if the escrow is not completed. + +## Specification + +There are two types of contract for the escrow process: + +- *Payable Contract*: The sellers and buyers use this token to fund the *Escrow Contract*. This contract MUST override [EIP-20](./eip-20.md) interfaces. +- *Escrow Contract*: Defines the escrow policies and holds *Payable Contract*'s token for a certain period. This contract does not requires override [EIP-20](./eip-20.md) interfaces. + +### Methods + +#### `constructor` + +The *Escrow Contract* demonstrates details of escrow policies as none-mutable matter in constructor implementation. + +The *Escrow Contract* MUST define the following policies: + +- Seller token contract address +- Buyer token contract address + +The *Escrow Contract* MAY define the following policies: + +- Escrow period +- Maximum (or minimum) number of investors +- Maximum (or minimum) number of tokens to fund +- Exchange rates of seller/buyer token +- KYC verification of users + +#### `escrowFund` + +Funds `_value` amount of tokens to address `_to`. + +In the case of *Escrow Contract*: + + - `_to` MUST be the user address. + - `msg.sender` MUST be the *Payable Contract* address. + - MUST check policy validations. + +In the case of *Payable Contract*: + + - The address `_to` MUST be the *Escrow Contract* address. + - MUST call the same function of the *Escrow Contract* interface. The parameter `_to` MUST be `msg.sender` to recognize the user address in the *Escrow Contract*. + +```solidity +function escrowFund(address _to, uint256 _value) public returns (bool) +``` + +#### `escrowRefund` + +Refunds `_value` amount of tokens from address `_from`. + +In the case of *Escrow Contract*: + + - `_from` MUST be the user address. + - `msg.sender` MUST be the *Payable Contract* address. + - MUST check policy validations. + +In the case of *Payable Contract*: + + - The address `_from` MUST be the *Escrow Contract* address. + - MUST call the same function of the *Escrow Contract* interface. The parameter `_from` MUST be `msg.sender` to recognize the user address in the *Escrow Contract*. + +```solidity +function escrowRefund(address _from, uint256 _value) public returns (bool) +``` + +#### `escrowWithdraw` + +Withdraws funds from the escrow account. + +In the case of *Escrow Contract*: + + - MUST check the escrow process is completed. + - MUST send the remaining balance of seller and buyer tokens to `msg.sender`'s seller and buyer contract wallets. + +In the case of *Payable Contract*, it is optional. + +```solidity +function escrowWithdraw() public returns (bool) +``` + +### Example of interface + +This example demonstrates simple exchange of one seller and one buyer in one-to-one exchange rates. + +```solidity +pragma solidity ^0.4.20; + +interface IERC5528 { + + function escrowFund(address _to, uint256 _value) public returns (bool); + + function escrowRefund(address _from, uint256 _value) public returns (bool); + + function escrowWithdraw() public returns (bool); + +} + +contract PayableContract is IERC5528, IERC20 { + /* + General ERC20 implementations + */ + + function _transfer(address from, address to, uint256 amount) internal { + uint256 fromBalance = _balances[from]; + require(fromBalance >= amount, "ERC20: transfer amount exceeds balance"); + _balances[from] = fromBalance - amount; + _balances[to] += amount; + } + + function transfer(address to, uint256 amount) public returns (bool) { + address owner = msg.sender; + _transfer(owner, to, amount); + return true; + } + + function escrowFund(address _to, uint256 _value) public returns (bool){ + bool res = IERC5528(to).escrowFund(msg.sender, amount); + require(res, "Fund Failed"); + _transfer(msg.sender, to, amount); + return true; + } + + function escrowRefund(address _from, uint256 _value) public returns (bool){ + bool res = IERC5528(_from).escrowRefund(msg.sender, _value); + require(res, "Refund Failed"); + _transfer(_from, msg.sender, _value); + return true; + } +} + +contract EscrowContract is IERC5528 { + + enum State { Inited, Running, Success, Closed } + struct BalanceData { + address addr; + uint256 amount; + } + + address _addrSeller; + address _addrBuyer; + BalanceData _fundSeller; + BalanceData _fundBuyer; + EscrowStatus _status; + + constructor(address sellerContract, address buyerContract){ + _addrSeller = sellerContract; + _addrBuyer = buyerContract; + _status = State.Inited; + } + + function escrowFund(address _to, uint256 _value) public returns (bool){ + if(msg.sender == _addrSeller){ + require(_status.state == State.Running, "must be running state"); + _fundSeller.addr = _to; + _fundSeller.amount = _value; + _status = State.Success; + }else if(msg.sender == _addrBuyer){ + require(_status.state == State.Inited, "must be init state"); + _fundBuyer.addr = _to; + _fundBuyer.amount = _value; + _status = State.Running; + }else{ + require(false, "Invalid to address"); + } + return true; + } + + function escrowRefund(address _from, uint256 amount) public returns (bool){ + require(_status.state == State.Running, "refund is only available on running state"); + require(msg.sender == _addrBuyer, "invalid caller for refund"); + require(_fundBuyer.addr == _from, "only buyer can refund"); + require(_fundBuyer.amount >= amount, "buyer fund is not enough to refund"); + _fundBuyer.amount = _fundBuyer.amount - amount + return true; + } + + function escrowWithdraw() public returns (bool){ + require(_status.state == State.Success, "withdraw is only available on success state"); + uint256 common = MIN(_fundBuyer.amount, _fundSeller.amount); + + if(common > 0){ + _fundBuyer.amount = _fundBuyer.amount - common; + _fundSeller.amount = _fundSeller.amount - common; + + // Exchange + IERC5528(_addrSeller).transfer(_fundBuyer.addr, common); + IERC5528(_addrBuyer).transfer(_fundSeller.addr, common); + + // send back the remaining balances + if(_fundBuyer.amount > 0){ + IERC5528(_addrBuyer).transfer(_fundBuyer.addr, _fundBuyer.amount); + } + if(_fundSeller.amount > 0){ + IERC5528(_addrSeller).transfer(_fundSeller.addr, _fundSeller.amount); + } + } + + _status = State.Closed; + } + +} + +``` + +## Rationale + +The interfaces cover the escrow operation's refundable issue. + +The suggested 3 functions (`escrowFund`, `escrowRefund` and `escrowWithdraw`) are based on `transfer` function in EIP-20. + +`escrowFund` send tokens to the *Escrow Contract*. The *Escrow Contract* can hold the contract in the escrow process or reject tokens if the policy does not meet. + +`escrowRefund` can be invoked in the middle of the escrow process or when the escrow process fails. + +`escrowWithdraw` allows users (sellers and buyers) to transfer tokens from the escrow account. When the escrow process completes, the seller can get the buyer's token, and the buyers can get the seller's token. + +## Backwards Compatibility + +The *Payable Contract* which implements this EIP is fully backward compatible with the [EIP-20](./eip-20.md) specification. + +## Test Cases + +[Unit test example by truffle](../assets/eip-5528/truffule-test.js). + +This test case demonstrates the following conditions for exchanging seller/buyer tokens. + +- The exchange rate is one-to-one. +- If the number of buyers reaches 2, the escrow process will be terminated(success). +- Otherwise (not meeting success condition yet), buyers can refund (or withdraw) their funded tokens. + +## Security Considerations + +Since the *Escrow Contract* controls seller and buyer rights, flaws within the *Escrow Contract* will directly lead to unexpected behavior and potential loss of funds. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5539.md b/EIPS/eip-5539.md new file mode 100644 index 0000000..eb3e50c --- /dev/null +++ b/EIPS/eip-5539.md @@ -0,0 +1,252 @@ +--- +eip: 5539 +title: Revocation List Registry +description: Registry of revocation lists for revoking arbitrary data. +author: Philipp Bolte (@strumswell), Lauritz Leifermann (@lleifermann), Dennis von der Bey (@DennisVonDerBey) +discussions-to: https://ethereum-magicians.org/t/eip-5539-revocation-list-registry/10573 +status: Draft +type: Standards Track +category: ERC +created: 2022-08-26 +requires: 712 +--- + +## Abstract +This EIP proposes a set of methods and standards for a role-based registry of indicators aimed for usage in revocations. + +## Motivation +Revocation is a universally needed construct both in the traditional centralized and decentralized credential attestation. This EIP aims to provide an interface to standardize a decentralized approach to managing and resolving revocation states in a contract registry. + +The largest problem with traditional revocation lists is the centralized aspect of them. Most of the world's CRLs rely on HTTP servers as well as caching and are therefore vulnerable to known attack vectors in the traditional web space. This aspect severely weakens the underlying strong asymmetric key architecture in current PKI systems. + +In addition, issuers in existing CRL approaches are required to host an own instance of their public revocation list, as shared or centralized instances run the risk of misusage by the controlling entity. +This incentivizes issuers to shift this responsibility to a third party, imposing the risk of even more centralization of the ecosystem (see Cloudflare, AWS). +Ideally, issuers should be able to focus on their area of expertise, including ownership of their revocable material, instead of worrying about infrastructure. + +We see value in a future of the Internet where anyone can be an issuer of verifiable information. This proposal lays the groundwork for anyone to also own the lifecycle of this information to build trust in ecosystems. + +## Specification +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. + +This EIP specifies a contract called `EthereumRevocationRegistry` that is deployed once and may then be commonly used by everyone. By default, an Ethereum address **MAY** own and manage a multitude of revocation lists in a namespace that **MUST** contain the revocation states for a set of revocation keys. + +An owner of a namespace **MAY** allow delegates to manage one or more of its revocation lists. Delegates **MUST** be removable by the respective list's owner. In certain situations, an owner **MAY** also want to transfer a revocation list in a namespace and its management rights to a new owner. + +### Definitions +- `namespace`: A namespace is a representation of an Ethereum address inside the registry that corresponds to its owners address. All revocation lists within a namespace are initially owned by the namespace's owner address. +- `revocation list`: A namespace can contain a number of revocation lists. Each revocation list is identified by a unique key of the type bytes32 that can be used to address it in combination with the namespace address. +- `revocation key`: A revocation list can contain a number of revocation keys of the type bytes32. In combination with the namespace address and the revocation list key, it resolves to a boolean value that indicates whether the revocation key is revoked or not. +- `owner`: An Ethereum address that has modifying rights to revocation lists within its own and possibly foreign namespaces. An owner can give up modifying rights of revocation lists within its namespace by transferring ownership to another address. +- `delegate`: An Ethereum address that received temporary access to a revocation list in a namespace. It has to be granted by the current owner of the revocation list in question. + +### Revocation Management + +#### isRevoked +**MUST** implement a function that returns the revocation status of a particular revocation key in a namespace's revocation list. It **MAY** also respect the revocation lists revocation status. +```solidity +function isRevoked(address namespace, bytes32 list, bytes32 key) public view returns (bool); +``` + +#### changeStatus +**MUST** implement a function to change the revocation status of a particular revocation key in a namespace's revocation list +```solidity +function changeStatus(bool revoked, address namespace, bytes32 revocationList, bytes32 revocationKey) public; +``` + +#### changeStatusSigned ([see Meta Transactions](#MetaTransactions)) +**OPTIONAL** implements a function to change the revocation status of a particular revocation key in a namespace's revocation list with a raw signature. +```solidity +function changeStatusSigned(bool revoked, address namespace, bytes32 revocationList, bytes32 revocationKey, address signer, bytes calldata signature) public; +``` + +#### changeStatusDelegated +**OPTIONAL** implements a function to change the revocation status of a particular revocation key in a namespace's revocation list by a revocation list's delegate. +```solidity +function changeStatusDelegated(bool revoked, address namespace, bytes32 revocationList, bytes32 revocationKey) public; +``` + +#### changeStatusDelegatedSigned ([see Meta Transactions](#MetaTransactions)) +**OPTIONAL** implements a function to change the revocation status of a particular revocation key in a namespace's revocation list with a raw signature. +```solidity +function changeStatusDelegatedSigned(bool revoked, address namespace, bytes32 revocationList, bytes32 revocationKey, address signer, bytes calldata signature) public; +``` + +#### changeStatusesInList +**OPTIONAL** implements a function to change multiple revocation statuses in a namespace's revocation list at once. +```solidity +function changeStatusesInList(bool[] memory revoked, address namespace, bytes32 revocationList, bytes32[] memory revocationKeys) public; +``` + +#### changeStatusesInListSigned ([see Meta Transactions](#MetaTransactions)) +**OPTIONAL** implements a function to change multiple revocation statuses in a namespace's revocation list at once with a raw signature. +```solidity +function changeStatusesInListSigned(bool[] memory revoked, address namespace, bytes32 revocationList, bytes32[] memory revocationKeys, address signer, bytes calldata signature) public; +``` + +#### changeStatusesInListDelegated +**OPTIONAL** implements a function to change multiple revocation statuses in a namespace's revocation list at once by a revocation list's delegate. +```solidity +function changeStatusesInListDelegated(bool[] memory revoked, address namespace, bytes32 revocationList, bytes32[] memory revocationKeys) public; +``` + +#### changeStatusesInListDelegatedSigned ([see Meta Transactions](#MetaTransactions)) +**OPTIONAL** implements a function to change multiple revocation statuses in a namespace's revocation list at once with a raw signature generated by a revocation list's delegate. +```solidity +function changeStatusesInListDelegatedSigned(bool[] memory revoked, address namespace, bytes32 revocationList, bytes32[] memory revocationKeys, address signer, bytes calldata signature) public; +``` + +### Revocation List Management + +#### +**OPTIONAL** implements a function that returns the revocation status of a particular revocation list in a namespace. +```solidity +function listIsRevoked(address namespace, bytes32 revocationList) view public returns (bool); +``` + +#### changeListStatus +**OPTIONAL** implements a function to change the revocation of a revocation list itself. If a revocation list is revoked, all its keys are considered revoked as well. +```solidity +function changeListStatus(bool revoked, address namespace, bytes32 revocationList) public; +``` + +#### changeListStatusSigned ([see Meta Transactions](#MetaTransactions)) +**OPTIONAL** implements a function to change the revocation of a revocation list itself with a raw signature. If a revocation list is revoked, all its keys are considered revoked as well. +```solidity +function changeListStatusSigned(bool revoked, address namespace, bytes32 revocationList, address signer, bytes calldata signature) public; +``` + +### Owner management + +#### changeListOwner +**OPTIONAL** implement a function to change the revocation status of a revocation list. If a revocation list is revoked, all keys in it are considered revoked. +```solidity +function changeListOwner(address newOwner, address namespace, bytes32 revocationList) public; +``` + +#### changeListOwnerSigned ([see Meta Transactions](#MetaTransactions)) +**OPTIONAL** implement a function to change the revocation status of a revocation list with a raw signature. If a revocation list is revoked, all keys in it are considered revoked. +```solidity +function changeListOwnerSigned(address newOwner, address namespace, bytes32 revocationList, address signer, bytes calldata signature) public; +``` + +### Delegation management + +#### addListDelegate +**OPTIONAL** implements a function to add a delegate to an owner's revocation list in a namespace. +```solidity +function addListDelegate(address delegate, address namespace, bytes32 revocationList) public; +``` + +#### addListDelegateSigned ([see Meta Transactions](#MetaTransactions)) +**OPTIONAL** implements a function to add a delegate to an owner's revocation list in a namespace with a raw signature. +```solidity +function addListDelegateSigned(address delegate, address namespace, bytes32 revocationList, address signer, bytes calldata signature) public; +``` + +#### removeListDelegate +**OPTIONAL** implements a function to remove a delegate from an owner's revocation list in a namespace. +```solidity +function removeListDelegate(address delegate, address owner, bytes32 revocationList) public; +``` + +#### removeListDelegateSigned ([see Meta Transactions](#MetaTransactions)) +**OPTIONAL** implements a function to remove a delegate from an owner's revocation list in a namespace with a raw signature. +```solidity +function removeListDelegateSigned(address delegate, address namespace, bytes32 revocationList, address signer, bytes calldata signature) public; +``` + +### Events + +#### RevocationStatusChanged +**MUST** be emitted when `changeStatus`, `changeStatusSigned`, `changeStatusDelegated`, `changeStatusDelegatedSigned`, `changeStatusesInList`, `changeStatusesInListSigned`, `changeStatusesInListDelegated`, or `changeStatusesInListDelegatedSigned` was successfully executed. + +```solidity +event RevocationStatusChanged( + address indexed namespace, + bytes32 indexed revocationList, + bytes32 indexed revocationKey, + bool revoked +); +``` + +#### RevocationListOwnerChanged +**MUST** be emitted when `changeListOwner` or `changeListOwnerSigned` was successfully executed. + +```solidity +event RevocationListOwnerChanged( + address indexed namespace, + bytes32 indexed revocationList, + address indexed newOwner +); +``` + +#### RevocationListDelegateAdded +**MUST** be emitted when `addListDelegate` or `addListDelegateSigned` was successfully executed. + +```solidity +event RevocationListDelegateAdded( + address indexed namespace, + bytes32 indexed revocationList, + address indexed delegate +); +``` + +#### RevocationListDelegateRemoved +**MUST** be emitted when `removeListDelegate` or `removeListDelegateSigned` was successfully executed. + +```solidity +event RevocationListDelegateRemoved( + address indexed namespace, + bytes32 indexed revocationList, + address indexed delegate +); +``` + +#### RevocationListStatusChanged +**MUST** be emitted when `changeListStatus` or `changeListStatusSigned` was successfully executed. + +```solidity +event RevocationListStatusChanged( + address indexed namespace, + bytes32 indexed revocationlist, + bool revoked +); +``` + +### Meta Transactions + +This section uses the following terms: +- **`transaction signer`**: An Ethereum address that signs arbitrary data for the contract to execute **BUT** does not commit the transaction. +- **`transaction sender`**: An Ethereum address that takes signed data from a **transaction signer** and commits it wrapped with its own signature to the smart contract. + +An address (**transaction signer**) **MAY** be able to deliver a signed payload off-band to another address (**transaction sender**) that initiates the Ethereum interaction with the smart contract. The signed payload **MUST** be limited to be used only once ([Signed Hash](#SignedHash) + [nonces](#Nonce)). + +#### Signed Hash + +The signature of the **transaction signer** **MUST** conform [EIP-712](./eip-712.md). This helps users understand what the payload they're signing consists of & it improves the protection against replay attacks. + +#### Nonce + +This EIP **RECOMMENDS** the use of a **dedicated nonce mapping** for meta transactions. If the signature of the **transaction sender** and its meta contents are verified, the contract increases a nonce for this **transaction signer**. This effectively removes the possibility for any other sender to execute the same transaction again with another wallet. + +## Rationale + +### Why the concept of namespaces? +This provides every Ethereum address a reserved space, without the need to actively claim it in the contract. Initially addresses only have owner access in their own namespace. + +### Why does a namespace always represent the initial owner address? +The change of an owner of a list shouldn't break the link to a revocation key in it, as already existing off-chain data may depend on it. + +## Backwards Compatibility +No backward compatibility issues were found. + +## Security Considerations + +### Meta Transactions +The signature of signed transactions could potentially be replayed on different chains or deployed versions of the registry implementing this ERC. This security consideration is addressed by the usage of [EIP-712](./eip-712.md) + +### Rights Management +The different roles and their inherent permissions are meant to prevent changes from unauthorized entities. The revocation list owner should always be in complete control over its revocation list and who has writing access to it. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5553.md b/EIPS/eip-5553.md new file mode 100644 index 0000000..b131e89 --- /dev/null +++ b/EIPS/eip-5553.md @@ -0,0 +1,256 @@ +--- +eip: 5553 +title: Representing IP and its Royalty Structure +description: A way of representing intellectual property and its respective royalty structure on chain +author: Roy Osherove (@royosherove) +discussions-to: https://ethereum-magicians.org/t/eip-5553-representing-intellectual-property-on-chain-with-royalty-rights/10551 +status: Review +type: Standards Track +category: ERC +created: 2022-08-17 +requires: 20, 721 +--- + +## Abstract +This proposal introduces a generic way to represent intellectual property on chain, along with a refined royalty representation mechanism and associated metadata link. This standard is not associated with a specific type of IP and could represent many types of IP, such as musical IP, videos, books, images, and more. +The standard is kept very generic to allow the industry to evolve new ecosystems that can all rely on the same basic standard at their core. + +This standard allows market participants to: +1) Observe the canonical on-chain representation of an intellectual property +2) Discover its attached metadata +3) Discover its related royalty structure +4) This will enable building registration, licensing, and payout mechanisms for intellectual property assets in the future. + +## Motivation + +There is no accepted standard mechanism to license intellectual property or to represent it, except using traditional NFTs. However, regular NFTs only represent a collectible item use case and cannot easily represent more complicated use cases of licensing IP for different types of uses. +We can enable such licensing mechanisms if we can: + +1) Declare that IP exists, SEPARATELY from its purchase ability +2) Declare possibly multiple interested parties to be paid for such IP + +For 1, no standard exists today. + +For 2, traditional split standards exist based on NFT purchases or through mechanisms like 0xsplits. While these solve the main problem, they do not contain the ability to name multiple types of collaboration participants. + + + +## Specification + +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. + +**contracts that want to represent IP on chain MUST implement [EIP-721](./eip-721.md) AND this Proposal** + +This standard extends [EIP-721](./eip-721.md) with the following `IIPRepresentation` (IPR for short) interface. +Implementers of this standard **MUST** have all of the following functions: + +### royaltyPortionTokens() function +This function MUST return an array of addresses related to [EIP-20](./eip-20.md) tokens that MUST represent royalty portions to different types of interested parties. These royalty portion tokens represent a more granular and streamlined way to declare royalty splits for multiple collaboration participants for the creation of the IP. + +For example, for a musical IP, we might have two tokens representing the composition/writing/publishing royalty portion side and the recording/master side. These royalty portion tokens are distributed to the collaboration participants and can later be queried by the various holders to distribute royalties. I.e., if one holds 10% of a royalty portion token, that holder will get 10% of the financial distribution related to that type of royalty. + +### metadataURI() function +This function MUST return the URI to a metadata file containing any required metadata for the IP or an empty string. Each IP type MAY implement its metadata standard, defined separately. The file MUST be hosted in IPFS, Arweave, or other decentralized content-addressable systems in which the file's contents are not changeable without changing the URI. + +### changeMetadataURI() function +This function allows changing the metadata URI to point to a new version of the metadata file. Calling this function MUST trigger the event `MetadataChanged` in case of success. + +### ledger() function +This function MUST return the registry or registrar contract address or an EOA account that initialized the IP and associated royalty tokens. An IP representation MAY be registered in multiple places by different actors for different purposes. This function enables market participants to discover which registry mechanism is the parent of the IP and might have special access rights to manage the IP. + +```solidity +// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.9; +import '@openzeppelin/contracts/interfaces/IERC165.sol'; + + +/// +/// @dev Interface for Intellectual Property Representation +/// +interface IIPRepresentation is IERC165 { + + /// @notice Called with the new URI to an updated metadata file + /// @param _newUri - the URI pointing to a metadata file (file standard is up to the implementer) + /// @param _newFileHash - The hash of the new metadata file for future reference and verification + function changeMetadataURI(string memory _newUri, string memory _newFileHash) external ; + + /// @return array of addresses of ERC20 tokens representing royalty portion in the IP + /// @dev i.e implementing ERC5501 (IRoyaltyInterestToken interface) + function royaltyPortionTokens() external view returns (address[] memory) ; + + /// @return the address of the contract or EOA that initialized the IP registration + /// @dev i.e., a registry or registrar, to be implemented in the future + function ledger() external view returns (address) ; + + /// @return the URI of the current metadata file for the II P + function metadataURI() external view returns (string memory) ; + + /// @dev event to be triggered whenever metadata URI is changed + /// @param byAddress the addresses that triggered this operation + /// @param oldURI the URI to the old metadata file before the change + /// @param oldFileHash the hash of the old metadata file before the change + /// @param newURI the URI to the new metadata file + /// @param newFileHash the hash of the new metadata file + event MetadaDataChanged(address byAddress, string oldURI, string oldFileHash, string newURI, string newFileHash); +} +``` + + +## Rationale + +### Returning an array of EIP-20 tokens presents a more robust royalty portions structure/ + +Current royalty implementations deal only with a single type of royalty payment: NFT sales. They also only allow a single type of royalty - i.e., Music NFTs cannot pay different people in different scenarios. +In other words, currently, a royalty split works the same way no matter what type of purchase or license deal has happened for all parties involved. + +With this proposal, multiple **types** of royalty scenarios are allowed. A classic case is the music industry, in which we have writing/composition royalties and recording/master royalties. Different licensing types will pay different percentages to different parties based on context. + +In the case of a song cover, a license payment formula can be created so that that +a) Original IP's writers get paid for using the lyrics or composition of the song +b) recording artists of the original song do not get paid since their recording is not used +c) recording artists of the new IP will get paid +d) there are no writing royalties for the creators of the cover. + +Moreover, this EIP has a single structure that connects to all types of royalty types and allows finding them more easily. +Lastly, moving EIP-20 tokens around is much easier than managing an 0xsplits contract. + +### Separating the IP contract from the collectible and licensing NFTs enables scaling licensing types +By separating the canonical version of the IP from its various licensed uses (NFT purchase, streaming, usage of art and more.), this EIP introduces a path for an ecosystem of various license types and payment distributions to evolve. +In other words, when people use this scheme, they will not start by creating a music NFT or art NFT; they start by creating the IP Representation and then create types of licenses or collectibles for it, each as its own sellable NFT. + +### A single pointer to the IP's metadata +The IPR points to metadata housed in IPFS or Arweave and allows changing it and keeping track of the changes in a simple and standard way. Today the only metadata standard is NFT metadata extension, but it is impossible to know to which standard the document adheres. With different IP types, different metadata standards for different IP types can be formulated and have a simple, easy place to discover attached metadata. + +## Reference Implementation + +#### Implementing a Musical IP Representation (MIPR for short) based on IIPRepresentation +```solidity +// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.9; +import '@openzeppelin/contracts/token/ERC721/ERC721.sol'; +import "./interfaces/IIPRepresentation.sol"; +import "./interfaces/Structs.sol"; + + +contract MusicalIP is ERC721, IIPRepresentation { + address public songLedger; + address public compToken; + address public recToken; + string public metadataURI; + string public fileHash; + uint256 public tokenId; + bool public activated =false; + + function supportsInterface(bytes4 interfaceId) public view virtual override( ERC721, IERC165) returns (bool) { + return + interfaceId == type(IIPRepresentation).interfaceId || + super.supportsInterface(interfaceId); + } + + function getInterfaceId() public pure returns (bytes4){ + return type(IIPRepresentation).interfaceId; + } + + constructor ( + uint256 _tokenId, + address _songLedger, + SongMintingParams memory _params, + address _compAddress, + address _recAddress + ) + ERC721(_params.shortName, _params.symbol){ + + songLedger = _songLedger; + compToken = _compAddress; + recToken = _recAddress; + metadataURI = _params.metadataUri; + fileHash = _params.fileHash; + tokenId = _tokenId; + + _safeMint(_songLedger, _tokenId); + emit Minted(_params.shortName,_songLedger,_compAddress,_recAddress,_msgSender(),tokenId,_params.metadataUri); + } + + function changeMetadataURI(string memory _newURI,string memory _newFileHash) public + { + string memory oldURI = metadataURI; + string memory oldHash = fileHash; + metadataURI = _newURI; + fileHash = _newFileHash; + + emit MetadataChanged(oldURI, oldHash,_newURI,_newFileHash); + } + + function royaltyPortionTokens() external view returns (address[] memory) { + address[] memory items = new address[](2); + items[0] = compToken; + items[1] = recToken; + return items; + } + function ledger() external view returns (address) { + return songLedger; + } + + event MetadataChanged( + string oldUri, string oldFileHash, + string newUri, string newFileHash + ); + event Minted( + string abbvName, + address ledger, + address compToken, + address recToken, + address creator, + uint256 tokenId, + string metadataUri + ); +} + + + +``` + +#### Deploying a new Musical IP using a simple song registry contract + +```solidity +// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.9; +import "@openzeppelin/contracts/utils/Counters.sol"; +import "./MusicalIP.sol"; +import "./CompositionRoyaltyToken.sol"; +import "./RecordingRoyaltyToken.sol"; + + +contract SimpleSongLedger is IERC721Receiver { + using Counters for Counters.Counter; + Counters.Counter private mipIds; + function onERC721Received(address, address, uint256, bytes calldata) external pure returns (bytes4) { + return IERC721Receiver.onERC721Received.selector; + } + + function mintSong(SongMintingParams memory _params) public { + CompositionRoyaltyToken comp = new CompositionRoyaltyToken(address(this),"SONGCOMP","COMP"); + RecordingRoyaltyToken rec = new RecordingRoyaltyToken(address(this),"SONGREC","REC"); + mipIds.increment(); + + MusicalIP mip = new MusicalIP( + mipIds.current(), + address(this), + _params, + address(comp), + address(rec) + ); + } +} + + +``` +## Security Considerations + +There might be potential security challenges of attackers persuading holders of royalty portion tokens to send them those tokens and gaining royalty portion in various IPRs. However, these are not specific to royalties and are a common issue with EIP-20 tokens. + +In the case of the IP registration ownership, it will be recommended that registry contracts own the IP registration, which will be non-transferrable (account bound to the registry that created it). + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5554.md b/EIPS/eip-5554.md new file mode 100644 index 0000000..eb70c86 --- /dev/null +++ b/EIPS/eip-5554.md @@ -0,0 +1,213 @@ +--- +eip: 5554 +title: NFT Legal Use, Repurposing, and Remixing +description: An interface for describing and enforcing the legal use and remix of an NFT. On-chain registry of rights, attribution and derivative links. +author: Isaac Patka (@ipatka), COALA Licensing Taskforce +discussions-to: https://ethereum-magicians.org/t/eip-5999-legal-use-sharing-repurposing-and-remixing-standard-compatible-with-creative-commons/10553 +status: Draft +type: Standards Track +category: ERC +created: 2022-07-07 +requires: 5218 +--- + +## Abstract + +This EIP extends any other token standard to provide: + +* Explicit rights for the token holder related to commercial exploitation, derivative works, and reproduction; +* [EIP-5218](./eip-5218.md) interface for creating, viewing, and checking the status of licenses +* Standard format for extended license information in the token metadata; +* Standard events to track off chain creation of derivative works, commercial exploitation, and reproduction; +* On chain tracking of derivative works and reproductions +* Additional required fields in the smart contract to reference the copyright owner +* Function calls for commercial exploitation, derivative works and reproduction. + +## Motivation +NFTs still face legal uncertainty, and many now realize that the rights associated with an NFT are just as important as the NFT itself. Our goal is to help the ecosystem reach clear consensus and broad understanding of what purchasers of NFTs are acquiring in terms of copyright or other rights. + +Today, purchasing the NFT of a digital work is not the same as purchasing the copyright in that work. In most cases, the NFT does not even incorporate the digital work; it only references it via a hash. Hence, the NFT holder owns a unique digital copy of the work, but does not necessarily enjoy the right to reproduce, redistribute, or otherwise exploit that work—unless explicitly provided for by the copyright owner. It typically only includes the right to privately enjoy the work and display it publicly on social media or in virtual galleries. + +We aim to create a new set of licenses with modular terms and conditions—à la Creative Commons—in order to enable artists to increase the value of their NFT by associating additional rights to them (e.g. the right to create derivative works, or to allow for the commercial usage of the underlying works). Our solution will allow for any licensed rights to be granted, only and exclusively, to the current holders of an NFT, and to be transferred automatically to the new token holders every time the NFT is being transferred. + +An on chain registry of copyrighted material will help in discovery of the rights associated with the NFTs that have been created with this protocol. + +Our current work is drafting the legalese and technical specifications. + +## Specification +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. + +Every contract compliant with this EIP must implement the `IERC5554` interface: + +```solidity +pragma solidity ^0.8.0; + +interface IERC5554 is IERC5218 { + + event CommercialExploitation(uint256 _tokenId, uint256 _licenseId, string _externalUri); + event ReproductionCreated(uint256 _tokenId, uint256 _licenseId, uint256 _reproductionId, address _reproduction, uint256 _reproductionTokenId); + event DerivativeCreated(uint256 _tokenId, uint256 _licenseId, uint256 _derivativeId, address _derivative, uint256 _derivativeTokenId); + + /// @notice Retrieve the copyright owner address + /// @dev Throws unless the token exists + /// @param tokenId The identifier for the queried token + /// @return address of the copyright owner + function getCopyrightOwner(uint256 tokenId) + external + virtual + returns (address); + + /// @notice Requests to log an execution of a license + /// @dev Throws unless the token issuance conditions are met + /// @param tokenId The identifier for the queried token + /// @return uint256 tracking reproduction ID + function logReproduction(uint256 tokenId, address reproduction, uint256 reproductionTokenId) + external + virtual + returns (uint256); + + /// @notice Requests to log an executions of a license + /// @dev Throws unless the token issuance conditions are met + /// @param tokenId The identifier for the queried token + /// @return uint256 tracking derivative ID + function logDerivative(uint256 tokenId, address derivative, uint256 derivativeTokenId) + external + virtual + returns (uint256); + + /// @notice Requests to log an execution of a license + /// @dev Throws unless the commercial exploitation conditions are met + /// @param tokenId The identifier for the queried token + function logCommercialExploitation(uint256 tokenId, string calldata uri) + external; + + /// @notice Retrieve the token associated with a reproduction + /// @dev Throws unless the reproduction exists + /// @param _reproductionId The identifier for the reproduction + /// @return uint256 The identifier for the token used to generate the reproduction + function getReproductionTokenId(uint256 _reproductionId) + external + view + returns (uint256); + + /// @notice Retrieve the token associated with a reproduction + /// @dev Throws unless the reproduction exists + /// @param _reproductionId The identifier for the reproduction + /// @return uint256 The identifier for the license used to generate the reproduction + function getReproductionLicenseId(uint256 _reproductionId) + external + view + returns (uint256); + + /// @notice Retrieve the token associated with a reproduction + /// @dev Throws unless the reproduction exists + /// @param _reproductionId The identifier for the derivative work + /// @return address The address of the reproduction collection + function getReproductionCollection(uint256 _reproductionId) + external + view + returns (address); + + /// @notice Retrieve the token associated with a derivative + /// @dev Throws unless the derivative exists + /// @param _derivativeId The identifier for the derivative work + /// @return uint256 The identifier for the token used to generate the derivative work + function getDerivativeTokenId(uint256 _derivativeId) + external + view + returns (uint256); + + /// @notice Retrieve the token associated with a derivative + /// @dev Throws unless the derivative exists + /// @param _derivativeId The identifier for the derivative work + /// @return uint256 The identifier for the license used to generate the derivative work + function getDerivativeLicenseId(uint256 _derivativeId) + external + view + returns (uint256); + + /// @notice Retrieve the token associated with a derivative + /// @dev Throws unless the derivative exists + /// @param _derivativeId The identifier for the derivative work + /// @return address The address of the derivative collection + function getDerivativeCollection(uint256 _derivativeId) + external + view + returns (address); + +} +``` + + + +### Token based Attribution/ Remix +On chain derivative works and reproductions +* Reproductions and derivative works are tracked in the contract. + + +### Event based attribution +For commercial exploitation or other off-chain uses of a creative work, this EIP defines events to be emitted to track the use of the work. + +```solidity +event CommercialExploitation(uint256 tokenID, string uri) + +function logCommercialExploitation(uint256 tokenId, string calldata uri) external returns bool; +``` + +#### Example: +When a token holder uses an NFT for off-chain merchandise, log a reference to the off-chain work in the event uri + +### Required fields + +```solifity +function copyrightOwner(uint256 tokenId) external returns address; +``` + +Copyright owner per tokenID. Could just be the tokenID owner in a simple use case, or something else if desired by the creator. + +## Rationale +We expand here upon the Motivation section to justify every decision made with regard to the specs of the standard: + +The `getLicenseId()` function takes a tokenID as a parameter, making it possible for different tokenID to be associated with different licensing terms. + +LicenseURI links to a content-addressed file that stipulates the terms and conditions of the license in actual legal language, so that the license can be read and understood by those who want to understand which rights are associated with the work of authorship, and which additional rights are granted through the acquisition of the NFT. + +When the license allows for the reproduction and/or for the creation of a derivative work only to the token holders, there needs to be a way to verify that the new NFT or the derivative NFT was created legitimately. The standard ensures this by enabling the current token holder to call a function, e.g. logDerivative which checks that the caller has a valid license to execute + +For commercial exploitation or other off-chain uses of a creative work, the standard implements the `logCommercialExploitation()` that makes it possible to keep track of which commercial exploitations have been made, and when. This makes it possible to verify that all commercial exploitation were legitimately done. + +The standard introduces a new field, `copyrightOwner`, which indicates the address of the current holder of the copyright in the work. If multiple copyright owners exist, a multisig address (or DAO) can be used. + +The artist address is not registered as an on-chain variable, but rather as part of the metadata, because it is an immutable field. + +If any, the parents of the work (i.e. the works that it is derived upon) must be part of the metadata information, so that people can verify that the NFT has obtained a DerivativeWork for each one of its parents. + +This licensing framework is intended to create a system to facilitate the licensing of rights that “follow the token” through a public licensing framework. This is not meant to be used for cases in which an exclusive right is licensed through a personal license to a specific actor (e.g. the copyright owner providing a third-party with the right to commercially exploit the work, regardless of whether they hold the token). This also is not designed to account for the sub-licensing case (e.g. licensing the right to one party to license third parties to engage in commercial exploitation), since this should rather be done via a personal copyright licensing scheme. + + +### Examples + +#### Bored Koalas merchandising + +Vigdís creates a PFP collection of Bored Koalas, which is subject to standard copyright restrictions: no one has the right to reproduce, distribute, communicate, commercialize or remix these works. However, she wants to give specific permissions to those who hold a NFT from the collection. She mints the collection with this EIP, introducing a conditional license that allows for the current token holder to display the Bored Koala associated with each NFT and commercialize it for the purpose of merchandising only. + +Neža has purchased one of these Bored Koalas. She wants to produce merchandising to be distributed at his blockchain conference. She goes to a print shop and asks them to make t-shirts with the Bored Koala image of the NFT she has purchased. The print shop can verify that she has the right to commercially exploit the work by verifying that they are the holder of the Bored Koala NFT, and verifying the terms of the license associated with it. (NB: this does not require a sub-license to be granted to the print shop, because the commercial exploitation implies the right to commission third parties to engage in such commercial exploitation). Neža brings the t-shirts to her conference and puts them for sale. When doing so, she calls the `logCommercialExploitation()` function from the NFT smart contract in order to track that the commercial exploitation was done at a time while she was the token holder. + +#### Musical Remix + +Matti is an up and coming songwriter in the emerging web3 music ecosystem. For the upcoming crypto conference, he creates a hit song called “Degens in the Night”. Instead of listing the song on a web2 platform, Matti mints the song as an NFT using this EIP, with a dual licensing scheme: a general public licenses that allows for the free reproduction and redistribution of the work, given proper attribution (e.g. Creative Commons BY-NC-ND) and a conditional license which allows for the token holder to remix the song, in exchange of a particular lump sum (e.g. 1ETH) and under the condition that the derivative work is released under the same licensing terms as the original work Lyyli wants to create a cover of that song, which she calls “Degens in the Parisian Night”. She purchases the NFT and mints a new derivative NFT under a new smart contract using this EIP standard. She then calls the `requestDerivativeToken()` function and send 1ETH to the original NFT smart contract, in order to request that a DerivativeToken be assigned to the new smart contract she has created. The smart contract automatically approves the request to assign a Derivative Token to the new smart contract of Lyyli. This can be used as a proof that the derivative work is indeed a legitimate work, which has been approved by the copyright owner of the original work. During the conference hundreds of other web3 music creators host a side event with Degens in the Night remixes playing until 4am. + +#### Royalties Remix + +Alice created a 3D model of a motorcycle, which she wants everyone to remix, under the condition that she gets royalty from the commercial exploitation of all derivative works. She release her work as an NFT with this EIP, with a dual licensing scheme: a general public licenses that allows for the free reproduction and redistribution of the work, given proper attribution (e.g. Creative Commons BY-NC-ND) and a conditional license which allows for the token holder to remix the song, under the condition that the derivative work is released under the same licensing terms as the original work, and that there is a split of the royalties between himself and the remixer. + +Jane wants to create a derivative work of the motorcycle. She purchases the NFT and mints a new derivative NFT under a new smart contract that uses this EIP, which includes a royalty split for Alice. She then calls the `requestDerivativeToken()` function from the original NFT smart contract in order to request that a DerivativeToken be assigned to the new smart contract she has created. Alice decided that the smart contract shall not automate the approval or rejection of the request, but rather wait for her to validate or invalidate the request, after she has verified that the design and provisions of the new smart contract, namely that it does indeed replicate the same terms and conditions as the original work and that it incorporates the proper amount of royalties. She approves the request to assign a Derivative Token to the new smart contract of Jane. When people purchase Jane’s NFT, the royalties are split to ensure the proper redistribution of the generated profit to Alice. + +## Backwards Compatibility +The interface defined in this standard is backward compatible with most NFT standards used in the Ethereum ecosystem as of this writing. + +## Security Considerations +Needs discussion. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5559.md b/EIPS/eip-5559.md new file mode 100644 index 0000000..471ca9f --- /dev/null +++ b/EIPS/eip-5559.md @@ -0,0 +1,471 @@ +--- +eip: 5559 +title: "Cross Chain Write Deferral Protocol" +description: The cross chain write deferral protocol provides a mechanism to defer the storage & resolution of mutations to off-chain handlers +author: Paul Gauvreau (@0xpaulio), Nick Johnson (@arachnid) +discussions-to: https://ethereum-magicians.org/t/eip-cross-chain-write-deferral-protocol/10576 +status: Stagnant +type: Standards Track +category: ERC +created: 2022-06-23 +requires: 712 +--- + +## Abstract +The following standard provides a mechanism in which smart contracts can request various tasks to be resolved by an external handler. This provides a mechanism in which protocols can reduce the gas fees associated with storing data on mainnet by deferring the handling of it to another system/network. These external handlers act as an extension to the core L1 contract. + +This standard outlines a set of handler types that can be used for managing the execution and storage of mutations (tasks), as well as their corresponding tradeoffs. Each handler type has associated operational costs, finality guarantees, and levels of decentralization. By further specifying the type of handler that the mutation is deferred to, the protocol can better define how to permission and secure their system. + +This standard can be implemented in conjunction with [EIP-3668](./eip-3668) to provide a mechanism in which protocols can reside on and be interfaced through an L1 contract on mainnet, while being able to resolve and mutate data stored in external systems. + +## Motivation +[EIP-3668](./eip-3668) provides a mechanism by which off-chain lookups can be defined inside smart contracts in a transparent manner. In addition, it provides a scheme in which the resolved data can be verified on-chain. However, there lacks a standard by which mutations can be requested through the native contract, to be performed on the off-chain data. Furthermore, with the increase in L2 solutions, smart contract engineers have additional tools that can be used to reduce the storage and transaction costs of performing mutations on the Ethereum mainnet. + +A specification that allows smart contracts to defer the storage and resolution of data to external handlers facilitates writing clients agnostic to the storage solution being used, enabling new applications that can operate without knowledge of the underlying handlers associated with the contracts they interact with. + +Examples of this include: + - Allowing the management of ENS domains externally resolved on an L2 solution or off-chain database as if they were native L1 tokens. + - Allowing the management of digital identities stored on external handlers as if they were in the stored in the native L1 smart contract. + +## Specification +### Overview +There are two main handler classifications: L2 Contract and Off-Chain Database. These are determined based off of where the handler is deployed. The handler classifications are used to better define the different security guarantees and requirements associated with its deployment. + +From a high level: +- Handlers hosted on an L2 solution are EVM compatible and can use attributes native to the Ethereum ecosystem (such as address) to permission access. +- Handlers hosted on an Off-Chain Database require additional parameters and signatures to correctly enforce the authenticity and check the validity of a request. + +A deferred mutation can be handled in as little as two steps. However, in some cases the mutation might be deferred multiple times. + +1. Querying or sending a transaction to the contract +2. Querying or sending a transaction to the handler using the parameters provided in step 1 + +In step 1, a standard blockchain call operation is made to the contract. The contract either performs the operation as intended or reverts with an error that specifies the type of handler that the mutation is being deferred to and the corresponding parameters required to perform the subsequent mutation. There are two types of errors that the contract can revert with, but more may be defined in other EIPs: + +- `StorageHandledByL2(chainId, contractAddress)` +- `StorageHandledByOffChainDatabase(sender, url, data)` + +In step 2, the client builds and performs a new request based off of the type of error received in (1). These handshakes are outlined in the sections below: + +- [StorageHandledByL2](#data-stored-in-an-l2) +- [StorageHandledByOffChainDatabase](#data-stored-in-an-off-chain-database) + +In some cases, the mutation may be deferred multiple times +- [Storage Deferred Twice L1 > L2 > Off-Chain](#data-stored-in-an-l2--an-off-chain-database) + +### Data Stored in an L1 +``` +┌──────┐ ┌───────────┐ +│Client│ │L1 Contract│ +└──┬───┘ └─────┬─────┘ + │ │ + │ somefunc(...) │ + ├─────────────────────────►│ + │ │ + │ response │ + │◄─────────────────────────┤ + │ │ +``` + +In the case in which no reversion occurs, data is stored in the L1 contract when the transaction is executed. + +### Data Stored in an L2 + +``` +┌──────┐ ┌───────────┐ ┌─────────────┐ +│Client│ │L1 Contract│ │ L2 Contract │ +└──┬───┘ └─────┬─────┘ └──────┬──────┘ + │ │ │ + │ somefunc(...) │ │ + ├────────────────────────────────────────────────────►│ │ + │ │ │ + │ revert StorageHandledByL2(chainId, contractAddress) │ │ + │◄────────────────────────────────────────────────────┤ │ + │ │ │ + │ Execute Tx [chainId] [contractAddress] [callData] │ │ + ├─────────────────────────────────────────────────────┼──────────────►│ + │ │ │ + │ response │ │ + │◄────────────────────────────────────────────────────┼───────────────┤ + │ │ │ +``` + +The call or transaction to the L1 contract reverts with the `StorageHandledByL2(chainId, contractAddress)` error. + +In this case, the client builds a new transaction for `contractAddress` with the original `callData` and sends it to a RPC of their choice for the corresponding `chainId`. The `chainId` parameter corresponds to an L2 Solution that is EVM compatible. + +#### Example + +Suppose a contract has the following method: + +```solidity +function setAddr(bytes32 node, address a) external; +``` + +Data for this mutations is stored and tracked on an EVM compatible L2. The contract author wants to reduce the gas fees associated with the contract, while maintaining the interoperability and decentralization of the protocol. Therefore, the mutation is deferred to a off-chain handler by reverting with the `StorageHandledByL2(chainId, contractAddress)` error. + +One example of a valid implementation of `setAddr` would be: + +```solidity +function setAddr(bytes32 node, address a) external { + revert StorageHandledByL2( + 10, + _l2HandlerContractAddress + ); +} +``` + +For example, if a contract returns the following data in an `StorageHandledByL2`: + +```text +chainId = 10 +contractAddress = 0x0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff +``` + +The user, receiving this error, creates a new transaction for the corresponding `chainId`, and builds a transaction with the original `callData` to send to `contractAddress`. The user will have to choose an RPC of their choice to send the transaction to for the corresponding `chainId`. + +### Data Stored in an Off-Chain Database +``` +┌──────┐ ┌───────────┐ ┌────────────────────┐ +│Client│ │L1 Contract│ │ Off-Chain Database │ +└──┬───┘ └─────┬─────┘ └──────────┬─────────┘ + │ │ │ + │ somefunc(...) │ │ + ├────────────────────────────────────────────────────►│ │ + │ │ │ + │ revert StorageHandledByOffChainDatabase(sender, | │ + │ urls, requestParams) │ │ + │◄────────────────────────────────────────────────────┤ │ + │ │ │ + │ HTTP Request [requestParams, signature] │ │ + ├─────────────────────────────────────────────────────┼──────────────────►│ + │ │ │ + │ response │ │ + │◄────────────────────────────────────────────────────┼───────────────────┤ + │ │ │ +``` + +The call or transaction to the L1 contract reverts with the `StorageHandledByOffChainDatabase(sender, url, data)` error. + +In this case, the client performs a HTTP POST request to the gateway service. The gateway service is defined by `url`. The body attached to the request is a JSON object that includes `sender`, `data`, and a signed copy of `data` denoted `signature`. The signature is generated according to a [EIP-712](./eip-712), in which a typed data signature is generated using domain definition, `sender`, and the message context, `data`. + +`sender` ia an ABI-encoded struct defined as: + +```solidity +/** +* @notice Struct used to define the domain of the typed data signature, defined in EIP-712. +* @param name The user friendly name of the contract that the signature corresponds to. +* @param version The version of domain object being used. +* @param chainId The ID of the chain that the signature corresponds to (ie Ethereum mainnet: 1, Goerli testnet: 5, ...). +* @param verifyingContract The address of the contract that the signature pertains to. +*/ +struct domainData { + string name; + string version; + uint64 chainId; + address verifyingContract; +} +``` + +`data` ia an abi encoded struct defined as: + +```solidity +/** +* @notice Struct used to define the message context used to construct a typed data signature, defined in EIP-712, +* to authorize and define the deferred mutation being performed. +* @param functionSelector The function selector of the corresponding mutation. +* @param sender The address of the user performing the mutation (msg.sender). +* @param parameter[] A list of pairs defining the inputs used to perform the deferred mutation. +*/ +struct messageData { + bytes4 functionSelector; + address sender; + parameter[] parameters; + uint256 expirationTimestamp; +} + +/** +* @notice Struct used to define a parameter for Off-Chain Database Handler deferral. +* @param name The variable name of the parameter. +* @param value The string encoded value representation of the parameter. +*/ +struct parameter { + string name; + string value; +} +``` + +`signature` is generated by using the `sender` & `data` parameters to construct an [EIP-712](./eip-712) typed data signature. + +The body used in the HTTP POST request is defined as: + +```json +{ + "sender": "", + "data": "", + "signature": "" +} +``` + +#### Example + +Suppose a contract has the following method: + +```solidity +function setAddr(bytes32 node, address a) external; +``` + +Data for this mutations is stored and tracked in some kind of off-chain database. The contract author wants the user to be able to authorize and make modifications to their `Addr` without having to pay a gas fee. Therefore, the mutation is deferred to a off-chain handler by reverting with the `StorageHandledByOffChainDatabase(sender, url, data)` error. + +One example of a valid implementation of `setAddr` would be: + +```solidity +function setAddr(bytes32 node, address a) external { + IWriteDeferral.parameter[] memory params = new IWriteDeferral.parameter[](3); + + params[0].name = "node"; + params[0].value = BytesToString.bytes32ToString(node); + + params[1].name = "coin_type"; + params[1].value = Strings.toString(coinType); + + params[2].name = "address"; + params[2].value = BytesToString.bytesToString(a); + + revert StorageHandledByOffChainDatabase( + IWriteDeferral.domainData( + { + name: WRITE_DEFERRAL_DOMAIN_NAME, + version: WRITE_DEFERRAL_DOMAIN_VERSION, + chainId: 1, + verifyingContract: address(this) + } + ), + _offChainDatabaseUrl, + IWriteDeferral.messageData( + { + functionSelector: msg.sig, + sender: msg.sender, + parameters: params, + expirationTimestamp: block.timestamp + _offChainDatabaseTimeoutDuration + } + ) + ); +} +``` + +For example, if a contract reverts with the following: + +```text +StorageHandledByOffChainDatabase( + ( + "CoinbaseResolver", + "1", + 1, + 0x32f94e75cde5fa48b6469323742e6004d701409b + ), + "https://example.com/r/{sender}", + ( + 0xd5fa2b00, + 0x727f366727d3c9cc87f05d549ee2068f254b267c, + [ + ("node", "0x418ae76a9d04818c7a8001095ad01a78b9cd173ee66fe33af2d289b5dc5f4cba"), + ("coin_type", "60"), + ("address", "0x727f366727d3c9cc87f05d549ee2068f254b267c") + ], + 181 + ) +) +``` + +The user, receiving this error, constructs the typed data signature, signs it, and performs that request via a HTTP POST to `url`. + +Example HTTP POST request body including `requestParams` and `signature`: + +```json +{ + "sender": "", + "data": "", + "signature": "" +} +``` + +Note that the message could be altered could be altered in any way, shape, or form prior to signature and request. It is the backend's responsibility to correctly permission and process these mutations. From a security standpoint, this is no different then a user being able to call a smart contract with any params they want, as it is the smart contract's responsibility to permission and handle those requests. + + +### Data Stored in an L2 & an Off-Chain Database + +```text +┌──────┐ ┌───────────┐ ┌─────────────┐ ┌────────────────────┐ +│Client│ │L1 Contract│ │ L2 Contract │ │ Off-Chain Database │ +└──┬───┘ └─────┬─────┘ └──────┬──────┘ └──────────┬─────────┘ + │ │ │ │ + │ somefunc(...) │ │ │ + ├────────────────────────────────────────────────────►│ │ │ + │ │ │ │ + │ revert StorageHandledByL2(chainId, contractAddress) │ │ │ + │◄────────────────────────────────────────────────────┤ │ │ + │ │ │ │ + │ Execute Tx [chainId] [contractAddress] [callData] │ │ │ + ├─────────────────────────────────────────────────────┼──────────────►│ │ + │ │ │ │ + │ revert StorageHandledByOffChainDatabase(sender, url, data) │ │ + │◄────────────────────────────────────────────────────┼───────────────┤ │ + │ │ │ │ + │ HTTP Request {requestParams, signature} │ │ │ + ├─────────────────────────────────────────────────────┼───────────────┼───────────────────►│ + │ │ │ │ + │ response │ │ │ + │◄────────────────────────────────────────────────────┼───────────────┼────────────────────┤ + │ │ │ │ +``` + +The call or transaction to the L1 contract reverts with the `StorageHandledByL2(chainId, contractAddress)` error. + +In this case, the client builds a new transaction for `contractAddress` with the original `callData` and sends it to a RPC of their choice for the corresponding `chainId`. + +That call or transaction to the L2 contract then reverts with the `StorageHandledByOffChainDatabase(sender, url, data)` error. + +In this case, the client then performs a HTTP POST request against the gateway service. The gateway service is defined by `url`. The body attached to the request is a JSON object that includes `sender`, `data`, and `signature` -- a typed data signature corresponding to [EIP-712](./eip-712). + +### Events + +When making changes to core variables of the handler, the corresponding event MUST be emitted. This increases the transparency associated with different managerial actions. Core variables include `chainId` and `contractAddress` for L2 solutions and `url` for Off-Chain Database solutions. The events are outlined below in the WriteDeferral Interface. + +### Write Deferral Interface + +Below is a basic interface that defines and describes all of the reversion types and their corresponding parameters. + +```solidity +pragma solidity ^0.8.13; + +interface IWriteDeferral { + /*////////////////////////////////////////////////////////////// + EVENTS + //////////////////////////////////////////////////////////////*/ + + /// @notice Event raised when the default chainId is changed for the corresponding L2 handler. + event L2HandlerDefaultChainIdChanged(uint256 indexed previousChainId, uint256 indexed newChainId); + /// @notice Event raised when the contractAddress is changed for the L2 handler corresponding to chainId. + event L2HandlerContractAddressChanged(uint256 indexed chainId, address indexed previousContractAddress, address indexed newContractAddress); + + /// @notice Event raised when the url is changed for the corresponding Off-Chain Database handler. + event OffChainDatabaseHandlerURLChanged(string indexed previousUrl, string indexed newUrl); + + /*////////////////////////////////////////////////////////////// + STRUCTS + //////////////////////////////////////////////////////////////*/ + + /** + * @notice Struct used to define the domain of the typed data signature, defined in EIP-712. + * @param name The user friendly name of the contract that the signature corresponds to. + * @param version The version of domain object being used. + * @param chainId The ID of the chain that the signature corresponds to (ie Ethereum mainnet: 1, Goerli testnet: 5, ...). + * @param verifyingContract The address of the contract that the signature pertains to. + */ + struct domainData { + string name; + string version; + uint64 chainId; + address verifyingContract; + } + + /** + * @notice Struct used to define the message context used to construct a typed data signature, defined in EIP-712, + * to authorize and define the deferred mutation being performed. + * @param functionSelector The function selector of the corresponding mutation. + * @param sender The address of the user performing the mutation (msg.sender). + * @param parameter[] A list of pairs defining the inputs used to perform the deferred mutation. + */ + struct messageData { + bytes4 functionSelector; + address sender; + parameter[] parameters; + uint256 expirationTimestamp; + } + + /** + * @notice Struct used to define a parameter for off-chain Database Handler deferral. + * @param name The variable name of the parameter. + * @param value The string encoded value representation of the parameter. + */ + struct parameter { + string name; + string value; + } + + + /*////////////////////////////////////////////////////////////// + ERRORS + //////////////////////////////////////////////////////////////*/ + + /** + * @dev Error to raise when mutations are being deferred to an L2. + * @param chainId Chain ID to perform the deferred mutation to. + * @param contractAddress Contract Address at which the deferred mutation should transact with. + */ + error StorageHandledByL2( + uint256 chainId, + address contractAddress + ); + + /** + * @dev Error to raise when mutations are being deferred to an Off-Chain Database. + * @param sender the EIP-712 domain definition of the corresponding contract performing the off-chain database, write + * deferral reversion. + * @param url URL to request to perform the off-chain mutation. + * @param data the EIP-712 message signing data context used to authorize and instruct the mutation deferred to the + * off-chain database handler. + * In order to authorize the deferred mutation to be performed, the user must use the domain definition (sender) and message data + * (data) to construct a type data signature request defined in EIP-712. This signature, message data (data), and domainData (sender) + * are then included in the HTTP POST request, denoted sender, data, and signature. + * + * Example HTTP POST request: + * { + * "sender": , + * "data": , + * "signature": + * } + * + */ + error StorageHandledByOffChainDatabase( + domainData sender, + string url, + messageData data + ); +} +``` + +### Use of transactions with storage-deferral reversions +In some cases the contract might conditionally defer and handle mutations, in which case a transaction may be required. It is simple to use this method for sending transactions that may result in deferral reversions, as a client should receive the corresponding reversion while `preflighting` the transaction. + +This functionality is ideal for applications that want to allow their users to define the security guarantees and costs associated with their actions. For example, in the case of a decentralized identity profile, a user might not care if their data is decentralized and chooses to defer the handling of their records to the off-chain handler to reduce gas fees and on-chain transactions. + +## Rationale +### Use of `revert` to convey call information +[EIP-3668](./eip-3668) adopted the idea of using a `revert` to convey call information. It was proposed as a simple mechanism in which any pre-existing interface or function signature could be satisfied while maintain a mechanism to instruct and trigger an off-chain lookup. + +This is very similar for the write deferral protocol, defined in this EIP; without any modifications to the ABI or underlying EVM, `revert` provides a clean mechanism in which we can "return" a typed instruction - and the corresponding elements to complete that action - without modifying the signature of the corresponding function. This makes it easy to comply with pre-existing interfaces and infrastructure. + +### Use of multiple reversion & handler types to better define security guarantees +By further defining the class of the handler, it gives the developer increased granularity to define the characteristics and different guarantees associated storing the data off-chain. In addition, different handlers require different parameters and verification mechanisms. This is very important for the transparency of the protocol, as they store data outside of the native ethereum ecosystem. Common implementations of this protocol could include storing non-operational data in L2 solutions and off-chain databases to reduce gas fees, while maintaining open interoperability. + + +## Backwards Compatibility +Existing contracts that do not wish to use this specification are unaffected. Clients can add support for Cross Chain Write Deferrals to all contract calls without introducing any new overhead or incompatibilities. + +Contracts that require Cross Chain Write Deferrals will not function in conjunction with clients that do not implement this specification. Attempts to call these contracts from non-compliant clients will result in the contract throwing an exception that is propagated to the user. + +## Security Considerations +Deferred mutations should never resolve to mainnet ethereum. Such attempts to defer the mutation back to ETH could include hijacking attempts in which the contract developer is trying to get the user to sign and send a malicious transaction. Furthermore, when a transaction is deferred to an L2 system, it must use the original `calldata`, this prevents against potentially malicious contextual changes in the transaction. + +### Fingerprinting attacks +As all deferred mutations will include the `msg.sender` parameter in `data`, it is possible that `StorageHandledByOffChainDatabase` reversions could fingerprint wallet addresses and the corresponding IP address used to make the HTTP request. The impact of this is application-specific and something the user should understand is a risk associated with off-chain handlers. To minimize the security impact of this, we make the following recommendations: + +1. Smart contract developers should provide users with the option to resolve data directly on the network. Allowing them to enable on-chain storage provides the user with a simple cost-benefit analysis of where they would like their data to resolve and different guarantees / risks associated with the resolution location. +2. Client libraries should provide clients with a hook to override Cross Chain Write Deferral `StorageHandledByOffChainDatabase` calls - either by rewriting them to use a proxy service, or by denying them entirely. This mechanism or another should be written so as to easily facilitate adding domains to allowlists or blocklists. + +We encourage applications to be as transparent as possible with their setup and different precautions put in place. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5560.md b/EIPS/eip-5560.md new file mode 100644 index 0000000..a0ba5a4 --- /dev/null +++ b/EIPS/eip-5560.md @@ -0,0 +1,125 @@ +--- +eip: 5560 +title: Redeemable NFTs +description: Makes an NFT redeemable for a physical object +author: Olivier Fernandez (@fernandezOli), Frédéric Le Coidic (@FredLC29), Julien Béranger (@julienbrg) +discussions-to: https://ethereum-magicians.org/t/eip-redeemable-nft-extension/10589 +status: Stagnant +type: Standards Track +category: ERC +created: 2022-08-30 +requires: 165, 721 +--- + +## Abstract + +The EIP is a Redeemable NFT extension which adds a `redeem` function to [EIP-721](./eip-721.md). It can be implemented when an NFT issuer wants his/her NFT to be redeemed for a physical object. + +## Motivation + +An increasing amount of NFT issuers such as artists, fine art galeries, auction houses, brands and others want to offer a physical object to the holder of a given NFT. This standard allows EIP-721 NFTs to signal reedemability. + +## Specification + +_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._ + +`EIP-721` compliant contracts MAY implement this EIP to provide a standard method of receiving information on redeemability. + +The NFT issuer **MUST** decide who is allowed to redeem the NFT, and restrict access to the `redeem()` function accordingly. + +Anyone **MAY** access the `isRedeemable()` function to check the redeemability status: it returns `true` when the NFT redeemable, and `false` when already redeemed. + +Third-party services that support this standard **MAY** use the `Redeem` event to listen to changes on the redeemable status of the NFT. + +Implementers of this standard **MUST** have all of the following functions: + +```solidity +import '@openzeppelin/contracts/utils/introspection/ERC165.sol'; + +/** + * @dev Implementation of Redeemable for ERC-721s + * + */ + +interface IRedeemable is ERC165 { + /* + * ERC165 bytes to add to interface array - set in parent contract implementing this standard + * + * bytes4 private constant _INTERFACE_ID_ERC721REDEEM = 0x2f8ca953; + */ + + /// @dev This event emits when a token is redeemed. + event Redeem(address indexed from, uint256 indexed tokenId); + + /// @notice Returns the redeem status of a token + /// @param tokenId Identifier of the token. + function isRedeemable(uint256 _tokenId) external view returns (bool); + + /// @notice Redeeem a token + /// @param tokenId Identifier of the token to redeeem + function redeem(uint256 _tokenId) external; +} +``` + +The `Redeem` event is emitted when the `redeem()` function is called. + +The `supportsInterface` method **MUST** return `true` when called with `0x2f8ca953`. + +## Rationale + +When the NFT contract is deployed, the `isRedeemable()` function returns `true` by default. + +By default, the `redeem()` function visibility is public, so anyone can trigger it. It is **RECOMMENDED** to add a `require` to restrict the access: + +```solidity +require(ownerOf(tokenId) == msg.sender, "ERC721Redeemable: You are not the owner of this token"); +``` + +After the `redeem()` function is triggered, `isRedeemable()` function returns `false`. + +### `Redeem` event + +When the `redeem()` function is triggered, the following event **MUST** be emitted: + +```solidity +event Redeem(address indexed from, uint256 indexed tokenId); +``` + +## Backwards Compatibility + +This standard is compatible with EIP-721. + +## Reference Implementation + +Here's an example of an EIP-721 that includes the Redeemable extension: + +```solidity +contract ERC721Redeemable is ERC721, Redeemable { + + constructor(string memory name, string memory symbol) ERC721(name, symbol) { + } + + function isRedeemable(uint256 tokenId) public view virtual override returns (bool) { + require(_exists(tokenId), "ERC721Redeemable: Redeem query for nonexistent token"); + return super.isRedeemable(tokenId); + } + + function redeem(uint256 tokenId) public virtual override { + require(_exists(tokenId), "ERC721Redeemable: Redeem query for nonexistent token"); + require(ownerOf(tokenId) == msg.sender, "ERC721Redeemable: You are not the owner of this token"); + super.redeem(tokenId); + } + + function supportsInterface(bytes4 interfaceId) public view override(ERC721, Redeemable) returns (bool) { + return super.supportsInterface(interfaceId); + } +} +``` + +## Security Considerations + +Needs discussion. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5564.md b/EIPS/eip-5564.md new file mode 100644 index 0000000..2ce1a3f --- /dev/null +++ b/EIPS/eip-5564.md @@ -0,0 +1,323 @@ +--- +eip: 5564 +title: Stealth Addresses +description: Private, non-interactive transfers and interactions +author: Toni Wahrstätter (@nerolation), Matt Solomon (@mds1), Ben DiFrancesco (@apbendi), Vitalik Buterin (@vbuterin) +discussions-to: https://ethereum-magicians.org/t/eip-5566-stealth-addresses-for-smart-contract-wallets/10614 +status: Draft +type: Standards Track +category: ERC +created: 2022-08-13 +--- + +## Abstract + +This specification defines a standardized way of creating stealth addresses. This EIP enables senders of transactions/transfers to non-interactively generate private stealth addresses for their recipients that only the recipients can unlock. + +## Motivation + +The standardization of non-interactive stealth address generation holds the potential to greatly enhance the privacy capabilities of Ethereum by enabling the recipient of a transfer to remain anonymous when receiving an asset. This is achieved through the generation of a stealth address by the sender, using a shared secret between the sender and recipient. Only the recipient is able to unlock the funds at the stealth address, as they are the only ones with access to the private key required for this purpose. As a result, observers are unable to link the recipient's stealth address to their identity, preserving the privacy of the recipient and leaving only the sender with this information. + +## Specification + +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. + +Definitions: + +- A "stealth meta-address" is a set of one or two public keys that can be used to compute a stealth address for a given recipient. +- A "spending key" is a private key that can be used to spend funds sent to a stealth address. A "spending public key" is the corresponding public key. +- A "viewing key" is a private key that can be used to determine if funds sent to a stealth address belong to the recipient who controls the corresponding spending key. A "viewing public key" is the corresponding public key. + +Different stealth address schemes will have different expected stealth meta-address lengths. A scheme that uses public keys of length `n` bytes MUST define stealth meta-addresses as follows: + +- A stealth meta-address of length `n` uses the same stealth meta-address for the spending public key and viewing public key. +- A stealth meta-address of length `2n` uses the first `n` bytes as the spending public key and the last `n` bytes as the viewing public key. + +Given a recipient's stealth meta-address, a sender MUST be able generate a stealth address for the recipient by calling a method with the following signature: + +```solidity +/// @notice Generates a stealth address from a stealth meta address. +/// @param stealthMetaAddress The recipient's stealth meta-address. +/// @return stealthAddress The recipient's stealth address. +/// @return ephemeralPubKey The ephemeral public key used to generate the stealth address. +/// @return viewTag The view tag derived from the shared secret. +function generateStealthAddress(bytes memory stealthMetaAddress) + external + view + returns (address stealthAddress, bytes memory ephemeralPubKey, bytes1 viewTag); +``` + +A recipient MUST be able to check if a stealth address belongs to them by calling a method with the following signature: + +```solidity +/// @notice Returns true if funds sent to a stealth address belong to the recipient who controls +/// the corresponding spending key. +/// @param stealthAddress The recipient's stealth address. +/// @param ephemeralPubKey The ephemeral public key used to generate the stealth address. +/// @param viewingKey The recipient's viewing private key. +/// @param spendingPubKey The recipient's spending public key. +/// @return True if funds sent to the stealth address belong to the recipient. +function checkStealthAddress( + address stealthAddress, + bytes memory ephemeralPubKey, + bytes memory viewingKey, + bytes memory spendingPubKey +) external view returns (bool); +``` + +A recipient MUST be able to compute the private key for a stealth address by calling a method with the following signature: + +```solidity +/// @notice Computes the stealth private key for a stealth address. +/// @param stealthAddress The expected stealth address. +/// @param ephemeralPubKey The ephemeral public key used to generate the stealth address. +/// @param spendingKey The recipient's spending private key. +/// @return stealthKey The stealth private key corresponding to the stealth address. +/// @dev The stealth address input is not strictly necessary, but it is included so the method +/// can validate that the stealth private key was generated correctly. +function computeStealthKey( + address stealthAddress, + bytes memory ephemeralPubKey, + bytes memory spendingKey +) external view returns (bytes memory); +``` + +The implementation of these methods is scheme-specific. The specification of a new stealth address scheme MUST specify the implementation for each of these methods. Additionally, although these function interfaces are specified in Solidity, they do not necessarily ever need to be implemented in Solidity, but any library or SDK conforming to this specification MUST implement these methods with compatible function interfaces. + +A one byte integer (`schemeId`) is used to identify stealth address schemes. The `schemeId` represents an incrementing integer beginning at 0. A mapping from the `schemeId` to it's specification MUST be declared in the EIP that proposes to standardize a new stealth address scheme. Furthermore, the schemeId MUST be added to [this overview](../assets/eip-5564/scheme_ids.md). These EIP extensions MUST specify: + +- The integer identifier for the scheme. + +- The algorithm for encoding a stealth meta-address (i.e. the spending public key and viewing public key) into a `bytes` array, and decoding it from `bytes` to the native key types of that scheme. + +- The algorithm for the `generateStealthAddress` method. + +- The algorithm for the `checkStealthAddress` method. + +- The algorithm for the `computeStealthKey` method. + +This specification additionally defines a singleton `ERC5564Messenger` contract that emits events to announce when something is sent to a stealth address. This MUST be a singleton contract, with one instance per chain. The contract is specified as follows: + +```solidity +/// @notice Interface for announcing when something is sent to a stealth address. +contract IERC5564Messenger is StakeManager{ + /// @dev Emitted when sending something to a stealth address. + /// @dev See the `announce` method for documentation on the parameters. + event Announcement ( + uint256 indexed schemeId, + address indexed stealthAddress, + bytes ephemeralPubKey, + bytes metadata + ); + + /// @dev Called by integrators to emit an `Announcement` event. + /// @param schemeId The applied stealth address scheme (f.e. secp25k1). + /// @param stealthAddress The computed stealth address for the recipient. + /// @param ephemeralPubKey Ephemeral public key used by the sender. + /// @param metadata An arbitrary field MUST include the view tag in the first byte. + /// Besides the view tag, the metadata can be used by the senders however they like, + /// but the below guidelines are recommended: + /// The first byte of the metadata MUST be the view tag. + /// - When sending ERC-20 tokens, the metadata SHOULD be structured as follows: + /// - Byte 1 MUST be the view tag, as specified above. + /// - Bytes 2-5 are the method Id, which the hash of the canonical representation of the function to call. + /// - Bytes 6-25 are the token contract address. + /// - Bytes 26-57 are the amount of tokens being sent. + /// - When approving a stealth address to spend ERC-20 tokens, the metadata SHOULD be structured as follows: + /// - Byte 1 MUST be the view tag, as specified above. + /// - Bytes 2-5 are 0xe1f21c67, which the signature for the ERC-20 approve method. + /// - Bytes 6-25 are the token address. + /// - Bytes 26-57 are the approval amount. + /// - When sending ERC-721 tokens, the metadata SHOULD be structured as follows: + /// - Byte 1 MUST be the view tag, as specified above. + /// - Bytes 2-5 are the method Id. + /// - Bytes 6-25 are the token address. + /// - Bytes 26-57 are the token ID of the token being sent. + function announce ( + uint256 schemeId, + address stealthAddress, + bytes memory ephemeralPubKey, + bytes memory metadata + ) + external + { + emit Announcement(schemeId, stealthAddress, ephemeralPubKey, metadata); + } +} +``` + +The `ERC5564Messenger` contract inherits the `StakeManager` contract allowing users to stake ETH that is uses as an anti-DoS measure. More details in the ... section. + +```solidity +/// @notice Interface for the Stake Manager contract. +contract StakeManager{ + + /// @dev Emitted when stake is deposited. + /// @param account The address of the staker who deposited the stake. + /// @param totalStaked The new total amount of staked tokens for the account. + event StakeDeposit ( + address indexed account, + uint256 totalStaked + ); + + /// @dev Emitted when stake is withdrawn. + /// @param account The address of the staker who withdrew the stake. + /// @param withdrawAddress The address to which the withdrawn amount was sent. + /// @param amount The amount of tokens withdrawn. + event StakeWithdrawal ( + address indexed account, + address withdrawAddress, + uint256 amount + ); + + /** + * @notice Returns the stake of the account. + * @param account The address of the staker to check the stake for. + * @return uint256 The amount of staked tokens for the account. + */ + function balanceOf(address account) external view returns (uint256); + + /** + * @notice Adds the specified amount to the account's stake. + * @dev The function is payable, so the amount is sent as value with the transaction. + * @param staker The address of the staker to add the stake for. + */ + function addStake(address staker) external payable; + + /** + * @notice Withdraws the stake for the caller and sends it to the specified address. + * @param withdrawAddress The address to send the withdrawn amount to. + */ + function withdrawStake(address payable withdrawAddress) external; +} +``` + + +### Stealth meta-address format + +The new address format for the stealth meta-addresses is based on [ERC-3770](./eip-3770.md) and extends it by adding a `st:` (*stealth*) as prefix. +Thus, stealth meta-addresses on Ethereum come with the following format: + +``` +st:eth:0x +``` + +*Notably, the address-format is only used to differentiate stealth addresses from standard addresses, as the prefix is sliced away before doing any computations on the stealth meta-address.* + +--- + +### Initial Implementation of SECP256k1 with View Tags + +This improvement proposal provides a foundation that is not tied to any specific cryptographic system through the `IERC5564Messenger` contract. In addition, it introduces the first implementation of a [stealth address scheme](../assets/eip-5564/scheme_ids.md) that utilizes the SECP256k1 elliptic curve and `view tags`. The SECP256k1 elliptic curve is defined with the equation $y^2 = x^3 + 7 \pmod{p}$, where $p = 2^{256} - 2^{32} - 977$. + +The following reference is divided into three sections: + +1. Stealth address generation + +2. Parsing announcements + +3. Stealth private key derivation + + Definitions: + +- $G$ represents the generator point of the curve. + +#### Generation - Generate stealth address from stealth meta-address: + +- Recipient has access to the private keys $p_{spend}$, $p_{view}$ from which public keys $P_{spend}$ and $P_{view}$ are derived. + +- Recipient has published a stealth meta-address that consists of the public keys $P_{spend}$ and $P_{view}$. + +- Sender passes the stealth meta-address to the `generateStealthAddress` function. + +- The `generateStealthAddress` function performs the following computations: + - Generate a random 32-byte entropy ephemeral private key $p_{ephemeral}$. + - Derive the ephemeral public key $P_{ephemeral}$ from $p_{ephemeral}$. + - Parse the spending and viewing public keys, $P_{spend}$ and $P_{view}$, from the stealth meta-address. + - A shared secret $s$ is computed as $s = p_{ephemeral} \cdot P_{view}$. + - The secret is hashed $s_{h} = \textrm{h}(s)$. + - The view tag $v$ is extracted by taking the most significant byte $s_{h}[0]$, + - Multiply the hashed shared secret with the generator point $S_h = s_h \cdot G$. + - The recipient's stealth public key is computed as $P_{stealth} = P_{spend} + S_h$. + - The recipient's stealth address $a_{stealth}$ is computed as $\textrm{pubkeyToAddress}(P_{stealth})$. + - The function returns the stealth address $a_{stealth}$, the ephemeral public key $P_{ephemeral}$ and the view tag $v$. + + +#### Parsing - Locate one's own stealth address(es): + +- User has access to the viewing private key $p_{view}$ and the public spending key $P_{spend}$. + +- User has access to a set of `Announcement` events and applies the `checkStealthAddress` function to each of them. + +- The `checkStealthAddress` function performs the following computations: + - Shared secret $s$ is computed by multiplying the viewing private key with the ephemeral public key of the announcement $s = p_{view}$ * $P_{ephemeral}$. + - The secret is hashed $s_{h} = h(s)$. + - The view tag $v$ is extracted by taking the most significant byte $s_{h}[0]$ and can be compared to the given view tag. If the view tags do not match, this `Announcement` is not for the user and the remaining steps can be skipped. If the view tags match, continue on. + - Multiply the hashed shared secret with the generator point $S_h = s_h \cdot G$. + - The stealth public key is computed as $P_{stealth} = P_{spend} + S_h$. + - The derived stealth address $a_{stealth}$ is computed as $\textrm{pubkeyToAddress}(P_{stealth})$. + - Return `true` if the stealth address of the announcement matches the derived stealth address, else return `false`. + +#### Private key derivation - Generate the stealth address private key from the hashed shared secret and the spending private key. + +- User has access to the viewing private key $p_{view}$ and spending private key $p_{spend}$. + +- User has access to a set of `Announcement` events for which the `checkStealthAddress` function returns `true`. + +- The `computeStealthKey` function performs the following computations: + - Shared secret $s$ is computed by multiplying the viewing private key with the ephemeral public key of the announcement $s = p_{view}$ * $P_{ephemeral}$. + - The secret is hashed $s_{h} = h(s)$. + - The stealth private key is computed as $p_{stealth} = p_{spend} + s_h$. + + + +### Parsing considerations + +Usually, the recipient of a stealth address transaction has to perform the following operations to check weather he was the recipient of a certain transaction: + +- 2x ecMUL, + +- 2x HASH, + +- 1x ecADD, + +The view tags approach is introduced to reduce the parsing time by around 6x. Users only need to perform 1x ecMUL and 1x HASH (skipping 1x ecMUL, 1x ecADD and 1x HASH) for every parsed announcement. The 1 bytes length was is based on the maximum required space to reliably filter not matching announcement. With 1 bytes as `viewTag` the probability for users to skip the remaining computations after hashing the shared secret $h(s)$ is $1/256$. This means that users can almost certainly skip the above three operations for any announcements that to do not involve them. Since the view tag reveals one byte of the shared secret, the security margin is reduced from 128 bits to 124 bits. Notably, this only affects the privacy and not the secure generation of a stealth address. + +--- + +## Rationale + +This EIP emerged from the need of having privacy-preserving ways to transfer ownership without revealing the recipient's identity. Tokens can reveal sensitive private information about the owner. While users might want to donate money to a specific organization/country but they might not want to reveal personal account-related information at the same time. The standardization of stealth address generation represents a significant effort for privacy: privacy-preserving solutions require standards to gain adoption, therefore it is critical to focus on generalizable ways of implementing related solutions. + +The stealth address extension standardizes a protocol for generating and locating stealth addresses, enabling the transfer of assets without the need for prior interaction with the recipient and allowing recipients to verify the receipt of a transfer without interacting with the blockchain. Importantly, stealth addresses allow the recipient of a token transfer to verify receipt while maintaining their privacy, as only the recipient is able to see that they have been the recipient of the transfer. + +The authors identify the trade-off between on- and off-chain efficiency: Although, including a Monero-like `view tags` mechanism helps recipients to parse announcements more quickly, it adds complexity to the announcement event. + +The address of the recipient and the `viewTag` MUST be included in the announcement event, allowing users to quickly verify ownership without having to query the chain for positive account balances. + +## Backwards Compatibility + +This EIP is fully backward compatible. + +## Reference Implementation + +You can find an implementation of this standard in TBD. + +## Security Considerations + +### DoS Measures + +There are potential DoS attack vectors that are not mitigated by network transaction fees. Stealth transfer senders cause an externality for recipients, as parsing announcement events consumes computational resources that are not compensated with gas. Therefore, spamming announcement events *can* be a detriment to the user experience, as it *can* lead to longer parsing times. +We consider the incentives to carry out such an attack as low because **no monetary benefit can be obtained** and, in theory, nothing prevents parsing providers to ignore the spamming when serving announcements to users. +However, sophisticated spamming (*sybil attacks*), which are not considered worth the associated costs, could make it difficult for paprsing providers to develop filters for such announcement events. +Therefore, to counter spamming directly, a staking mechanism is introduced in the EIP that allows users to stake an unslashable amount of ETH. Staking allows parsing providers to better tackle potential spam through *sybil attacks*, enabling them to filter spam more effectively filter. + +Similar to [ERC-4337](./eip-4337), parsing providers agree on a `MINIMUM_STAKE`, such that the minimum required stake is not enforce on-chain. Users *can* withdraw their stake at any time without any delay. Parsing providers can de-prioritized senders who have not staked a certain minimum amount or withdrew their stake immediatly. + +### Recipients' transaction costs + +The funding of the stealth address wallet represents a known issue that might breach privacy. The wallet that funds the stealth address MUST NOT have any physical connection to the stealth address owner in order to fully leverage the privacy improvements. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5568.md b/EIPS/eip-5568.md new file mode 100644 index 0000000..c7ceb70 --- /dev/null +++ b/EIPS/eip-5568.md @@ -0,0 +1,68 @@ +--- +eip: 5568 +title: Revert Reason for Required Actions +description: Signal to wallets that an action is needed by returning a custom revert code +author: Gavin John (@Pandapip1) +discussions-to: https://ethereum-magicians.org/t/eip-5568-revert-signals/10622 +status: Review +type: Standards Track +category: ERC +created: 2022-08-31 +requires: 140 +--- + +## Abstract + +This ERC introduces a minimalistic machine-readable (binary) format to signal to wallets that an action needs to be taken by the user using a well-known revert reason. This custom revert reason contains just enough data to be extendable by future ERCs and to take in arbitrary parameters (up to 64 kB of data). Example use cases could include approving a token for an exchange, sending an HTTP request, or requesting the user to rotate their keys after a certain period of time to enforce good hygiene. + +## Motivation + +Oftentimes, a smart contract needs to signal to a wallet that an action needs to be taken, such as to sign a transaction or send an HTTP request to a URL. Traditionally, this has been done by hard-coding the logic into the frontend, but this ERC allows the smart contract itself to request the action. + +This means that, for example, an exchange or a market can directly tell the wallet to approve the smart contract to spend the token, vastly simplifying the front-end code. + +## Specification + +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. + +### Custom Revert Reason + +To signal an action needs to be taken, a compliant smart contract MUST revert with the following error: + +```solidity +error WalletSignal24(uint24 instruction_id, bytes instruction_data) +``` + +The `instruction_id` of an instruction defined by an ERC MUST be its ERC number unless there are exceptional circumstances (be reasonable). An ERC MUST define exactly zero or one `instruction_id`. The structure of the instruction data for any `instruction_id` MUST be defined by the ERC that defines the `instruction_id`. + +### Responding to a Revert + +Before submitting a transaction to the mempool, it MUST be evaluated locally. If it reverts and the revert signature matches the custom error, then the following applies. + +The `instruction_id`, and `instruction_data` MUST be parsed from the revert data. The instruction SHOULD be evaluated as per the relevant ERC. If the instruction is not supported by the wallet, it MUST display an error to the user indicating that is the case. The wallet MUST then re-evaluate the transaction, except if an instruction explicitly states that the transaction MUST NOT be re-evaluated. + +If an instruction is invalid, or the `instruction_id`, and `instruction_data` cannot be parsed, then an error MUST be displayed to the user indicating that is the case. The transaction MUST NOT be re-evaluated. + +## Rationale + +This ERC was explicitly optimized for deployment gas cost and simplicity. It is expected that libraries will eventually be developed that makes sending and receiving these well-known reverts more developer-friendly. + +## Backwards Compatibility + +### Human-Readable Revert Messages + +See [Revert Reason Collisions](#revert-reason-collisions). + +### [ERC-3668](./eip-3668.md) + +ERC-3668 can be used alongside this ERC, but it uses a different mechanism than this ERC. + +## Security Considerations + +### Revert Reason Collisions + +It is unlikely that the signature of the custom error matches any custom errors in the wild. In the case that it does, no harm is caused unless the data happen to be a valid instruction, which is even more unlikely. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5570.md b/EIPS/eip-5570.md new file mode 100644 index 0000000..0070791 --- /dev/null +++ b/EIPS/eip-5570.md @@ -0,0 +1,287 @@ +--- +eip: 5570 +title: Digital Receipt Non-Fungible Tokens +description: Non-Fungible Tokens as digital receipts for physical purchases, where the metadata represents a JSON receipt +author: Sean Darcy (@darcys22) +discussions-to: https://ethereum-magicians.org/t/idea-standard-digital-receipts-using-erc-721/9908 +status: Review +type: Standards Track +category: ERC +created: 2022-09-01 +requires: 721 +--- + +## Abstract + +This ERC proposes a standard schema for digital receipts of transactions. Digital Receipt Non-Fungible Tokens are issued by a vendor when a customer makes a purchase from their store and contains transaction details necessary for record keeping. Digital Receipt Non-Fungible Tokens extend [ERC-721](./eip-721.md) which allows for the management and ownership of unique tokens. + +## Motivation + +Purchases from online retailers include a receipt that is emailed and/or physically provided to the customer. These receipts are critical for many reasons but are provided in an analogue form which is difficult to parse by financial systems. Digital receipts have never gained traction dispite the fact that point of sales systems are already digital and the customers often want this information in their own digital systems. So we are left with a redundant Digital -> Analogue -> Digital process which requires unnecessary data entry or the use of clunky receipt-scanning applications. + +Digital receipts are relatively simple and can be specified with a schema that can be parsed into JSON or other structured formats. In addition we can prove the receipts validity by digitally signing the receipt using the vendors private keys. + +As Ethereum scales tooling will need to be developed to provide end users with features (such as receipts) already available to fiat transactions. NFTs provide a unique opportunity to link an on chain purchase with its transaction details directly through the transaction state update. If we conceptually think of a transaction as funds provided to one participant and goods provided to another, then our real life state includes two sides of a transaction, 1) Funds changing ownership and 2) goods changing ownership. NFT receipts are first class citizens of a transaction reflecting the goods changing ownership as part of the transaction state. They will bring our on chain transaction state in line with the changes happening in the real world. + +The convenience of a direct link to the transaction receipt via the transaction state is significant, other methods of distributing receipts either off chain or through smart contracts separate to the initial transaction lose this link and force the end user to manually locate the transaction details when needed. +The benefit can be demonstrated by comparing a wallet that allows a user to click through a transaction to its receipt (available immediately after purchase without any further action) verses a user needing to search through a datastore to locate a receipt for a transaction that they can see in their wallet history. + +Digital receipt as NFTs can also conceptually include other important information such as item serial numbers and delivery tracking etc. + +One of the major roadblocks to fully automating our finance world has been the difficulty in tracking transaction details. Human beings physically tracking paper receipts is archaic and NFTs on the blockchain provide a pathway for these systems to be significantly improved. + +## Specification + +Transaction Flow: + + - A customer purchases an item from an online retailer, checking out leads the customer to an option to mint a NFT. + - The smart contract provides the user with a Digital Receipt Non-Fungible Token. + - When fulfilling the order, the retailer will upload the digital receipt specified in in the JSON schema below as the metadata to the previously minted NFT. + +### Digital Receipt JSON Schema + +The JSON schema is composed of 2 parts. The root schema contains high level details of the receipt (for example Date and Vendor) and another schema for the optionally recurring line items contained in the receipt. + +#### Root Schema + +```json +{ + "id": "receipt.json#", + "description": "Receipt Schema for Digital Receipt Non-Fungible Tokens", + "type": "object", + "required": ["name", "description", "image", "receipt"], + "properties": { + "name": { + "title": "Name", + "description": "Identifies the token as a digital receipt", + "type": "string" + }, + "description": { + "title": "Description", + "description": "Brief description of a digital receipt", + "type": "string" + }, + "receipt": { + "title": "Receipt", + "description": "Details of the receipt", + "type": "object", + "required": ["id", "date", "vendor", "items"], + "properties": { + "id": { + "title": "ID", + "description": "Unique ID for the receipt generated by the vendor", + "type": "string" + }, + "date": { + "title": "Date", + "description": "Date Receipt Issued", + "type": "string", + "format": "date" + }, + "vendor": { + "title": "Vendor", + "description": "Details of the entity issuing the receipt", + "type": "object", + "required": ["name", "website"], + "properties": { + "name": { + "title": "Name", + "description": "Name of the vendor. E.g. Acme Corp", + "type": "string" + }, + "logo": { + "title": "Logo", + "description": "URL of the issuer's logo", + "type": "string", + "format": "uri" + }, + "address": { + "title": "Address", + "description": "List of strings comprising the address of the issuer", + "type": "array", + "items": { "type": "string" }, + "minItems": 2, + "maxItems": 6 + }, + "website": { + "title": "Website", + "description": "URL of the issuer's website", + "type": "string", + "format": "uri" + }, + "contact": { + "title": "Contact Details", + "description": "Details of the person to contact", + "type": "object", + "required": [], + "properties": { + "name": { + "title": "Name", + "description": "Name of the contact person", + "type": "string" + }, + "position": { + "title": "Position", + "description": "Position / Role of the contact person", + "type": "string" + }, + "tel": { + "title": "Telephone Number", + "description": "Telephone number of the contact person", + "type": "string" + }, + "email": { + "title": "Email", + "description": "Email of the contact person", + "type": "string", + "format": "email" + }, + "address": { + "title": "Address", + "description": "List of strings comprising the address of the contact person", + "type": "array", + "items": { "type": "string" }, + "minItems": 2, + "maxItems": 6 + } + } + } + } + }, + "items": { + "title": "Items", + "description": "Items included into the receipt", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "$ref": "item.json#" + } + }, + "comments": { + "title": "Comments", + "description": "Any messages/comments the issuer wishes to convey to the customer", + "type": "string" + }, + } + }, + "image": { + "title": "Image", + "description": "Viewable/Printable Image of the Digital Receipt", + "type": "string" + }, + "signature": { + "title": "Signature", + "description": "Digital signature by the vendor of receipts data", + "type": "string" + } + "extra": { + "title": "Extra", + "description": "Extra information about the business/receipt as needed", + "type": "string" + } + } +} +``` + +#### Line Items Schema + +```json +{ + "type": "object", + "id": "item.json#", + "required": ["id", "title", "date", "amount", "tax", "quantity"], + "properties": { + "id": { + "title": "ID", + "description": "Unique identifier of the goods or service", + "type": "string" + }, + "title": { + "title": "Title", + "description": "Title of the goods or service", + "type": "string" + }, + "description": { + "title": "Description", + "description": "Description of the goods or service", + "type": "string" + }, + "link": { + "title": "Link", + "description": "URL link to the web page for the product or sevice", + "type": "string", + "format": "uri" + }, + "contract": { + "title": "Contract", + "description": "URL link or hash to an external contract for this product or service", + "type": "string" + }, + "serial_number": { + "title": "Serial Number", + "description": "Serial number of the item", + "type": "string" + }, + "date": { + "title": "Supply Date", + "description": "The date the goods or service were provided", + "type": "string", + "format": "date" + }, + "amount": { + "title": "Unit Price", + "description": "Unit Price per item (excluding tax)", + "type": "number" + }, + "tax": { + "title": "Tax", + "description": "Amount of tax charged for unit", + "type": "array", + "items": { + "type": "object", + "required": ["name", "rate", "amount"], + "properties": { + "name": { + "title": "Name of Tax", + "description": "GST/PST etc", + "type": "string" + }, + "rate": { + "title": "Tax Rate", + "description": "Tax rate as a percentage", + "type": "number" + }, + "amount": { + "title": "Tax Amount", + "description": "Total amount of tax charged", + "type": "number" + } + } + } + }, + "quantity": { + "title": "Quantity", + "description": "Number of units", + "type": "integer" + } + } +} +``` + +## Rationale + +The schema introduced complies with ERC-721's metadata extension, conveniently allowing previous tools for viewing NFTs to show our receipts. The new property "receipt" contains our newly provided receipt structure and the signature property optionally allows the vendor to digitally sign the receipt structure. + +## Backwards Compatibility + +This standard is an extension of ERC-721. It is compatible with both optional extensions, Metadata and Enumerable, mentioned in ERC-721. + +## Security Considerations + +The data stored in the receipt contains personally identifying information. This information should be encrypted to ensure privacy for the customer. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). + diff --git a/EIPS/eip-5573.md b/EIPS/eip-5573.md new file mode 100644 index 0000000..16229f2 --- /dev/null +++ b/EIPS/eip-5573.md @@ -0,0 +1,330 @@ +--- +eip: 5573 +title: Sign-In with Ethereum Capabilities, ReCaps +description: Mechanism on top of Sign-In with Ethereum for informed consent to delegate capabilities with an extensible scope mechanism +author: Oliver Terbu (@awoie), Jacob Ward (@cobward), Charles Lehner (@clehner), Sam Gbafa (@skgbafa), Wayne Chang (@wyc), Charles Cunningham (@chunningham) +discussions-to: https://ethereum-magicians.org/t/eip-5573-siwe-recap +status: Draft +type: Standards Track +category: ERC +created: 2021-07-20 +requires: 4361 +--- + +## Abstract + +[ERC-4361](./eip-4361.md), or Sign-In with Ethereum (SIWE), describes how Ethereum accounts authenticate with off-chain services. This proposal, known as ReCaps, describes a mechanism on top of SIWE to give informed consent to authorize a Relying Party to exercise certain scoped capabilities. How a Relying Party authenticates against the target resource is out of scope for this specification and depends on the implementation of the target resource. + +## Motivation + +SIWE ReCaps unlock integration of protocols and/or APIs for developers by reducing user friction, onchain state and increasing security by introducing informed consent and deterministic capability objects on top of Sign-In With Ethereum (ERC-4361). + +While SIWE focuses on authenticating the Ethereum account against the service (relying party or SIWE client) initiating the SIWE flow, there is no canonical way for the authenticated Ethereum account to authorize a relying party to interact with a third-party service (resource service) on behalf of the Ethereum account. A relying party may want to interact with another service on behalf of the Ethereum account, for example a service that provides data storage for the Ethereum account. This specification introduces a mechanism that allows the service (or more generally a Relying Party) to combine authentication and authorization of such while preserving security and optimizing UX. + +Note, this approach is a similar mechanism to combining OpenID Connect (SIWE auth) and OAuth2 (SIWE ReCap) where SIWE ReCap implements capabilities-based authorization on top of the authentication provided by SIWE. + +## Specification + +This specification has three different audiences: + +- Web3 application developers that want to integrate ReCaps to authenticate with any protocols and APIs that support object capabilities. +- Protocol or API developers that want to learn how to define their own ReCaps. +- Wallet implementers that want to improve the UI for ReCaps. + +### Terms and Definitions + +- ReCap - A SIWE Message complying with this specification, i.e. containing at least one ReCap URI in the `Resources` section and the corresponding human-readable ReCap Statement appended to the SIWE `statement`. +- ReCap URI - A type of URI that resolves to a ReCap Details Object. +- ReCap Details Object - A JSON object describing the actions and optionally the resources associated with a ReCap Capability. +- Resource Service (RS) - The entity that is providing third-party services for the Ethereum account. +- SIWE Client (SC) - The entity initiating the authorization (SIWE authentication and ReCap flow). +- Relying Party (RP) - same as SC in the context of authorization. + +### Overview + +This specification defines the following: + +- ReCap SIWE Extension +- ReCap Capability + - ReCap URI Scheme + - ReCap Details Object Schema +- ReCap Translation Algorithm +- ReCap Verification + +### ReCap SIWE Extension + +A ReCap is an ERC-4361 message following a specific format that allows an Ethereum account to delegate a set of ReCap Capabilities to a Relying Party through informed consent. ReCap Capabilities MUST be represented by the final entry in the `Resources` array of the SIWE message that MUST deterministically translate the ReCap Capability in human-readable form to the `statement` field in the SIWE message using the ReCap Translation Algorithm. + +The following SIWE message fields are used to further define (or limit) the scope of all ReCap Capabilities: + +- The `URI` field MUST specify the intended Relying Party, e.g., `https://example.com`, `did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK`. It is expected that the RS authenticates the Relying Party before invoking an action for the ReCap Capability. +- The `Issued At` field MUST be used to specify the issuance date of the ReCap Capabilities. +- If present, the `Expiration Time` field MUST be used as the expiration time of the ReCap Capabilities, i.e. the time at which the RS will no longer accept an invocation of the capabilities expressed in this form. +- If present, the `Not Before` field MUST be used as the time that has to expire before the RS starts accepting invocations of the capabilities expressed in the message. + +The following is a non-normative example of a SIWE message with the SIWE ReCap Extension: + +```text +example.com wants you to sign in with your Ethereum account: +0x0000000000000000000000000000000000000000 + +I further authorize the stated URI to perform the following actions on my behalf: (1) "example": "append", "read" for "https://example.com". (2) "other": "action" for "https://example.com". (3) "example": "append", "delete" for "my:resource:uri.1". (4) "example": "append" for "my:resource:uri.2". (5) "example": "append" for "my:resource:uri.3". + +URI: did:key:example +Version: 1 +Chain ID: 1 +Nonce: mynonce1 +Issued At: 2022-06-21T12:00:00.000Z +Resources: +- urn:recap:eyJhdHQiOnsiaHR0cHM6Ly9leGFtcGxlLmNvbSI6eyJleGFtcGxlL2FwcGVuZCI6W10sImV4YW1wbGUvcmVhZCI6W10sIm90aGVyL2FjdGlvbiI6W119LCJteTpyZXNvdXJjZTp1cmkuMSI6eyJleGFtcGxlL2FwcGVuZCI6W10sImV4YW1wbGUvZGVsZXRlIjpbXX0sIm15OnJlc291cmNlOnVyaS4yIjp7ImV4YW1wbGUvYXBwZW5kIjpbXX0sIm15OnJlc291cmNlOnVyaS4zIjp7ImV4YW1wbGUvYXBwZW5kIjpbXX19LCJwcmYiOltdfQ +``` + +#### ReCap Capability + +A ReCap Capability is identified by their ReCap URI that resolves to a ReCap Details Object which defines the associated actions and optional target resources. The scope of each ReCap Capability is attenuated by common fields in the SIWE message as described in the previous chapter, e.g., `URI`, `Issued At`, `Expiration Time`, `Not Before`. + +##### ReCap URI Scheme + +A ReCap URI starts with `urn:recap:` followed by `:` and the unpadded base64url-encoded payload of the ReCap Details Object. Note, the term base64url is defined in RFC4648 - Base 64 Encoding with URL and Filename Safe Alphabet. If present, a Recap URI MUST occupy the final entry of the SIWE resource list. + +The following is a non-normative example of a ReCap Capability: + +```text +urn:recap:eyJhdHQiOnsiaHR0cHM6Ly9leGFtcGxlLmNvbS9waWN0dXJlcy8iOnsiY3J1ZC9kZWxldGUiOltdLCJjcnVkL3VwZGF0ZSI6W10sIm90aGVyL2FjdGlvbiI6W119LCJtYWlsdG86dXNlcm5hbWVAZXhhbXBsZS5jb20iOnsibXNnL3JlY2VpdmUiOlt7Im1heF9jb3VudCI6NSwidGVtcGxhdGVzIjpbIm5ld3NsZXR0ZXIiLCJtYXJrZXRpbmciXX1dLCJtc2cvc2VuZCI6W3sidG8iOiJzb21lb25lQGVtYWlsLmNvbSJ9LHsidG8iOiJqb2VAZW1haWwuY29tIn1dfX0sInByZiI6WyJiYWZ5YmVpZ2s3bHkzcG9nNnV1cHhrdTNiNmJ1YmlycjQzNGliNnRmYXltdm94NmdvdGFhYWFhYWFhYSJdfQ +``` + +##### Ability Strings + +Ability Strings identify an action or Ability within a Namespace. They are serialized as `/`. Namespaces and Abilities MUST contain only alphanumeric characters as well as the characters `.`, `*`, `_`, `+`, `-`, conforming to the regex `^[a-zA-Z0-9.*_+-]$`. The ability string as a whole MUST conform to `^[a-zA-Z0-9.*_+-]+\/[a-zA-z0-9.*_+-]+$`. For example, `crud/update` has an ability-namespace of `crud` and an ability-name of `update`. + +##### ReCap Details Object Schema + +The ReCap Details Object denotes which actions on which resources the Relying Party is authorized to invoke on behalf of the Ethereum account for the validity period defined in the SIWE message. It can also contain additional information that the RS may require to verify a capability invocation. A ReCap Details Object MUST follow the following JSON Schema: + +```jsonc +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "att": { + "type": "object", + "propertyNames": { + "format": "uri" + }, + "patternProperties": { + "^.+:.*$": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9.*_+-]+\/[a-zA-z0-9.*_+-]+$": { + "type": "array", + "items": { + "type": "object" + } + } + }, + "additionalProperties": false, + "minProperties": 1 + } + }, + "additionalProperties": false, + "minProperties": 1 + }, + "prf": { + "type": "array", + "items": { + "type": "string", + "format": "CID" + }, + "minItems": 1 + } + } +} +``` + +A ReCap Details Object defines the following properties: + +- `att`: (CONDITIONAL) If present, `att` MUST be a JSON object where each key is a URI and each value is an object containing Ability Strings as keys and a corresponding value which is an array of qualifications to the action (i.e. a restriction or requirement). The keys of the object MUST be ordered lexicographically. +- `prf`: (CONDITIONAL) If present, `prf` MUST be a JSON array of string values with at least one entry where each value is a valid Base58-encoded CID which identifies a parent capability, authorizing the Ethereum account for one or more of the entries in `att` if the SIWE `address` does not identify the controller of the `att` entries. + +Objects in the `att` field (including nested objects) MUST NOT contain duplicate keys and MUST have their keys ordered lexicographically with two steps: + +1. Sort by byte value. +2. If a string starts with another, the shorter string comes first (e.g. `msg/send` comes before `msg/send-to`) + +This is the same as the `Array.sort()` method in Javascript. In the example below, `crud/delete` must appear before `crud/update` and `other/action`, similarly `msg/receive` must appear before `msg/send`. + +The following is a non-normative example of a ReCap Capability Object with `att` and `prf`: + +```jsonc +{ + "att":{ + "https://example.com/pictures/":{ + "crud/delete": [], + "crud/update": [], + "other/action": [] + }, + "mailto:username@example.com":{ + "msg/receive": [{ + "max_count": 5, + "templates": ["newsletter", "marketing"] + }], + "msg/send": [{ "to": "someone@email.com" }, { "to": "joe@email.com" }] + } + }, + "prf":["bafybeigk7ly3pog6uupxku3b6bubirr434ib6tfaymvox6gotaaaaaaaaa"] +} +``` + +In the example above, the Relying Party is authorized to perform the actions `crud/update`, `crud/delete` and `other/action` on resource `https://example.com/pictures` without limitations for any. Additionally the Relying Party is authorized to perform actions `msg/send` and `msg/recieve` on resource `mailto:username@example.com`, where `msg/send` is limited to sending to `someone@email.com` or `joe@email.com` and `msg/recieve` is limited to a maximum of 5 and templates `newsletter` or `marketing`. Note, the Relying Party can invoke each action individually and independently from each other in the RS. Additionally the ReCap Capability Object contains some additional information that the RS will need during verification. The responsibility for defining the structure and semantics of this data lies with the RS. These action and restriction semantics are examples not intended to be universally understood. + +It is expected that RS implementers define which resources they want to expose through ReCap Details Objects and which actions they want to allow users to invoke on them. + +This example is expected to transform into the following `recap-transformed-statement` (for `URI` of `https://example.com`): + +```text +I further authorize the stated URI to perform the following actions on my behalf: (1) "crud": "delete", "update" for "https://example.com/pictures". (2) "other": "action" for "https://example.com/pictures". (3) "msg": "recieve", "send" for "mailto:username@example.com". +``` + +This example is also expected to transform into the following `recap-uri`: + +```text +urn:recap:eyJhdHQiOnsiaHR0cHM6Ly9leGFtcGxlLmNvbS9waWN0dXJlcy8iOnsiY3J1ZC9kZWxldGUiOltdLCJjcnVkL3VwZGF0ZSI6W10sIm90aGVyL2FjdGlvbiI6W119LCJtYWlsdG86dXNlcm5hbWVAZXhhbXBsZS5jb20iOnsibXNnL3JlY2VpdmUiOlt7Im1heF9jb3VudCI6NSwidGVtcGxhdGVzIjpbIm5ld3NsZXR0ZXIiLCJtYXJrZXRpbmciXX1dLCJtc2cvc2VuZCI6W3sidG8iOiJzb21lb25lQGVtYWlsLmNvbSJ9LHsidG8iOiJqb2VAZW1haWwuY29tIn1dfX0sInByZiI6WyJiYWZ5YmVpZ2s3bHkzcG9nNnV1cHhrdTNiNmJ1YmlycjQzNGliNnRmYXltdm94NmdvdGFhYWFhYWFhYSJdfQ +``` + +##### Merging Capability Objects + +Any two Recap objects can be merged together by recursive concatenation of their field elements as long as the ordering rules of the field contents is followed. For example, two recap objects: + +```jsonc +{ + "att": { + "https://example1.com": { + "crud/read": [] + } + }, + "prf": ["bafyexample1"] +} + +{ + "att": { + "https://example1.com": { + "crud/update": [{ + "max_times": 1 + }] + }, + "https://example2.com": { + "crud/delete": [] + } + }, + "prf": ["bafyexample2"] +} +``` + +combine into: + +```jsonc +{ + "att": { + "https://example1.com": { + "crud/read": [], + "crud/update": [{ + "max_times": 1 + }] + }, + "https://example2.com": { + "crud/delete": [] + } + }, + "prf": ["bafyexample1", "bafyexample2"] +} +``` + +#### ReCap Translation Algorithm + +After applying the ReCap Translation Algorithm on a given SIWE message that MAY include a pre-defined `statement`, the `recap-transformed-statement` in a ReCap SIWE message MUST conform to the following ABNF: + +```text +recap-transformed-statement = statement recap-preamble 1*(" " recap-statement-entry ".") + ; see ERC-4361 for definition of input-statement +recap-preamble = "I further authorize the stated URI to perform the following actions on my behalf:" +recap-statement-entry = "(" number ") " action-namespace ": " + action-name *("," action-name) "for" + recap-resource + ; see RFC8259 for definition of number +ability-namespace = string + ; see RFC8259 for definition of string +ability-name = string + ; see RFC8259 for definition of string +recap-resource = string + ; see RFC8259 for definition of string +``` + +The following algorithm or an algorithm that produces the same output MUST be performed to generate the SIWE ReCap Transformed Statement. + +Inputs: + +- Let `recap-uri` be a ReCap URI, which represents the ReCap Capabilities that are to be encoded in the SIWE message, and which contains a ReCap Details Object which conforms to the ReCap Details Object Schema. +- [Optional] Let `statement` be the statement field of the input SIWE message conforming to ERC-4361. +Algorithm: +- Let `recap-transformed-statement` be an empty string value. +- If `statement` is present, do the following: + - Append the value of the `statement` field of `siwe` to `recap-transformed-statement`. + - Append a single space character `" "` to `recap-transformed-statement`. +- Append the following string to `recap-transformed-statement`: `"I further authorize the stated URI to perform the following actions on my behalf:"`. +- Let `numbering` be an integer starting with 1. +- Let `attenuations` be the `att` field of the ReCap Details Object +- For each key and value pair in `attenuations` (starting with the first entry), perform the following: + - Let `resource` be the key and `abilities` be the value + - Group the keys of the `abilities` object by their `ability-namespace` + - For each `ability-namespace`, perform the following: + - Append the string concatenation of `" ("`, `numbering`, `")"` to `recap-transformed-statement`. + - Append the string concatenation of `"`, `ability-namespace`, `":` to `recap-transformed-statement`. + - For each `ability-name` in the `ability-namespace` group, perform the following: + - Append the string concatenation of `"`, `ability-name`, `"` to `recap-transformed-statement` + - If not the final `ability-name`, append `,` to `recap-transformed-statement` + - Append `for "`, `resource`, `".` to `recap-transformed-statement` + - Increase `numbering` by 1 +- Return `recap-transformed-statement`. + +#### ReCap Verification Algorithm + +The following algorithm or an algorithm that produces the same output MUST be performed to verify a SIWE ReCap. + +Inputs: + +- Let `recap-siwe` be the input SIWE message conforming to ERC-4361 and this EIP. +- Let `siwe-signature` be the output of signing `recap-siwe`, as defined in ERC-4361. +Algorithm: +- Perform ERC-4361 signature verification with `recap-siwe` and `siwe-signature` as inputs. +- Let `uri` be the uri field of `recap-siwe`. +- Let `recap-uri` be a recap URI taken from the last entry of the resources field of `recap-siwe`. +- Let `recap-transformed-statement` be the result of performing the above `ReCap Translation Algorithm` with `uri` and `recap-uri` as input. +- Assert that the statement field of `recap-siwe` ends with `recap-transformed-statement`. + +### Implementer's Guide + +TBD + +#### Web3 Application Implementers + +TBD + +#### Wallet Implementers + +TBD + +#### Protocol or API Implementers + +TBD + +## Rationale + +TBD + +## Security Considerations + +Resource service implementer's should not consider ReCaps as bearer tokens but instead require to authenticate the Relying Party in addition. The process of authenticating the Relying Party against the resource service is out of scope of this specification and can be done in various different ways. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5585.md b/EIPS/eip-5585.md new file mode 100644 index 0000000..8e5462b --- /dev/null +++ b/EIPS/eip-5585.md @@ -0,0 +1,168 @@ +--- +eip: 5585 +title: ERC-721 NFT Authorization +description: Allows NFT owners to authorize other users to use their NFTs. +author: Veega Labs (@VeegaLabsOfficial), Sean NG (@ngveega), Tiger (@tiger0x), Fred (@apan), Fov Cao (@fovcao) +discussions-to: https://ethereum-magicians.org/t/nft-authorization-erc721-extension/10661 +status: Review +type: Standards Track +category: ERC +created: 2022-08-15 +requires: 721 +--- + +## Abstract + +This EIP separates the [ERC-721](./eip-721.md) NFT's commercial usage rights from it's ownership to allow for the independent management of those rights. + +## Motivation + +Most NFTs have a simplified ownership verification mechanism, with a sole owner of an NFT. Under this model, other rights, such as display, or creating derivative works or distribution, are not possible to grant, limiting the value and commercialization of NFTs. Therefore, the separation of an NFT's ownership and user rights can enhance its commercial value. + +Commercial right is a broad concept based on the copyright, including the rights of copy, display, distribution, renting, commercial use, modify, reproduce and sublicense etc. With the development of the Metaverse, NFTs are becoming more diverse, with new use cases such as digital collections, virtual real estate, music, art, social media, and digital asset of all kinds. The copyright and authorization based on NFTs are becoming a potential business form. + +## Specification + +The keywords “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. + +### Contract Interface + +```solidity +interface IERC5585 { + + struct UserRecord { + address user; + string[] rights; + uint256 expires + } + + /// @notice Get all available rights of this NFT project + /// @return All the rights that can be authorized to the user + function getRights() external view returns(string[]); + + /// @notice NFT holder authorizes all the rights of the NFT to a user for a specified period of time + /// @dev The zero address indicates there is no user + /// @param tokenId The NFT which is authorized + /// @param user The user to whom the NFT is authorized + /// @param duration The period of time the authorization lasts + function authorizeUser(uint256 tokenId, address user, uint duration) external; + + /// @notice NFT holder authorizes specific rights to a user for a specified period of time + /// @dev The zero address indicates there is no user. It will throw exception when the rights are not defined by this NFT project + /// @param tokenId The NFT which is authorized + /// @param user The user to whom the NFT is authorized + /// @param rights Rights autorised to the user, such as renting, distribution or display etc + /// @param duration The period of time the authorization lasts + function authorizeUser(uint256 tokenId, address user, string[] rights, uint duration) external; + + /// @notice NFT holder extends the duration of authorization + /// @dev The zero address indicates there is no user. It will throw exception when the rights are not defined by this NFT project + /// @param tokenId The NFT which has been authorized + /// @param user The user to whom the NFT has been authorized + /// @param duration The new duration of the authorization + function extendDuration(uint256 tokenId, address user, uint duration) external; + + /// @notice NFT holder updates the rights of authorization + /// @dev The zero address indicates there is no user + /// @param tokenId The NFT which has been authorized + /// @param user The user to whom the NFT has been authorized + /// @param rights New rights autorised to the user + function updateUserRights(uint256 tokenId, address user, string[] rights) external; + + /// @notice Get the authorization expired time of the specified NFT and user + /// @dev The zero address indicates there is no user + /// @param tokenId The NFT to get the user expires for + /// @param user The user who has been authorized + /// @return The authorization expired time + function getExpires(uint256 tokenId, address user) external view returns(uint); + + /// @notice Get the rights of the specified NFT and user + /// @dev The zero address indicates there is no user + /// @param tokenId The NFT to get the rights + /// @param user The user who has been authorized + /// @return The rights has been authorized + function getUserRights(uint256 tokenId, address user) external view returns(string[]); + + /// @notice The contract owner can update the number of users that can be authorized per NFT + /// @param userLimit The number of users set by operators only + function updateUserLimit(unit256 userLimit) external onlyOwner; + + /// @notice resetAllowed flag can be updated by contract owner to control whether the authorization can be revoked or not + /// @param resetAllowed It is the boolean flag + function updateResetAllowed(bool resetAllowed) external onlyOwner; + + /// @notice Check if the token is available for authorization + /// @dev Throws if tokenId is not a valid NFT + /// @param tokenId The NFT to be checked the availability + /// @return true or false whether the NFT is available for authorization or not + function checkAuthorizationAvailability(uint256 tokenId) public view returns(bool); + + /// @notice Clear authorization of a specified user + /// @dev The zero address indicates there is no user. The function works when resetAllowed is true and it will throw exception when false + /// @param tokenId The NFT on which the authorization based + /// @param user The user whose authorization will be cleared + function resetUser(uint256 tokenId, address user) external; + + + /// @notice Emitted when the user of a NFT is changed or the authorization expires time is updated + /// param tokenId The NFT on which the authorization based + /// param indexed user The user to whom the NFT authorized + /// @param rights Rights autorised to the user + /// param expires The expires time of the authorization + event authorizeUser(uint256 indexed tokenId, address indexed user, string[] rights, uint expires); + + /// @notice Emitted when the number of users that can be authorized per NFT is updated + /// @param userLimit The number of users set by operators only + event updateUserLimit(unit256 userLimit); +} +``` + +The `getRights()` function MAY be implemented as pure and view. + +The `authorizeUser(uint256 tokenId, address user, uint duration)` function MAY be implemented as `public` or `external`. + +The `authorizeUser(uint256 tokenId, address user, string[] rights; uint duration)` function MAY be implemented as `public` or `external`. + +The `extendDuration(uint256 tokenId, address user, uint duration)` function MAY be implemented as `public` or `external`. + +The `updateUserRights(uint256 tokenId, address user, string[] rights)` function MAY be implemented as `public` or `external`. + +The `getExpires(uint256 tokenId, address user)` function MAY be implemented as `pure` or `view`. + +The `getUserRights(uint256 tokenId, address user)` function MAY be implemented as pure and view. + +The `updateUserLimit(unit256 userLimit)` function MAY be implemented as`public` or `external`. + +The `updateResetAllowed(bool resetAllowed)` function MAY be implemented as `public` or `external`. + +The `checkAuthorizationAvailability(uint256 tokenId)` function MAY be implemented as `pure` or `view`. + +The `resetUser(uint256 tokenId, address user)` function MAY be implemented as `public` or `external`. + +The `authorizeUser` event MUST be emitted when the user of a NFT is changed or the authorization expires time is updated. + +The `updateUserLimit` event MUST be emitted when the number of users that can be authorized per NFT is updated. + +## Rationale + +First of all, NFT contract owner can set the maximum number of authorized users to each NFT and whether the NFT owner can cancel the authorization at any time to protect the interests of the parties involved. + +Secondly, there is a resetAllowed flag to control the rights between the NFT owner and the users for the contract owner. If the flag is set to true, then the NFT owner can disable usage rights of all authorized users at any time. + +Thirdly, the rights within the user record struct is used to store what rights has been authorized to a user by the NFT owner, in other words, the NFT owner can authorize a user with specific rights and update it when necessary. + +Finally, this design can be seamlessly integrated with third parties. It is an extension of ERC-721, therefore it can be easily integrated into a new NFT project. Other projects can directly interact with these interfaces and functions to implement their own types of transactions. For example, an announcement platform could use this EIP to allow all NFT owners to make authorization or deauthorization at any time. + +## Backwards Compatibility + +This standard is compatible with [ERC-721](./eip-721.md) since it is an extension of it. + +## Security Considerations + +When the resetAllowed flag is false, which means the authorization can not be revoked by NFT owner during the period of authorization, users of the EIP need to make sure the authorization fee can be fairly assigned if the NFT was sold to a new holder. + +Here is a solution for taking reference: the authorization fee paid by the users can be held in an escrow contract for a period of time depending on the duration of the authorization. For example, if the authorization duration is 12 months and the fee in total is 10 ETH, then if the NFT is transferred after 3 months, then only 2.5 ETH would be sent and the remaining 7.5 ETH would be refunded. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5593.md b/EIPS/eip-5593.md new file mode 100644 index 0000000..3f68a16 --- /dev/null +++ b/EIPS/eip-5593.md @@ -0,0 +1,87 @@ +--- +eip: 5593 +title: Restrict Ethereum Provider API Injection +description: Wallet guidance for restricting Ethereum Provider API access to secure contexts for improved privacy and security for wallet users. +author: Yan Zhu (@diracdeltas), Brian R. Bondy (@bbondy), Andrea Brancaleoni (@thypon), Kyle Den Hartog (@kdenhartog) +discussions-to: https://ethereum-magicians.org/t/rfc-limiting-provider-object-injection-to-secure-contexts/10670 +status: Draft +type: Standards Track +category: Interface +created: 2022-09-05 +requires: 1193 +--- + +## Abstract + +Historically the web platform has had a notion of “powerful” APIs like those defined in W3C's Geolocation specification and W3C's Mediastreams specification, which are subject to additional security restrictions such as those defined by W3C's secure contexts specification. Since the Ethereum Provider APIs allow dApp websites to request access to sensitive user data and to request use of user funds, new Ethereum Provider APIs generally should align to the security considerations defined by W3C's Secure Context specification in order to better protect the users data and users funds managed via the web. + +### Author's Note + +Unfortunately, because of a difference in interpretations by EIP editors of RFC 2119 terminology around linking in [EIP-1](./eip-1.md), the authors cannot directly link to other W3C specifications which this EIP builds upon. The author's attempted to provide as much context as possible within the text while complying with the editor bot in order to get this merged. If this policy is updated or further clarified before this EIP reaches final call in the future this EIP will be updated with links. + +## Motivation + +Wallets are oftentimes maintaining security and safety of users' funds that can be equivalent to large portions of money. For this reason, it's a good idea to restrict access to the Ethereum Provider APIs to align it with other powerful APIs on the web platform. This will assist in reducing the surface area that attacks can be conducted to access users funds or data. Additionally, by adding in restrictions we're reducing the surface area that malicious web pages could fingerprint the user via the Ethereum Provider APIs providing some additional privacy benefits. An example of a specific attack that's avoided by this is one where a malicious advertisement is loaded on a legitimate dApp that attempts to interact with a users wallet to maliciously request the user to access funds. With this EIP implemented the advertisement frame would be considered a third-party iframe and therefore would not have the Ethereum Provider API injected into it's sub frame because it's not a secure context. + +## Specification + +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. + +### Restrictions for providers + +The provider objects, e.g. `window.ethereum`, are expected to only inject the Ethereum Provider APIs in secure context when conforming with this specification. The following restrictions are REQUIRED for conformant wallets: + +- Provider objects MAY be accessible in private (incognito) windows. +- The origin MUST be a "potentially trustworthy origin" (defined in W3C's Secure Contexts specification in section 3.1) to have access to `window.ethereum`. This can be checked using `window.isSecureContext`, including inside iframes. + - Secure contexts include sites that are served from HTTPS but also HTTP `localhost`. + - The User Agent implementation MAY also support configured [potentially trustworthy origins] that would normally not be considered trustworthy if the user configures their User Agent to do so. See section 7.2 titled "Development Environments" of W3C's Secure Contexts specification for additional details. For example, in Chromium based User Agents this is done via the `chrome://flags/#unsafely-treat-insecure-origin-as-secure` flag. +- By default the Ethereum Provider APIs MUST NOT be exposed to third-party iframes. +- `window.ethereum` MUST be `undefined` in an iframe where `window.isSecureContext` returns `false` in that iframe. +- If the iframe is a third party to the top-level secure origin, it SHOULD be blocked. +- If the iframe is first-party to the top-level origin AND the `sandbox` attribute is set on the iframe, the provider object MUST be blocked. If the `sandbox` attribute is set to `sandbox="allow-same-origin"` it MUST be injected for a first party frame. + - Note `"allow-same-origin"` does nothing if the iframe is third-party. The case of the third party iframe is dictated by the usage of the `allow` attribute and the Permissions API as defined in the rule above. + +## Rationale + +By limiting the capabilities of where the Ethereum Provider APIs are being injected we can reduce the surface area of where attacks can be executed. Given the sensitivity of data that's passed to the Ethereum Provider APIs some basic levels of authentication and confidentiality should be met in order to ensure that request data is not being intercepted or tampered with. While there have been attempts to [limit request access via the wallet](./eip-2255.md) interface itself, there have not been limitations that have been set to where these Ethereum Provider APIs are expected to be or not be injected. Since the secure contexts web platform API is a well developed boundary that's been recommended by W3C and the fact that the Ethereum Provider APIs are extending the traditional web platform APIs, no other alternative solutions have been considered in order to extend current established prior art. + + +## Backwards Compatibility + +Wallet extensions SHOULD consider adding a "developer mode" toggle via a UX so that dApp developers have the capability to disable the insecure context (http) check for the `http://localhost:` origin only in the event that localhost does not return `true` for secure context. See section 5.2 of W3C's Secure Context specification for more details. This will allow dApp developers to be able to continue to host dApps on the localhost origin if a User Agent has chosen to not already consider localhost a secure context. All major User Agent implementations tested do consider localhost a secure context already. This toggle MUST be set to disabled by default. + +## Test Cases + +### Required Test Cases + +- Top level `http://a.com` -> blocked (insecure/top level) +- Top level `https://a.com` -> allowed +- Top level `https://a.com` with `