diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000000000..21f1400916aee --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @eth-bot diff --git a/.github/workflows/auto-label-bot.yml b/.github/workflows/auto-label-bot.yml index 217da0fd0e3df..1d67c8536541a 100644 --- a/.github/workflows/auto-label-bot.yml +++ b/.github/workflows/auto-label-bot.yml @@ -1,12 +1,14 @@ on: pull_request_target: +name: Auto Label Bot jobs: jekyll-label-action: - name: Automatic Label Bot + name: Label runs-on: ubuntu-latest steps: - - uses: Pandapip1/jekyll-label-action@b5fe10dda78ac1cd09f60b30008509932c40ac9f + - uses: Pandapip1/jekyll-label-action@5fcfb7bef793a76df290f8b491a5529accb7ae46 with: token: ${{ secrets.GITHUB_TOKEN }} + config-path: config/.jekyll-labels.yml diff --git a/.github/workflows/auto-merge-bot.yml b/.github/workflows/auto-merge-bot.yml deleted file mode 100644 index 7792529ec8f5e..0000000000000 --- a/.github/workflows/auto-merge-bot.yml +++ /dev/null @@ -1,26 +0,0 @@ -on: [ pull_request_target ] -name: Auto-Merge Bot -jobs: - auto_merge_bot: - runs-on: ubuntu-latest - name: EIP Auto-Merge Bot - if: github.repository == 'ethereum/eips' - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Setup Node.js Environment - uses: actions/setup-node@v2 - with: - node-version: '14' - - name: auto-merge-bot - uses: ethereum/EIP-Bot@16ab13b9d6ce0ba7404afc4a75f2da9e3db69dbb # master - id: auto-merge-bot - with: - GITHUB-TOKEN: ${{ secrets.TOKEN }} - CORE_EDITORS: "@MicahZoltu,@lightclient,@axic,@gcolvin,@SamWilsn,@Pandapip1" - ERC_EDITORS: "@lightclient,@axic,@SamWilsn,@Pandapip1" - NETWORKING_EDITORS: "@MicahZoltu,@lightclient,@axic,@SamWilsn" - INTERFACE_EDITORS: "@lightclient,@axic,@SamWilsn,@Pandapip1" - META_EDITORS: "@lightclient,@axic,@gcolvin,@SamWilsn,@Pandapip1" - INFORMATIONAL_EDITORS: "@lightclient,@axic,@gcolvin,@SamWilsn,@Pandapip1" - MAINTAINERS: "@alita-moore,@mryalamanchi" diff --git a/.github/workflows/auto-review-bot.yml b/.github/workflows/auto-review-bot.yml new file mode 100644 index 0000000000000..43800fd457de8 --- /dev/null +++ b/.github/workflows/auto-review-bot.yml @@ -0,0 +1,60 @@ +on: + workflow_run: + workflows: + - Auto Review Bot Trigger + types: + - completed + +name: Auto Review Bot +jobs: + auto-review-bot: + runs-on: ubuntu-latest + name: Run + steps: + - name: Fetch PR Number + uses: dawidd6/action-download-artifact@6765a42d86407a3d532749069ac03705ad82ebc6 + with: + name: pr-number + workflow: auto-review-trigger.yml + run_id: ${{ github.event.workflow_run.id }} + + - name: Save PR Number + id: save-pr-number + run: echo "::set-output name=pr::$(cat pr-number.txt)" + + - name: Checkout + uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b + with: + repository: ethereum/EIPs # Default, but best to be explicit here + ref: master + + - name: Setup Node.js Environment + uses: actions/setup-node@2fddd8803e2f5c9604345a0b591c3020ee971a93 + with: + node-version: 16 + + - name: Auto Review Bot + id: auto-review-bot + uses: ethereum/EIP-Bot@1e1bb6a58e02d28e9afa9462b00a518d9b47860e + with: + GITHUB-TOKEN: ${{ secrets.TOKEN }} + PR_NUMBER: ${{ steps.save-pr-number.outputs.pr }} + CORE_EDITORS: '@MicahZoltu,@lightclient,@axic,@gcolvin,@SamWilsn,@Pandapip1' + ERC_EDITORS: '@lightclient,@axic,@SamWilsn,@Pandapip1' + NETWORKING_EDITORS: '@MicahZoltu,@lightclient,@axic,@SamWilsn' + INTERFACE_EDITORS: '@lightclient,@axic,@SamWilsn,@Pandapip1' + META_EDITORS: '@lightclient,@axic,@gcolvin,@SamWilsn,@Pandapip1' + INFORMATIONAL_EDITORS: '@lightclient,@axic,@gcolvin,@SamWilsn,@Pandapip1' + MAINTAINERS: '@alita-moore,@mryalamanchi' + + - name: Enable Auto-Merge + uses: reitermarkus/automerge@a25ea0de41019ad13380d22e01db8f5638f1bcdc + with: + token: ${{ secrets.TOKEN }} + pull-request: ${{ steps.save-pr-number.outputs.pr }} + + - name: Submit Approval + uses: hmarr/auto-approve-action@24ec4c8cc344fe1cdde70ff37e55ace9e848a1d8 + with: + github-token: ${{ secrets.TOKEN }} + pull-request-number: ${{ steps.save-pr-number.outputs.pr }} diff --git a/.github/workflows/auto-review-trigger.yml b/.github/workflows/auto-review-trigger.yml new file mode 100644 index 0000000000000..39f095b5a480f --- /dev/null +++ b/.github/workflows/auto-review-trigger.yml @@ -0,0 +1,66 @@ +on: + pull_request_target: + types: + - opened + - reopened + - synchronize + pull_request_review: + types: + - submitted + - dismissed + workflow_dispatch: + inputs: + pr_number: + description: Pull Request Number + type: string + required: true + +name: Auto Review Bot Trigger +jobs: + pull-request: + runs-on: ubuntu-latest + name: Pull Request + if: github.event_name == 'pull_request_target' + steps: + - name: Write PR Number + run: echo $PR_NUMBER > pr-number.txt + env: + PR_NUMBER: ${{ github.event.number }} + + - name: Save PR Number + uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 + with: + name: pr-number + path: pr-number.txt + + pr-review: + runs-on: ubuntu-latest + name: Review + if: github.event_name == 'pull_request_review' && github.event.review.sender != 'eth-bot' && github.event.review.state == 'approved' + steps: + - name: Write PR Number + run: echo $PR_NUMBER > pr-number.txt + env: + PR_NUMBER: ${{ github.event.pull_request.number }} + + - name: Save PR Number + uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 + with: + name: pr-number + path: pr-number.txt + + dispatch: + runs-on: ubuntu-latest + name: Dispatch + if: github.event_name == 'workflow_dispatch' + steps: + - name: Write PR Number + run: echo $PR_NUMBER > pr-number.txt + env: + PR_NUMBER: ${{ inputs.pr_number }} + + - name: Save PR Number + uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 + with: + name: pr-number + path: pr-number.txt diff --git a/.github/workflows/enable-automerge.yml b/.github/workflows/enable-automerge.yml deleted file mode 100644 index 17d81bae1bc6a..0000000000000 --- a/.github/workflows/enable-automerge.yml +++ /dev/null @@ -1,13 +0,0 @@ -name: Automerge - -on: - pull_request_target: - -jobs: - automerge: - runs-on: ubuntu-latest - steps: - - uses: reitermarkus/automerge@a25ea0de41019ad13380d22e01db8f5638f1bcdc - with: - token: ${{ secrets.TOKEN }} - pull-request: ${{ github.event.number }} diff --git a/.github/workflows/manual-bot-rerun.yml b/.github/workflows/manual-bot-rerun.yml deleted file mode 100644 index 3b9e737381370..0000000000000 --- a/.github/workflows/manual-bot-rerun.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: Manual Bot Rerun -on: - workflow_dispatch: - inputs: - pullRequestNumber: - description: "PR number (with the run you'd like to re-run)" - required: true - eventType: - description: "event type (of the run you want to re-run)" - required: true - default: "pull_request_target" - idOfBotWorkflow: - description: "id of the bot workflow (just leave as default if you don't know)" - required: true - default: "6519716" - -jobs: - rerun-bot: - if: github.repository == 'ethereum/eips' - runs-on: ubuntu-latest - name: Manual Bot Rerun - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Setup Node.js Environment - uses: actions/setup-node@v2 - with: - node-version: '14' - - name: rerun-workflow - uses: ethereum/EIP-Bot@90d0591e71314dc1430c6cde91bb787e185e0b4b # manual-bot-rerun - id: rerun-workflow - with: - GITHUB-TOKEN: ${{ secrets.TOKEN }} - PULL-NUMBER: ${{github.event.inputs.pullRequestNumber }} - ID-TO-RERUN: ${{ github.event.inputs.idOfBotWorkflow }} - EVENT-TYPE: ${{ github.event.inputs.eventType }} diff --git a/.github/workflows/rerun-bot-pull-request-review.yml b/.github/workflows/rerun-bot-pull-request-review.yml deleted file mode 100644 index 86946b7605baa..0000000000000 --- a/.github/workflows/rerun-bot-pull-request-review.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: Trigger Auto Merge Bot Re-Run - -on: - pull_request_review: - types: [ submitted ] - -jobs: - rerun_bot_on_review: - name: Trigger Auto Merge Bot Re-Run - - runs-on: ubuntu-latest - if: github.repository == 'ethereum/eips' - - steps: - - name: Explanation - run: echo 'This bot is used to trigger another workflow using the workflow_run github event. This is necessary because forked PRs do not have access to repo secrets. Normally, this is circumvented using the pull_request_target event, but because GitHub actions does not trigger that event on review, a hack is required to allow that behavior. This workaround will no longer be necessary if GitHub ever implements a pull_request_review_target or something similar.' diff --git a/.github/workflows/rerun-bot-workflow-run.yml b/.github/workflows/rerun-bot-workflow-run.yml deleted file mode 100644 index 892a0ad841a69..0000000000000 --- a/.github/workflows/rerun-bot-workflow-run.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: Workflow run re-run auto-merge-bot on review -on: - workflow_run: - workflows: - - Rerun Bot - types: - - requested - -jobs: - rerun-bot: - if: github.repository == 'ethereum/eips' - runs-on: ubuntu-latest - name: Rerun Bot (workflow_run) - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Setup Node.js Environment - uses: actions/setup-node@v2 - with: - node-version: '14' - - name: auto-merge-bot - uses: ethereum/EIP-Bot@1f05ace5691062379bd910aa27402eecb8f295ac # rerun-pull-request-target-on-review - id: rerun-auto-merge-bot - with: - GITHUB-TOKEN: ${{ secrets.TOKEN }} - WORKFLOW-ID: ${{github.event.workflow_run.id}} - ID-TO-RERUN: "6519716" - RUN-EVENT-TYPE: "pull_request_target" diff --git a/EIPS/eip-1.md b/EIPS/eip-1.md index 15afc33ef311c..a2e6d239720c4 100644 --- a/EIPS/eip-1.md +++ b/EIPS/eip-1.md @@ -211,17 +211,17 @@ The current EIP editors are - Gavin John (@Pandapip1) - Greg Colvin (@gcolvin) - Matt Garnett (@lightclient) -- Micah Zoltu (@MicahZoltu) - Sam Wilson (@SamWilsn) Emeritus EIP editors are - Casey Detrio (@cdetrio) -- Nick Johnson (@arachnid) -- Vitalik Buterin (@vbuterin) - Hudson Jameson (@Souptacular) -- Nick Savers (@nicksavers) - 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). diff --git a/EIPS/eip-2315.md b/EIPS/eip-2315.md index 8a206bb412066..fd65b4cd38558 100644 --- a/EIPS/eip-2315.md +++ b/EIPS/eip-2315.md @@ -1,7 +1,7 @@ --- eip: 2315 title: Simple Subroutines for the EVM -description: Two opcodes for static, safe, and efficient subroutines. +description: Two opcodes for static, safe, and efficient subroutines. author: Greg Colvin (@gcolvin), Greg Colvin , Martin Holst Swende (@holiman), Brooklyn Zelenka (@expede), John Skaller discussions-to: https://ethereum-magicians.org/t/eip-2315-simple-subroutines-for-the-evm/3941 status: Draft @@ -13,48 +13,52 @@ requires: 3540, 3670, 4200 ## Abstract -This proposal introduces two opcodes to support simple subroutines: -* `RJUMPSUB` call to subroutine -* `RETURNSUB` return from call - -It depends on two previously proposed opcodes: -* `RJUMP` relative jump -* `RJUMPI` conditional relative jump +This proposal provides a _static_, _complete_, _efficient_, and _safe_ control-flow facility. It deprecates `JUMP` and `JUMPI`. -It provides constraints on code and an algorithm for code validation to provide a _static_, _complete_, _efficient_, and _safe_ control-flow facility. +It introduces two opcodes to support simple subroutines: +* `RJUMPSUB destination`-- call to subroutine +* `RETURNSUB` -- return from call + +It depends on the two opcodes proposed by [EIP-4200](./eip-4200.md): +* `RJUMP destination` -- relative jump +* `RJUMPI destination` -- conditional relative jump + +It places constraints on valid code and gives an algorithm for static validation that code will not underflow stack, jump to invalid locations, or execute invalid instructions. ## Motivation Subroutines were first proposed by Alan Turing in 1945 as a means of structuring code for his Automatic Computing Engine: -> We also wish to be able to arrange for the splitting up of operations into subsidiary operations ... To start on a subsidiary operation we need only make a note of where we left off the major operation and then apply the first instruction of the subsidiary. When the subsidiary is over we look up the note and continue with the major operation. +> "We also wish to be able to arrange for the splitting up of operations into subsidiary operations ... To start on a subsidiary operation we need only make a note of where we left off the major operation and then apply the first instruction of the subsidiary. When the subsidiary is over we look up the note and continue with the major operation." +> +> -- Alan Turing -- *cf. B.E. Carpenter, R.W. Doran, "The other Turing machine." The Computer Journal, Volume 20, Issue 3, January 1977.* -Facilities vary, but operations like these have proven their value for efficient implementation and straightforward coding of subroutines across a long line of machines going back over 75 years. This includes all of the machines we have programmed or implemented -- physical machines like the Burroughs 5000, CDC 7600, IBM 360, PDP-11, VAX, M68000, Sun SPARC, ARM, and Intel x86s as well as virtual machines for Lisp, Forth, Pascal, Java, Wasm, and others. +The details vary, but operations for calling and returning from subroutines have proven their value for efficient implementation and straightforward coding of subroutines across a long line of machines going back over 75 years. This includes most all of the machines we have programmed or implemented -- physical machines including the Burroughs 5000, CDC 7600, IBM 360, PDP-11, VAX, M68000, Sun SPARC, ARM, and Intel x86s as well as virtual machines for Scheme, Forth, Pascal, Java, Wasm, and others. -The Ethereum Virtual Machine does not provide subroutine operations. Instead, subroutines can be synthesized by pushing the return address and subroutine address on the data stack and executing a dynamic `JUMP` to the subroutine; returns can be synthesized by getting the return address to the top of the stack and doing another dynamic `JUMP` back to it. These conventions cost gas, increase program size, and use stack slots unnecessarily. And they create unnecessary complexity that is borne by the humans and programs writing, reading, and analyzing EVM code. +The Ethereum Virtual Machine does not provide subroutine operations. Instead, subroutines can be synthesized by pushing the return address and subroutine address on the data stack and executing a dynamic `JUMP` to the subroutine; returns can be synthesized by swapping the return address to the top of the stack and executing another dynamic `JUMP` back to the caller. These (or other) conventions cost gas, increase program size, and use resources unnecessarily. And they create unnecessary complexity that is borne by the humans and programs writing, reading, and analyzing EVM code. -**We propose** -- together with [EIP-4200](./eip-4200.md) -- to replace dynamic jumps with a _complete, static, efficient_ and _safe_ control-flow facility. -* _Complete_. Jumps, conditional jump, subroutine call, and subroutine returns. -* _Static_. All control paths are known at contract initialization time. -* _Efficient_. Substantial reductions in the costs and complexity of calling and optimizing simple subroutines. -* _Safe_. Simple rules and validtion algorithm to ensure that code will not underflow stack, jump to invalid locations, or execute invalid instructions. +**We propose** to replace dynamic jumps with a _complete, static, efficient_ and _safe_ control-flow facility. +* _Complete_. Jumps, conditional jumps, subroutine calls and returns. +* _Static_. All control paths are known at contract validation time. +* _Efficient_. Substantial reductions in the costs and complexity of calling and optimizing simple subroutines are possible. +* _Safe_. Valid code will not underflow stack, jump to invalid locations, or execute invalid instructions. The control flow of valid code will be reducible, with consistent numbers of inputs and outputs for subroutines. ## Specification ### Opcodes -#### `RJUMPSUB (0x??) jmpdest: uint16` +#### `RJUMPSUB (0x5f) jmpdest: uint16` Transfers control to a subroutine. -1. Decode the `destination` from the immediate data at `pc`. -2. Push the current `pc + 3` to the `return stack`. -3. Set `pc` to `destination`. +1. Decode the `destination` from the immediate data at `PC`. +2. Push the current `PC + 3` to the `return stack`. +3. Set `PC` to `destination`. The gas cost is _low_. -#### `RETURNSUB (0x??)` +#### `RETURNSUB (0x5e)` Returns control to the caller of a subroutine. @@ -77,7 +81,7 @@ If the execution of an instruction would violate a condition, then the execution 4. Invalid jump destination 5. Invalid instruction -We would like to consider EVM code valid iff no execution of the program can lead to an exceptional halting state. In practice, we must test at runtime for conditions 1 and 2 —- sufficient gas and sufficient stack. We don’t know how much gas there will be, we don’t know how deep a recursion may go, and analysis of stack depth even for non-recursive programs is nontrivial. All of the remaining conditions MUST be validated statically, in time and space linear in the size of the code. +We would like to consider EVM code valid iff no execution of the program can lead to an exceptional halting state. In practice, we must test at runtime for conditions 1 and 2 —- sufficient gas and sufficient stack. We don’t know how much gas there will be, we don’t know how deep a recursion may go, and analysis of stack depth even for non-recursive programs is nontrivial. All of the remaining conditions MUST be validated statically, in time and space quasi-linear in the size of the code. #### Constraints on Valid Code @@ -93,33 +97,29 @@ We would like to consider EVM code valid iff no execution of the program can lea ## Rationale -The `RJUMP`, `RJUMPI` and `RJUMPSUB` instructions take their destination as an immediate argument, which cannot change at runtime. +This is a purely semantic specification, placing no constraints on the syntax of code sections beyond being a sequence of opcodes and immediate data. This allows for optimizations at the machine-code level, but cannot make all the guarantees that a structured computer language can. It only promises that valid code will not, as it were, jam up the gears of the machine. -Having constant destinations for all jumps means that all jump destinations can be validated at initialization time, not runtime. -Requiring a consistently aligned`data stack` prevents stack underflow. It can also catch irreducible control flows and calls to subroutines with the wrong number of arguments. +### Validation -Taken together, these rules allow for EVM code to be validated by traversing its control-flow graph, in time and space linear in the size of the code, following each edge only once. +The `RJUMP`, `RJUMPI` and `RJUMPSUB` instructions take their destination as an immediate argument, which cannot change at runtime. Having constant destinations for all jumps means that all jump destinations can be validated at initialization time, not runtime. -### Costs +Requiring a consistently aligned`data stack` prevents stack underflow. It also prevents irreducible control flows and inconsistent numbers of inputs to or outputs from subroutines. -The _low_ cost of `RJUMPSUB` versus the _mid_ cost of `JUMP` is justified by needing only to decode the immediate two byte destination to the `PC` and push the return address on the `return stack`, all using native arithmetric, versus using the data stack with emulated 256-bit instructions. +Taken together, these rules allow for EVM code to be validated by traversing its control-flow graph, in time and space linear in the size of the code, following each edge only once. This in turn is required to prevent the validation phase from being a quadratic denial of service vulnerability. -The _verylow_ cost of `RETURNSUB` is justified by needing only to pop the `return stack` into the `PC`. Benchmarking will be needed to tell if the costs are well-balanced. +*Note: These rules also allow some other static analyses and code transformations that might otherwise require quadratic time to run in linear time, including linear-time complilers and streaming machine code generators.* -### Alternatives +### Alternative Designs There are a few major designs for a subroutine facility, two of which are considered here. The others are mostly not appropriate for the EVM, such as self-modifying code that writes return addresses into called subroutines. -#### *Keep return addresses on a dedicated return stack.* +*1. Keep return addresses on a dedicated return stack.* Turing's design is often used in stack machines, including those for Forth, Java, Wasm, and others. The data stack is almost entirely used for computation, with a dedicated stack for return addresses. A single instruction suffices to call, and another to return from a routine. -Turing's design is often used in stack machines, including those for Forth, Java, Wasm, and others. The data stack is almost entirely used for computation, with a dedicated stack for return addresses. +*2. Keep return addresses on the data stack.* This design is used by most all of the register machines we have programmed. The registers are used primarily for computation, and the stack maintains call frames for return addresses, arguments, and local variables. On the EVM this approach requires awkward pushes and swaps of addresses on the data stack, as shown below. -#### *Keep return addresses on the data stack.* +#### We prefer the dedicated return stack. -This design is used by all of the register machines we have programmed. The registers are used primarily for computation, and the stack contains call frames for return addresses, arguments, and local variables. - -#### **We prefer the separate control stack.** * It maintains a clear separation between calculation and flow of control: * the data stack is free of vulnerable return addresses and * it's impossible to overwrite the return stack. @@ -127,12 +127,9 @@ This design is used by all of the register machines we have programmed. The reg * uses native arithmetic rather than 256-bit EVM instructions for the return address, * doesn't use a `data stack` slot for the return address and * needs less motion of 256-bit data on the stack. -* It supports linear-time valiation. -* It has a 76-year history of success, especially on stack machines. - -#### **We must use a separate control stack.** -On the EVM we use return addresses on the data stack using dynamic jumps, as shown below. But dynamic jumps are an obstacle to proving validity in linear time. Because they can be to any destination in the code, quadratic "path explosions" are possible when attempting the symbolic execution we need to validate the code. This is why we propose to deprecate `JUMP` and `JUMPI`. +#### We cannot safely use the data stack. + * We propose a validation algorithm that must have quasi-linear complexity to avoid being a DoS vulnerability. But at runtime dynamic jumps can be to any destination in the code, so quadratic "path explosions" are possible when attempting symbolic execution. Code validation requires that program control flow be static and that subroutine calls be distinguisable from ordinary jumps. We cannot safely use `JUMP` to code subroutines. ### Efficiency @@ -160,11 +157,10 @@ Subroutine call, using `JUMP`: ``` TEST_SQUARE: jumpdest ; 1 gas - RTN_SQUARE ; 3 gas - 0x02 ; 3 gas - SQUARE ; 3 gas + push 0x02 ; 3 gas + push RTN_SQUARE ; 3 gas + push SQUARE ; 3 gas jump ; 8 gas - RTN_SQUARE: jumpdest ; 1 gas swap1 ; 3 gas @@ -172,9 +168,9 @@ RTN_SQUARE: SQUARE: jumpdest ; 1 gas + swap1 ; 3 gas dup1 ; 3 gas mul ; 5 gas - swap1 ; 3 gas jump ; 8 gas ``` _Total: 50 gas_. @@ -188,7 +184,7 @@ Of course in cases like this one we can optimize the tail call, so that the retu Tail call optimization, using `RJUMPSUB` and `RETURNSUB`: ``` TEST_SQUARE: - 0x02 ; 3 gas + push 0x02 ; 3 gas rjump SQUARE ; 3 gas SQUARE: @@ -203,12 +199,13 @@ Tail call optimization, using `JUMP`: ``` TEST_SQUARE: jumpdest ; 1 gas - 0x02 ; 3 gas + push 0x02 ; 3 gas SQUARE ; 3 gas jump ; 8 gas SQUARE: jumpdest ; 1 gas + swap1 ; 3 gas dup1 ; 3 gas mul ; 5 gas swap1 ; 3 gas @@ -224,9 +221,12 @@ We can see that these instructions provide a simpler and more gas-efficient subr Clearly, the benefits of these efficiencies are greater for programs that have been factored into smaller subroutines. How small? A subroutine could use _90_ more gas than our first, _22_ gas example and `RJUMPSUB` would still use better than _20% less total gas_ than `JUMP`. -*Note: A _stack rotation_ operator to move items on the stack and implicitly shift the intervening items could simplify code using `JUMP`. It would be a potentionally expensive operation with a dynamic gas cost.* +### Costs + +The _low_ cost of `RJUMPSUB` versus the _mid_ cost of `JUMP` is justified by needing only to decode the immediate two byte destination to the `PC` and push the return address on the `return stack`, all using native arithmetric, versus using the data stack with emulated 256-bit instructions. + +The _verylow_ cost of `RETURNSUB` is justified by needing only to pop the `return stack` into the `PC`. Benchmarking will be needed to tell if the costs are well-balanced. -We specify a few simple safety rules to ensure that valid contracts will not underflow stack, jump to invalid locations, or execute invalid instructions. We also provide an algorithm to validate these rules at initialization time. The validation algorithm **must** have quasi-linear complexity to avoid being a DoS vulnerability, which in turn requires that the program control flow be static and that subroutine calls be distinguisable from ordinary jumps. In combination with the static relative jumps of [EIP-4200](./eip-4200.md) this EIP provides the control flow needed for efficient validation. ## Backwards Compatibility @@ -238,13 +238,13 @@ These changes affect the semantics of existing EVM code: bytes that would have b This should jump into a subroutine, back out and stop. -Bytecode: `0x60045e005b5d` (`PUSH1 0x04, JUMPSUB, STOP, JUMPDEST, RETURNSUB`) +Bytecode: `0x5f0003005e` (`RJUMPSUB 3, RETURNSUB, STOP`) | Pc | Op | Cost | Stack | RStack | |-------|-------------|------|-----------|-----------| -| 0 | JUMPSUB | 5 | [] | [] | -| 3 | RETURNSUB | 5 | [] | [0] | -| 4 | STOP | 0 | [] | [] | +| 0 | RJUMPSUB | 5 | [] | [] | +| 2 | STOP | 0 | [] | [] | +| 3 | RETURNSUB | 3 | [] | [] | Output: 0x Consumed gas: `10` @@ -253,39 +253,37 @@ Consumed gas: `10` This should execute fine, going into one two depths of subroutines -Bytecode: `0x -00000000000000c5e005b60115e5d5b5d` (`PUSH9 0x00000000000000000c, JUMPSUB, STOP, JUMPDEST, PUSH1 0x11, JUMPSUB, RETURNSUB, JUMPDEST, RETURNSUB`) +Bytecode: `0x5f00045F00025200` (`RJUMPSUB 4, RJUMPSUB 2, RETURNSUB, RETURNSUB, STOP`) | Pc | Op | Cost | Stack | RStack | |-------|-------------|------|-----------|-----------| -| 0 | JUMPSUB | 5 | [] | [] | -| 3 | JUMPSUB | 5 | [] | [0] | -| 4 | RETURNSUB | 5 | [] | [0,3] | -| 5 | RETURNSUB | 5 | [] | [3] | +| 0 | RJUMPSUB | 5 | [] | [] | +| 3 | RJUMPSUB | 5 | [] | [] | +| 4 | RETURNSUB | 5 | [] | [] | +| 5 | RETURNSUB | 5 | [] | [] | | 6 | STOP | 0 | [] | [] | Consumed gas: `20` ### Failure 1: invalid jump -This should fail, since the given location is outside of the code-range. The code is the same as previous example, -except that the pushed location is `0x01000000000000000c` instead of `0x0c`. +This should fail, since the given location is outside of the code-range. -Bytecode: (`PUSH9 0x01000000000000000c, JUMPSUB, `0x6801000000000000000c5e005b60115e5d5b5d`, STOP, JUMPDEST, PUSH1 0x11, JUMPSUB, RETURNSUB, JUMPDEST, RETURNSUB`) +Bytecode: '0X5fff'(`RJUMPSUB -1`) | Pc | Op | Cost | Stack | RStack | |-------|-------------|------|-----------|-----------| -| 0 | JUMPSUB | 10 |[18446744073709551628] | [] | +| 0 | RJUMPSUB | 10 | [] | [] | ``` -Error: at pc=10, op=JUMPSUB: invalid jump destination +Error: at pc=0, op=RJUMPSUB: invalid jump destination ``` ### Failure 2: shallow `return stack` This should fail at first opcode, due to shallow `return_stack` -Bytecode: `0x5d5858` (`RETURNSUB, PC, PC`) +Bytecode: `0x5e` (`RETURNSUB`) | Pc | Op | Cost | Stack | RStack | |-------|-------------|------|-----------|-----------| @@ -297,20 +295,18 @@ Error: at pc=0, op=RETURNSUB: invalid retsub ### Subroutine at end of code -In this example. the JUMPSUB is on the last byte of code. When the subroutine returns, it should hit the 'virtual stop' _after_ the bytecode, and not exit with error +In this example the RJUMPSUB is on the last byte of code. When the subroutine returns, it should hit the 'virtual stop' _after_ the bytecode, and not exit with error -Bytecode: `0x6005565b5d5b60035e` (`PUSH1 0x05, JUMP, JUMPDEST, RETURNSUB, JUMPDEST, PUSH1 0x03, JUMPSUB`) +Bytecode: `0x5c035e5ff` (`RJUMP 3, RETURNSUB, RJUMPSUB -1`) | Pc | Op | Cost | Stack | RStack | |-------|-------------|------|-----------|-----------| -| 0 | PUSH1 | 3 | [] | [] | -| 2 | JUMP | 8 | [5] | [] | -| 5 | JUMPDEST | 1 | [] | [] | -| 6 | JUMPSUB | 5 | [] | [] | -| 2 | RETURNSUB | 5 | [] | [2] | -| 7 | STOP | 0 | [] | [] | +| 0 | RJUMP | 5 | [] | [] | +| 2 | RETURNSUB | 5 | [] | [] | +| 3 | RJUMPSUB | 5 | [] | [] | +| 5 | STOP | 0 | [] | [] | -Consumed gas: `30` +Consumed gas: `15` ## Reference Implementation @@ -320,8 +316,6 @@ This algorithm performs a symbolic execution of the program that recursively tra It runs in time equal to `O(vertices + edges)` in the program's control-flow graph, where edges represent control flow and the vertices represent _basic blocks_ -- thus the algorithm takes time proportional to the size of the _code_. It uses recursion to maintain a stack of continuations for conditional jumps, the size of which is at most proportional to the size of the _code_ -*Note: All valid code has a control-flow graph that can be traversed in time and space linear in the length of the code, while avoiding stack underflow and misalignment. That means that some other static analyses and code transformations that might otherwise require quadratic time can also be written to run in linear time, including linear-time complilers and streaming machine code generators. - ### Validation Function For simplicity's sake we assume that _jumpdest analysis_ has been done and that we have some helper functions. @@ -329,7 +323,7 @@ For simplicity's sake we assume that _jumpdest analysis_ has been done and that * `is_valid_jumpdest(dest)` returns true if `dest` is a valid jumpdest. * `immediate_data(pc)` returns the immediate data for the instruction at `pc`. * `advance_pc(pc)` advances to and returns the next `pc`, skipping any immediate data. -* `removed_items(pc,sp)` returns the number of items removed from the `data_stack` by the instruction at `pc`. +* `remove_items(pc,sp)` returns the number of items removed from the `data_stack` by the instruction at `pc`. * `add_items(pc,sp)` returns the number of items added to the `data_stack` by the instruction at `pc`. ``` @@ -340,11 +334,11 @@ var return_stack []int var data_stack []uint256 // return nil or an error -func validate(pc := 0, sp := 0, bp := 0, rp := 0) int, error { +func validate(pc := 0, sp := 0, bp := 0) error { for pc < code_len { if !is_valid_instruction(pc) { - return pc, invalid_instruction + return invalid_instruction } // if available items on stack for `pc` are non-zero @@ -374,17 +368,12 @@ func validate(pc := 0, sp := 0, bp := 0, rp := 0) int, error { case REVERT: return nil case INVALID: - return pc, invalid_instruction + return invalid_instruction // track constants pushed on data stack case PUSH1 <= code[pc] && code[pc] <= PUSH32 { - sp++ - if (sp > 1023) { - return pc, stack_overflow - } - data_stack[sp] = immediate_data(pc) + sp = push(sp, immediate_data(pc)) pc = advance_pc(pc) - continue case RJUMP: @@ -403,26 +392,24 @@ func validate(pc := 0, sp := 0, bp := 0, rp := 0) int, error { if !is_valid_jumpdest(pc + jumpdest) { return pc, invalid_destination } - max_left, err = validate(jumpdest, sp, bp, rp) + err = validate(jumpdest, sp, bp) if err { - return pc, err + return err } - // recurse to validate false side of conditional + // continue to validate false side of conditional pc = advance_pc(pc) - max_right, err = validate(pc, sp, bp, rp) + err = validate(pc, sp, bp) if err { - return pc, err + return err } - - return nil case RJUMPSUB: // check for valid jump destination jumpdest = pc + imm_data(pc) if !is_valid_jumpdest(jumpdest) { - return pc, invalid_destination + return invalid_destination } // will enter subroutine at destination @@ -440,22 +427,19 @@ func validate(pc := 0, sp := 0, bp := 0, rp := 0) int, error { // pop return address and check for valid destination pc = pop(return_stack) - if !code[pc - 1] == JUMPSUB { - return pc, invalid_destination - - data_stack[sp++] = immediate_data(pc) - pc = advance_pc(pc) + if !code[pc - 1] == RJUMPSUB { + return invalid_destination default: pc = advance_pc(pc) - // apply other instructions to stack pointer - sp -= removed_items(pc, sp) + // apply other instructions to stack + sp -= remove_items(pc, sp) if (sp < 0) { - return pc, stack_underflow + return stack_underflow } - sp += added_items(pc, sp) + sp += add_items(pc, sp) } } @@ -468,14 +452,5 @@ func validate(pc := 0, sp := 0, bp := 0, rp := 0) int, error { These changes introduce new flow control instructions. They do not introduce any new security considerations. This EIP is intended to improve security by validating a higher level of safety for EVM code deployed on the blockchain. - -> **References** -> -> A.M. Turing, "Proposals for the development in the Mathematics Division of an Automatic Computing Engine (ACE)." Report E882, Executive Committee, NPL February 1945. Available: http://www.alanturing.net/turing_archive/archive/p/p01/p01.php -> -> B.E. Carpenter, R.W. Doran, "The other Turing machine." The Computer Journal, Volume 20, Issue 3, January 1977. Available: https://doi.org/10.1093/comjnl/20.3.269 -> -> G. Wood, "Ethereum: A Secure Decentralized Generized Ledger", BERLIN VERSION 3078285, Ethereum & Parity – July 2022. Available: https://ethereum.github.io/yellowpaper/paper.pdf - ## Copyright Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4750.md b/EIPS/eip-4750.md index 82367c916b88c..f108e804d02ad 100644 --- a/EIPS/eip-4750.md +++ b/EIPS/eip-4750.md @@ -33,15 +33,17 @@ Furthermore it aims to improve analysis opportunities by encoding the number of 2. Total number of code sections MUST NOT exceed 1024. 3. All code sections MUST precede a data section, if data section is present. 4. New section with `kind = 3` is introduced called the *type section*. -5. Exactly one type section MUST be present. -6. The type section MUST directly precede all code sections. -7. The type section contains a sequence of pairs of bytes: first byte in a pair encodes number of inputs, and second byte encodes number of outputs of the code section with the same index. *Note:* This implies that there is a limit of 256 stack for the input and in the output. +5. Exactly one type section MAY be present. +6. The type section, if present, MUST directly precede all code sections. +7. The type section, if present, contains a sequence of pairs of bytes: first byte in a pair encodes number of inputs, and second byte encodes number of outputs of the code section with the same index. *Note:* This implies that there is a limit of 256 stack for the input and in the output. 8. Therefore type section size MUST be `n * 2` bytes, where `n` is the number of code sections. 9. First code section MUST have 0 inputs and 0 outputs. +10. Type section MAY be omitted if only a single code +section is present. In that case it implicitly defines 0 inputs and 0 outputs for this code section. To summarize, a well-formed EOF bytecode will have the following format: ``` -bytecode := format, magic, version, type_section_header, (code_section_header)+, [data_section_header], 0, type_section_contents, (code_section_contents)+, [data_section_contents] +bytecode := magic, version, [type_section_header], (code_section_header)+, [data_section_header], 0, [type_section_contents], (code_section_contents)+, [data_section_contents] type_section_header := 3, number_of_code_sections * 2 # section kind and size type_section_contents := 0, 0, code_section_1_inputs, code_section_1_outputs, code_section_2_inputs, code_section_2_outputs, ..., code_section_n_inputs, code_section_n_outputs @@ -88,7 +90,7 @@ stack_height = data_stack.height - types[code_section_index].inputs) Under `PC_post_instruction` we mean the PC position after the entire immediate argument of `CALLF`. Data stack height is saved as it was before function inputs were pushed. -*Note:* Code validation rules of EIP-3670 guarantee there is always an instruction following `CALLF` (since terminating instruction is required to be final one in the section), therefore `PC_post_instruction` always points to an instruction inside section bounds. +*Note:* Code validation rules of [EIP-3670](./eip-3670.md) guarantee there is always an instruction following `CALLF` (since terminating instruction is required to be final one in the section), therefore `PC_post_instruction` always points to an instruction inside section bounds. 6. Sets `current_section_index` to `code_section_index` and `PC` to `0`, and execution continues in the called section. diff --git a/EIPS/eip-4804.md b/EIPS/eip-4804.md index 1d3a99274cf73..a4bc21d69f3dc 100644 --- a/EIPS/eip-4804.md +++ b/EIPS/eip-4804.md @@ -40,12 +40,12 @@ contractName = address | [name "." [ subDomain0 "." ... ]] nsProviderSuffix path = ["/" method ["/" argument_0 ["/" argument_1 ... ]]] argument = [type "!"] value query = "attribute_1=value_1 [ "&" attribute_2=value_2 ... ] -attribute = "returnTypes" | other_attribute +attribute = "returns" | "returnTypes" | other_attribute ``` where -- **web3Schema** indicates the schema of the URL, which is "ethereum-web3://" or equivalently "eth-web3://" or "web3://" for short. +- **web3Schema** indicates the schema of the URL, which is "eth[ereum]://web3-" or "web3://" for short. - **userinfo** indicates which user is calling the EVM, i.e., "From" field in EVM call message. If not specified, the protocol will use 0x0 as the sender address. - **contractName** indicates the contract to be called, i.e., "To" field in the EVM call message. If the **contractName** is an **address**, i.e., 0x + 20-byte-data hex, then "To" will be the address. Otherwise, the name is from a name service. In the second case, **nsProviderSuffix** will be the suffix from name service providers such as "eth", etc. The way to translate the name from a name service to an address will be discussed in later EIPs. - **chainid** indicates which chain to resolve **contractName** and call the message. If not specified, the protocol will use the same chain as the name service provider, e.g., 1 for eth. If no name service provider is available, the default chainid is 1. @@ -74,7 +74,7 @@ The auto mode is the default mode to resolve (also applies when the "resolveMode Note that if **method** does not exist, i.e., **path** is empty or "/", then the contract will be called with empty calldata. -- **returnTypes** attribute in **query** tells the format of the returned data. If not specified, the returned message data will be parsed in "(bytes32)" and MIME will be set based on the suffix of the last argument. If **returnTypes** is "()", the returned data will be parsed in raw bytes in JSON. Otherwise, the returned message will be parsed in the specified "returnTypes" in JSON. If multiple **returnTypes** attributes are present, the value of the last **returnTypes** attribute will be applied. +- **returns** attribute in **query** tells the format of the returned data. If not specified, the returned message data will be parsed in "(bytes32)" and MIME will be set based on the suffix of the last argument. If **returns** is "()", the returned data will be parsed in raw bytes in JSON. Otherwise, the returned message will be parsed in the specified **returns** attribute in JSON. If multiple **returns** attributes are present, the value of the last **returns** attribute will be applied. Note that **returnTypes** is the alias of **returns**, but it is not recommended to use and is mainly for backward-compatible purpose. ### Examples @@ -113,14 +113,14 @@ The protocol will call the address with "To" = "0x9e081Df45E0D167636DB9C61C7ce71 #### Example 5 ``` -web3://wusdt.eth:4/balanceOf/charles.eth?returnTypes=(uint256) +web3://wusdt.eth:4/balanceOf/charles.eth?returns=(uint256) ``` The protocol will find the addresses of **wusdt.eth** and **charles.eth** and then call the method "balanceOf(address)" of the contract with the **charles.eth**'s address. The returned data will be parsed as uint256 like `[ "10000000000000" ]`. #### Example 6 ``` -web3://wusdt.eth:4/balanceOf/charles.eth?returnTypes=() +web3://wusdt.eth:4/balanceOf/charles.eth?returns=() ``` The protocol will find the address of **wusdt.eth** and then call the method "balanceOf(address)" of the address. The returned data will be parsed as raw bytes like `["0x000000000000000000000000000000000000000000000000000009184e72a000"]`. diff --git a/EIPS/eip-4883.md b/EIPS/eip-4883.md new file mode 100644 index 0000000000000..352d63dbb2e6d --- /dev/null +++ b/EIPS/eip-4883.md @@ -0,0 +1,73 @@ +--- +eip: 4883 +title: Composable SVG NFT +description: Compose an SVG NFT by concatenating the SVG with the rendered SVG of another NFT. +author: Andrew B Coathup (@abcoathup), Alex (@AlexPartyPanda), Damian Martinelli (@damianmarti), blockdev (@0xbok), Austin Griffith (@austintgriffith) +discussions-to: https://ethereum-magicians.org/t/eip-4883-composable-svg-nft/8765 +status: Draft +type: Standards Track +category: ERC +created: 2022-03-08 +requires: 165, 721 +--- + +## Abstract + +Compose an SVG (Scalable Vector Graphics) NFT by concatenating the SVG with the SVG of another NFT rendered as a string for a specific token ID. + +## Motivation + +On-chain SVG NFTs allow for NFTs to be entirely on-chain by returning artwork as SVG in a data URI of the `tokenUri` function. Composability allows on-chain SVG NFTs to be crafted. e.g. adding glasses & hat NFTs to a profile pic NFT or a fish NFT to a fish tank NFT. + +## Specification +The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119. + +```solidity +/// @title EIP-4883 Non-Fungible Token Standard +interface IERC4883 is IERC165 { + function renderTokenById(uint256 id) external view returns (string memory); +} +``` + +`renderTokenById` must return the SVG body for the specified token `id` and must either be an empty string or valid SVG element(s). + +If the caller of the render function composes an SVG NFT they must have rights to use the SVG body in a composable SVG. The token holder could optionally hold both the SVG and rendered SVG NFTs or the token holder could hold the SVG NFT, and the SVG NFT could hold the rendered SVG NFT. + +## Rationale + +SVG elements can be string concatenated to compose an SVG. + +### Ordering of concatenation + +SVG uses a "painters model" of rendering. + +**Scalable Vector Graphics (SVG) 1.1 (Second Edition)**, section: **3.3 Rendering Order** +>Elements in an SVG document fragment have an implicit drawing order, with the first elements in the SVG document fragment getting "painted" first. Subsequent elements are painted on top of previously painted elements. + +The ordering of the SVG concatenation determines the drawing order rather than any concept of a z-index. + +This EIP only specifies the rendering of the rendered SVG NFT and does not require any specific ordering when composing. This allows the SVG NFT to use a rendered SVG NFT as a foreground or a background as required. + +### Alternatives to concatenation + +SVG specifies a `link` tag. Linking could allow for complex SVGs to be composed but would require creating a URI format and then getting ecosystem adoption. As string concatenation of SVG's is already supported, the simpler approach of concatenation is used. + +### Sizing + +This EIP doesn't specify any requirements on the size of the rendered SVG. Any scaling based on sizing can be performed by the SVG NFT as required. + +### Render function name + +The render function is named `renderTokenById` as this function name was first used by Loogies and allows existing deployed NFTs to be compatible with this EIP. + +## Backwards Compatibility +This EIP has no backwards compatibility concerns + + +## Security Considerations + +- SVG uses a "painters model" of rendering. A rendered SVG body could be added and completely obscure the existing SVG NFT artwork. +- SVG is XML and can contain malicious content, and while it won't impact the contract, it could impact the use of the SVG. + +## Copyright +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-4973.md b/EIPS/eip-4973.md index 531b0e8f07c47..b7d93c2c1e75e 100644 --- a/EIPS/eip-4973.md +++ b/EIPS/eip-4973.md @@ -33,6 +33,8 @@ The purpose of this document is to make ABTs a reality on Ethereum by creating c ## Specification +### Solidity Interface + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119. `EIP-4973` tokens _must_ implement the interfaces: @@ -139,89 +141,11 @@ interface IERC4973 { } ``` -[`EIP-712`](./eip-712.md) typed structure for generating a `bytes signature`: - -```js -keccak256(abi.encodePacked( - hex"1901", - DOMAIN_SEPARATOR, - keccak256(abi.encode( - keccak256("Agreement(address active,address passive,string tokenURI)"), - activeAddress, - passiveAddress, - tokenURI - )) -)) -``` - -`DOMAIN_SEPARATOR` should be unique to the contract and chain to prevent replay attacks from other domains, and satisfy the requirements of [`EIP-712`](./eip-712.md): - -```js -DOMAIN_SEPARATOR = keccak256( - abi.encode( - keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ), - keccak256(bytes(name)), - keccak256(bytes(version)), - chainid, - address(this) - ) -); -``` +See [`EIP-721`](./eip-721.md) for a definition of its metadata JSON Schema. -```js -{ - "types": { - "EIP712Domain": [ - { - "name": "name", - "type": "string" - }, - { - "name": "version", - "type": "string" - }, - { - "name": "chainId", - "type": "uint256" - }, - { - "name": "verifyingContract", - "type": "address" - } - ], - "Agreement": [ - { - "name": "active", - "type": "address" - }, - { - "name": "passive", - "type": "address" - }, - { - "name": "tokenURI", - "type": "string" - } - ], - "primaryType": "Agreement", - "domain": { - "name": name, - "version": version, - "chainId": chainid, - "verifyingContract": address(this) - }, - "message": { - "active": activeAddress, - "passive": passiveAddress, - "tokenURI": tokenURI - } - } -} -``` +### [EIP-712](./eip-712.md) Typed Structured Data Hashing and [EIP-2098](./eip-2098) Compact Signature Creation -See [`EIP-721`](./eip-721.md) for a definition of its metadata JSON Schema. +To invoke `function give(...)` and `function take(...)` an [EIP-2098](./eip-2098.md) compact signature must be created using [EIP-712](./eip-712.md). A tested reference implementation is attached at [../assets/eip-4973/generateSignature.mjs](../assets/eip-4973/generateSignature.mjs) and [../assets/eip-4973/generateSignature_test.mjs](../assets/eip-4973/generateSignature_test.mjs). ## Rationale diff --git a/EIPS/eip-5069.md b/EIPS/eip-5069.md index 7a91f070ce67d..f951f947576ac 100644 --- a/EIPS/eip-5069.md +++ b/EIPS/eip-5069.md @@ -11,14 +11,17 @@ requires: 1 --- ## Abstract + An Ethereum Improvement Proposal (EIP) is a design document providing information to the Ethereum community, or describing a new feature for Ethereum or its processes or environment. The EIP standardization process is a mechanism for proposing new features, for collecting community technical input on an issue, and for documenting the design decisions that have gone into Ethereum. Because improvement proposals are key components of Ethereum blockchain, it is important that they are well reviewed before reaching `Final` status. EIPs are stored in text files in a versioned repository which is monitored by the EIP editors. This EIP describes the recommended process for becoming an EIP editor. ## Specification + 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. ### Application and Onboarding Process + Anyone having a good understanding of the EIP standardization and network upgrade process, intermediate level experience on the core and/or application side of the Ethereum blockchain, and willingness to contribute to the process management MAY apply to become an EIP editor. Potential EIP editors SHOULD have the following skills: - Good communication skills - Ability to handle contentious discourse @@ -41,10 +44,10 @@ index 79558a3..079b196 100644 Emeritus EIP editors are -diff --git a/.github/workflows/auto-merge-bot.yml b/.github/workflows/auto-merge-bot.yml +diff --git a/.github/workflows/auto-review-bot.yml b/.github/workflows/auto-review-bot.yml index 5730343..3d162ea 100644 ---- a/.github/workflows/auto-merge-bot.yml -+++ b/.github/workflows/auto-merge-bot.yml +--- a/.github/workflows/auto-review-bot.yml ++++ b/.github/workflows/auto-review-bot.yml @@ -17,12 +17,12 @@ jobs: id: auto-merge-bot with: @@ -62,16 +65,17 @@ index 5730343..3d162ea 100644 + META_EDITORS: "@lightclient,@axic,@gcolvin,@SamWilsn" + INFORMATIONAL_EDITORS: "@lightclient,@axic,@gcolvin,@SamWilsn" MAINTAINERS: "@alita-moore,@mryalamanchi" - enable-auto-merge: - if: github.repository == 'ethereum/eips' ``` ### Special Merging Rules for this EIP + This EIP MUST have the same rules regarding changes as [EIP-1](./eip-1.md). ## Rationale + - "6 months" was chosen as the cutoff for denoting `Stagnant` EIPs terminally-`Stagnant` arbitrarily. - This EIP requires special merging rules for the same reason [EIP-1](./eip-1.md) does. ## Copyright + Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5131.md b/EIPS/eip-5131.md index acffd67258256..0fb28b1530616 100644 --- a/EIPS/eip-5131.md +++ b/EIPS/eip-5131.md @@ -1,8 +1,8 @@ --- eip: 5131 -title: ENS Subdomain Authentication -description: Using ENS subdomains to facilitate safer and more convenient signing operations. -author: Wilkins Chung (@wwhchung), Jalil Wahdatehagh (@jwahdatehagh), Cry (@crydoteth), Sillytuna (@sillytuna) +title: SAFE Authentication For ENS +description: Using ENS Text Records to facilitate safer and more convenient signing operations. +author: Wilkins Chung (@wwhchung), Jalil Wahdatehagh (@jwahdatehagh), Cry (@crydoteth), Sillytuna (@sillytuna), Cyberpnk (@CyberpnkWin) discussions-to: https://ethereum-magicians.org/t/eip-5131-ens-subdomain-authentication/9458 status: Draft type: Standards Track @@ -17,14 +17,14 @@ This EIP links one or more signing wallets via Ethereum Name Service Specificati ## Motivation Proving ownership of an asset to a third party application in the Ethereum ecosystem is common. Users frequently sign payloads of data to authenticate themselves before gaining access to perform some operation. However, this method--akin to giving the third party root access to one's main wallet--is both insecure and inconvenient. -*** Examples: *** +***Examples:*** 1. In order for you to edit your profile on OpenSea, you must sign a message with your wallet. 2. In order to access NFT gated content, you must sign a message with the wallet containing the NFT in order to prove ownership. 3. In order to gain access to an event, you must sign a message with the wallet containing a required NFT in order to prove ownership. 4. In order to claim an airdrop, you must interact with the smart contract with the qualifying wallet. 5. In order to prove ownership of an NFT, you must sign a payload with the wallet that owns that NFT. - In all the above examples, one interacts with the dApp or smart contract using the wallet itself, which may be +In all the above examples, one interacts with the dApp or smart contract using the wallet itself, which may be - inconvenient (if it is controlled via a hardware wallet or a multi-sig) - insecure (since the above operations are read-only, but you are signing/interacting via a wallet that has write access) @@ -38,7 +38,7 @@ In addition to this, many users keep significant portions of their assets in 'co Some solutions propose dedicated registry smart contracts to create this link, or new protocols to be supported. This is problematic from an adoption standpoint, and there have not been any standards created for them. ### Proposal: Use the Ethereum Name Service (EIP-137) -Rather than 're-invent the wheel', this proposal aims to use the widely adopted Ethereum Name Service in order to bootstrap a safer and more convenient way to sign and authenticate, and provide 'read only' access to a main wallet via one or more secondary wallets. +Rather than 're-invent the wheel', this proposal aims to use the widely adopted Ethereum Name Service in conjunction with the ENS Text Records feature ([EIP-634](./eip-634.md)) in order to achieve a safer and more convenient way to sign and authenticate, and provide 'read only' access to a main wallet via one or more secondary wallets. From there, the benefits are twofold. This EIP gives users increased security via outsourcing potentially malicious signing operations to wallets that are more accessible (hot wallets), while being able to maintain the intended security assumptions of wallets that are not frequently used for signing operations. @@ -52,51 +52,47 @@ In order for a non-hardware wallet to be used on multiple devices, you must impo Instead, each device can have its own unique wallet that is an authorized secondary wallet of the main wallet. If a device specific wallet was ever compromised or lost, you could simply remove the authorization to authenticate. +Further, wallet authentication can be chained so that a secondary wallet could itself authorize one or many tertiary wallets, which then have signing rights for both the secondary address as well as the root main address. This, can allow teams to each have their own signer while the main wallet can easily invalidate an entire tree, just by revoking rights from the root stem. + #### Improving Convenience Many invididuals use hardware wallets for maximum security. However, this is often inconvenient, since many do not want to carry their hardware wallet with them at all times. Instead, if you approve a non-hardware wallet for authentication activities (such as a mobile device), you would be able to use most dApps without the need to have your hardware wallet on hand. - ## Specification The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119. - Let: - `mainAddress` represent the wallet address we are trying to authenticate or prove asset ownership for. - `mainENS` represent the reverse lookup ENS string for `mainAddress`. - `authAddress` represent the address we want to use for signing in lieu of `mainAddress`. - `authENS` represent the reverse lookup ENS string for `authAddress`. - - `authKey` represents a string in the format `[0-9A-Za-z]+` + - `authKey` represents a string in the format `[0-9A-Za-z]+`. Control of `mainAddress` and ownership of `mainAddress` assets by `authAddress` is proven if all the following conditions are met: - `mainAddress` has an ENS resolver record and a reverse record set to `mainENS`. - - `authAddress` has an ENS resolver record and a reverse record set to `authENS` - - `authENS` has an ENS TEXT record `eip5131:vault` in the format `:` - - `mainAddress` has an ENS resolver record and a reverse record set to `mainENS`. - - `mainENS` has an ENS TEXT record `` - + - `authAddress` has an ENS resolver record and a reverse record set to `authENS`. + - `authENS` has an ENS TEXT record `eip5131:vault` in the format `:`. + - `mainENS` has an ENS TEXT record `eip5131:`. ### Setting up one or many `authAddress` records on a single ENS domain -The pre-requisite assumes that the `mainAddress` has an ENS resolver record and reverse record configured. +The `mainAddress` MUST have an ENS resolver record and reverse record configured. +In order to automatically discover the linked account, the `authAddress` SHOULD have an ENS resolver record and reverse record configured. -1. Using your `mainAddress` wallet create a subdomain record for `mainENS`, which will be the `authENS`. -2. Set the ETH resolver record for the subdomain created in step 1 (`authENS`) to the `authAddress`. -3. Using `authAddress`, set the ENS reverse record to `authENS`. -4. Using `mainAddress` configure the `eip5131:` (this can be any string in the format `[0-0A-Za-z]+`) TEXT record of `mainENS` to `authAddress` -5. Using `authAddress`, set the `eip5131:vault` TEXT record to `authENS` to `:` +1. Choose an unused ``. This can be any string in the format `[0-0A-Za-z]+`. +2. Set a TEXT record `eip5131:` on `mainENS`, with the value set to the desired `authAddress`. +3. Set a TEXT record `eip5131:vault` on `authENS`, with the value set to the `:mainAddress`. -Currently this EIP does not enforce an upper-bound on the number of `authAddress` entries you can include. Users can repeat this process with as many address as they like. +Currently this EIP does not enforce an upper-bound on the number of `authAddress` entries you can include. Users can repeat this process with as many address as they like. ### Authenticating `mainAddress` via `authAddress` -Control of `mainAddress` and ownership of `mainAddress` assets is proven if any associated `authAddress` is the msg.sender or has signed the message. +Control of `mainAddress` and ownership of `mainAddress` assets is proven if any associated `authAddress` is the `msg.sender` or has signed the message. Practically, this would work by performing the following operations: 1. Get the resolver for `authENS` 2. Get the `eip5131:vault` TEXT record of `authENS` 3. Parse `:` to determine the `authKey` and `mainAddress`. -3. Do a lookup on the linked ENS record to determine the linked `mainAddress` -4. MUST get the reverse ENS record for `mainAddress` and verify that it matches `` +4. MUST get the reverse ENS record for `mainAddress` and verify that it matches ``. - Otherwise one could set up other ENS nodes (with auths) that point to `mainAddress` and authenticate via those. 5. Get the `eip5131:` TEXT record of `mainENS` and ensure it matches `authAddress`. @@ -112,6 +108,8 @@ The proposed specification makes use of EIP-137 rather than introduce another re However, the drawback to EIP-137 is that any linked `authAddress` must contain some ETH in order to set the `authENS` reverse record as well as the `eip5131:vault` TEXT record. This can be solved by a separate reverse lookup registry that enables `mainAddress` to set the reverse record and TEXT record with a message signed by `authAddress`. +With the advent of L2s and ENS Layer 2 functionalities, off chain verification of linked addresses is possible even with domains managed across different chains. + ### One-to-Many Authentication Relationship This proposed specification allows for a one (`mainAddress`) to many (`authAddress`) authentication relationship. i.e. one `mainAddress` can authorize many `authAddress` to authenticate, but an `authAddress` can only authenticate itself or a single `mainAddress`. @@ -190,7 +188,7 @@ interface ENS { /** * ENS Resolver Interface */ -interface Resolver{ +interface Resolver { function addr(bytes32 node) external view returns (address); function name(bytes32 node) external view returns (string memory); function text(bytes32 node, string calldata key) external view returns (string memory); @@ -341,6 +339,5 @@ library LinkedAddress { ## Security Considerations The core purpose of this EIP is to enhance security and promote a safer way to authenticate wallet control and asset ownership when the main wallet is not needed and assets held by the main wallet do not need to be moved. Consider it a way to do 'read only' authentication. - ## Copyright Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5289.md b/EIPS/eip-5289.md new file mode 100644 index 0000000000000..d39ade1ff67e0 --- /dev/null +++ b/EIPS/eip-5289.md @@ -0,0 +1,74 @@ +--- +eip: 5289 +title: Ethereum Notary Interface +description: Allows Smart Contracts to be Legally Binding Off-Chain +author: Pandapip1 (@Pandapip1) +discussions-to: https://ethereum-magicians.org/t/pr-5289-discussion-notary-interface/9980 +status: Draft +type: Standards Track +category: ERC +created: 2022-07-16 +requires: 165 +--- + +## Abstract + +Currently, the real-world applications of smart contracts are limited by the fact that they aren't legally binding. This EIP proposes a standard that allows smart contracts to be legally binding by providing IPFS links to legal documents and ensuring that the users of the smart contract have privity with the relevant legal documents. + +## Motivation + +NFTs have oftentimes been branded as a way to hold and prove copyright of a specific work. However, this, in practice, has almost never been the case. Most of the time, NFTs have no legally-binding meaning, and in the rare cases that do, the NFT simply provides a limited license for the initial holder to use the work (but cannot provide any license for any future holders). + +## Specification + +The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119. + +### Legal Contract Library Interface + +See [`IERC5289Library`](../assets/eip-5289/interfaces/IERC5289Library.sol). + +### Requesting a Signature + +To request that certain documents be signed, revert with the following reason: + +```solidity +string.concat("5289:", libraryAddress1, "-", documentId1OfAddress1, "-", documentId2OfAddress1 ",", libraryAddress2, "-", documentId2, ...) +``` + +NOTE: If an address begins with one or more zeros, they may be omitted. +NOTE 2: The document IDs are represented in hexadecimal. + +Example: + +```solidity +"5289:0x1-1-2,0xdead-7b3" +``` + +### Signing a Document + +When a signature is requested, wallets MUST call `legalDocument` and fetch the file off of IPFS, and render that file to the user. If the user agrees, the wallet MUST call `signDocument`. Using a form of account abstraction is RECOMMENDED. + +## Rationale + +- `uint64` was chosen for the timestamp return type as 64-bit time registers are standard. +- `uint16` was chosen for the document ID as 65536 documents are likely sufficient for any use case, and the contract can always be re-deployed. +- `signDocument` used to take a signature, but due to account abstraction being imminent, this was deemed unnecessary. +- IPFS is mandatory because the authenticity of the signed document can be proven. + +## Backwards Compatibility + +No backwards compatibility issues found. + +## Reference Implementation + +### Legal Contract Library + +See [`ERC5289Library`](../assets/eip-5289/ERC5289Library.sol). + +## Security Considerations + +No security considerations found. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-5313.md b/EIPS/eip-5313.md index da7c4782cf2bf..3032fe356f35b 100644 --- a/EIPS/eip-5313.md +++ b/EIPS/eip-5313.md @@ -4,7 +4,8 @@ title: Light Contract Ownership description: An interface for identifying ownership of contracts author: William Entriken (@fulldecent) discussions-to: https://ethereum-magicians.org/t/eip-5313-light-contract-ownership/10052 -status: Review +status: Last Call +last-call-deadline: 2022-08-15 type: Standards Track category: ERC created: 2022-07-22 diff --git a/EIPS/eip-5375.md b/EIPS/eip-5375.md new file mode 100644 index 0000000000000..a2be530850589 --- /dev/null +++ b/EIPS/eip-5375.md @@ -0,0 +1,305 @@ +--- +eip: 5375 +title: NFT Author Information and Consent +description: An extension of EIP-721 for NFT authorship and author consent. +author: Samuele Marro (@samuelemarro), Luca Donno (@lucadonnoh) +discussions-to: https://ethereum-magicians.org/t/eip-5375-nft-authorship/10182 +status: Draft +type: Standards Track +category: ERC +created: 2022-07-30 +requires: 155, 712, 721, 1155, 1812 +--- + +## Abstract + +This document outlines a standard to provide off-chain information in JSON format regarding NFT authors. Specifically, it adds a new field which provides both a list of author names/addresses and a proof of _authorship consent_, i.e. a proof that a certain author agreed to be named as the author of the NFT. Note that a proof of authorship consent is not a proof of authorship: an address could consent to be named as the author without actually being the author. + +## Motivation + +There is currently no unified protocol to identify the author of an NFT. Some existing techniques include: + +- Using the mint transaction signer + - Requires that the minter and the author are the same + - Does not support multiple authors +- Using the first Transfer event for a given ID + - Contract/minter can claim that someone else is the author without their consent + - Does not support multiple authors +- Using a custom method/custom JSON field + - Requires per-contract support by NFT platforms + - Contract/minter can claim that someone else is the author without their consent + +The first practice is the most common, mainly due to the fact that relying on minters to provide truthful information opens up avenues for frauds (e.g. selling an NFT while claiming that it was made by a famous artist). However, there are several situations where the minter and the author might not be the same, such as: + +- NFTs minted by a contract +- Lazy minting +- NFTs minted by an intermediary (which can be particularly useful when the author is not tech-savvy and/or the minting process is convoluted) + +This document thus defines a standard which allows the minter to provide authorship information, while also preventing authorship claims without the author's consent. + +## Specification + +The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119. + +### Definitions + +- **Authors**: creators of an NFT +- **Minter**: entity responsible for the actual minting transaction; the minter and the authors MAY be the same +- **Verifier**: entity that wants to verify the authorship of an NFT (e.g. a user or an NFT marketplace) +- **Author Consent Proof (ACP)**: a signed message that proves that the signer agrees to be considered the author of the NFT + +### Authorship Support + +The standard introduces a new JSON field, named `authorInfo`. It provides a REQUIRED interface for authorship claiming, as well as an OPTIONAL interface for author consent proofs. + +`authorInfo` is a top-level field of the NFT metadata. Specifically: + +- If a contract supports the metadata extension for [EIP-721](./eip-721.md), the JSON document pointed by `tokenURI(uint256 _tokenId)` MUST include the top-level field `authorInfo` +- If a contract supports the metadata extension for [EIP-1155](./eip-1155.md), the JSON document pointed by `uri(uint256 _id)` MUST include a top-level field `authorInfo` + +The JSON schema of `authorInfo` (named `ERC5375AuthorInfoSchema`) is defined as follows: + +```json +{ + "type": "object", + "properties": { + "consentInfo": { + "type": "object", + "description": "Helper fields for consent verification", + "properties": { + "chainId": { + "type": "integer", + "description": "EIP-155 chain id" + }, + "id": { + "type": "string", + "description": "NFT id" + }, + "contractAddress": { + "type": "string", + "description": "0x-prefixed address of the smart contract" + } + } + }, + "authors": { + "type": "array", + "items": "ERC5375AuthorSchema" + } + }, + "required": [ "authors" ] +} +``` + +Note that `authors` MAY be an empty array. + +`ERC5375AuthorSchema` is defined as follows: + +```json +{ + "type": "object", + "properties": { + "address": { + "type": "string", + "description": "0x-prefixed address of the author" + }, + "consent": { + "type": "ERC5375AuthorConsentSchema", + "description": "Author consent information" + } + }, + "required": [ "address" ] +} +``` + +Moreover, if the `consent` field is present, the `consentInfo` field of `authorInfo` MUST be present. + +`ERC5375AuthorConsentSchema` is defined as follows: + +```json +{ + "type": "object", + "properties": { + "consentData": { + "type": "object", + "properties": { + "version": { + "type": "string", + "description": "NFT authorship consent schema version" + }, + "issuer": { + "type": "string", + "description": "0x-prefixed address of the author" + }, + "metadataFields": { + "type": "object" + } + }, + "required": ["version", "issuer", "metadataFields"] + }, + "publicKey": { + "type": "string", + "description": "EVM public key of the author" + }, + "signature": { + "type": "string", + "description": "EIP-712 signature of the consent message" + } + }, + "required": ["consentData", "publicKey", "signature"] +} +``` + +where `metadataFields` is an object containing the JSON top-level fields (excluding `authorInfo`) that the author will certify. Note that the keys of `metadataFields` MAY be a (potentially empty) subset of the set of fields. + +`consentData` MAY support additional fields as defined by other EIPs. `consentData` MUST contain all the information (which is not already present in other fields) required to verify the validity of an authorship consent proof. + +### Author Consent + +Consent is obtained by signing an [EIP-712](./eip-712.md) compatible message. Specifically, the structure is defined as follows: + +```solidity +struct Author { + address subject; + uint256 tokenId; + string metadata; +} +``` + +where `subject` is the address of the NFT contract, `tokenId` is the id of the NFT and `metadata` is the JSON encoding of the fields listed in `metadataFields`. `metadata`: + +- MUST contain exactly the same fields as the ones listed in `metadataFields`, in the same order +- MUST escape all non-ASCII characters. If the escaped character contains hexadecimal letters, they MUST be uppercase +- MUST not contain any whitespace that is not part of a field name or value + +For example, if the top-level JSON fields are: + +```json +{ + "name": "The Holy Hand Grenade of Antioch", + "description": "Throw in the general direction of your favorite rabbit, et voilà", + "damage": 500, + "authors": [...], + ... +} +``` + +and the content of `metadataFields` is `["name", "description"]`, the content of `metadata` is: + +```json +{ + "name": "The Holy Hand Grenade of Antioch", + "description": "Throw in the general direction of your favorite rabbit, et voilà" +} +``` + +Similarly to `consentData`, this structure MAY support additional fields as defined by other EIPs. + +The domain separator structure is + +```solidity +struct EIP712Domain { + string name; + string version; + uint256 chainId; +} +``` + +where `name` and `version` are the same fields described in `consentData` + +This structure MAY support additional fields as defined by other EIPs. + +### Author Consent Verification + +Verification is performed using EIP-712 on an author-by-author basis. Specifically, given a JSON document D1, a consent proof is valid if all of the following statements are true: + +- D1 has a top-level `authorInfo` field that matches `ERC5375AuthorInfoSchema` +- `consent` exists and matches `ERC5375AuthorConsentSchema`; +- If calling `tokenURI` (for EIP-721) or `uri` (for EIP-1155) returns the URI of a JSON document D2, all the top-level fields listed in `metadataFields` MUST exist and have the same value; +- The EIP-712 signature in `signature` (computed using the fields specified in the JSON document) is valid; + +Verifiers MUST NOT assume that an NFT with a valid consent proof from address X means that X is the actual author. On the other hand, verifiers MAY assume that if an NFT does not provide a valid consent proof for address X, then X is not the actual author. + +## Rationale + +### Why provide only an author consent proof? + +Adding support for full authorship proofs (i.e. Alice is the author and no one else is the author) requires a protocol to prove that someone is the only author of an NFT. +In other words, we need to answer the question: "Given an NFT Y and a user X claiming to be the author, is X the original author of Y?". + +For the sake of the argument, assume that there exists a protocol that, given an NFT Y, can determine the original author of Y. Even if such method existed, an attacker could slightly modify Y, thus obtaining a new NFT Y', and rightfully claim to be the author of Y', despite the fact that it is not an original work. Real-world examples include changing some pixels of an image or replacing some words of a text with synonyms. +Preventing this behavior would require a general formal definition of when two NFTs are semantically equivalent. Even if defining such a concept were possible, it would still be beyond the scope of this EIP. + +Note that this issue is also present when using the minter's address as a proxy for the author. + +### Why off-chain? + +There are three reasons: + +- Adding off-chain support does not require modifications to existing smart contracts; +- Off-chain storage is usually much cheaper than on-chain storage, thus reducing the implementation barrier; +- While there may be some use cases for full on-chain authorship proofs (e.g. a marketplace providing special features for authors), there are limited applications for on-chain author consent, due to the fact that it is mostly used by users to determine the subjective value of an NFT. + +### Why repeat id, chainId and contractAddress? + +In many cases, all three data can be derived from contextual information. However, requiring their inclusion in the JSON document ensures that author consent can be verified by only accessing the JSON document. + +### Why not implement a revocation system? + +Authorship is usually final: either someone created an NFT or they didn't. Moreover, a revocation system would impose additional implementation requirements on smart contracts and increase the complexity of verification. Smart contracts MAY implement a revocation system, such as the one defined in [EIP-1812](./eip-1812.md). + +#### Why escape non-ASCII characters in the signature message? + +EIP-712 is designed with the possibility of on-chain verification in mind; while on-chain verification is not a priority for this EIP, non-ASCII characters are escaped due to the high complexity of dealing with non-ASCII strings in smart contracts. + +### Usability Improvements for Authors + +Since the author only needs to sign an EIP-712 message, this protocol allows minters to handle the technical aspects of minting while still preserving the secrecy of the author's wallet. Specifically, the author only needs to: + +- Obtain an EVM wallet; +- Learn how to read and sign a EIP-712 message (which can often be simplified by using a Dapp) +without needing to: +- Obtain the chain's native token (e.g. through trading or bridging) +- Sign a transaction +- Understand the pricing mechanism of transactions +- Verify if a transaction has been included in a block + +This reduces the technical barrier for authors, thus increasing the usability of NFTs, without requiring authors to hand over their keys to a tech-savvy intermediary. + +### Limitations of Address-Based Consent + +The standard defines a protocol to verify that a certain _address_ provided consent. However, it does not guarantee that the address corresponds to the expected author (such as the one provided in the `name` field). Proving a link between an address and the entity behind it is beyond the scope of this document. + +## Backwards Compatibility + +The signed messages are compatible with EIP-1812; however, EIP-1812 also imposes additional requirements/recommendations, such as adding a `verify` method to the smart contract and managing a signature verification system. From the point of view of this EIP, these features: + +- Increase the complexity of the protocol for both developers and users +- Require developers to modify their contracts to guarantee compliance +- Do not provide additional security features +For this reason, supporting EIP-1812 is OPTIONAL. + +## Security Considerations + +### Attacks + +A potential attack that exploits this EIP involves tricking authors into signing authorship consent messages against their wishes. For this reason, authors MUST verify that all signature fields match the required ones. + +A more subtle approach involves not adding important fields to `metadataFields`. By doing so, the author signature might be valid even if the minter changes critical information. + +### Deprecated Features + +`ERC5375AuthorInfoSchema` also originally included a field to specify a human-readable name for the author (without any kind of verification). This was scrapped due to the high risk of author spoofing, i.e.: + +- Alice mints an NFT using Bob's name and Alice's address +- Charlie does not check the address and instead relies on the provided name +- Charlie buys Alice's NFT while believing that it was created by Bob + +For this reason, smart contract developers SHOULD NOT add support for unverifiable information to the JSON document. We believe that the most secure way to provide complex authorship information (e.g. the name of the author) is to prove that the information is associated with the _author's address_, instead of with the NFT itself. + +### Replay Attack Resistance + +The chain id, the contract address and the token id uniquely identify an NFT; for this reason, there is no need to implement additional replay attack countermeasures (e.g. a nonce system). + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). diff --git a/EIPS/eip-831.md b/EIPS/eip-831.md index 7c718d381620a..7813d21de26f5 100644 --- a/EIPS/eip-831.md +++ b/EIPS/eip-831.md @@ -1,17 +1,16 @@ --- eip: 831 title: URI Format for Ethereum -author: ligi +description: A way of creating Ethereum URIs for various use-cases. +author: ligi (@ligi) +discussions-to: https://ethereum-magicians.org/t/eip-831-uri-format-for-ethereum/10105 +status: Review type: Standards Track category: ERC -status: Stagnant created: 2018-01-15 +requires: 67, 681 --- -## Simple Summary - -A standard way of creating Ethereum URIs for various use-cases. - ## Abstract URIs embedded in QR-codes, hyperlinks in web-pages, emails or chat messages provide for robust cross-application signaling between very loosely coupled applications. A standardized URI format allows for instant invocation of the user's preferred wallet application. @@ -20,27 +19,25 @@ URIs embedded in QR-codes, hyperlinks in web-pages, emails or chat messages prov ### Syntax -Ethereum URIs contain "ethereum" in their schema (protocol) part and are constructed as follows: +Ethereum URIs contain "ethereum" or "eth" in their schema (protocol) part and are constructed as follows: - request = "ethereum" ":" [ prefix "-" ] payload + request = "eth" [ "ereum" ] ":" [ prefix "-" ] payload prefix = STRING payload = STRING ### Semantics -`prefix` is optional and defines the use-case for this URI. If no prefix is given: "pay-" is assumed to be concise and ensure backward compatibility to ERC-67. When the prefix is omitted, the payload must start with `0x`. Also prefixes must not start with `0x`. So starting with `0x` can be used as a clear signal that there is no prefix. - -`payload` is mandatory and the content depends on the prefix. Structuring of the content is defined in the ERC for the specific use-case and not in the scope of this document. One example is ERC-681 for the pay- prefix. +`prefix` is optional and defines the use-case for this URI. If no prefix is given: "pay-" is assumed to be concise and ensure backward compatibility to [EIP-67](./eip-67.md). When the prefix is omitted, the payload must start with `0x`. Also prefixes must not start with `0x`. So starting with `0x` can be used as a clear signal that there is no prefix. +`payload` is mandatory and the content depends on the prefix. Structuring of the content is defined in the ERC for the specific use-case and not in the scope of this document. One example is [EIP-681](./eip-681) for the pay- prefix. ## Rationale -The need for this ERC emerged when refining ERC-681. We need a container that does not carry the weight of the use-cases. ERC-67 was the first attempt on defining Ethereum-URIs. This ERC tries to keep backward compatibility and not break existing things. This means ERC-67 URIs should still be valid and readable. Only if the prefix feature is used, ERC-67 parsers might break. No way was seen to avoid this and innovate on the same time. This is also the reason this open prefix approach was chosen to being able to adopt to future use-cases and not block the whole "ethereum:" scheme for a limited set of use-cases that existed at the time of writing this. +The need for this ERC emerged when refining EIP-681. We need a container that does not carry the weight of the use-cases. EIP-67 was the first attempt on defining Ethereum-URIs. This ERC tries to keep backward compatibility and not break existing things. This means EIP-67 URIs should still be valid and readable. Only if the prefix feature is used, EIP-67 parsers might break. No way was seen to avoid this and innovate on the same time. This is also the reason this open prefix approach was chosen to being able to adopt to future use-cases and not block the whole "ethereum:" scheme for a limited set of use-cases that existed at the time of writing this. -## References +## Security Considerations -1. ERC-681, ./eip-681.md -2. ERC-67, ./eip-67.md +There are no known security considerations at this time. ## Copyright diff --git a/assets/eip-4973/generateSignature.mjs b/assets/eip-4973/generateSignature.mjs new file mode 100644 index 0000000000000..b9a9f3eef8e64 --- /dev/null +++ b/assets/eip-4973/generateSignature.mjs @@ -0,0 +1,15 @@ +// "ethers": "5.6.9" +import { utils } from "ethers"; + +// See: https://docs.ethers.io/v5/api/signer/#Signer-signTypedData for more +// detailed instructions. +export async function generateCompactSignature( + signer, + types, + domain, + agreement +) { + const signature = await signer._signTypedData(domain, types, agreement); + const { compact } = utils.splitSignature(signature); + return compact; +} diff --git a/assets/eip-4973/generateSignature_test.mjs b/assets/eip-4973/generateSignature_test.mjs new file mode 100644 index 0000000000000..eb110676a125a --- /dev/null +++ b/assets/eip-4973/generateSignature_test.mjs @@ -0,0 +1,50 @@ +// @format +// "ava": "4.3.1" +import test from "ava"; + +// "ethers": "5.6.9" +import { Wallet } from "ethers"; + +import { generateCompactSignature } from "../src/index.mjs"; + +test("generating a compact signature for function give", async (t) => { + // from: https://docs.ethers.io/v5/api/signer/#Wallet--methods + const passiveAddress = "0x0f6A79A579658E401E0B81c6dde1F2cd51d97176"; + const passivePrivateKey = + "0xad54bdeade5537fb0a553190159783e45d02d316a992db05cbed606d3ca36b39"; + const signer = new Wallet(passivePrivateKey); + t.is(signer.address, passiveAddress); + + const types = { + Agreement: [ + { name: "active", type: "address" }, + { name: "passive", type: "address" }, + { name: "tokenURI", type: "string" }, + ], + }; + const domain = { + name: "Name", + version: "Version", + chainId: 31337, // the chainId of foundry + verifyingContract: "0xce71065d4017f316ec606fe4422e11eb2c47c246", + }; + + const agreement = { + active: "0xb4c79dab8f259c7aee6e5b2aa729821864227e84", + passive: passiveAddress, + tokenURI: "https://contenthash.com", + }; + const compactSignature = await generateCompactSignature( + signer, + types, + domain, + agreement + ); + t.truthy(compactSignature); + // For length of compact signature, see https://eips.ethereum.org/EIPS/eip-2098#backwards-compatibility + t.is(compactSignature.length, 64 * 2 + 2); + t.is( + compactSignature, + "0x238e1616c507f9779469b0276eef73a3a438b65706ca18c6ab38062c588674f9719c9f5412b0379e7918f19da1de71b9370ed9917fadcb6690e71f5a1de24816" + ); +}); diff --git a/assets/eip-5289/ERC5289Library.sol b/assets/eip-5289/ERC5289Library.sol new file mode 100644 index 0000000000000..b4af009e19bad --- /dev/null +++ b/assets/eip-5289/ERC5289Library.sol @@ -0,0 +1,38 @@ +/// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.0; + +import "./interfaces/ERC5289Library.sol"; +import "./interfaces/IERC165.sol"; + +contract ERC5289Library is IERC5289Library, IERC165 { + uint16 private counter = 0; + mapping(uint16 => bytes memory) private multihashes; + mapping(uint16 => mapping(address => uint64)) signedAt; + + constructor() { } + + function registerDocument(bytes memory multihash) public { + multihashes[counter++] = multihash; + } + + function legalDocument(uint16 documentId) public view returns (bytes memory) { + return multihashes[documentId]; + } + + function documentSigned(address user, uint16 documentId) public view returns (boolean isSigned) { + return signedAt[documentId][user] != 0; + } + + function documentSignedAt(address user, uint16 documentId) public view returns (uint64 timestamp) { + return signedAt[documentId][user]; + } + + function signDocument() public { + signedAt[documentId][msg.sender] = uint64(block.timestamp); + emit DocumentSigned(msg.sender, documentId); + } + + function supportsInterface(bytes4 _interfaceId) public view returns (bool) { + return _interfaceId == type(IERC5289Library).interfaceId; + } +} diff --git a/assets/eip-5289/interfaces/IERC165.sol b/assets/eip-5289/interfaces/IERC165.sol new file mode 100644 index 0000000000000..3e6ec8ea3e222 --- /dev/null +++ b/assets/eip-5289/interfaces/IERC165.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.0; + +interface IERC165 { + /// @notice Query if a contract implements an interface + /// @param interfaceID The interface identifier, as specified in ERC-165 + /// @dev Interface identification is specified in ERC-165. This function + /// uses less than 30,000 gas. + /// @return `true` if the contract implements `interfaceID` and + /// `interfaceID` is not 0xffffffff, `false` otherwise + function supportsInterface(bytes4 interfaceID) external view returns (bool); +} diff --git a/assets/eip-5289/interfaces/IERC5289Library.sol b/assets/eip-5289/interfaces/IERC5289Library.sol new file mode 100644 index 0000000000000..e57b90227661d --- /dev/null +++ b/assets/eip-5289/interfaces/IERC5289Library.sol @@ -0,0 +1,23 @@ +/// SPDX-License-Identifier: CC0-1.0 +pragma solidity ^0.8.0; + +import "./IERC165.sol"; + +interface IERC5289Library is IERC165 { + /// @notice Emitted when signDocument is called + event DocumentSigned(address indexed signer, uint16 indexed documentId); + + /// @notice The IPFS multihash of the legal document. This MUST use a common file format, such as PDF, HTML, TeX, or Markdown. + function legalDocument(uint16 documentId) public pure returns (bytes memory); + + /// @notice Returns whether or not the given user signed the document. + function documentSigned(address user, uint16 documentId) public view returns (boolean signed); + + /// @notice Returns when the the given user signed the document. + /// @dev If the user has not signed the document, the timestamp may be anything. + function documentSignedAt(address user, uint16 documentId) public view returns (uint64 timestamp); + + /// @notice Provide a signature + /// @dev This MUST be validated by the smart contract. This MUST emit DocumentSigned or throw. + function signDocument(address signer, uint16 documentId, bytes memory signature) public; +} diff --git a/.jekyll-labels.yml b/config/.jekyll-labels.yml similarity index 58% rename from .jekyll-labels.yml rename to config/.jekyll-labels.yml index 4d7d66c8dda94..18c3b65b659e9 100644 --- a/.jekyll-labels.yml +++ b/config/.jekyll-labels.yml @@ -3,5 +3,6 @@ "type: Interface": this?.new?.category == "Interface" "type: ERC": this?.new?.category == "ERC" "type: Informational": this?.new?.type == "Informational" -"type: Meta": this?.new?.type == "Meta" && this?.old?.eip != "1" -"type: EIP1 (Process)": this?.old?.eip == "1" +"type: Meta": this?.new?.type == "Meta" +"type: EIP1 (Process)": this?.old?.status == "Living" || this?.old?.eip == "" +"waiting: EIP number": '!isNaN(this?.new?.eip)'