Accessing historic public state from private functions

What are the downstream effects of this? Can the sequencer recover by sending another rollup? Or have they missed the epoch causing a liveness issue

Depends on how quick they are to create a valid one. Contract will essentially not “know” that your tx reverted, would just be as if it never happened so if you can give it a new one in a timely manner its all good. Essentially it will be similar to if no blocks are proposed for that time period.


An issue with relying on the fee at the time of epoch update to be covered by what came earlier could be for an attacker to pump fees after an attack such that an upgrade pausing or adding him to a blacklist is “skipped”. It puts a lot of extra stress into gas prices at the end of epoch as all changes want in but no-one want to sit there with an underpriced tx.

Ideally it should be possible to execute the tx, and then have the update take effect when epoch is passed. As txs that use this storage would need the last data, any pending txs of this type will be invalidated when the epoch boundary is crossed. So you will need to have a public input with the active epoch root to be valid.

Say that you accept this, and play along. Introduce the slow poke storage

struct SlowPoke {
  Field currentValue; 
  Field pendingValue;
  Field pendingActiveFrom;
}

mapping(slot => SlowPoke) public slowPokes;

When reading the slowpoke, you will use currentValue if blockNumber < pendingActiveFrom and pendingValue otherwise.

As we have bounded the blocknumber to the epoch, the user cannot just pick something stale. But the cost is that we might throw away a lot of pending messages when updated :grimacing:.

To update, you set currentValue = pendingValue and then update pendingValue = newValue and pendingActiveFrom = nextEpochblock. For users in the current epoch, they will be using the currentValue and after the epoch boundary is crossed, they need to use the new value.

Note, if we allow writing further into the future for the pendingActiveFrom it can also be used as a timelock until X in the private domain, which I think could be quite fun to see for governance tokens turning on transferability etc. Some governance tokens when minted cannot be transferred until decision is taken to support it, you could do that this way with the event being somewhat timed in the future (beyond one epoch).

“updating at new epoch” is no longer an action that is done at the exact jump, but could happen any time in the epoch. This allows something like USDC to send one tx that is updating theirs blacklists in both domains with the private just being a bit slower to kick in, but there should not be more pressure at the end of epoch than at beginning to include. And you don’t need to have l1 handle this separately. Also if there multiple tx’s that update the same, only the last would take over, e.g., say protocol updates a pause flag, but then figure it was a mistake, so they revert their change by updating the flag again.

I might be rambling, so please let me know

7 Likes