Skip to content

Commit

Permalink
Add article on the "compression" of access lists by execution clients (
Browse files Browse the repository at this point in the history
…#4)

* Add article on the "compression" of access lists by execution clients

* Make a compressed access list as a required p2p protocol change

* Introduce a 32 gas minimum base fee equivalent for including an entry in ACL (#6)

* Set minimum cost of ACL entry to 32 gas; apply also to the Priority Fee
  • Loading branch information
forshtat committed Nov 14, 2023
1 parent d7dffe0 commit 5a97629
Showing 1 changed file with 45 additions and 2 deletions.
47 changes: 45 additions & 2 deletions EIPS/eip-000.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,16 @@ transactions using one, it is reasonable for the protocol to only burn the gas c
As all transactions in the same block pay exactly the same `baseFeePerGas`, the single cost of accessing a cold item is
divided evenly among all transactions containing such access and the rest of the burned base fee is refunded.

### Setting an absolute minimal cost of cold state access

If a large number of transactions all access the same addresses or slots, the cost of each cold access may get
way too low which may represent a potential DoS attack vector.

In order to prevent that, the gas cost of including an entity in the access list cannot be lower
than `MIN_ACCESS_LIST_ENTRY_COST`, which is set to `32 gas`.

This value is equivalent to the calldata cost of including two bytes long identifier of entries in `block_access_list`.

### Calculating a refund of the charged priority fee

Each transaction pays an individual `priorityFeePerGas` value and redistributing this part of the cold access cost
Expand All @@ -171,6 +181,35 @@ The most expensive transaction contributes a different value because it determin
A single transaction accessing an address or a slot that is not shared by other transactions does not trigger a refund,
and therefore has a zero marginal contribution.

### Efficiently storing the access lists in the block history

The contents of the `accessList` parameter are part of the Ethereum history and the potential cost of keeping this
data in the blockchain must be accounted for when implementing this change.
There is currently no additional charge applied to the `accessList` parameter, due to the cost of including
an address or a storage slot in the `accessList` being a constant value that is significantly higher than the
potential cost of storing the `accessList` at the cost of a
dynamically sized `calldata` field.

With the block-level warming there is a change that makes it possible for a transaction sender to construct
transactions with a large `accessList` that cost very little to be included, and this can be used to bloat the
blockchain size.

This potential bloating of the block size also presents a challenge for the propagation of the block in a P2P network.

In order to minimize the cost of permanently storing access lists, we propose the following changes to
the `execution_payload` structure:

1. Add a new `block_access_list` field. \
The execution clients create a combined block-level "access list" that contains all unique entries from all
transactions in the block.
2. All individual transaction `accessList` fields replace the full entries with a
compact 2 bytes long reference to the `block_access_list` in the same order they appeared originally.

With this approach shared entries in the access lists cannot cause sufficient bloating of the block size.

There is no need to introduce any observable changes to the RPC API as this "compression" can be unwrapped by the
clients in real time.

### Pseudocode implementation of the refund calculation algorithm

```typescript
Expand Down Expand Up @@ -201,7 +240,8 @@ function calculateItemColdAccessRefund (
for (let i = 0; i < sortedAccessDetails.length; i++) {
const accessor = sortedAccessDetails[i]
const refund = refunds.get(accessor.sender) ?? { refundFromBurn: 0n, refundFromCoinbase: 0n }
refund.refundFromBurn += BigInt(Math.floor(parseInt(accessGasCost) * parseInt(baseFeePerGas) * refundPercent))
const adjustedAccessGasCost = Math.max(MIN_ACCESS_LIST_ENTRY_COST, parseInt(accessGasCost) * refundPercent)
refund.refundFromBurn += BigInt(adjustedAccessGasCost * parseInt(baseFeePerGas))
refund.refundFromCoinbase += BigInt(refundsFromCoinbase[i])
refunds.set(accessor.sender, refund)
}
Expand Down Expand Up @@ -234,7 +274,10 @@ export function calculatePriorityFeeRefunds (sortedAccesses: AccessDetails[], ac
const refunds = [Math.floor(totalRefund * topTransactionContribution / totalContributions)]
for (let i = 1; i < sortedAccesses.length; i++) {
const charge = parseInt(sortedAccesses[i].priorityFeePerGas) * parseInt(accessGasCost)
refunds.push(Math.floor(totalRefund * charge / totalContributions))
const calldataCharge = parseInt(sortedAccesses[i].priorityFeePerGas) * MIN_ACCESS_LIST_ENTRY_COST
const refundToCalldata = charge - calldataCharge
const refundToContribution = Math.floor(totalRefund * charge / totalContributions)
refunds.push(Math.min(refundToCalldata, refundToContribution))
}
return refunds
}
Expand Down

0 comments on commit 5a97629

Please sign in to comment.