Micro-Rollups for Keystores

If you’ve been deep in the trenches of Account Abstraction (AA) and Rollup scaling, you’ve probably come across the term “Keystore Rollup”. Since this concept sits at the intersection of two of "The Three Transitions", it represent a significant step towards a more global-scale and user-friendly Ethereum ecosystem.

The Three Transitions
The Three Transitions

The aim of this blogpost is to provide a high-level yet detailed deep dive into the concept of a Keystore Rollup, what problems does it solve, an example implementation of a Keystore Micro-Rollup while covering both the advantages and disadvantages of such an approach.

Why is a Keystore Rollup needed?

As part of the two of "The Three Transitions", the Ethereum ecosystem is encouraging users to migrate to rollups (L2s) for cheaper transactions and promoting the use of Smart Contract Wallets (SCW) for better security. As a result, a user is now required to deploy a smart contract account on every rollup they wish to transact on. Unlike Externally Owned Accounts (EOAs), which are stateless and function similarly on L1 and all L2s, a smart contract account holds state enabling features like key rotation, recovery, multi-sigs and more.

This is where lies the root of the problem: each rollup’s state is isolated from other rollups. If a user wishes to update her account state, such as adding or removing a signer from a multi-sig, this change needs to be synchronized across all her SCWs on all networks. This complexity is why many wallet providers are heavily tied to one network, leaving the user with no choice but to use separate wallets. Although some providers are developing modules to synchronize these changes to all networks for the user, but even that has significant difficulties in practice:

  • Complexity: Building and submitting transactions, handling transaction failure, settlement or re-orgs across all networks is intricate.

  • Cost: If rollups provide 10-100x cost savings compared to Ethereum, but a permission change needs propagation to 10-100 networks, we haven’t really saved anything on fees.

  • Security: Urgent permission revoking changes, such as incase of a key becoming leaked, need to applied immediately but different networks have different block times, time to finality, censorship guarantees therefore leaving users vulnerable if changes are not applied promptly or left unapplied altogether.

Essentially, in an increasingly modular world where a user’s assets are spread across many rollups, using smart wallets becomes more challenging, and their advantages over EOAs, such as better UX and security, start to fade. Wat to do?

Keystore Rollup: A solution for cross-chain smart accounts

The fundamental idea behind a Keystore rollup is to concentrate account state in one rollup, the Keystore rollup, which can serve as the source of truth for all other rollups. All accounts, regardless of what other networks they need to transact on, refer to the Keystore Rollup to authenticate users. Instead of storing mutable permissions in the account smart contract on each rollup, an immutable link to a “virtual account” id is stored which points to the actual permission data on the Keystore Rollup. Hence, by providing a global repository for account permissions, Keystore Rollups can streamline account management for users.

Drawing an analogy to OAuth, a standard widely used in Web2 systems, the Keystore rollup functions as the Identity Provider (IdP), while all other rollups serve as Service Providers (SPs).

Gollum: “One rollup to auth them all”
Gollum: “One rollup to auth them all”

We can compare how such a construction addresses the shortcomings of current systems:

  • Complexity: is reduced by removing the need to manage state across multiple networks and propagate duplicate transactions to make permission changes.

  • Cost: is reduced by removing these duplicated transaction fees.

  • Security: is improved by guaranteeing immediate application of removed permissions which automatically (implicitly) gets applied to all networks.

While this sounds promising, the subsequent sections delve into the existing approaches, implementation, and user stories of the Keystore Rollup revealing various drawbacks and considerations, which are discussed in depth at the end of this article.

Basic architecture of a Keystore Rollup

E2E mechanics of a Keystore Rollup
E2E mechanics of a Keystore Rollup

The basic architecture of a Keystore Rollup comprises several components:

  1. Rollup node

  2. Keystore Rollup Contract on L1

  3. Keystore Contract on each L2

  4. Keystore Account Factory on each L2

  5. Validator Library and State Verification Library

Let’s briefly go over each of these one-by-one:

  1. Rollup node

    The Keystore Rollup node maintains the state, has functionality for registering and updating accounts and generating proof for an account. It periodically rolls down its state root to the L1.

  2. Keystore Rollup Contract on L1

    The Keystore Rollup Contract on L1 stores the state root and sends messages to L2 contracts to update their state root variable.

  3. Keystore Contract on each L2

    The Keystore Contract on each L2 also stores the state root for direct read-access by Smart Contract Wallets (SCWs) on that L2.

  4. Keystore Account Factory on each L2

    The Keystore Account Factory on each L2 is responsible for creating SCWs deterministically. These SCWs point to the unique key of the Keystore Rollup, allowing users to authenticate across all networks with the same key.

  5. Validator Library and the State Verification Library

    These are included with the SCW on each L2. Validator Library verifies the validity of transaction signature against the SCW config, while the State Verification Library verifies the user-provided proof against the state root of the Keystore Rollup.

Existing approaches

While the basic architecture discussed above remains the same across different approaches, deciding on a proof scheme is a crucial detail that impacts the complexity of implementation and costs for users in how data (and its proofs) are communicated cross-chain.

The following is a comparative analysis of a figure taken from Vitalik’s blog on Deeper dive on cross-L2 reading for wallets and other use cases which mentions different approaches for proof schemes:

Vitalik’s ranking for different proof schemes
Vitalik’s ranking for different proof schemes
Comparison between different approaches
Comparison between different approaches

While the merkle tree model is not the most cost effective for the user, they are widely used everywhere in Web3 with a large amount of available tooling. For the sake of simplicity, we will implement a merkle tree model since that is the default proofing mechanism employed by Stackr Micro-Rollups as well.

Quick Primer on Micro-Rollups

Micro-Rollup e2e Workflow
Micro-Rollup e2e Workflow

In oversimplification, micro-rollups are essentially state machines at the core.

  • The state machine has a defined shape of the state & the genesis state.

  • The state machine has actions(read: transaction types) which when invoked trigger a state transition function on the machine.

  • The State Transition Function(STF) in effect performs computation and mutates the state of the machine.

After the STF execution, the actions are rolled together in a block & shipped to Vulcan(Stackr’s Verification Middleware).

Finally, Vulcan —

  1. Pessimistically re-executes the actions in the block to check for validity of the STF.

  2. Settles on L1 & DA.

    1. Micro-Rollup’s updated state is sent to the DA.

    2. The metadata of the verified block & the updated state root is settled to the micro-rollup’s inbox contract on L1.

The above pipeline collectively forms Stackr’s Micro-Rollup Framework.

Keystore 🤝 Micro-Rollup

So, why are micro-rollups uniquely suited to build a keystore?

  1. Micro-rollups enable specialized minimal VMs

    In the original proposal for a dedicated minimal rollup for keystores, Vitalik advocates for a simple implementation without a full EVM for reduced security risk. Micro-Rollups lets you selectively expose only necessary functionality through deterministic and rigid State Transition Functions (STFs) tailored to the state of the rollup, significantly reducing the attack surface area.

  2. Micro-rollups are flexible sovereign systems

    Since micro-rollups break free from the shackles of the EVM, it’s trivial to add support for different signature schemes for sending transactions to the micro-rollup. In context of a keystore rollup, this could translate to supporting transactions signed using passkeys, multi-sigs, etc.

Let’s build a Keystore Micro-Rollup

Disclaimer: This demonstration showcases the framework's capabilities and represents a preliminary proof of concept.

When developing a micro-rollup, it's crucial to conceptualize your logic in terms of a state machine. This involves carefully considering the state of the micro-rollup - that is, the data it will hold - and the actions that will dictate the behavior of the state transition function, which in turn operates on this state.

The State machine development paradigm
The State machine development paradigm

With the above in mind, we start by designing the state of the micro-rollup using Stackr’s SDK.

The Design

  1. Accounts are stored off-chain inside a state machine as a key-value map

  2. User sends actions which trigger a state transition function inside the state machine

  3. User can send action to register a new account or update an account

  4. After each set epoch a block is generated which contains details of accounts state

  5. The block is sent to Vulcan network for verification

  6. If the block conforms the rules of the state machine it is approved

  7. The block data is split between L1 and DA for settlement.

Architecture of a Keystore Micro-Rollup using Stackr's SDK
Architecture of a Keystore Micro-Rollup using Stackr's SDK

Quick Demo before diving into the implementation details

Implementation using the Stackr SDK

The Stackr SDK, written in TypeScript, lets you inject the application logic directly into the blockchain. This environment delivers a developer experience akin to traditional server-side or backend application development. Let’s see how:

1. As a starting point, let’s define accounts in our state.

accounts state
accounts state

Bear with us, these fields are explained in the next section.

After we setup our minimum viable state, we need to define state transition functions that update the state.

2. Let’s define two functions, register which is responsible for registering a new account with the keystore, and update which is responsible for updating an existing account entry.

state transition function for registering an account
state transition function for registering an account
state transition function for updating an account
state transition function for updating an account

Breaking down the register function,

  • The user submits an action to register a new account, providing two fields (ideally generated by the user’s wallet):

    • codeHash: hash of the smart contract’s bytecode of the validation library

    • configHash: hash of the SCW config (could be a public key or in the case of a multisig a concatenation of multiple public keys)

  • Together this makes up the data i.e.,data = hash([codeHash, configHash])

  • vk: verification key that specifies instructions for changing the data and vk. In this case, it’s the address who initiates the action but it could be different depending on the type of approach.

  • key = hash([data,vk]): this key serves as the unique “virtual account” id and returned to the user.

  • Finally, the new account entry is added to the state against this key, accounts[key] = (data, vk).

Breaking down the update function,

  • The user submits an action to update an existing account, referencing the related key among other fields they wish to update. While it may not be immediately obvious, update functionality is crucial to enable security-critical features like recovery or changing signers.

  • The address initiating the action must match the current vk.

  • Finally, account entry at accounts[key] is updated with the new values.

3. Ultimately, we define how the state root is computed for L1 settlement.

As mentioned at the beginning, micro-rollup’s state root is settled on L1. It is interesting to note that the developer can chose what part of the state settles on L1 vs what part can go on DA as metadata, thereby, unlocking hybrid security assumptions.

In this case, we extract the accounts and settle it’s merklized root on L1, thereby opening up the possibility of direct inclusion proofs of accounts in the merkle tree.

We’ve reached the point where the minimum viable system works. Surprisingly easy to give on-chain superpowers to a backend server, no?

Note: The contracts side of code is intentionally omitted as out of scope for this article. It is well discussed in Conner’s Solidity Account circuits blog post. Although that is an approach for ZK-SNARK based proof scheme, the Validator Library and State Verification Library remains similar for this type of system too.

What does using this Keystore Micro-Rollup look like?

There are two primary user stories here:

  1. User registering a keystore account

  2. User sending a transaction on an L2 referencing their keystore account

Transaction flow and user stories of interacting with the Keystore Micro-Rollup
Transaction flow and user stories of interacting with the Keystore Micro-Rollup

Breaking these down,

  1. User can register an account with the Keystore Micro-Rollup. As described in the previous section, this would involve their wallet provider generating the codeHash and configHash and passing it onto the keystore. The wallet provider also deploys a new SCW KeyStoreAccount (bundled with the Validator Library and State Verification Library) using the KeystoreAccountFactory on each L2 the user wishes to transact on. This SCW simply stores a pointer to the immutable account key returned by the Keystore micro-rollup, which can be later used for lookups in the keystore during verification.

  2. When submitting a transaction on any L2, the user would first need to obtain a proof (in this case, a merkle proof) for their account from the Keystore Micro-Rollup. The wallet provider would essentially need to construct a User Operation, baking in the merkleProof, SCW config, and codeHash into its signature, like so: userOp.signature = abi.encode(codeHash, userOpSig, config, merkleProof). This will later be decoded inside the SCW at which point it will leverage the Validator Library to verify the validity of transaction signature against the SCW config, and the State Verification Library to verify the user-provided merkle proof against the state root stored in the Keystore Contract on that L2. If both checks pass, the user’s transaction is authenticated.

An interesting thing to note here is that since each (data, vk) pair corresponds to a unique key in the Keystore, the SCW need not be deployed immediately on all L2s but the user can still obtain a counterfactual address. This is a deterministic address for a smart contract that hasn't been deployed yet, but can still be used to receive funds on any chain at the same address

Questions & considerations

  • Can I migrate my existing SCW to a keystore rollup?

    • A: There is no spec that enables an existing SCW to “register” with the keystore. In all the approaches, user will deploy new SCW after registering a key with the keystore first. This is an essential feature to have though and requires further discussions within the community.
  • What does it mean that the L2 “requires” a proof?

    • A: Since the user’s account state is no longer on the L2s they may be transacting with but externalized on the keystore rollup, the L2 requires the relevant account state to be submitted alongside that account key’s inclusion proof for verifying the integrity of a transaction.
  • Aren’t the transactions going to be more expensive as there is more metadata?

    • A: Yes. With a keystore rollup, each user-op is packed with extra metadata (config, codeHash, proof) while also requiring extra on-chain compute for the verification of the proof. This incurs additional gas fees in both calldata and computation. As Merkle Tree of accounts grows, the size of the proof becomes larger and will cost more gas fees.
  • What about latency between the proof creation and proof verification?

    • A: Yes, latency issues persists for all approaches and in both directions. It’s possible that the proof obtained by the user is no longer valid by the time it is verified onchain as the state root stored in the keystore contract got updated in the meantime. On the other hand, the user might have obtained a proof for a state which is not yet synced with the L1 and all L2s.
  • What about the cost of syncing the state root across all L2s?

    • A: Yes, in this approach one transaction is required for message transfer to each L2 which could incur significant cost. This is because we rely on the messaging protocol of each L2 since there is no standardized way to read L1 state on L2s.
  • What if the keystore rollup censors me?

    • A: We see two paths here. Firstly, the rollup should allow force-inclusion of transactions directly from the L1 so the user can always update their account. Secondly, as long as the rollup guarantees publishing all its data to a Data Availability (DA) layer the user should be able to construct a proof without relying on the rollup operator.
  • What if the keystore rollup re-orgs?

    • A: Ideally the keystore rollup would keep an appropriate buffer of block confirmations to hang back before solidfying a state root as official and generating proofs against it. This feeds back into the latency issues and depletes UX.
  • What kinds of wallet configurations and signing methods can it support?

    • A: The general idea is to support all and any kind of signing methods as long as it can be implemented in the EVM. That is why the keystore rollup doesn’t assume anything and accepts arbitrary variables, namely, codeHash and configHash, which lets the developer write unique and custom signature verifications methods in the Validator Library. The possibilites are endless: multisigs, passkeys, rotating session keys, signing methods with aggregation like BLS or EdDSA, ZK Email, ZK-SNARKs, Quantum safe signing method like Winternitz Signatures, and more.

Conclusion

Keystore Rollups offer a promising solution for managing smart contract wallets across networks by centralizing account states on a dedicated rollup. At first glance, they seem to simplify permission management, reduce complexity, cut costs, and enhance security. However, as we delve deeper, we see that they are not without their own challenges, particularly related to costs and latency issues.

Being a complimentary feature to a more Chain Abstracted future of seamless cross-chain interaction, ideas like Keystore Rollup play a crucial role in paving a path to a more scalable, secure, and user-friendly Ethereum ecosystem. As developers and researchers, we must strive to explore and experiment with such ideas.

References


Interested in building your own Keystore?

We will soon be sharing a more “complete” version of the above mentioned experiment. Until then, if you’re someone who’s looking to build a Keystore Rollup for your protocol, we’d love to talk to you and find ways to collaborate.

Meanwhile - beta access to our SDK is still open, head over to the application form and sign-up. We are letting developers in on a rolling basis.

Contact us on Discord (preferable), Twitter or shoot an email at gm[at]stackrlabs.xyz

Subscribe to Stackr Labs
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.