[**Jonathan Passerat-Palmbach**: Efficiently Signing Encrypted Transactions with FHE](https://www.youtube.com/live/MeC2ttmDP1I?si=19y5Da5CFE1RPwvV)

---
**Summary:**
This talk introduces a practical method for signing encrypted transactions in FHE, enabling private backrunning. Standard cryptographic primitives like ECDSA and Keccak are prohibitively expensive in FHE. Instead we introduce a hybrid approach using Chameleon Hashes to pre-sign a placeholder hash offline, then compute a controlled collision online using FHE once the actual transaction is known.
The system leverages ERC-4337 account abstraction to support custom signature verification and builders running in TEEs. This architecture enables low-latency, privacy-preserving execution with modest onchain costs. Early results suggest the design is viable, composable, and performant within current infrastructure limits.
---
**Transcript:**
Today we’ll be talking about how to efficiently sign encrypted transactions in FHE.
So let me give you a bit of context first as to why we would even want to do that? You might be aware that we are working on something called MEV, Maximal Extractable Value. It's known to be a problem on public blockchains. I'm not going to give you a full rundown of MEV, we don't have time for that. And I've got many talented colleagues who are here speaking today, which I encourage you to attend their talk or look up online afterwards to learn everything you have to learn about MEV.
The only thing we'll have to agree about is that MEV is a problem we can tackle. And if we want to tackle it properly and not fall into the MEV-trilemma we have got to inject privacy-preserving methods to make it better.
Among all privacy-preserving methods I will focus particularly on FHE. So first one slide shortcut about what FHE is for those who don't know yet; FHE is this idea that instead of encrypting something and not being able to do anything with the ciphertext, you retain the possibility to compute over what you've encrypted.
This means that if I encrypt something and give it to you, and I don't trust you, you can still do computation on it and give me back the result, for example. This is great; it sounds like magic. There are a few things to keep in mind - there's a huge overhead, it's not free, and you add noise, which you have to manage.
Most of the time you want to be careful with what you do with it and especially, how you're doing it. So it's not as trivial as taking an algorithm and throwing it into the FHE circuit.
The idea is that we want to use this tool as a first layer to maybe build a mini-encrypted mempool in a way. So most of the issues we've seen with MEV stem from the fact that all transactions are visible in the traditional old-school mempool. You could encrypt them, but what if you want to retain the ability to compute over them?
Okay, so we did that actually, in a paper from this year's FC-conference.To summarize it, we want to enable the ability for searchers to backrun a user transaction. Back running means that searchers are able to react based on what they see in a user transaction, and create their own transaction to be executed right after the user transaction.
That's because in a blockchain, you have perfect certainty on the state between these two transaction, because there's nothing changes between the two transactions. So if you're trying to do something based on external events, like the price on a centralized exchange, you know that at least whatever happens onchain, it's not going to change between the user's transaction and yours. Hence you want to backrun.
The settings we had, were relatively simple. We assumed a user encrypts a transaction and sends it to a searcher via an encrypted mempool, direct RPC, or other methods. And the searcher can run an FHE circuit that basically encodes a searching strategy that computes, over the user's transaction, and tries to see how it's going to impact the price on the Uniswap pool, for example, and generate its own back-running transaction based on that to capture an arbitrage opportunity.
So the searcher now has an encrypted resulting transaction as a result of the FHE program, and it sends it to a builder, which in our setting is trusted. The builder can then decrypt and package all that input in a block onchain. That's great. Or is it? What's missing here?
Well, we've not talked about signing, right?
So the FHE searcher is generating a transaction, which is basically like a raw transaction in a way, but it's not signed. So it's not going to land onchain. So the obvious thing you tell me would be: Why is it hard to sign in FHE?
Well, if you know your Ethereum, you know that we're using a combination of Keccac-hashes and ECDSA to send transactions. And these two things happen to be pretty cumbersome to handle in FHE. ECDSA is an elliptic curve-based signature scheme. It tends to be expensive to run in FHE. There's an interesting article on the zama-blog that estimates that it takes like 1 to 2 days on a 60-core machine to crunch an ECDSA. To be fair, that's the extreme setting. Like they try to do everything in the encrypted realm. They don't pre-compute anything. They do all the heavy modular arithmetic over fields within the FHE circuit. That's the very high end of like what you can expect. But before you can even sign that, you need to hash your transaction, right?
So you need to run Kaccak within the FHE-circuit, and that's tricky as well. There is also a paper at this years FC, that showed that even if they try to really push it to the limit, they can basically bring down the runtime of a Kaccak from 2h30 to 1h30 on a single core machine.
So you can make that faster, but it's still not going to be super great. Well, you could maybe just, try to sign the resulting ciphertext, right? The searcher generates this thing, it's just a blob of data, it doesn't know what's in there, it just signs it and sends it to the builder.
Maybe, but remember that if we want to eventually have a valid transaction, once the builder decrypts it, that's still not signed. So it needs a new instruction between the searcher and the builder to finally get the searcher's transaction. But that adds latency, which we don't like in MEV. There are also many more things that could break, which makes composability hard, and you’d require further cryptographic guarantees to do that properly.
I hope that now we are aligned on what the issue is. But before we dive into the approach, we still need to go through 2 methods that are going to be relevant to the rest of the presentation.
The first is Account Abstraction. ERC-4337, Account abstration. The idea is to introduce and change the way user accounts are handled from EOAs to smart contract-based accounts. And you can do a few cool things, like delegate payment, but we don't care about that in this setting. The interesting thing you can do with that thing is that you can change the way that transactions are validated.
So you see, like there's like a ValidateUserOp at step 7 in the diagram. And that is custom. So you can do whatever you want to validate the transaction, you’re not bound to Keccac and ECDSA anymore. That's interesting. And all you have to do is package your thing into this data structure called a UserOperation. And then a new actor, a new role, that we call the bundler, is going to make sure to send that to your smart contract wallet somehow.
Okay. So let's see what's in there. So this user operation thing, has two stages that are relevant so that you customize to execute the transaction.
One, is the validation, which is the one we want to focus here in this talk. So the idea here is that's why you implement the logic of how do you make sure that this blob of data in the structure that we call the UserOperation is valid and that the smart contract wallet can use it to generate a transaction on your behalf.
That's what you want to do with the validation. And then the execution is basically executing the logic of the dapp, based on the validated structure. But if we want to zoom in on this ValidateUserOp-function, that's where we can have our custom logic. So here that's where we can start encoding non-ECDSA signatures, which is great if we want to get rid of these signatures in FHC. So that's like one building block.
The bundler, this new role, doesn't have to be a separate actor to be fair, just think of it as a new role that's offchain. It could be like a separate mempool, aggregating user operations. The one thing we want to know about this actor or this role is that it needs plaintext access to these UserOperations because it needs to then post them onchain.
So that's that's really a critical part of the solution. It could be anyone, because these actors can be incentivized by getting a tip, based on what's in the UserOperation. But maybe that's a natural fit for a block builder to double up as a bundler. And we'll see why that could be interesting later.
The one thing I want you to remember from that is that they do need plaintext access to UserOperations. So keep that in mind as another stepping stone in our solution.
New concepts. We've seen that account abstraction allows us to get flexibility on what we use to sign, and hash. So what do we want to hash with?
Well, there is this concept called Chameleon Hashes. If there's one thing that you might know about hashes is that's one property we want is collision resistance. So if I inject two different things into the hash, it should be super hard to get the same digest from the hash function.
Well here we actually want collisions. We want controlled collisions. We want a way for two different messages to on demand generate the same hash. And that doesn't come for free, of course. We want to do that by introducing what we call a trapdoor.
So think about this as like a private key. If I know a private key, I can run a different hash function that allows me to find the exact same result as I had before in a different message. And hence I get a collision.
Why is it useful in our case? Well, it means, if I can do that, I know the hash beforehand, so even before I know the actual message that I want to sign another hash so I can sign this hash even before knowing the message, and I can sign it with whatever signature scheme I want, ECDSA, or whatever.
So we've got this secret key, just like a randomly chosen field element. And that looks like a group generator, so like a discrete log assumption. We just generate a public key on that. This public key can be shared and included in a smart contract, for example. And the only thing we do is that's the operation at the bottom that we're using to calculate the hash.
So as you see, it relies on the message, our public key and a piece of randomness that we generated on the fly. And using some simple arithmetic in the exponent we can just get a new value here that is the hash. Interestingly, we've not seen the magic in it yet.
What's relevant for that is that we want to be able to generate these collisions. How do we do that? Well, if we observe that, you know, we've got our first equation that we had before. And now imagine the same equation. We want the same hash. So this h value, we want it for different m, m’, and a different random value, r’.
If we observe that they're both using the same generator, we can just equate these things. And that's the equation we need to solve. So at the end of the day we basically when we want to find a collision, we just want to run this equation. So that's basically the state that we want to compute in FHE to generate a hash, a collision in that case.
So that's, pretty cool because like using this trapdoor-based approach, we've basically reduced the complexity of hashing from multiple rounds of Keccak, multiple rounds of bitwise operations of a large state to a handful of friendly operations. So that should be faster.
Method
Let's put this all together in the methods. Okay so remember, our goal is to sign an encrypted transaction without relying on very expensive FHE-signing operations.
And now we've got the building blocks to split the signing process into an offline and an online phase. So offline, the signer can prepare the signature. Now, we know how to know the hash of a future message in a way. So we can do this notion of delayed binding of a message to a hash. We don't even know the message yet, it is going to come from the FHE, from the user, actually.
But we can still precompute the hash and we can still pre-sign this hash even before starting the FHE-bit. And the FHe-bit just ends up calculating this collision that we've seen before.
So just to make it clear what we do concretely is that offline, the searcher in clear text, so not in FHE, picks a random number, computes, the initial hash over a dummy piece of data. Signs it with whatever signing algorithm it wants and keeps it somewhere privately. Then online when it receives the input from the user encrypted. It does all the MEV magic that he wants to extract, and when it needs to sign, then it just has to inject the precomputed values and calculate the collision as we've seen before.
So that's what we need to sign a transaction in the FHE. Now when we want to verify that onchain, assume our bundle has landed into the different mempool using the bundler. Well, the ValidateUserOp-function only has to recompute the hash. And we know it's trivial using the public key that we stored maybe on the contract.
And you verify the signature on this hash. So that's the two operations. We can even think about it as having different hashes. We can reuse, the Keccak of the envelope for the UserOperation if we want to skip more signature verification and save some gas. But that's just details. But basically that's the two steps you need to do.
So to summarize. Offline the searcher picks a random number, pre-calculates a hash, signs it, and then when it comes online, just looks for the collision using the simple equation we've seen. Online, the user wallet checks the hash using the public key and then verifies the signature on this hash.
And the way our builder comes into play. And I hinted before at the fact that the builder was a good fit to be a bundler. Is that, remember, we still need to have plaintext access to the UserOperation in this structure. So once the searcher circuit has generated the hash, using the Chameleon algorithm, it sends this bundle to our builder. And we happy with the builder being trusted because in runs in a TEEs. Most of the builders these days, happened to run in TEEs, so we can kind of trust them and give them secrets.
That's an OK assumption. So the builder can decrypt whatever it received and pack that into UserOperation that it sends to the user’s smart contract wallet. So basically, we've closed the loop, and solved all requirements.
How does that look concretely? Well, this is like a very early prototype, but I just wanted to give you an idea of how I could look like. It's using insecure values so don't assume that these are going to be the final numbers for these kind of approaches. But basically the overhead, in terms of the smart contract should be around 14-16 K gas.It's basically dominated by doing an ECDSA recover to verify the signature and modular exponentiation, to calculate the Chameleon hash. So it's not too bad.
And on the FHE side, for a relatively unsafe and low modulus, we can get to 1 million bootstrap operations, which is what is really like the juice of FHE in what is where the overhead comes from. And it runs in a couple of seconds on a laptop.
So going to, higher secure moduli, we'll have to go for larger servers, of course, but there's a good chance that it still remain manageable, which is great. One thing that's not too great right now is we currently mixing different security assumptions. FHE ispost-quantum, we've injected discrete log assumptions in there. So it might be great to have everything post-quantum, down the line.
But that's also good use as we've seen for, the synergy between TEEs and FHE. One thing that I really like to remind every time is that all of these privacy-enhancing methods should not be thought of as a silver bullet. Most of the time you want a combination of them to operate together. And that's like a very good example here. If that's inspiring, we happen to have many more of these problems online.
We've just shared our [research problems database](https://flashbots.notion.site/21f6b4a0d87680a2b08dca1eda93ff6f?v=21f6b4a0d87681ddb959000c44242e52), and you're more than welcome to check it and come to chat with us about it!
Another info from our sponsor is that [we are hiring](https://www.flashbots.net/jobs), and we are looking for different roles both in product and research. And we happen to have, like our amazing recruiters at EthCC this week. So you can come and speak to us. You can come to speak to our recruiters, or you can just look online if you want to.