forked from DecentralizedClimateFoundation/DCIPs
137 lines
12 KiB
Markdown
137 lines
12 KiB
Markdown
---
|
|
eip: 2711
|
|
title: Sponsored, expiring and batch transactions.
|
|
author: Micah Zoltu (@MicahZoltu)
|
|
discussions-to: https://ethereum-magicians.org/t/eip-2711-separate-gas-payer-from-msg-sender/4353
|
|
status: Withdrawn
|
|
type: Standards Track
|
|
category: Core
|
|
created: 2020-06-11
|
|
requires: 2718
|
|
---
|
|
|
|
## Simple Summary
|
|
Creates a new transaction type that supports sponsored transactions (separate gas payer from sender), batch transactions (multiple transactions executed in sequence), and expiring transactions (transactions which are not valid after a certain timestamp).
|
|
|
|
## Abstract
|
|
An EIP-2718 transaction with the type number `2` is a new type of transaction that includes support for:
|
|
1. **Sponsored Transactions**: an optional additional signature from which the account that will pay for gas (`GAS_PAYER`) can be recovered
|
|
2. **Batch Transactions**: multiple transactions from the same sender that will be executed in sequence
|
|
3. **Expiring Transactions**: an optional `validUntil` field that makes the transaction invalid after a certain point in time
|
|
|
|
## Motivation
|
|
### Sponsored Transactions
|
|
With the advent of tokens and especially stable coins, it has become common for users to not hold ETH in an account while they may have other assets of value in that account. Some users don't want to be exposed to the perceived volatility of ETH and instead would prefer to transact using other assets. Unfortunately, since gas **MUST** be paid for with ETH, this prevents the user from transacting with their assets without first acquiring some ETH using some other means, and then using that ETH to pay fees.
|
|
|
|
This EIP proposes a mechanism by which we can allow people to transact without ever having to own any ETH by allowing someone else to cover gas costs. The arrangements that enable the covering of gas costs is out of scope for this EIP but it could be an extra-protocol monthly subscription, payment could occur as part of the transaction being submitted, the recpient may be willing to cover gas costs, or it could be a free service offered as a value-add by a company that you are working with.
|
|
|
|
While it is possible to implement these sort of mechanisms at the individual contract layer, such solutions require integration by just about every contract and those solutions also end up depending on gas costs being stable with time in order to appropriately bake them into contracts without putting either party at risk of malicious participants in the system. For this reason, it is believed that separating out `GAS_PAYER` from `msg.sender` at the protocol layer is valuable.
|
|
|
|
### Batch Transactions
|
|
Often times an EOA may want to execute a series of transactions with a strong guarantee that they happen in order with nothing occurring between them. For example, one may want to send some tokens to a contract and then follow that up with another transaction that makes a contract call on the destination address that causes those tokens to be registered to them. By supporting transaction batching at layer 1, we can ensure that the user can get strong guarantees at signing time of cross-transaction atomicity.
|
|
|
|
### Expiring Transactions
|
|
* If any form of dust-account clearing is introduced, e.g. (https://github.com/ethereum/EIPs/issues/168), it will be necessary to introduce a replay protection, such as https://github.com/ethereum/EIPs/issues/169 . Having temporal replay protection removes the need to change nonce-behaviour in the state, since transactions would not be replayable at a later date than explicitly set by the user.
|
|
* In many cases, such as during ICOs, a lot of people want their transactions to either become included soon (within a couple of hours) or not at all. Currently, transactions are queued and may not execute for several days, at a cost for both the user (who ends up paying gas for a failing purchase) and the network, dealing with the large transaction queues.
|
|
* Node implementations have no commonly agreed metric for which transactions to keep, discard or propagate. Having a TTL on transactions would make it easier to remove stale transactions from the system.
|
|
|
|
## Specification
|
|
### Definitions
|
|
**`TransactionType`** 2. See [EIP-2718](./eip-2718.md)
|
|
|
|
**`TransactionSubtype`** is either 1, 2, 3, or 4.
|
|
|
|
**`ChainId`** The transaction is valid if this value is `0` or it is included in a block on a chain whose ID is equal to this value.
|
|
|
|
**`ValidUntil`** The transaction is valid if this value is `0` or it is included in a block whose `timestamp` is less than or equal to this value.
|
|
|
|
**`YParity`** The parity (0 for even, 1 for odd) of the y-value of a secp256k1 signature.
|
|
|
|
**`ChildTransaction`** A nested transaction consisting of `[to, value, data]`.
|
|
|
|
**`SenderPayload`** Defined based on the `TransactionSubtype` as follows:
|
|
1. `[1, ChildTransaction[], nonce, ChainId, ValidUntil, gasLimit, gasPrice]`
|
|
2. `[2, ChildTransaction[], nonce, ChainId, ValidUntil, gasLimit, gasPrice]`
|
|
3. `[3, ChildTransaction[], nonce, ChainId, ValidUntil, gasLimit]`
|
|
4. `[4, ChildTransaction[], nonce, ChainId, ValidUntil]`
|
|
|
|
**`SenderSignature`** `[YParity, r, s]` of `secp256k1(keccak256(rlp([TransactionType, SenderPayload])))`
|
|
|
|
**`GasPayerPayload`** Defined based on the `TransactionSubtype` as follows:
|
|
1. `[]`
|
|
2. `[]`
|
|
3. `[gasPrice]`
|
|
4. `[gasLimit, gasPrice]`
|
|
|
|
**`GasPayerSignature`** is `[]` for `TransactionSubType` `1` or `[YParity, r, s]` of `secp256k1(keccak256(rlp([SenderPayload, SenderSignature, GasPayerPayload])))` for others.
|
|
|
|
### New Transaction Type
|
|
|
|
As of `FORK_BLOCK_NUMBER` an [EIP-2718](./eip-2718.md) transaction with a `TransactionType` of `2` will have its `Payload` interpreted as an RLP encoded tuple of:
|
|
```
|
|
[...SenderPayload, ...SenderSignature, ...GasPayerPayload, ...GasPayerSignature]
|
|
```
|
|
|
|
The address recovered from `SenderSignature` is the address...
|
|
1. ...returned by the `CALLER` opcode (0x33, aka `msg.sender`) during the first call frame of the transaction
|
|
2. ...returned by the `ORIGIN` opcode (0x32, aka `tx.origin`)
|
|
3. ...whose `nonce` is used
|
|
4. ...whose ETH balance is deducted if any value is attached to the transaction
|
|
5. ...whose ETH balance is deducted to pay for gas if `GasPayerSignature` is not present
|
|
|
|
If `GasPayerSignature` is present, then the address recovered from it is the address...
|
|
1. ...whose ETH balance is deducted to pay for gas
|
|
|
|
The base gas cost of transactions of this type will be `TRANSACTION_TYPE_2_BASE_GAS_PRICE` + `TRANSACTION_TYPE_2_CHILD_GAS_PRICE` * `n`, rather than the cost associated with transactions of type `0` and legacy transactions.
|
|
|
|
### New Transaction Receipt
|
|
|
|
As of `FORK_BLOCK_NUMBER` an [EIP-2718](./eip-2718.md) transaction receipt with a `TransactionType` of `2` will have its `Payload` interpreted as a `rlp([status, cumulativeGasUsed, logsBloom, logs][])` where each item of the array corresponds to the child-transaction at matching offset in the transaction type 2 `Payload`.
|
|
|
|
## Rationale
|
|
### One Monolithic EIP
|
|
This EIP could be split up into multiple EIPs, one for each of the subtypes and one for the meta-type. Alternatively, each of the subtypes could be a unique TransactionType. The reason we chose to go with a single EIP with subtypes is because these 4 transactions all have a *lot* in common and each separate EIP would be almost identical to the previous. We felt that in this case, splitting into multiple EIPs wasn't worth the duplication of EIP content.
|
|
### ChainID not encoded with `v`
|
|
While we could save one byte in the common case by bundling the y-parity bit of the signature with the Chain ID like in EIP-155, this adds complexity to signing tools that the authors deem not worth it given the size of the transaction overall.
|
|
### Optionality of ChainID
|
|
Sometimes it is useful to have a transaction that *can* be replayed on multiple chains. An example of this is when you construct a vanity signature for a transaction and have the `from` be whatever address that signature recovers to. With the ability to have someone else be a gas payer (setting both the gas limit and the gas price), one can have transactions that deploy contracts which live at the same address on every chain. While this can be accomplished with CREATE2 using legacy transactions, we have the opportunity here to simplify the process and enable potentially other future uses of deterministic transactions by making ChainID optional.
|
|
### Optionality of ValidUntil
|
|
A user can set `ValidUntil` to a very large number which effectively makes it non-expiring. By making `ValidUntil` optional, we can save some bytes on the wire by allowing such transcations to simply have a `0` (1 byte in RLP) value for this field.
|
|
### `SENDER` sets `gasLimit` and `gasPrice`
|
|
This type of transaction is useful when the transaction may execute differently depending on what these values are set to. By having the `SENDER` set both, we ensure that the `SENDER` has full control over the transaction details.
|
|
### `SENDER` sets `gasLimit`, `GAS_PAYER` sets `gasPrice`
|
|
This type of transaction is useful when the transaction may execute differently depending on how much gas it is allowed (e.g., number of loops) but where the `SENDER` would like to give the `GAS_PAYER` the ability to price the transaction to maximize chances of inclusion.
|
|
### `GAS_PAYER` sets `gasLimit` and `gasPrice`
|
|
This type of transaction allows the `SENDER` to define what they want to do, and leaves all worry about gas to the `GAS_PAYER`. This is useful for transactions where the sender doesn't care how much gas is used or the price that is paid and also either trusts the `GAS_PAYER` to be non-malicious or doesn't care if the `SENDER`'s nonce is increased. Such situations are useful when you have extra-protocol trust between the `SENDER` and `GAS_PAYER` and you want to separate concerns (what to do vs how to get included) for security or complexity reasons.
|
|
### Nonces
|
|
The inner transaction needs a nonce to protect themselves from replay attacks. Since the inner transaction has a nonce, we get replay protection on the outer transaction as well, so it is not critical for security to have multiple parties provide a nonce.
|
|
|
|
We could have the `GAS_PAYER` provide a second nonce, but this would increase the payload size and require `GAS_PAYER` to do replace-by-fee (noisy for gossip) if they want to slip in a new (different inner) transaction with a higher gas price. It would also create the possibility of a deadlock if the `SENDER` nonces aren't ordered the same as the `GAS_PAYER` nonces, and if the `SENDER` nonce isn't the lowest valid nonce for the `SENDER` then the `GAS_PAYER` can't sign and submit yet. Finally, client complexity increases slightly if a transaction has two nonces because you have to protect yourself from deadlocks and do more work to determine validity.
|
|
### ValidUntil
|
|
For the dust-account clearing usecase,
|
|
- This change is much less invasive in the consensus engine.
|
|
- No need to maintain a consensus-field of 'highest-known-nonce' or cap the number of transactions from a sender in a block.
|
|
- Only touches the transaction validation part of the consensus engine
|
|
- Other schemas which uses the `nonce` can have unintended side-effects,
|
|
- such as inability to create contracts at certain addresses.
|
|
- more difficult to integrate with offline signers, since more elaborate nonce-schemes requires state access to determine.
|
|
- More intricate schemes like `highest-nonce` are a lot more difficult, since highest-known-nonce will be a consensus-struct that is incremented and possibly reverted during transaction execution, requiring one more journalled field.
|
|
|
|
### ValidUntil as timestamp instead of block number
|
|
- The unix time is generally available in most settings, even on a computer which is offline. This means that even a setup where blockchain information is unavailable, the party signing a transaction can generate a transaction with the desired properties.
|
|
- The correlation between time and block number is not fixed; even though a 13s blocktime is 'desired', this varies due to both network hashrate and difficulty bomb progression.
|
|
- The block number is even more unreliable as a timestamp for testnets and private networks.
|
|
- unix time is more user-friendly, a user can more easily decide on reasonable end-date for a transaction, rather than a suitalbe number of valid blocks.
|
|
|
|
## Backwards Compatibility
|
|
No known issues.
|
|
|
|
## Test Cases
|
|
|
|
## Implementation
|
|
|
|
## Security Considerations
|
|
|
|
## Copyright
|
|
Copyright and related rights waived via [CC0](../LICENSE.md).
|