mirror of
https://github.com/status-im/EIPs.git
synced 2025-02-22 11:48:19 +00:00
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:
parent
3c82f6001a
commit
36fee37ece
133
EIPS/eip-2733.md
133
EIPS/eip-2733.md
@ -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
|
||||
transactions for single users.
|
||||
|
||||
|
||||
## Specification
|
||||
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 {
|
||||
chain_id: u256,
|
||||
children: [Child],
|
||||
children: [ChildPackage],
|
||||
nonce: u64,
|
||||
gas_price: u256,
|
||||
v: u256,
|
||||
@ -72,7 +71,7 @@ struct TransactionPackage {
|
||||
`keccak256(rlp([2, chain_id, children, nonce, gas_price])`
|
||||
|
||||
##### 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`.
|
||||
|
||||
```
|
||||
@ -89,21 +88,28 @@ struct ChildReceipt {
|
||||
```
|
||||
|
||||
#### Child Transaction
|
||||
Let `Child` be interpreted as follows.
|
||||
Let `ChildPackage` be interpreted as follows.
|
||||
|
||||
```
|
||||
struct Child {
|
||||
struct ChildPackage {
|
||||
type: u8,
|
||||
nonce: u64,
|
||||
transactions: [ChildTransaction],
|
||||
max_gas_price: u256,
|
||||
v: u256,
|
||||
r: u256,
|
||||
s: u256
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
struct ChildTransaction {
|
||||
flags: u8,
|
||||
to: Address,
|
||||
value: u256,
|
||||
data: [u8],
|
||||
extra: [u8],
|
||||
max_gas_price: u256,
|
||||
gas_limit: u256,
|
||||
v: u256,
|
||||
r: u256,
|
||||
s: u256
|
||||
gas_limit: u256
|
||||
}
|
||||
```
|
||||
|
||||
@ -114,8 +120,8 @@ signer.
|
||||
|
||||
| type | signature hash |
|
||||
|---|---|
|
||||
| `0x00` | `keccak256(rlp([0, nonce, to, value, data, extra, max_gas_price, gas_limit])` |
|
||||
| `0x01` | `keccak256(rlp([1, nonce, to, value, data, extra])` |
|
||||
| `0x00` | `keccak256(rlp([0, nonce, transactions, max_gas_price])` |
|
||||
| `0x01` | `keccak256(rlp([1, nonce, transactions_without_gas_limit])` |
|
||||
|
||||
### Validity
|
||||
|
||||
@ -123,38 +129,36 @@ A `TransactionPackage` can be deemed valid or invalid as follows.
|
||||
|
||||
```rust
|
||||
fn is_valid(config: &Config, state: &State, tx: TransactionPackage) bool {
|
||||
if config.chain_id() != tx.chain_id {
|
||||
false
|
||||
if (
|
||||
config.chain_id() != tx.chain_id ||
|
||||
tx.children.len() == 0 ||
|
||||
state.nonce(tx.from()) + 1 != tx.nonce
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if tx.children.len() == 0 {
|
||||
false
|
||||
}
|
||||
|
||||
if state.nonce(tx.from()) + 1 != tx.nonce {
|
||||
false
|
||||
}
|
||||
|
||||
let cum_limit = tx.children.map(|x| x.gas_limit).sum();
|
||||
if state.balance(tx.from()) < cum_limit * tx.gas_price + intrinsic_gas(tx) {
|
||||
false
|
||||
return false;
|
||||
}
|
||||
|
||||
for child in tx.children {
|
||||
if state.nonce(child.from()) + 1 != child.nonce {
|
||||
false
|
||||
if (
|
||||
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 {
|
||||
false
|
||||
}
|
||||
|
||||
if child.max_gas_price < tx.gas_price {
|
||||
false
|
||||
}
|
||||
|
||||
if child.extra.len() != 0 {
|
||||
false
|
||||
for tx in child.txs {
|
||||
if (
|
||||
tx.flags != 0 ||
|
||||
tx.extra.len() != 0 ||
|
||||
tx.gas_limit < intrinsic_gas(tx)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,10 +168,10 @@ fn is_valid(config: &Config, state: &State, tx: TransactionPackage) bool {
|
||||
|
||||
### Results
|
||||
|
||||
Subsequent transactions will be able to receive the result of the previous
|
||||
transaction via `RETURNDATACOPY (0x3E)` in first frame of execution, before
|
||||
making any subcalls. Each element, except the last, will be `0`-padded left to
|
||||
32 bytes.
|
||||
Subsequent `ChildTransaction`s will be able to receive the result of the
|
||||
previous `ChildTransaction` via `RETURNDATACOPY (0x3E)` in first frame of
|
||||
execution, before making any subcalls. Each element, except the last, will be
|
||||
`0`-padded left to 32 bytes.
|
||||
|
||||
```
|
||||
struct Result {
|
||||
@ -193,34 +197,34 @@ Let the intrinsic cost of the transaction package be defined as follows:
|
||||
|
||||
```
|
||||
fn intrinsic_gas(tx: TransactionPackage) u256 {
|
||||
let data_cost = tx.children.map(|c| data_cost(&c.data)).sum();
|
||||
17000 + 8000 * tx.children.len() + data_cost
|
||||
let data_gas = tx.children.map(|c| c.txs.map(|t| data_cost(&c.data)).sum()).sum();
|
||||
17000 + 8000 * tx.children.len() + data_gas
|
||||
}
|
||||
```
|
||||
|
||||
### Execution
|
||||
Transaction packages should be executed as follows:
|
||||
1. Deduct the cumulative cost from the outer signer's balance.
|
||||
2. Execute the first child in the list.
|
||||
3. Record all state changes, logs, and the receipt.
|
||||
4. If there are no more transactions, stop.
|
||||
2. Load the first child package, and execute the first child transaction.
|
||||
3. Record all state changes, logs, the receipt, and refund any unused gas.
|
||||
4. If there are no more child transactions, goto `8`.
|
||||
5. Compute `Result` for the previously executed transaction.
|
||||
6. Prepare `Result` to be available via return opcodes in the next
|
||||
transaction's first frame
|
||||
7. Execute the next transaction
|
||||
8. Goto `3`
|
||||
transaction's first frame.
|
||||
7. Execute the next transaction, then goto `3`.
|
||||
8. Load the next child package, then goto `7`.
|
||||
|
||||
## Rationale
|
||||
|
||||
### Each `Child` has its own signature
|
||||
For simplicity, the author has chosen to require each child transaction to
|
||||
specify its own signature, even if the signer is the same as the package
|
||||
signer. This choice is made to allow for maximum flexibility, with minimal
|
||||
client changes. A future transaction type can be specified with only a single
|
||||
signature, if such an optimization is desired.
|
||||
For simplicity, the author has chosen to require each child package to specify
|
||||
its own signature, even if the signer is the same as the package signer. This
|
||||
choice is made to allow for maximum flexibility, with minimal client changes.
|
||||
This transaction can still be used by a single user at the cost of only one
|
||||
additional signature recovery.
|
||||
|
||||
### `Child` specifies `max_gas_price` instead of `gas_price`
|
||||
Allowing child transactions to specify a range of acceptable gas prices is
|
||||
### `ChildPackage` specifies `max_gas_price` instead of `gas_price`
|
||||
Allowing child packages to specify a range of acceptable gas prices is
|
||||
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
|
||||
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.
|
||||
the relayer should package it with the max price immediately vs. slowly
|
||||
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 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
|
||||
@ -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
|
||||
limit.
|
||||
|
||||
The type also simplifies upgradability to the child transactions. For example,
|
||||
suppose [EIP-2803](./eip-2803.md) is implemented. The upper 4 bits of the type
|
||||
can be used as a flag, alongside the specified types in this EIP.
|
||||
|
||||
### The `extra` field isn't used
|
||||
This field is included to better support future changes to the transaction
|
||||
type. This would likely be used in conjunction with the `type` field. Avoiding
|
||||
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.
|
||||
### The `flags` and `extra` fields aren't used
|
||||
These fields are included to better support future changes to the transaction
|
||||
type. This would likely be used in conjunction with the `flags` and `type`
|
||||
fields. A benefit of explicitly defining them is that specialized serialization
|
||||
of RLP can be avoided, simplifing clients and downstream infrastructure. The
|
||||
author believe the cost of 2 bytes per transaction is acceptable for smoother
|
||||
integration of future features.
|
||||
|
||||
## Backwards Compatibility
|
||||
Contracts which rely on `ORIGIN (0x32) == CALLER (0x33) && RETURNDATASIZE
|
||||
|
Loading…
x
Reference in New Issue
Block a user