# Stealth Address AA Plugin
[Stealth Address AA Plugin](https://github.com/moonchute/stealth-address-aa-plugin/tree/develop) is a smart account plugin built on top of modular smart account provider including [ZeroDev Kernel](https://zerodev.app/) and [Biconomy](https://www.biconomy.io/).
> đź’ˇ If you're well-versed in Account Abstraction and stealth addresses, please skip to [Section III](#III-Why-Stealth-Address-AA-Plugin).
## I. What is Account Abstraction (AA) Plugin
### a. Account Abstraction
Account Abstraction ([ERC-4337](https://eips.ethereum.org/EIPS/eip-4337)) changes how you interact with your crypto wallet. To better understand this, let's use a real-world analogy: think of your crypto wallet as your house, a secure place where you store all your digital assets.
In the pre-AA era, entering this 'house' to manage your assets required a singular, specific 'key'—your private key. You'd need this one and only key to both access your assets and perform transactions.
However, Account Abstraction significantly expands your options. Now, instead of relying solely on that traditional private key, you can also gain entry through various other means of identification like fingerprint scans, voice recognition, or any other form of proof that establishes your identity.
Beyond that, you can add custom rules and logic to your smart account. For instance, you could implement a daily transaction limit for added security, or automate a small contribution to the developers behind Account Abstraction each time you access your wallet, as a way to express gratitude for their groundbreaking work.
The wallet with account abstraction is called smart wallet or smart account compared to external owned account (EOA).
### b. Modular Smart Contract Account and Plugin
As smart accounts evolve, gaining new capabilities for enhanced security and user experience, the question arises: What if we want to adapt our smart accounts to different scenarios or upgrade them just like we update software on smartphones? The traditional method of asset migration between smart accounts designed for different use cases has been cumbersome, to say the least.
Enter [ERC-6900: Modular Smart Contract Account and Plugin](https://eips.ethereum.org/EIPS/eip-6900), which addresses these challenges head-on. Extending our 'house' analogy, with ERC-6900, you no longer have to build your digital house from scratch. You can now start with a robust foundational structure and customize it with your choice of 'furniture,' such as specific gates, modules, or plugins, to fit your unique needs.
Platforms like [ZeroDev Kernel](https://github.com/zerodevapp/kernel) and [Biconomy](https://github.com/bcnmy/scw-contracts) have already implemented this modular approach and make it remarkably straightforward to create these modular plugins or modules for your smart account.
For those interested in diving deeper into the realms of Account Abstraction and its modular extension, we recommend checking out our previous article [Account Abstraction Lego](https://moonchute.xyz/blog/account-abstraction-lego). Additional comprehensive guides can be found [here](https://blog.getclave.io/p/ultimate-account-abstraction-guide), [here](https://www.alchemy.com/blog/account-abstraction) and [here](https://blog.rhinestone.wtf/part-1-modular-account-abstraction-for-everyone-else-84567422bc46).
## II. What is Stealth Address
While the transparency of blockchain technology brings numerous benefits, it also has its downsides—particularly when it comes to privacy. For instance, if you're paying your employee's salary or buying a cup of coffee, the transactions are publicly visible. This means anyone can trace the addresses involved and discern financial details like salary amounts or a café's monthly earnings.
A workaround might be to create a new address each time you receive tokens. However, this strategy makes managing assets across multiple addresses a complicated and cumbersome task.
To address this privacy concern, the concept of stealth addresses was [introduced](https://bitcointalk.org/index.php?topic=5965.0) for Bitcoin back in 2014, and has since been refined ([improved](https://bytecoin.org/old/whitepaper.pdf), [dual-key](https://github.com/shadowproject)). It's now available for Ethereum as [ERC-5564](https://eips.ethereum.org/EIPS/eip-5564).
<figure style="text-align: center;">
<img src="https://hackmd.io/_uploads/rky3M_Fya.png" alt="stealth-flow" style="margin: 0 auto;">
<figcaption style="text-align: center;">Workflow from Vitalik's Post</figcaption>
</figure>
With a stealth address, you don't send tokens directly to the receiver's public address. Instead, a stealth address is generated using an ephemeral key combined with the receiver's public address. Only the sender and receiver are privy to who actually owns this stealth address. Furthermore, only the receiver has the ability to withdraw tokens sent to the stealth address, ensuring both privacy and security.
For those eager to delve into the finer details of stealth addresses, we highly recommend reading [An incomplete guide to stealth addresses](https://vitalik.ca/general/2023/01/20/stealth.html) post by Vitalik, as well as this insightful [post](https://hackernoon.com/blockchain-privacy-enhancing-technology-series-stealth-address-i-c8a3eb4e4e43) that provides an excellent introduction to the subject.
## III. Why Stealth Address AA Plugin
In this section, we will delve into the dual motivations behind our Stealth Address Account Abstraction (AA) Plugin, looking at it through the lenses of both Account Abstraction and stealth addresses:
1. Account Abstraction: privacy-preserving plugin
2. Stealth address: transition from EOA to smart contract account
### a. Account Abstraction: privacy-preserving plugin
We believe that privacy should be an inherent property of blockchain technology, not just an optional add-on.
While Account Abstraction (AA) has been instrumental in providing flexible verification logic beyond the traditional ECDSA signature, there remains the need to confirm your identity and prove ownership of the wallet. Whether it's a private key, an enclave-based key, or even biometric data, some form of identification is essential.
At MoonChute, we've created [Unified Smart Account Managers](https://app.moonchute.xyz/) that associates an individual's EOA address with their respective smart accounts. From the feedback we've gathered, this utility has been instrumental in simplifying the management of multiple smart accounts. However, we believe that users should also have the freedom to choose how much privacy they want to maintain.
<figure style="text-align: center;">
<img src="https://hackmd.io/_uploads/SJ0F-YKya.png" alt="smart-account-manager" style="margin: 0 auto;">
<figcaption style="text-align: center;">Unified Smart Account Manager</figcaption>
</figure>
To strike a balance between the utility of Account Abstraction and the need for privacy, stealth addresses serve as an ideal solution. They fulfill our aim of enhancing user privacy without sacrificing the flexibility and user experience that smart accounts offer.
### b. Stealth address: transition from EOA to SCA
If we are going to [allow existing EOAs to upgrade to contract ](https://www.youtube.com/watch?v=iLf8qpOmxQc&t=2488s), why not make stealth address a contract account?
## IV. How Stealth Address AA Plugin works
### a. ECDSA validator plugin
Within the ZeroDev Kernel's [ECDSA Validator](https://github.com/zerodevapp/kernel/blob/main/src/validator/ECDSAValidator.sol), a user operation (userop) is validated based on whether the signature comes from the owner address specified in the validator contract. As the validator facilitates a seamless transition from to smart accounts, there's an opportunity to boost privacy. Specifically, we can break the direct link between the smart account and its owner.
```solidity
struct ECDSAValidatorStorage {
address owner;
}
function validateUserOp(UserOperation calldata _userOp, bytes32 _userOpHash, uint256)
external
payable
override
returns (ValidationData validationData)
{
address owner = ecdsaValidatorStorage[_userOp.sender].owner;
bytes32 hash = ECDSA.toEthSignedMessageHash(_userOpHash);
if (owner == ECDSA.recover(hash, _userOp.signature)) {
return ValidationData.wrap(0);
}
if (owner != ECDSA.recover(_userOpHash, _userOp.signature)) {
return SIG_VALIDATION_FAILED;
}
}
```
### b. Stealth address
For simplicity, let's focus on the basic version of stealth addresses. The objective here is for a sender to transfer tokens to a receiver in a way that masks the identity of the receiver, while also ensuring that only the receiver can access and spend the tokens.
First, the sender obtains the public key of the receiver and randomly generates an ephemeral key pair. The sender then publishes the ephemeral public key. Both parties can now compute a shared secret via the [Diffie-Hellman key exchange](https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange), which is established between the receiver's public key and the ephemeral key, followed by hashing.
The stealth address is formulated by combining the receiver's public key and the public key of the shared secret. The corresponding stealth private key can only be calculated by the receiver, as it combines the shared secret and the receiver's private key.
<figure style="text-align: center;">
<img src="https://hackmd.io/_uploads/SkV5cp9k6.png" alt="smart-account-manager" style="margin: 0 auto;">
</figure>
### c. Stealth address validator plugin
The Stealth Address Validator Plugin are employed to mask the identity of smart account owners. This results in the decoupling of the visible link between smart account owners and their respective smart accounts, thereby ensuring a higher degree of privacy.
```solidity
struct StealthValidatorStorage {
address stealthAddress;
}
function validateUserOp(UserOperation calldata _userOp, bytes32 _userOpHash, uint256)
external
payable
override
returns (ValidationData validationData)
{
address stealthAddress = stealthValidatorStorage[_userOp.sender].stealthAddress;
bytes32 hash = ECDSA.toEthSignedMessageHash(_userOpHash);
if (stealthAddress == ECDSA.recover(hash, _userOp.signature)) {
return ValidationData.wrap(0);
}
if (stealthAddress != ECDSA.recover(_userOpHash, _userOp.signature)) {
return SIG_VALIDATION_FAILED;
}
}
```
## V. Validation with Stealth address plugin
### a. Signature signed by stealth address
In the simplest scenario, we verify a signature generated by the stealth address's private key. However, the practical limitations of generating shared secrets and corresponding private keys within existing wallet UIs present a challenge. To overcome this, we propose using aggregate signatures that can be verified by the contract.
### b. Aggregate signature
#### 1. Shared secret
The shared secret, denoted by $s$, can be generated using the ephemeral public key and the user's private key, or vice versa:
$$priv_{shared}=Hash(bR)=Hash(rB)$$
In standard stealth address implementations, this usually requires manual management of private keys by users. However, ephemeral private keys could be stored when creating the smart account, simplifying shared secret retrieval within existing wallet UIs.
#### 2. Private key of stealth address
The private key of the stealth address, denoted as $priv_{stealth}$, is calculated as:
$$priv_{stealth}=priv_{shared}+b$$ Where $s$ is the shared secret and $b$ is the private key of the owner.
The challenge we face is that we're unable to alter the private key within the wallet's user interface. Nevertheless, we do have the latitude to make subtle adjustments in the way signatures are generated and verified, which could provide us with a viable path forward.
Here's original [ECDSA signing](https://cryptobook.nakov.com/digital-signatures/ecdsa-sign-verify-messages)
#### ECDSA Signing
Given a private key: $privKey$, and a message: $m$
1. Calculate the message hash: $h=hash(m)$
2. Generate a random number $k$
3. Calculate $R=k*G$ and take its x-coordinate $r=R.x$
4. Calculate $s=k^{-1}*(h+r*privKey)\mod n$
5. The signature is $(r,s)$
#### ECDSA Verifying
1. Calculate the inverse of signature s: $s_1=s^{-1}\mod n$
2. Calculate $R'=(h*s_1)*G+(r*s_1)*pubkey$, and take its x-coordinate r'=R'.x
3. The result is $r==r'$
We propose an adjustment to the signing and verification steps to facilitate stealth address usage:
#### Aggregate ECDSA Signing
Given private key of owner: $priv_{owner}$, shared secret key: $priv_{shared}$ and message: $m$
1. Perform step 1 - 4 as in ECDSA signing using $priv_{owner}$
2. Calculate the aggregate signature $s'=s(h+r*priv_{shared})\mod n$
3. The aggregate signature is $(r,s')$
#### Aggregate ECDSA Verifying
The aggregated signature
$$\begin{equation}
\begin{split}
s' &=s(h+r*priv_{shared}) \\
&=k^{-1}(h+r*priv_{owner})(h+r*priv_{shared}) \\
&=k^{-1}[h^2+hr*(priv_{owner}+priv_{shared})+r^2*priv_{owner}*priv_{shared}]
\end{split}
\end{equation}$$
We notice that:
$$pub_{stealth}=G*(priv_{owner}+priv_{shared})\\
dh_{owner\_shared}=G*{priv_{owner}*priv_{shared}}
$$
We can thus verify the aggregate signature by:
1. Calculate the inverse of aggregate signature
$$s_1=s'^{-1}\mod n$$
2. Calculate $R'$ and take its x-coordinate $r'=R'.x$
$$R'=(h^2*s_1)*G+(h*r*s_1)*pubkey_{stealth}+(r^2*s)*dh_{owner\_shared}$$
4. The result is $r==r'$
### c. Masked Message Signature
Performing secp256k1 elliptic curve operations, specifically point addition and multiplication on chain is prohibitively costly. For instance, validating a user operation with an aggregated signature consumes around 1 million gas. To mitigate this, we introduce a masked message scheme. This approach allows for the verification of signatures from a stealth address using only the owner's private key, bypassing the need for on-chain elliptic curve operations.
#### Signature Transformation
Consider a signature pair $(r,s)$ produced by the owner's private key $priv_{owner}$ signing a message $h$. This can equivalently be viewed as a signature produced by the stealth address's private key $priv_{stealth}$ signing a modified message $h'$:
\begin{split}
s &=k^{-1}*(h+r*priv_{owner}) \\
&=k^{-1}*(h-r*priv_{shared}+r*(priv_{owner}+priv_{shared})) \\
&=k^{-1}*(h'+r*(priv_{stealth}))
\end{split}
This mathematical transformation enables the recovery of the stealth address using the signature signed by the owner's private key. The key challenge lies in deriving $h'$ from $h$ on-chain.
#### Masked Message
To address this, we propose a method where, given $m'$, we can prove it is derived from $m$ without revealing $priv_{shared}$. This is achievable using a zero-knowledge proof. However, direct exposure of $m'$ risks revealing $priv_{shared}$, as it can be computed from the original message $m$ and signature $r$. To prevent this, we introduce a masking mechanism using a random number $r'$, which is a one-way function of message $m$ and private key $priv_{shared}$.
$$m'=m-Hash(m,priv_{shared})$$
#### Masked ECDSA Signing
Given the owner's private key $priv_{owner}$, shared secret key $priv_{shared}$ and a message $m$, the following steps are taken:
1. Generate a random number $r'=Hash(m,priv_{shared})$ and calculate $m'=m-r'$
2. Sign $m'$ using the owner's private key $priv_{owner}$ producing the signature $(r,s)$ when $s=k^{-1}*(m'+r*priv_{owner})$
3. Formulate message $m''$
\begin{split}
m'' &=m'-r*priv_{shared} \\
&=m-Hash(m,priv_{shared})-r*priv_{shared}
\end{split}
4. Generate a zero-knowledge proof $p$ to validate that $m''$ is derived from $m$ without disclosing $priv_{shared}$
#### Masked ECDSA Verifying
1. Confirm that $p$ is valid zero-knowledge proof
2. Recover the stealth address from the signature $(r,s)$
Utilizing the masked scheme decreases the gas consumption to just **one-sixth** compared to the aggregated signature.
## VI. Workflow
In this section, we outline the complete workflow for creating a smart account with the Stealth Address Account Abstraction (AA) Plugin.
### a. Create stealth smart account
1. **Generate Ephemeral Key Pair**: Start by generating a random ephemeral key $r$
2. **Compute Shared Secret**: Use the random ephemeral private key $r$ and the owner's public key $B$ to compute the Diffie-Hellman shared secret, $s$, through hashing:
$$s=Hash(rB)$$
3. **Compute Stealth Public Key and Address**: Calculate the stealth public key by adding the public keys of the owner and the shared secret:
$$pub_{stealth}=pub_{owner}+pub_{shared}$$
Then calculate the stealth address:
$$addr_{stealth}=bytes20(keccak256(pub_{stealth})$$
4. **Compute Diffie-Hellman Key**: Now calculate the Diffie-Hellman key between the owner and the shared secret:
$$dh_{owner\_shared}=pub_{owner}*s$$
5. **Call CreateAccount**: Finally, call the createAccount function in the Smart Account Factory Contract.
<figure style="text-align: center;">
<img src="https://hackmd.io/_uploads/Sypwwqfga.png" alt="smart-account-manager" style="margin: 0 auto;">
</figure>
Within the framework of our validator, we'll securely store the stealth address, stealth public key, and the Diffie-Hellman key. Importantly, to bolster user privacy, **the owner's address will not be stored** in the validator. This design ensures that there is no explicit connection between the smart account and its respective owner.
```
struct StealthAddressValidatorStorage {
uint256 stealthPubkey;
uint256 dhkey;
address stealthAddress;
uint8 stealthPubkeyPrefix;
uint8 dhkeyPrefix;
}
```
### b. Verify signature
The signature in the userOp is constructed with the signature or aggregated signature generated by stealth smart account owner with the 1 Byte prefix **mode**.
**Mode 0: ECDSA Verification**
If the mode is set to 0, the signature will be verified using standard ECDSA verification logic that matches the stealth address's private key. In other words, the signature is considered valid if it can be mathematically confirmed to have been generated by the stealth address's private key.
**Mode 1: Aggregated Signature Verification**
If the mode is different from 0, the signature will undergo aggregated signature verification as detailed in the previously discussed procedure for [Aggregate ECDSA Verifying](#b-Aggregate-signature).
**Mode 2: Masked Signature Verification**
When the mode is set to 2, the signature is verified using the masked signature verification method as previously described in [Masked Signature Verifying](#Masked-ECDSA-Verifying).
<figure style="text-align: center;">
<img src="https://hackmd.io/_uploads/HyqIuvXKp.png" alt="smart-account-manager" style="margin: 0 auto;">
<figcaption style="text-align: center;">Signature of stealth smart account </figcaption>
</figure>
## VII. Benchmark
| | Stealth address signature | Aggregated signature | Masked signature |
| ----|---------- |---------- |------------ |
| Gas | 3,921 | 1,380,946 | 262,126 |
| Support all wallet | x | o | o |
## VIII. What's next
**ERC-5564 Compatibility and Stealth Address Scanning**
While our stealth smart accounts are self-created, eliminating the need for separate viewing and spending keys used in standard stealth address implementations, our aim is to ensure compatibility with [ERC-5564](https://eips.ethereum.org/EIPS/eip-5564). This will enable senders to transfer tokens to recipients while maintaining the recipient's anonymity.
One of the issues widely [discussed](https://ethresear.ch/t/open-problem-improving-stealth-addresses/7438) with stealth addresses is stealth address scanning overhead. It's worth exploring an efficient solution to minimize the cost in the realm of smart account.
**Enhancing privacy through Account Abstraction**
Since the stealth smart accounts are essentially smart contract wallets, they offer a greater degree of flexibility. For example, we could use a paymaster to sponsor userOps in such a way that the link between the stealth smart account and its owner remains concealed.