Skip to content

Commit

Permalink
Automatically merged updates to draft EIP(s) 2718 (#2970)
Browse files Browse the repository at this point in the history
Hi, I'm a bot! This change was automatically merged because:

 - It only modifies existing Draft or Last Call EIP(s)
 - The PR was approved or written by at least one author of each modified EIP
 - The build is passing
  • Loading branch information
MicahZoltu authored Sep 12, 2020
1 parent 6ce0652 commit 7e97f16
Showing 1 changed file with 21 additions and 56 deletions.
77 changes: 21 additions & 56 deletions EIPS/eip-2718.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,72 +7,48 @@ status: Draft
type: Standards Track
category: Core
created: 2020-06-13
requires: 155
---

## Simple Summary
Defines a new transaction type that is an envelope for future transaction types.

## Abstract
`TransactionType || TransactionPayload` will become a valid transaction and `TransactionType || TransactionPayload` will become a valid transaction receipt identifying the format of the transaction and `*Payload` is the transaction/receipt contents, which are defined in future EIPs.

The first two new transaction types will be a wrapped legacy transactions with the formats:
* `0x00 || yParity || r || s || 0x65000000 || rlp(nonce, gasPrice, gasLimit, to, value, data)`
* `0x01 || yParity || r || s || 0x65000000 || rlp(nonce, gasPrice, gasLimit, to, value, data, chainId, 0, 0)`

The first new receipt will be a wrapped legacy receipt with the format `0 || ssz(status, cumulativeGasUsed, logsBloom, logs)`.
`TransactionType || TransactionPayload` is a valid transaction and `TransactionType || ReceiptPayload` is a valid transaction receipt where `TransactionType` identifies the format of the transaction and `*Payload` is the transaction/receipt contents, which are defined in future EIPs.

## Motivation
In the past, when we have wanted to add new transaction types we have had to ensure they were backward compatible with all other transactions, meaning that you could differentiate them based only on the encoded payload, and it was not possible to have a transaction that matched both types.
This was seen in [EIP-155](./eip-155.md) where the new value was bit-packed into one of the encoded fields.
There are multiple proposals in discussion that define new transaction types such as one that allows EOA accounts to execute code directly within their context, one that enables someone besides `msg.sender` to pay for gas, and proposals related to layer 0 multi-sig transactions.
There are multiple proposals in discussion that define new transaction types such as one that allows EOA accounts to execute code directly within their context, one that enables someone besides `msg.sender` to pay for gas, and proposals related to layer 1 multi-sig transactions.
These all need to be defined in a way that is mutually compatible, which quickly becomes burdensome to EIP authors and to clients who now have to follow complex rules for differentiating transaction type.

By introducing an envolope transaction type, we only need to ensure backward compatibility with existing transactions and from then on we just need to solve the much simpler problem of ensuring there is no numbering conflict between `TransactionType`s.

## Specification
### Definitions
* `||` is the byte/byte-array concatenation operator.
* `yParity` is the parity (0 for even, 1 for odd) of the `y` value of the curve point for which `r` is the `x` value in the secp256k1 signing process.

### Transactions
As of `FORK_BLOCK_NUMBER`, `rlp([nonce, gasPrice, gasLimit, to, value, data, v, r, s])` (legacy transaction) will no longer be a valid Ethereum transaction over the devp2p protocol or in a block.

As of `FORK_BLOCK_NUMBER`, all transactions sent over devp2p **MUST** be of the form `TransactionType || TransactionPayload` where `TransactionType` is a positive unsigned number between `0` and `0x7f` that represents the type of the transcation and `TransactionPayload` is an opaque byte array whose interpretation is dependent on the `TransactionType`.
New `TransactionType`s **SHOULD** include the `TransactionType` as the first byte in any signatures they include to minimize the chance of unintentional replay attacks between different transaction types.
The transaction hash of all transactions **MUST** be `keccak256(TransactionType || TransactionPayload)`
As of `FORK_BLOCK_NUMBER`, the transaction root in the block header **MUST** be the root hash of `patriciaTrie(rlp(Index) => Transaction)` where:
* `Index` is the index in the block of this transaction
* `Transaction` is either `TransactionType || TransactionPayload` or `LegacyTransaction`
* `TransactionType` is a positive unsigned 8-bit number between `0` and `0x7f` that represents the type of the transcation
* `TransactionPayload` is an opaque byte array whose interpretation is dependent on the `TransactionType` and defined in future EIPs
* `LegacyTransaction` is `rlp([nonce, gasPrice, gasLimit, to, value, data, v, r, s])`

As of `FORK_BLOCK_NUMBER`, the transaction root in the block header **MUST** be the root hash of `patriciaTrie(transactionIndexInBlock => TransactionType || TransactionPayload)` where `transactionIndexInBlock` is encoded as a big endian 16-bit number.

As of `FORK_BLOCK_NUMBER`, `0x00 || yParity || r || s || 0x65000000 || rlp(nonce, gasPrice, gasLimit, to, value, data)` will be a valid transaction where the RLP encoded transaction portion is signed/processed/handled exactly the same as legacy transactions were signed/processed/handled, with the exception of the final encoding.

As of `FORK_BLOCK_NUMBER`, `0x01 || yParity || r || s || 0x65000000 || rlp(nonce, gasPrice, gasLimit, to, value, data, chainId, 0, 0)` will be a valid transaction where the RLP encoded transaction portion is signed/processed/handled exactly the same as legacy transactions were signed/processed/handled, with the exception of the final encoding.
All signatures for future transaction types **MUST** include the `TransactionType` as the first byte of the signed data.
This makes it so we do not have to worry about signatures for one transaction type being used as signaturese for a different transaction type.

### Receipts
As of `FORK_BLOCK_NUMBER`, `rlp([status, cumulativeGasUsed, logsBloom, logs])` (legacy receipt) will no longer be a valid Ethereum transaction receipt over the devp2p protocol or as part of a block.

As of `FORK_BLOCK_NUMBER`, all receipts sent over devp2p **MUST** be of the form `TransactionType || ReceiptPayload` where `TransactionType` is a number between `0` and `0x7f` that represents the type of the transaction and `ReceiptPayload` is an opaque byte array whose interpretation is dependent on the `TransactionType`.
As of `FORK_BLOCK_NUMBER`, the receipt root in the block header **MUST** be the root hash of `patriciaTrie(rlp(Index) => Receipt)` where:
* `Index` is the index in the block of the transaction this receipt is for
* `Receipt` is either `TransactionType || ReceiptPayload` or `LegacyReceipt`
* `TransactionType` is a positive unsigned 8-bit number between `0` and `0x7f` that represents the type of the transcation
* `ReceiptPayload` is an opaque byte array whose interpretation is dependent on the `TransactionType` and defined in future EIPs
* `LegacyReceipt` is `rlp([status, cumulativeGasUsed, logsBloom, logs])`

As of `FORK_BLOCK_NUMBER`, the receipt root in the block header **MUST** be the root hash of `patriciaTrie(transactionIndexInBlock => TransactionType || ReceiptPayload)` where `transactionIndexInBlock` is encoded as a big endian 16-bit number.

As of `FORK_BLOCK_NUMBER`, `0 || ssz(status, cumulativeGasUsed, logsBloom, logs)` will be a valid receipt where the `ReceiptPayload` will be generated/processed/handled exactly the same as legacy receipts were processed/handled with the exception of its encoding.

As of `FORK_BLOCK_NUMBER`, `1 || ssz(status, cumulativeGasUsed, logsBloom, logs)` will be a valid receipt where the `ReceiptPayload` will be generated/processed/handled exactly the same as legacy receipts were processed/handled with the exception of its encoding.

### Fork Block Transition
Between `FORK_BLOCK_NUMBER - 1` and `FORK_BLOCK_NUMBER` clients **SHOULD** convert all transactions in their local pending pool into type 0 transactions.
Clients **MAY** choose to flush their pending pool instead, if it is not possible to do the wrapping in time for the next block, though this is discouraged.

As of `FORK_BLOCK_NUMBER`, clients **SHOULD** accept incoming transactions over user/application facing APIs such as JSON-RPC in both the new wrapped format and the legacy format.
If a legacy format transaction is received, the client **MUST** wrap it in a type 0 envelope before sending it over devp2p or including it in a block.

As of `FORK_BLOCK_NUMBER`, clients **SHOULD** return type 0 receipts over user/application facing APIs such as JSON-RPC in the **legacy** format.
Clients **SHOULD** devise a long term strategy for deprecating the legacy format over APIs in favor of the typed receipt envelope format.
The `TransactionType` of the receipt **MUST** match the `TransactionType` of the transaction with a matching `Index`.

## Rationale
### Not including the type in type 6 signatures
While this EIP recommends that the envelope be included in any signatures, it also specifies that for Transaction Type 0 the envelope is not included in the signature.
This EIP disobayes its own recommendation because we don't want all signing tools to break on the fork block and require updates to start working again.
### TransactionType only goes up to 0x7f
For the forseable future, 0x7f is plenty and it leaves open a number of options for extending the range such as using the high bit as a continuation bit. This also prevents us from colliding with legacy transaction types, which always start with a byte `>= 0xc0`.
### TransactionType selection algorithm
Expand All @@ -82,26 +58,15 @@ A future EIP may introduce a standard for TransactionType identifier assignment
### Opaque byte array rather than an RLP array
By having the second byte on be opaque bytes, rather than an RLP (or other encoding) list, we can support different encoding formats for the transaction payload in the future such as SSZ, LEB128, or a fixed width format.
### ORIGIN and CALLER
There was discussion about having ORIGIN and CALLER opcodes be dependent on the transaction type, so that each transaction type could define what those opcodes returned.
There was discussion about having ORIGIN and CALLER opcodes become dependent on the transaction type, so that each transaction type could define what those opcodes returned.
However, there is a desire to make transaction type opaque to the contracts to discourage contracts treating different different types of transactions differently and there also were concerns over backward compatibility with existing contracts which make assumptions about ORIGIN and CALLER opcodes.
Going forward, we will assume that all transaction types will have an address that reasonably represents a `CALLER` of the first EVM frame and `ORIGIN` will be the same address in all cases.
If a transaction type needs to supply additional information to contracts, they will need a new opcode.
### Hashing the outer transaction
If you submit a transaction prior to the fork block and it is mined after the fork block, the transaction hash of the mined transaction will not match the the transaction hash of the transaction you originally submitted.
This is because the mined transaction will be a wrapped one, while the submitted transaction will be unwrapped.
Applications that use the transaction hash to lookup the transaction result will fail to ever find it or see it ever mined.
Clients can optionally choose to track type 6 transactions internally by both hashes for a period of time so that lookups of the old hash won't fail for the user, but this is not required for consensus.
### TransactionType 0 & 1 instead of just 0 that matches legacy
Legacy transactions were quite complicated due partially to EIP-155 and partially to the way they are encoded.
This change should reduce complexity in clients by making it easier to validate transactions, and it moves the complexities around EIP-155 and transaction encoding out near the JSON-RPC layer.
* Legacy transactions have to be RLP decoded first, the signature extracted, and then RLP encoded without the signature in order to validate them.
* Legacy transactions have to extract and process the `v` value before they can recreate the signed data for validation.

### TransactionType 0/1 0x65000000 element
`0x00 || yParity || r || s || 0x65000000 || rlp(nonce, gasPrice, gasLimit, to, value, data)` is the same as `ssz(0, yParity, r, s, rlp(nonce, gasPrice, gasLimit, to, value, data))`, which may prove convenient for some clients. It costs us 4 extra bytes on the wire (mostly zeros), but the authors deem this a worthwhile trade to be SSZ encodable/decodable.

## Backwards Compatibility
Clients can differentiate between the legacy transactions and typed transactions by looking at the first byte. If it starts with a value in the range `[0, 0x7f]` then it is a new transaction type, if it starts with a value in the range `[0xc0, 0xff]` then it is a legacy transaction type.
Clients can differentiate between the legacy transactions and typed transactions by looking at the first byte.
If it starts with a value in the range `[0, 0x7f]` then it is a new transaction type, if it starts with a value in the range `[0xc0, 0xfe]` then it is a legacy transaction type.
`0xff` is not realistic for an RLP encoded transaction, so it is reserved for future use as an extension sentinel value.

## Test Cases
TBD
Expand Down

0 comments on commit 7e97f16

Please sign in to comment.