mirror of
https://github.com/status-im/EIPs.git
synced 2025-02-23 04:08:09 +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
131
EIPS/eip-2733.md
131
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
|
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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user