Automatically merged updates to draft EIP(s) 2733 (#3039)

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
This commit is contained in:
lightclient 2020-10-12 11:37:29 -06:00 committed by GitHub
parent 3c82f6001a
commit 36fee37ece
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -48,7 +48,6 @@ they are protected from future changes to the gas schedule.
An important byproduct of this EIP is that it also facilitates bundling An important byproduct of this EIP is that it also facilitates bundling
transactions for single users. transactions for single users.
## Specification ## Specification
Introduce a new [EIP-2718](./eip-2718.md) transaction type where `id = 2`. Introduce a new [EIP-2718](./eip-2718.md) transaction type where `id = 2`.
@ -56,7 +55,7 @@ Introduce a new [EIP-2718](./eip-2718.md) transaction type where `id = 2`.
``` ```
struct TransactionPackage { struct TransactionPackage {
chain_id: u256, chain_id: u256,
children: [Child], children: [ChildPackage],
nonce: u64, nonce: u64,
gas_price: u256, gas_price: u256,
v: u256, v: u256,
@ -72,7 +71,7 @@ struct TransactionPackage {
`keccak256(rlp([2, chain_id, children, nonce, gas_price])` `keccak256(rlp([2, chain_id, children, nonce, gas_price])`
##### Receipt ##### Receipt
Each `Child` transaction will generate a `ChildReceipt` after execution. Each Each `ChildTransaction` transaction will generate a `ChildReceipt` after execution. Each
of these receipts will be aggregated into a `Receipt`. of these receipts will be aggregated into a `Receipt`.
``` ```
@ -89,21 +88,28 @@ struct ChildReceipt {
``` ```
#### Child Transaction #### Child Transaction
Let `Child` be interpreted as follows. Let `ChildPackage` be interpreted as follows.
``` ```
struct Child { struct ChildPackage {
type: u8, type: u8,
nonce: u64, nonce: u64,
transactions: [ChildTransaction],
max_gas_price: u256,
v: u256,
r: u256,
s: u256
}
```
```
struct ChildTransaction {
flags: u8,
to: Address, to: Address,
value: u256, value: u256,
data: [u8], data: [u8],
extra: [u8], extra: [u8],
max_gas_price: u256, gas_limit: u256
gas_limit: u256,
v: u256,
r: u256,
s: u256
} }
``` ```
@ -114,8 +120,8 @@ signer.
| type | signature hash | | type | signature hash |
|---|---| |---|---|
| `0x00` | `keccak256(rlp([0, nonce, to, value, data, extra, max_gas_price, gas_limit])` | | `0x00` | `keccak256(rlp([0, nonce, transactions, max_gas_price])` |
| `0x01` | `keccak256(rlp([1, nonce, to, value, data, extra])` | | `0x01` | `keccak256(rlp([1, nonce, transactions_without_gas_limit])` |
### Validity ### Validity
@ -123,38 +129,36 @@ A `TransactionPackage` can be deemed valid or invalid as follows.
```rust ```rust
fn is_valid(config: &Config, state: &State, tx: TransactionPackage) bool { fn is_valid(config: &Config, state: &State, tx: TransactionPackage) bool {
if config.chain_id() != tx.chain_id { if (
false config.chain_id() != tx.chain_id ||
} tx.children.len() == 0 ||
state.nonce(tx.from()) + 1 != tx.nonce
if tx.children.len() == 0 { ) {
false return false;
}
if state.nonce(tx.from()) + 1 != tx.nonce {
false
} }
let cum_limit = tx.children.map(|x| x.gas_limit).sum(); let cum_limit = tx.children.map(|x| x.gas_limit).sum();
if state.balance(tx.from()) < cum_limit * tx.gas_price + intrinsic_gas(tx) { if state.balance(tx.from()) < cum_limit * tx.gas_price + intrinsic_gas(tx) {
false return false;
} }
for child in tx.children { for child in tx.children {
if state.nonce(child.from()) + 1 != child.nonce { if (
false child.nonce != state.nonce(child.from()) + 1 ||
child.value > state.balance(child.from()) ||
child.max_gas_price < tx.gas_price
) {
return false;
} }
if state.balance(child.from()) < child.value { for tx in child.txs {
false if (
tx.flags != 0 ||
tx.extra.len() != 0 ||
tx.gas_limit < intrinsic_gas(tx)
) {
return false;
} }
if child.max_gas_price < tx.gas_price {
false
}
if child.extra.len() != 0 {
false
} }
} }
@ -164,10 +168,10 @@ fn is_valid(config: &Config, state: &State, tx: TransactionPackage) bool {
### Results ### Results
Subsequent transactions will be able to receive the result of the previous Subsequent `ChildTransaction`s will be able to receive the result of the
transaction via `RETURNDATACOPY (0x3E)` in first frame of execution, before previous `ChildTransaction` via `RETURNDATACOPY (0x3E)` in first frame of
making any subcalls. Each element, except the last, will be `0`-padded left to execution, before making any subcalls. Each element, except the last, will be
32 bytes. `0`-padded left to 32 bytes.
``` ```
struct Result { struct Result {
@ -193,34 +197,34 @@ Let the intrinsic cost of the transaction package be defined as follows:
``` ```
fn intrinsic_gas(tx: TransactionPackage) u256 { fn intrinsic_gas(tx: TransactionPackage) u256 {
let data_cost = tx.children.map(|c| data_cost(&c.data)).sum(); let data_gas = tx.children.map(|c| c.txs.map(|t| data_cost(&c.data)).sum()).sum();
17000 + 8000 * tx.children.len() + data_cost 17000 + 8000 * tx.children.len() + data_gas
} }
``` ```
### Execution ### Execution
Transaction packages should be executed as follows: Transaction packages should be executed as follows:
1. Deduct the cumulative cost from the outer signer's balance. 1. Deduct the cumulative cost from the outer signer's balance.
2. Execute the first child in the list. 2. Load the first child package, and execute the first child transaction.
3. Record all state changes, logs, and the receipt. 3. Record all state changes, logs, the receipt, and refund any unused gas.
4. If there are no more transactions, stop. 4. If there are no more child transactions, goto `8`.
5. Compute `Result` for the previously executed transaction. 5. Compute `Result` for the previously executed transaction.
6. Prepare `Result` to be available via return opcodes in the next 6. Prepare `Result` to be available via return opcodes in the next
transaction's first frame transaction's first frame.
7. Execute the next transaction 7. Execute the next transaction, then goto `3`.
8. Goto `3` 8. Load the next child package, then goto `7`.
## Rationale ## Rationale
### Each `Child` has its own signature ### Each `Child` has its own signature
For simplicity, the author has chosen to require each child transaction to For simplicity, the author has chosen to require each child package to specify
specify its own signature, even if the signer is the same as the package its own signature, even if the signer is the same as the package signer. This
signer. This choice is made to allow for maximum flexibility, with minimal choice is made to allow for maximum flexibility, with minimal client changes.
client changes. A future transaction type can be specified with only a single This transaction can still be used by a single user at the cost of only one
signature, if such an optimization is desired. additional signature recovery.
### `Child` specifies `max_gas_price` instead of `gas_price` ### `ChildPackage` specifies `max_gas_price` instead of `gas_price`
Allowing child transactions to specify a range of acceptable gas prices is Allowing child packages to specify a range of acceptable gas prices is
strictly more versatile than a static price. It gives relayers more flexibility strictly more versatile than a static price. It gives relayers more flexibility
in terms of building transaction bundles, and it makes it possible for relayers in terms of building transaction bundles, and it makes it possible for relayers
to try and achieve the best price for the transaction sender. With a fixed to try and achieve the best price for the transaction sender. With a fixed
@ -229,8 +233,10 @@ transactions, with varying prices. This can be avoided by specifying a max
price, and communicating out-of-band how the urgency of the transaction (e.g. price, and communicating out-of-band how the urgency of the transaction (e.g.
the relayer should package it with the max price immediately vs. slowly the relayer should package it with the max price immediately vs. slowly
increasing the gas price). increasing the gas price).
A future transaction type can be specified with only a single
signature, if such an optimization is desired.
### `Child` is also typed ### `ChildPackage` is also typed
The type element serves a modest role in the transaction type, denoting whether The type element serves a modest role in the transaction type, denoting whether
the transaction signer wishes to delegate control of the gas price and gas the transaction signer wishes to delegate control of the gas price and gas
limit to the outer signer. This is a useful UX improvement when interacting limit to the outer signer. This is a useful UX improvement when interacting
@ -238,16 +244,13 @@ with a trusted relayer, as once the user decides to make a transaction the
relayer can ensure it is included on chain by choosing the best gas price and relayer can ensure it is included on chain by choosing the best gas price and
limit. limit.
The type also simplifies upgradability to the child transactions. For example, ### The `flags` and `extra` fields aren't used
suppose [EIP-2803](./eip-2803.md) is implemented. The upper 4 bits of the type These fields are included to better support future changes to the transaction
can be used as a flag, alongside the specified types in this EIP. type. This would likely be used in conjunction with the `flags` and `type`
fields. A benefit of explicitly defining them is that specialized serialization
### The `extra` field isn't used of RLP can be avoided, simplifing clients and downstream infrastructure. The
This field is included to better support future changes to the transaction author believe the cost of 2 bytes per transaction is acceptable for smoother
type. This would likely be used in conjunction with the `type` field. Avoiding integration of future features.
specialized serialization of RLP simplifies clients and downstream
infrastructure. The author believe the cost of 1 byte per transaction is
acceptable for smoother integration of future features.
## Backwards Compatibility ## Backwards Compatibility
Contracts which rely on `ORIGIN (0x32) == CALLER (0x33) && RETURNDATASIZE Contracts which rely on `ORIGIN (0x32) == CALLER (0x33) && RETURNDATASIZE