forked from DecentralizedClimateFoundation/DCIPs
Adding EIPs website fork with some Gem Updates
This commit is contained in:
parent
3aa53ebbd7
commit
58b766a923
|
@ -0,0 +1,23 @@
|
||||||
|
---
|
||||||
|
layout: default
|
||||||
|
---
|
||||||
|
|
||||||
|
<style type="text/css" media="screen">
|
||||||
|
.container {
|
||||||
|
margin: 10px auto;
|
||||||
|
max-width: 600px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
margin: 30px 0;
|
||||||
|
font-size: 4em;
|
||||||
|
line-height: 1;
|
||||||
|
letter-spacing: -1px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<h1>404</h1>
|
||||||
|
<p><strong>Page not found :(</strong></p>
|
||||||
|
<p>The requested page could not be found.</p>
|
||||||
|
</div>
|
|
@ -0,0 +1,402 @@
|
||||||
|
---
|
||||||
|
eip: 1
|
||||||
|
title: EIP Purpose and Guidelines
|
||||||
|
status: Living
|
||||||
|
type: Meta
|
||||||
|
author: Martin Becze <mb@ethereum.org>, Hudson Jameson <hudson@ethereum.org>, 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-###/<filename>`. 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:
|
||||||
|
|
||||||
|
<!-- markdownlint-capture -->
|
||||||
|
<!-- markdownlint-disable code-block-style -->
|
||||||
|
|
||||||
|
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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<!-- markdownlint-restore -->
|
||||||
|
|
||||||
|
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).
|
|
@ -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/
|
|
@ -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.
|
|
@ -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).
|
|
@ -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).
|
|
@ -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).
|
|
@ -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`
|
||||||
|
|
|
@ -0,0 +1,119 @@
|
||||||
|
---
|
||||||
|
eip: 1015
|
||||||
|
title: Configurable On Chain Issuance
|
||||||
|
author: Alex Van de Sande <avsa@ethereum.org>
|
||||||
|
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).
|
|
@ -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).
|
|
@ -0,0 +1,59 @@
|
||||||
|
---
|
||||||
|
eip: 1051
|
||||||
|
title: Overflow checking for the EVM
|
||||||
|
author: Nick Johnson <arachnid@notdot.net>
|
||||||
|
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).
|
|
@ -0,0 +1,78 @@
|
||||||
|
---
|
||||||
|
eip: 1052
|
||||||
|
title: EXTCODEHASH opcode
|
||||||
|
author: Nick Johnson <arachnid@notdot.net>, Paweł Bylica <pawel@ethereum.org>
|
||||||
|
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).
|
|
@ -0,0 +1,286 @@
|
||||||
|
---
|
||||||
|
eip: 1056
|
||||||
|
title: Ethereum Lightweight Identity
|
||||||
|
author: Pelle Braendgaard <pelle.braendgaard@consensys.net>, Joel Torstensson <oed@consensys.net>
|
||||||
|
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).
|
||||||
|
|
|
@ -0,0 +1,575 @@
|
||||||
|
---
|
||||||
|
eip: 1057
|
||||||
|
title: ProgPoW, a Programmatic Proof-of-Work
|
||||||
|
author: Greg Colvin <greg@colvin.org>, Andrea Lanfranchi (@AndreaLanfranchi), Michael Carter (@bitsbetrippin), IfDefElse <ifdefelse@protonmail.com>
|
||||||
|
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.
|
|
@ -0,0 +1,84 @@
|
||||||
|
---
|
||||||
|
eip: 1062
|
||||||
|
title: Formalize IPFS hash into ENS(Ethereum Name Service) resolver
|
||||||
|
author: Phyrex Tsai <phyrex@portal.network>, 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).
|
||||||
|
|
||||||
|
|
|
@ -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 256<sup>4</sup> (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).
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,229 @@
|
||||||
|
---
|
||||||
|
eip: 1077
|
||||||
|
title: Gas relay for contract calls
|
||||||
|
author: Alex Van de Sande <avsa@ethereum.org>, 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
|
|
@ -0,0 +1,121 @@
|
||||||
|
---
|
||||||
|
eip: 1078
|
||||||
|
title: Universal login / signup using ENS subdomains
|
||||||
|
author: Alex Van de Sande <avsa@ethereum.org>
|
||||||
|
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).
|
|
@ -0,0 +1,184 @@
|
||||||
|
---
|
||||||
|
eip: 1080
|
||||||
|
title: Recoverable Token
|
||||||
|
author: Bradley Leatherwood <bradleat@inkibra.com>
|
||||||
|
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).
|
|
@ -0,0 +1,121 @@
|
||||||
|
---
|
||||||
|
eip: 1081
|
||||||
|
title: Standard Bounties
|
||||||
|
author: Mark Beylin <mark.beylin@consensys.net>, Kevin Owocki <kevin.owocki@consensys.net>, 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).
|
|
@ -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).
|
|
@ -0,0 +1,138 @@
|
||||||
|
---
|
||||||
|
eip: 1102
|
||||||
|
title: Opt-in account exposure
|
||||||
|
author: Paul Bouchon <mail@bitpshr.net>, 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<Array<string>>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 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<any>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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).
|
|
@ -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<sup>[1]</sup> | 150 |
|
||||||
|
| `ECMUL` | `0x07` | 40 000<sup>[1]</sup> | 6 000 |
|
||||||
|
| Pairing check | `0x08` | 80 000 * k + 100 000<sup>[2]</sup>| 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<sup>[3]</sup>.
|
||||||
|
|
||||||
|
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).
|
|
@ -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).
|
File diff suppressed because it is too large
Load Diff
|
@ -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).
|
|
@ -0,0 +1,152 @@
|
||||||
|
---
|
||||||
|
eip: 1132
|
||||||
|
title: Extending ERC20 with token locking capability
|
||||||
|
author: nitika-goel <nitika@govblocks.io>
|
||||||
|
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).
|
|
@ -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).
|
|
@ -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
|
||||||
|
|
||||||
|
<dl>
|
||||||
|
<dt>Oracle</dt>
|
||||||
|
<dd>An entity which reports data to the blockchain.</dd>
|
||||||
|
|
||||||
|
<dt>Oracle consumer</dt>
|
||||||
|
<dd>A smart contract which receives data from an oracle.</dd>
|
||||||
|
|
||||||
|
<dt>ID</dt>
|
||||||
|
<dd>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.</dd>
|
||||||
|
|
||||||
|
<dt>Result</dt>
|
||||||
|
<dd>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.</dd>
|
||||||
|
|
||||||
|
<dt>Report</dt>
|
||||||
|
<dd>A pair (ID, result) which an oracle sends to an oracle consumer.</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
```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).
|
|
@ -0,0 +1,710 @@
|
||||||
|
---
|
||||||
|
eip: 1155
|
||||||
|
title: Multi Token Standard
|
||||||
|
author: Witek Radomski <witek@enjin.io>, Andrew Cooke <ac0dem0nk3y@gmail.com>, Philippe Castonguay <pc@horizongames.net>, James Therien <james@turing-complete.com>, Eric Binet <eric@enjin.io>, Ronan Sandford <wighawag@gmail.com>
|
||||||
|
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 `<uint128: base token id><uint128: index of non-fungible>`.
|
||||||
|
|
||||||
|
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 `<uint128: base token id><uint128: zero>`. 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).
|
|
@ -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).
|
|
@ -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).
|
|
@ -0,0 +1,156 @@
|
||||||
|
---
|
||||||
|
eip: 1178
|
||||||
|
title: Multi-class Token Standard
|
||||||
|
author: Albert Chon <achon@stanford.edu>
|
||||||
|
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.)
|
||||||
|
|
||||||
|
<!--
|
||||||
|
ActionPrior State_to addressNew StateEventClear unset approvalClear0ClearNoneSet new approvalClearXSet to XApproval(owner, X, _classId)Change approvalSet to XYSet to YApproval(owner, Y, _classId)Reaffirm approvalSet to XXSet to XApproval(owner, X, _classId)Clear approvalSet to X0ClearApproval(owner, 0, _classId)
|
||||||
|
Note: ANY change of ownership of an MCFT – whether directly through the `transfer` and `transferFrom` methods defined in this interface, or through any other mechanism defined in the conforming contract – MUST clear any and all approvals for the transferred MCFT. The implicit clearing of approval via ownership transfer MUST also fire the event `Approval(0, _classId)` if there was an outstanding approval. (i.e. All actions that transfer ownership must emit the same Approval event, if any, as would emitted by calling `approve(0, _classId)`.)-->
|
||||||
|
|
||||||
|
**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).
|
||||||
|
|
|
@ -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).
|
|
@ -0,0 +1,138 @@
|
||||||
|
---
|
||||||
|
eip: 1186
|
||||||
|
title: RPC-Method to get Merkle Proofs - eth_getProof
|
||||||
|
author: Simon Jentzsch <simon.jentzsch@slock.it>, Christoph Jentzsch <christoph.jentzsch@slock.it>
|
||||||
|
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).
|
|
@ -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).
|
||||||
|
|
|
@ -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<unknown>;
|
||||||
|
```
|
||||||
|
|
||||||
|
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<unknown>;
|
||||||
|
```
|
||||||
|
|
||||||
|
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 }`.
|
|
@ -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).
|
|
@ -0,0 +1,230 @@
|
||||||
|
---
|
||||||
|
eip: 1203
|
||||||
|
title: ERC-1203 Multi-Class Token Standard (ERC-20 Extension)
|
||||||
|
author: Jeff Huang <jeffishjeff@gmail.com>, Min Zu <crawlregister@gmail.com>
|
||||||
|
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).
|
|
@ -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).
|
|
@ -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).
|
|
@ -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).
|
|
@ -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).
|
|
@ -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).
|
|
@ -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).
|
|
@ -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).
|
|
@ -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).
|
|
@ -0,0 +1,42 @@
|
||||||
|
---
|
||||||
|
eip: 1285
|
||||||
|
title: Increase Gcallstipend gas in the CALL opcode
|
||||||
|
author: Ben Kaufman <ben@daostack.io>, Adam Levi <adam@daostack.io>
|
||||||
|
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).
|
|
@ -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).
|
|
@ -0,0 +1,176 @@
|
||||||
|
---
|
||||||
|
eip: 1319
|
||||||
|
title: Smart Contract Package Registry Interface
|
||||||
|
author: Piper Merriam <piper@ethereum.org>, Christopher Gewecke <christophergewecke@gmail.com>, g. nicholas d'andrea <nick.dandrea@consensys.net>, 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).
|
|
@ -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).
|
|
@ -0,0 +1,247 @@
|
||||||
|
---
|
||||||
|
eip: 1337
|
||||||
|
title: Subscriptions on the blockchain
|
||||||
|
author: Kevin Owocki <kevin@gitcoin.co>, Andrew Redden <andrew@blockcrushr.com>, Scott Burke <scott@blockcrushr.com>, Kevin Seagraves <k.s.seagraves@gmail.com>, Luka Kacil <luka.kacil@gmail.com>, Štefan Šimec <stefan.simec@gmail.com>, Piotr Kosiński (@kosecki123), ankit raj <tradeninja7@gmail.com>, John Griffin <john@atchai.com>, Nathan Creswell <nathantr@gmail.com>
|
||||||
|
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 - <richard@gnosis.pm> 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).
|
|
@ -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).
|
|
@ -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).
|
|
@ -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/).
|
|
@ -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).
|
|
@ -0,0 +1,386 @@
|
||||||
|
---
|
||||||
|
eip: 137
|
||||||
|
title: Ethereum Domain Name Service - Specification
|
||||||
|
author: Nick Johnson <arachnid@notdot.net>
|
||||||
|
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:
|
||||||
|
|
||||||
|
<pre><domain> ::= <label> | <domain> "." <label>
|
||||||
|
<label> ::= any valid string label per [UTS46](https://unicode.org/reports/tr46/)
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
### <a name="addr"></a>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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
|
@ -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).
|
|
@ -0,0 +1,88 @@
|
||||||
|
---
|
||||||
|
eip: 1386
|
||||||
|
title: Attestation management contract
|
||||||
|
author: Weiwu Zhang <a@colourful.land>, James Sangalli <j.l.sangalli@gmail.com>
|
||||||
|
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
|
|
@ -0,0 +1,49 @@
|
||||||
|
---
|
||||||
|
eip: 1387
|
||||||
|
title: Merkle Tree Attestations with Privacy enabled
|
||||||
|
author: Weiwu Zhang <a@colourful.land>, James Sangalli <j.l.sangalli@gmail.com>
|
||||||
|
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
|
|
@ -0,0 +1,87 @@
|
||||||
|
---
|
||||||
|
eip: 1388
|
||||||
|
title: Attestation Issuers Management List
|
||||||
|
author: Weiwu Zhang <a@colourful.land>, James Sangalli <j.l.sangalli@gmail.com>
|
||||||
|
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
|
|
@ -0,0 +1,58 @@
|
||||||
|
---
|
||||||
|
eip: 140
|
||||||
|
title: REVERT instruction
|
||||||
|
author: Alex Beregszaszi (@axic), Nikolai Mushegian <nikolai@nexusdev.us>
|
||||||
|
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).
|
|
@ -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).
|
|
@ -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).
|
|
@ -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.
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
To discuss:
|
||||||
|
|
||||||
|
- Can/should an evicted account be allowed to be un-evicted when paying past due rent?
|
||||||
|
-->
|
|
@ -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).
|
|
@ -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 × 10<sup>77</sup>), 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).
|
|
@ -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).
|
|
@ -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 <howard@startengine.com>, David Zhang <david@startengine.com>
|
||||||
|
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).
|
|
@ -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=<enr-root> l=<link-root> seq=<sequence-number> sig=<signature>
|
||||||
|
|
||||||
|
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:<h₁>,<h₂>,...,<hₙ>` is an intermediate tree entry containing
|
||||||
|
hashes of subtree entries.
|
||||||
|
- `enrtree://<key>@<fqdn>` 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:<node-record>` 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).
|
|
@ -0,0 +1,117 @@
|
||||||
|
---
|
||||||
|
eip: 1462
|
||||||
|
title: Base Security Token
|
||||||
|
author: Maxim Kupriianov <mk@atlant.io>, Julian Svirsky <js@atlant.io>
|
||||||
|
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).
|
|
@ -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).
|
File diff suppressed because it is too large
Load Diff
|
@ -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).
|
|
@ -0,0 +1,544 @@
|
||||||
|
---
|
||||||
|
eip: 1484
|
||||||
|
title: Digital Identity Aggregator
|
||||||
|
author: Anurag Angara <anurag.angara@gmail.com>, Andy Chorlian <andychorlian@gmail.com>, Shane Hampton <shanehampton1@gmail.com>, Noah Zinsmeister <noahwz@gmail.com>
|
||||||
|
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).
|
|
@ -0,0 +1,200 @@
|
||||||
|
---
|
||||||
|
eip: 1485
|
||||||
|
title: TEthashV1
|
||||||
|
author: trustfarm <trustfarm.info@gmail.com>, trustfarm <cpplover@trustfarm.net>
|
||||||
|
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/).
|
|
@ -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).
|
|
@ -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<sup>[1](https://github.com/ethereum/EIPs/issues/90)</sup> plus EIP-114<sup>[2](https://github.com/ethereum/EIPs/issues/114)</sup>). 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
|
|
@ -0,0 +1,351 @@
|
||||||
|
---
|
||||||
|
eip: 1504
|
||||||
|
title: Upgradable Smart Contract
|
||||||
|
author: Kaidong Wu <wukd94@pku.edu.cn>, Chuqiao Ren <cr025@bucknell.edu>, Ruthia He <rujiahe@gmail.com>, Yun Ma <mayun@pku.edu.cn>, Xuanzhe Liu <liuxuanzhe@pku.edu.cn>
|
||||||
|
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).
|
|
@ -0,0 +1,267 @@
|
||||||
|
---
|
||||||
|
eip: 152
|
||||||
|
title: Add BLAKE2 compression function `F` precompile
|
||||||
|
author: Tjaden Hess <tah83@cornell.edu>, 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).
|
|
@ -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).
|
|
@ -0,0 +1,468 @@
|
||||||
|
---
|
||||||
|
eip: 1538
|
||||||
|
title: Transparent Contract Standard
|
||||||
|
author: Nick Mudge <nick@perfectabstractions.com>
|
||||||
|
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.
|
||||||
|
<!--
|
||||||
|
A transparent contract must implement the [ERC-165 Standard Interface Detection standard](./eip-165.md) via a delegate contract by adding the `supportsInterface` function using the `updateContract` function. The interfaceID for the ERC1538 standard is `0x61455567`.
|
||||||
|
-->
|
||||||
|
|
||||||
|
### 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).
|
|
@ -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).
|
|
@ -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).
|
|
@ -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 `<account>.<MachineName>`. 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": ["<account>[.<MachineName>]", "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).
|
|
@ -0,0 +1,116 @@
|
||||||
|
---
|
||||||
|
eip: 1577
|
||||||
|
title: contenthash field for ENS
|
||||||
|
author: Dean Eigenmann <dean@ens.domains>, Nick Johnson <nick@ens.domains>
|
||||||
|
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:
|
||||||
|
|
||||||
|
```
|
||||||
|
<protoCode uvarint><value []byte>
|
||||||
|
```
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
```
|
||||||
|
<protoCode uvarint><cid-version><multicodec-content-type><multihash-content-address>
|
||||||
|
```
|
||||||
|
|
||||||
|
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).
|
|
@ -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
|
|
@ -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).
|
|
@ -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).
|
|
@ -0,0 +1,171 @@
|
||||||
|
---
|
||||||
|
eip: 1592
|
||||||
|
title: Address and ERC20-compliant transfer rules
|
||||||
|
author: Cyril Lapinte <cyril.lapinte@mtpelerin.com>, Laurent Aapro <laurent.aapro@mtpelerin.com>
|
||||||
|
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.
|
|
@ -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
|
|
@ -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.
|
|
@ -0,0 +1,300 @@
|
||||||
|
---
|
||||||
|
eip: 1613
|
||||||
|
title: Gas stations network
|
||||||
|
author: Yoav Weiss <yoav@tabookey.com>, Dror Tirosh <dror@tabookey.com>, Alex Forshtat <alex@tabookey.com>
|
||||||
|
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).
|
|
@ -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).
|
|
@ -0,0 +1,248 @@
|
||||||
|
---
|
||||||
|
eip: 162
|
||||||
|
title: Initial ENS Hash Registrar
|
||||||
|
author: Maurelian, Nick Johnson <nick@ethereum.org>, Alex Van de Sande <avsa@ethereum.org>
|
||||||
|
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
|
||||||
|
|
||||||
|
<!-- /MarkdownTOC -->
|
||||||
|
|
||||||
|
## 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. <sup>1</sup> | Bidder | 0.5% |
|
||||||
|
| An expired sealed bid is cancelled. <sup>2</sup> | Canceler | 0.5% |
|
||||||
|
| A registered hash is reported as invalid. <sup>3</sup> | Reporter | 50% |
|
||||||
|
| A registered hash is reported as invalid. <sup>3</sup> | 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).
|
|
@ -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 and oracles for time calculation
|
||||||
|
- GCD
|
||||||
|
- Miners
|
||||||
|
- Sidechain-compatible (and preferable)
|
||||||
|
- The `update` function
|
||||||
|
- Multi-hop streams
|
||||||
|
-->
|
||||||
|
|
||||||
|
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).
|
|
@ -0,0 +1,174 @@
|
||||||
|
---
|
||||||
|
eip: 1633
|
||||||
|
title: Re-Fungible Token Standard (RFT)
|
||||||
|
author: Billy Rennekamp (@okwme), Dan Long <dan@artblx.com>, Kiryl Yermakou <kiryl@artblx.com>, Nate van der Ende <nate@artblx.com>
|
||||||
|
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).
|
|
@ -0,0 +1,235 @@
|
||||||
|
---
|
||||||
|
eip: 165
|
||||||
|
title: Standard Interface Detection
|
||||||
|
author: Christian Reitwießner <chris@ethereum.org>, Nick Johnson <nick@ethereum.org>, Fabian Vogelsteller <fabian@lukso.network>, Jordi Baylina <jordi@baylina.cat>, Konrad Feldmeier <konrad.feldmeier@brainbot.com>, William Entriken <github.com@phor.net>
|
||||||
|
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).
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue