From bd143e000e0dca59c5fb9d703e7428824bd7e126 Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Sun, 19 Oct 2025 20:23:38 -0500 Subject: [PATCH 1/8] WIP --- .../tutorials/stealth-addr/index.md | 179 ++++++++++++++++++ src/data/internalTutorials.json | 3 +- 2 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 public/content/developers/tutorials/stealth-addr/index.md diff --git a/public/content/developers/tutorials/stealth-addr/index.md b/public/content/developers/tutorials/stealth-addr/index.md new file mode 100644 index 00000000000..709f48d6946 --- /dev/null +++ b/public/content/developers/tutorials/stealth-addr/index.md @@ -0,0 +1,179 @@ +--- +title: "Using Stealth Addresses" +description: "Stealth addresses allow users to transfer assets anonymously. After reading this article, you will be able to: Explain what stealth addresses are and how they work, understand how to use stealth addresses in a way that preserves anonymity, and write a web-based application that uses stealth addresses." +author: Ori Pomerantz +tags: ["Stealth address", "privacy", "cryptography"] +skill: intermediate +published: 2025-11-30 +lang: en +sidebarDepth: 3 +--- + +You're Bill. For reasons we won't go into, you want to donate money to the "Alice for Queen of the World" campaign, and you want Alice to know you donated so if she wins she'll reward you. Unfortunately, her victory is not guaranteed. There is a competing campaign, "Carol for Empress of the Solar System". If Carol wins, and she finds out you donated to Alice, you'll be in trouble. So you can't just transfer 200 ETH from your account to Alice's. + +[ERC-5564](https://eips.ethereum.org/EIPS/eip-5564) has the solution. This ERC explains how to use [stealth addresses](https://nerolation.github.io/stealth-utils) for anonymous transfer. + +**Warning**: The cryptography behind stealth addresses is, as far as we know, sound. However, there are possible side channel attacks. [Below](#go-wrong) you will see what you can do to reduce this risk. + +## How stealth addresses work {#how} + +This article will attempt to explain stealth addresses in two ways. The first is [how to use them](#how-use). This part is sufficient to understand the rest of the article. Then, there is [an explanation of the mathematics behind it](#how-math). If you are interested in cryptography, read this part as well. + +### The simple version (how to use stealth addresses) {#how-use} + +Alice creates two private keys, and then publishes the two corresponding public keys (which might appear like a double-length public key). Bill also creates a private key and publishes the corresponding public key. + +Using one party's public key and the other's private key, you can figure out a shared secret, which only Alice and Bill know (it can't be derived from the public keys alone). Using this shared secret, Bill gets the stealth address, and is able to send assets to it. + +Alice also gets the address from the shared secret, but because she knows the private keys to the public keys she published, she can also get the private key that lets her withdraw from that address. + +### The mathematics (why stealth addresses work like this) {#how-math} + +Standard stealth addresses use [elliptic-curve cryptography (ECC)](https://blog.cloudflare.com/a-relatively-easy-to-understand-primer-on-elliptic-curve-cryptography/#elliptic-curves-building-blocks-of-a-better-trapdoor) to get better performance with less key bits, while still keeping the same level of security. But we can ignore that and pretend we are using regular arithmetic (modulu a prime number). The reason we use ECC is because it acts so similarly to regular arithmetic. + +There is a number everybody knows, *G*. You can multiply by *G*. But because of the nature of ECC (or modular arithmetic), it is practically impossible to divide by *G* (or any other number). + +The way public key cryptography generally works in Ethereum is that you can use a private key, *Ppriv*, to sign transactions that are then verified by a public key, *Ppub = GPpriv*. + +Alice creates two private keys, *Kpriv* and *Vpriv*. *Kpriv* will be used to spend money out of the steal address, and *Vpriv* to view the addresses that belong to Alice. Alice then publishes the public keys: *Kpub = GKpriv* and *Vpub = GVpriv* + +Bill creates a third private key, *Rpriv*, and publishes *Rpub = GRpriv* to a central registry (Bill could also have sent it to Alice, but we assume Carol is listening). + +Bill calculates *RprivVpub = GRprivVpriv*, which he expects Alice to also know (explained below). This value is called *S*, the shared secret. This gives Bill a public key, *Ppub = Kpub+G\*hash(S)*. From this public key he can calculate an address and send it whatever resources he wants. In the future, if Alice wins, Bill can tell her *Rpriv* to prove he was the one who sent that money. + +Alice calculates *RpubVpriv = GRprivVpriv*. This gives her the same shared secret, *S*. Because she knows the private key, *Kpriv*, she can calculate *Ppriv = Kpriv+hash(S)*. This key lets her access assets in the address that results from *Ppub = GPpriv = GKpriv+G\*hash(S) = Kpub+G\*hash(S)*. + +We have a separate viewing key to allow Alice to subcontract to Dave's World Domination Campaign Services. Alice is willing to let Dave know the public addresses and inform her when more money is available, but she doesn't want him spending her campaign money. + +Because viewing and spending use separate keys, Alice can give Dave *Vpriv*. Then Dave can calculate *S = RpubVpriv = GRprivVpriv* and that way get the public keys (*Ppub = Kpub+G\*hash(S)*). But without *Kpriv* Dave cannot get the private key. + +To summarize, these are the values known by the different participants. + +| Alice | Published | Bill | Dave | +| - | - | - | - | +| G | G | G | G | +| *Kpriv* | - | - | - | +| *Vpriv* | - | - | *Vpriv* | +| *Kpub = GKpriv* | *Kpub* | *Kpub* | *Kpub* | +| *Vpub = GVpriv* | *Vpub* | *Vpub* | *Vpub* | +| - | - | *Rpriv* | - | +| *Rpub* | *Rpub* | *Rpub = GRpriv* | *Rpub* | +| *S = RpubVpriv = GRprivVpriv* | - | *S = RprivVpuv - GRprivVpriv* | *S = *RpubVpriv* = GRprivVpriv* | +| *Ppub = Kpub+G\*hash(S)* | - | *Ppub = Kpub+G\*hash(S)* | *Ppub = Kpub+G\*hash(S)* | +| *Address=f(Ppub)* | - | *Address=f(Ppub)* | *Address=f(Ppub)* | *Address=f(Ppub)* +| *Ppriv = Kpriv+hash(S)* | - | - | - | + +## When stealth addresses go wrong {#go-wrong} + +*There are no secrets on the blockchain*. While stealth addresses can provide you with privacy, that privacy is susceptible to traffic analysis. To pick a trivial example, imagine that Bill funds an address and immediately sends a transation to publish an *Rpub* value. Without Alice's *Vpriv*, we can't be sure that this is a stealth address, but that is the way to bet. Then, we see another transaction that transfers all the ETH from that address to Alice's campaign fund address. We may not be able to prove it, but it's likely that Bill just donated to Alice's campaign. Carol would certainly think so. + +It is easy for Bill to separate the publication of *Rpub* from the funding of the stealth address (do them at different times, from different addresses). However, that is insufficient. The pattern Carol looks for is that Bill funds an address, and then Alice's campaign fund withdraws from it. + +One solution is for Alice's campaign not to withdraw the money directly, but use it to pay a third party. If Alice's campaign sends 10 ETH to Dave's World Domination Campaign Services, Carol only knows that Bill donated to one of Dave's customers. If Dave has enough customers, Carol would not be able to know if Bill donated to Alice who competes with her, or to Adam, Albert, or Abigail that Carol doesn't care about. Alice can include a hashed value with the payment, and then provide Dave the preimage, to prove that it was her donation. Alternatively, as noted above, if Alice gives Dave her *Vpriv*, he already knows who the payment came from. + +The main problem with this solution is that it requires Alice to care about secrecy, when the secrecy is to Bill's benefit. Alice may want to maintain her reputation so Bill's friend Bob will also donate to her. But it's also possible that she wouldn't mind exposing Bill, because then he'll be afraid of what will happen if Carol wins and support Alice even more. + +Another solution is to layer [privacy solutions](/privacy). This way, Carol would need to break multiple forms of privacy to figure out who donated to Alice. + +## Writing a stealth-address application {#write-app} + +### Tools {#tools} + +There is [a typescript stealth address library](https://github.com/ScopeLift/stealth-address-sdk) we could use. However, cryptographic operations can be CPU intensive. I prefer to do them in a compiled language, such as [Rust](https://rust-lang.org/), using [WASM](https://webassembly.org/) to run the code in the browser. + +For the client-side code we are going to use [Vite](https://vite.dev/), [React](https://react.dev/), [Viem](https://viem.sh/) and [Wagmi](https://wagmi.sh/). These are industry standard tools, if you are not familiar with them, you can use [this tutorial](/developers/tutorials/creating-a-wagmi-ui-for-your-contract/). To use Vite we need Node. + +The blockchain we use is `anvil`, a local testing blockchain which is part of [Foundry](https://getfoundry.sh/introduction/installation). + +### Getting started {#getting-started} + +1. Install the necessary tools: [Rust](https://rust-lang.org/tools/install/), [Node](https://nodejs.org/en/download), and [Foundry](https://getfoundry.sh/introduction/installation). + +2. Create a Wagmi application. + + ```sh + npm create wagmi + ``` + + Name the project `stealth`. Select a **React** project of the **Vite** variant. + +3. Install the project. + + ```sh + cd stealth + npm install + ``` + +4. Prepare the Rust project + + ```sh + rustup target add wasm32-unknown-unknown + cargo new --lib rust-wasm + cd rust-wasm + cargo install wasm-pack + cargo add wasm-bindgen + cargo add getrandom@0.2 --features js + ``` + +5. Edit `Cargo.toml` to add these sections. Do not remove the other sections. + + ```toml + [lib] + crate-type = ["cdylib"] + + [target.'cfg(target_arch = "wasm32")'.dependencies] + getrandom = { version = "0.2", features = ["js"] } + ``` + +### Stealth address library {#stealth-addr-lib} + +The Rust library we use is [`eth-stealth-addresses`](https://crates.io/crates/eth-stealth-addresses). + +1. Install the library. + + ```sh + cargo add eth-stealth-addresses + ``` + +2. Build the default Rust program and copy the results to the web server. + + ```sh + wasm-pack build --target web + mkdir ../public + cp pkg/*.wasm pkg/*.js ../public + ``` + + +```js + + + + Rust WASM Demo + + +

Rust + WASM Example

+

+
+  
+
+
+```
+
+## Conclusion {#conclusion}
diff --git a/src/data/internalTutorials.json b/src/data/internalTutorials.json
index c59692c7455..174c7d84018 100644
--- a/src/data/internalTutorials.json
+++ b/src/data/internalTutorials.json
@@ -44,6 +44,7 @@
   "set-up-web3js-to-use-ethereum-in-javascript",
   "short-abi",
   "smart-contract-security-guidelines",
+  "stealth-addr",
   "testing-erc-20-tokens-with-waffle",
   "the-graph-fixing-web3-data-querying",
   "token-integration-checklist",
@@ -55,4 +56,4 @@
   "waffle-say-hello-world-with-hardhat-and-ethers",
   "waffle-test-simple-smart-contract",
   "yellow-paper-evm"
-]
\ No newline at end of file
+]

From 575f7ad7ccec282a43ffaede13f396070c5ebd6c Mon Sep 17 00:00:00 2001
From: Ori Pomerantz 
Date: Thu, 23 Oct 2025 17:28:34 -0500
Subject: [PATCH 2/8] WIP

---
 .../tutorials/stealth-addr/index.md           | 167 +++++++++++-------
 1 file changed, 100 insertions(+), 67 deletions(-)

diff --git a/public/content/developers/tutorials/stealth-addr/index.md b/public/content/developers/tutorials/stealth-addr/index.md
index 709f48d6946..1aee8722949 100644
--- a/public/content/developers/tutorials/stealth-addr/index.md
+++ b/public/content/developers/tutorials/stealth-addr/index.md
@@ -2,7 +2,7 @@
 title: "Using Stealth Addresses"
 description: "Stealth addresses allow users to transfer assets anonymously. After reading this article, you will be able to: Explain what stealth addresses are and how they work, understand how to use stealth addresses in a way that preserves anonymity, and write a web-based application that uses stealth addresses."
 author: Ori Pomerantz
-tags: ["Stealth address", "privacy", "cryptography"]
+tags: ["Stealth address", "privacy", "cryptography", "rust", "wasm"]
 skill: intermediate
 published: 2025-11-30
 lang: en
@@ -21,7 +21,7 @@ This article will attempt to explain stealth addresses in two ways. The first is
 
 ### The simple version (how to use stealth addresses) {#how-use}
 
-Alice creates two private keys, and then publishes the two corresponding public keys (which might appear like a double-length public key). Bill also creates a private key and publishes the corresponding public key.
+Alice creates two private keys, and then publishes the two corresponding public keys (which can appear as a double-length public key). Bill also creates a private key and publishes the corresponding public key.
 
 Using one party's public key and the other's private key, you can figure out a shared secret, which only Alice and Bill know (it can't be derived from the public keys alone). Using this shared secret, Bill gets the stealth address, and is able to send assets to it.
 
@@ -81,99 +81,132 @@ Another solution is to layer [privacy solutions](/privacy). This way, Carol woul
 
 There is [a typescript stealth address library](https://github.com/ScopeLift/stealth-address-sdk) we could use. However, cryptographic operations can be CPU intensive. I prefer to do them in a compiled language, such as [Rust](https://rust-lang.org/), using [WASM](https://webassembly.org/) to run the code in the browser.
 
-For the client-side code we are going to use [Vite](https://vite.dev/), [React](https://react.dev/), [Viem](https://viem.sh/) and [Wagmi](https://wagmi.sh/). These are industry standard tools, if you are not familiar with them, you can use [this tutorial](/developers/tutorials/creating-a-wagmi-ui-for-your-contract/). To use Vite we need Node.
+We are going to use [Vite](https://vite.dev/) and [React](https://react.dev/). These are industry standard tools, if you are not familiar with them, you can use [this tutorial](/developers/tutorials/creating-a-wagmi-ui-for-your-contract/). To use Vite we need Node.
 
-The blockchain we use is `anvil`, a local testing blockchain which is part of [Foundry](https://getfoundry.sh/introduction/installation).
+### See stealth addresses in action {#in-action}
 
-### Getting started {#getting-started}
+1. Install the necessary tools: [Rust](https://rust-lang.org/tools/install/) and [Node](https://nodejs.org/en/download).
 
-1. Install the necessary tools: [Rust](https://rust-lang.org/tools/install/), [Node](https://nodejs.org/en/download), and [Foundry](https://getfoundry.sh/introduction/installation).
-
-2. Create a Wagmi application.
+2. Clone the github repository.
 
    ```sh
-   npm create wagmi
+   git clone  https://github.com/qbzzt/251022-stealth-addresses.git
+   cd 251022-stealth-addresses
    ```
 
-   Name the project `stealth`. Select a **React** project of the **Vite** variant.
-
-3. Install the project.
+3. Install prerequisites and compile the Rust code.
 
    ```sh
-   cd stealth
-   npm install
+   cd src/rust-wasm
+   rustup target add wasm32-unknown-unknown   
+   cargo install wasm-pack   
+   wasm-pack build --target web
    ```
 
-4. Prepare the Rust project
+4. Start the web server.
 
    ```sh
-   rustup target add wasm32-unknown-unknown
-   cargo new --lib rust-wasm
-   cd rust-wasm
-   cargo install wasm-pack
-   cargo add wasm-bindgen
-   cargo add getrandom@0.2 --features js
+   cd ../..
+   npm install
+   npm run dev
    ```
 
-5. Edit `Cargo.toml` to add these sections. Do not remove the other sections.
+5. Browse to [the application](http://localhost:5173/). This application page has two frames, one is Alice's user interface and the other is Bill's. The two frames do not communicate, they are only on the same page for convenience.
 
-   ```toml
-   [lib]
-   crate-type = ["cdylib"]
+6. As Alice, click **Generate a Stealth Meta Address**. This will display the new stealth address and the corresponding private keys. Copy the stealth meta address to the clipboard.
 
-   [target.'cfg(target_arch = "wasm32")'.dependencies]
-   getrandom = { version = "0.2", features = ["js"] }    
-   ```
+7. As Bill, paste the new stealth meta address and click **Generate an address**. This gives you the address to fund for Alice. 
 
-### Stealth address library {#stealth-addr-lib}
+8. Copy the address and Bill's public key and paste them in the "Private key for address generated by Bill" area of Alice's user interface. Once those fields are filled, you will see the private key to access assets on the address.
 
-The Rust library we use is [`eth-stealth-addresses`](https://crates.io/crates/eth-stealth-addresses). 
+9. You can use [an online calculator](https://iancoleman.net/ethereum-private-key-to-address/) to ensure the private key corresponds to the address.
 
-1. Install the library.
+### How it works {#how-it-works}
 
-    ```sh
-    cargo add eth-stealth-addresses
-    ```
+#### The User interface {#ui}
 
-2. Build the default Rust program and copy the results to the web server.
+The user interface is written using [React](https://react.dev/) and served by [Vite](https://vite.dev/). You can learn about them using [this tutorial](/developers/tutorials/creating-a-wagmi-ui-for-your-contract/). There is no need for [WAGMI](https://wagmi.sh/) here because we do not interact directly with a blockchain or a wallet.
 
-   ```sh
-   wasm-pack build --target web
-   mkdir ../public
-   cp pkg/*.wasm pkg/*.js ../public
-   ```
+The only non obvious part of the user interface is WASM connectivity. Here is the explanation of how it works.
 
+**`vite.config.js`**
 
 ```js
-
-
-  
-  Rust WASM Demo
-
-
-  

Rust + WASM Example

-

-
-  
-
-
+    loadWasm()
+    }, []
+  )
 ```
 
+**`Bill.jsx`**
+
+```jsx
+import { wasm_generate_stealth_address } from './rust-wasm/pkg/rust_wasm.js'
+
+function Bill() {
+   .
+   .
+   .
+        { stealthAddress.length == 132 && (
+          
+ +

+ { + publicAddress.address && ( + <> + Address: {publicAddress.address} +
+ . + . + . + + ) + } +

+ )} +``` + +The code in `Alice.jsx` is very similar. + +#### The WASM component {#wasm} + ## Conclusion {#conclusion} + From d42253eeb1fad5d1d61bc9e6e863232cf1561fea Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Sun, 26 Oct 2025 07:47:53 -0500 Subject: [PATCH 3/8] WIP --- .../tutorials/stealth-addr/index.md | 52 ++++++++----------- 1 file changed, 21 insertions(+), 31 deletions(-) diff --git a/public/content/developers/tutorials/stealth-addr/index.md b/public/content/developers/tutorials/stealth-addr/index.md index 1aee8722949..e1665efa60a 100644 --- a/public/content/developers/tutorials/stealth-addr/index.md +++ b/public/content/developers/tutorials/stealth-addr/index.md @@ -123,11 +123,13 @@ We are going to use [Vite](https://vite.dev/) and [React](https://react.dev/). T ### How it works {#how-it-works} +#### The WASM component {#wasm} + #### The User interface {#ui} The user interface is written using [React](https://react.dev/) and served by [Vite](https://vite.dev/). You can learn about them using [this tutorial](/developers/tutorials/creating-a-wagmi-ui-for-your-contract/). There is no need for [WAGMI](https://wagmi.sh/) here because we do not interact directly with a blockchain or a wallet. -The only non obvious part of the user interface is WASM connectivity. Here is the explanation of how it works. +The only non-obvious part of the user interface is WASM connectivity. Here is the explanation of how it works. **`vite.config.js`** @@ -142,19 +144,21 @@ export default defineConfig({ }) ``` +We need two plugins into vite, for [react](https://www.npmjs.com/package/@vitejs/plugin-react) and [wasm](https://github.com/Menci/vite-plugin-wasm#readme). + **`App.jsx`** ```jsx import init from './rust-wasm/pkg/rust_wasm.js' +``` -. -. -. +When we use [`wasm-pack`](https://rustwasm.github.io/docs/wasm-pack/), it creaates two files we use here: a wasm file with the actual code (here, `src/rust-wasm/pkg/rust_wasm_bg.wasm`) and a JavaScript file with the definitions to use it (here, `src/rust_wasm/pkg/rust_wasm.js`). The default export of that JavaScript file is code that needs to run to initiate WASM. +```jsx function App() { - const [wasmReady, setWasmReady] = useState(false) - - + . + . + . useEffect(() => { const loadWasm = async () => { try { @@ -171,42 +175,28 @@ function App() { ) ``` +The [`useEffect` hook](https://react.dev/reference/react/useEffect) lets you specify a function that gets executed when state variables change. Here the list of state variables is empty (`[]`), so this function is only executed once when the page is loaded. + +The effect function has to return immediately. To use asynchronous code, such as the WASM `init` (which has to load the `.wasm` file and therefore takes time) we define an internal [`async`](https://en.wikipedia.org/wiki/Async/await) function and run it without an `await`. + **`Bill.jsx`** ```jsx import { wasm_generate_stealth_address } from './rust-wasm/pkg/rust_wasm.js' +``` -function Bill() { - . - . - . - { stealthAddress.length == 132 && ( -
+In addition to the default export, the JavaScript code generated by `wasm-pack` exports a function for every function in the WASM code. +```jsx

- { - publicAddress.address && ( - <> - Address: {publicAddress.address} -
- . - . - . - - ) - } -

- )} ``` -The code in `Alice.jsx` is very similar. +To call WASM functions we just call he -#### The WASM component {#wasm} + +The code in `Alice.jsx` is very similar. ## Conclusion {#conclusion} From c1a0cecef00abf00ccdf00ebe40ca898bf289a41 Mon Sep 17 00:00:00 2001 From: Ori Pomerantz Date: Sun, 26 Oct 2025 17:16:39 -0500 Subject: [PATCH 4/8] WIP --- .../tutorials/stealth-addr/index.md | 141 +++++++++++++++++- 1 file changed, 136 insertions(+), 5 deletions(-) diff --git a/public/content/developers/tutorials/stealth-addr/index.md b/public/content/developers/tutorials/stealth-addr/index.md index e1665efa60a..61655626637 100644 --- a/public/content/developers/tutorials/stealth-addr/index.md +++ b/public/content/developers/tutorials/stealth-addr/index.md @@ -113,9 +113,9 @@ We are going to use [Vite](https://vite.dev/) and [React](https://react.dev/). T 5. Browse to [the application](http://localhost:5173/). This application page has two frames, one is Alice's user interface and the other is Bill's. The two frames do not communicate, they are only on the same page for convenience. -6. As Alice, click **Generate a Stealth Meta Address**. This will display the new stealth address and the corresponding private keys. Copy the stealth meta address to the clipboard. +6. As Alice, click **Generate a Stealth Meta-Address**. This will display the new stealth address and the corresponding private keys. Copy the stealth meta-address to the clipboard. -7. As Bill, paste the new stealth meta address and click **Generate an address**. This gives you the address to fund for Alice. +7. As Bill, paste the new stealth meta-address and click **Generate an address**. This gives you the address to fund for Alice. 8. Copy the address and Bill's public key and paste them in the "Private key for address generated by Bill" area of Alice's user interface. Once those fields are filled, you will see the private key to access assets on the address. @@ -125,6 +125,127 @@ We are going to use [Vite](https://vite.dev/) and [React](https://react.dev/). T #### The WASM component {#wasm} +The source code that compiles into WASM is written in [Rust](https://rust-lang.org/), you can see it in [`src/rust_wasm/src/lib.rs`](https://github.com/qbzzt/251022-stealth-addresses/blob/main/src/rust-wasm/src/lib.rs). This code is primarily an interface between the JavaScript code and [the `eth-stealth-addresses` library](https://github.com/kassandraoftroy/eth-stealth-addresses). + +**`Cargo.toml`** + +[`Cargo.toml`](https://doc.rust-lang.org/cargo/reference/manifest.html) in Rust is analogous to [`package.json`](https://docs.npmjs.com/cli/v9/configuring-npm/package-json) in JavaScript. It contains the package information, declares dependencies, etc. + +```toml +[package] +name = "rust-wasm" +version = "0.1.0" +edition = "2024" + +[dependencies] +eth-stealth-addresses = "0.1.0" +hex = "0.4.3" +wasm-bindgen = "0.2.104" +getrandom = { version = "0.2", features = ["js"] } +``` + +The [`getrandom`](https://docs.rs/getrandom/latest/getrandom/) package needs to generate random values. That cannot be done by pure algorithmic means, it requires access to a physical process as a source of entropy. This definition specifies that we'll get that entropy by asking the browser in which we run. + +```toml +console_error_panic_hook = "0.1.7" +``` + +[This library](https://docs.rs/console_error_panic_hook/latest/console_error_panic_hook/) gives us more meaningful error messages when the WASM code panics and cannot continue. + + +```toml +[lib] +crate-type = ["cdylib", "rlib"] +``` + +The output type required to produce WASM code. + +**`lib.rs`** + +This is the actual code. + +```rust +use wasm_bindgen::prelude::*; +``` + +The definitions to create a WASM package out of Rust. They are documented [here](https://wasm-bindgen.github.io/wasm-bindgen/reference/attributes/index.html). + +```rust +use eth_stealth_addresses::{ + generate_stealth_meta_address, + generate_stealth_address, + compute_stealth_key +}; +``` + +The functions we need from [the `eth-stealth-addresses` library](https://github.com/kassandraoftroy/eth-stealth-addresses). + +```rust +use hex::{decode,encode}; +``` + +Rust typically uses byte [arrays](https://doc.rust-lang.org/std/primitive.array.html) (`[u8; ]`) for values. But in JavaScript we typically use hexadecimal strings. [The `hex` library](https://docs.rs/hex/latest/hex/) translates for us from one representation to the other. + +```rust +#[wasm_bindgen] +pub fn wasm_generate_stealth_meta_address() -> String { + let (address, spend_private_key, view_private_key) = + generate_stealth_meta_address(); + + format!("{{\"address\":\"{}\",\"view_private_key\":\"{}\",\"spend_private_key\":\"{}\"}}", + encode(address), + encode(view_private_key), + encode(spend_private_key) + ) +} + +fn str_to_array(s: &str) -> Option<[u8; N]> { + // decode returns Result, _> + let vec = decode(s).ok()?; + // ensure correct length + if vec.len() != N { return None; } + // try_into consumes vec and attempts to make [u8; N] + let array: [u8; N] = vec.try_into().ok()?; + Some(array) +} + +#[wasm_bindgen] +pub fn wasm_generate_stealth_address(stealth_address: &str) -> Option { + let (address, r_pub, scan) = + generate_stealth_address(&str_to_array::<66>(stealth_address)?); + + format!("{{\"address\":\"{}\",\"rPub\":\"{}\",\"scan\":\"{}\"}}", + encode(address), + encode(r_pub), + encode(&[scan]) + ).into() +} + + +#[wasm_bindgen] +pub fn wasm_compute_stealth_key( + address: &str, + bill_pub_key: &str, + view_private_key: &str, + spend_private_key: &str +) -> Option { + let private_key = + compute_stealth_key( + &str_to_array::<20>(address)?, + &str_to_array::<33>(bill_pub_key)?, + &str_to_array::<32>(view_private_key)?, + &str_to_array::<32>(spend_private_key)? + ); + encode(private_key).into() +} + +#[wasm_bindgen(start)] +pub fn main() { + console_error_panic_hook::set_once(); +} +``` + + #### The User interface {#ui} The user interface is written using [React](https://react.dev/) and served by [Vite](https://vite.dev/). You can learn about them using [this tutorial](/developers/tutorials/creating-a-wagmi-ui-for-your-contract/). There is no need for [WAGMI](https://wagmi.sh/) here because we do not interact directly with a blockchain or a wallet. @@ -133,6 +254,8 @@ The only non-obvious part of the user interface is WASM connectivity. Here is th **`vite.config.js`** +This fine contains [the Vite configuration](https://vite.dev/config/). + ```js import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' @@ -148,6 +271,8 @@ We need two plugins into vite, for [react](https://www.npmjs.com/package/@vitejs **`App.jsx`** +This file is the main component of the application. It is a container that includes two components: `Alice` and `Bill`, the user interfaces for those users. The relevant part for WASM is the initialization code. + ```jsx import init from './rust-wasm/pkg/rust_wasm.js' ``` @@ -181,6 +306,8 @@ The effect function has to return immediately. To use asynchronous code, such as **`Bill.jsx`** +This is the user interface for Bill. It has a single action, creating an address based on the stealth meta-address provided by Alice. + ```jsx import { wasm_generate_stealth_address } from './rust-wasm/pkg/rust_wasm.js' ``` @@ -189,14 +316,18 @@ In addition to the default export, the JavaScript code generated by `wasm-pack` ```jsx