Reading current public state from private functions

In this earlier post we discuss how to allow private functions to read historic or constant public state. Would it be possible to let private functions read current public state as well? This would be useful, for example, when querying the current implementation of an upgradeable contract.

The main issue with this is that public state can change over time, so the private function would need to “nail down” the values it depends on. Any changes to those public values would render the tx invalid, so a private tx would need to include a list of the public slots with their expected value for the sequencer to check before it accepts it. Note that checking the public state tree root would not work, since it changes too often, and checking any node lower in the tree already leaks information.

However, by publicly announcing what public values a private tx depends on, we’re leaking a lot of information. Is there a cryptographic magic we could use here to express this dependency, in a way that it’s easy to check by the sequencer, but without revealing info on what fields are actually being checked? Could something like OMR help here?

14 Likes

This issue is what I refer to in Private <--> Public execution | Aztec Docs (A note on L2 access control) as the primary usage I saw there was handling access control, e.g., access control needs to be up to date so stale information is no good.

You don’t need to tell exactly what values you depend on, but you would leak at least what public function you called and, thereby, what contract was used.

As mentioned at the bottom of the docs page, this is not a fantastic solution, so any magic ideas are very welcome :eyes:

To recap here the discussion going on in Slack: since the public state tree root may change frequently, we’ll most likely need the sequencer to be involved. One option is for the sequencer would generate a ZKP that the public values required by the private tx are still in the tree. But this leaks information to the sequencer, so we’d need a way for the sequencer to build this proof without knowing what it is even proving. Not just that, the sequencer should also be able to check that the values have not changed (without knowing which values it’s checking) in a cheap way, since any change renders the tx invalid.

This seems to be on the fringe of the cryptographic magic we’re working with right now :stuck_out_tongue:

You need a way for the sequencer to build this proof without knowing what it is even proving

You have an issue here. As it would be membership proof that some state is in the current state, the sequencer could brute force simulate updates to all contracts individually and see which ones would make the proof fail :grimacing:. The failing proof would leak which contract you read from.

You could make the leak less damning if you have multiple contracts share a separate “access control contract” with access control in. Essentially you will leak that it was one of these contracts but not which one. An issue is that a change in any access control for a contract in that set would invalidate pending transactions for the other contracts as well. e.g., if USDC and USDT use a blacklist and share access control registry (can still have fully separate lists etc) then USDT updating theirs would influence USDC users :sweat_smile:

Going back to this scheme, would it work with our current design of private vs public functions? Today private functions can only enqueue public functions, but they cannot depend on their results. We could design it so an enqueued public function that reverts then reverts the whole tx, but that means that fee payments done in the private part of the tx (assuming we go full AA) would also get reverted, leaving the sequencer with their hands empty…

EDIT: Conversation on this moved here.

Our current design runs the private then public, it still needs to revert en its entirety to not break horribly when doing anything that is moving funds between private and public domain

Example, if you deposit into uniswap or the like, you would do a private call for getting funds from private domain to the public domain where it will be swapped. If the sequencer can make it revert only partially, he can successfully steal your funds in many of these interaction types.

2 Likes

I posted a possible solution to this (with some tradeoffs) in this related thread.

1 Like

Actually wouldn’t also be useful to read the current state of the chain like block.number, block.timestamp etc?

1 Like