Chuyển đến nội dung chính

Sử dụng địa chỉ ẩn danh

địa chỉ ẩn danh
quyền riêng tư
mật mã học
Rust
wasm
Trung cấp
Ori Pomerantz
30 tháng 11, 2025
20 phút đọc

Bạn là Bill. Vì một số lý do mà chúng ta sẽ không đi sâu vào, bạn muốn quyên góp cho chiến dịch "Alice cho Nữ hoàng Thế giới" và muốn Alice biết bạn đã quyên góp để cô ấy sẽ trao phần thưởng cho bạn nếu cô ấy thắng. Thật không may, chiến thắng của cô ấy không được đảm bảo. Có một chiến dịch cạnh tranh là "Carol cho Nữ hoàng Hệ Mặt trời". Nếu Carol thắng và phát hiện ra bạn đã quyên góp cho Alice, bạn sẽ gặp rắc rối. Vì vậy, bạn không thể chỉ chuyển 200 ETH từ tài khoản của mình sang tài khoản của Alice.

ERC-5564 (opens in a new tab) có giải pháp cho vấn đề này. ERC này giải thích cách sử dụng địa chỉ ẩn danh (opens in a new tab) để chuyển ẩn danh.

Cảnh báo: Mật mã học đằng sau các địa chỉ ẩn danh, theo như chúng ta biết, là an toàn. Tuy nhiên, vẫn có những cuộc tấn công kênh kề (side-channel attacks) tiềm ẩn. Bên dưới, bạn sẽ thấy những gì bạn có thể làm để giảm thiểu rủi ro này.

Cách địa chỉ ẩn danh hoạt động

Bài viết này sẽ cố gắng giải thích địa chỉ ẩn danh theo hai cách. Cách đầu tiên là cách sử dụng chúng. Phần này là đủ để hiểu phần còn lại của bài viết. Sau đó, có một lời giải thích về toán học đằng sau nó. Nếu bạn quan tâm đến mật mã học, hãy đọc cả phần này.

Phiên bản đơn giản (cách sử dụng địa chỉ ẩn danh)

Alice tạo hai khóa riêng tư và công bố các khóa công khai tương ứng (có thể được kết hợp thành một địa chỉ meta có độ dài gấp đôi). Bill cũng tạo một khóa riêng tư và công bố khóa công khai tương ứng.

Bằng cách sử dụng khóa công khai của một bên và khóa riêng tư của bên kia, bạn có thể tạo ra một bí mật chung (shared secret) mà chỉ Alice và Bill biết (nó không thể được tạo ra nếu chỉ có các khóa công khai). Sử dụng bí mật chung này, Bill lấy được địa chỉ ẩn danh và có thể gửi tài sản vào đó.

Alice cũng nhận được địa chỉ từ bí mật chung, nhưng vì cô ấy biết các khóa riêng tư của các khóa công khai mà cô ấy đã công bố, cô ấy cũng có thể lấy được khóa riêng tư cho phép cô ấy rút tiền từ địa chỉ đó.

Toán học (tại sao địa chỉ ẩn danh hoạt động như thế này)

Các địa chỉ ẩn danh tiêu chuẩn sử dụng mật mã học đường cong elliptic (ECC) (opens in a new tab) để có hiệu suất tốt hơn với ít bit khóa hơn, trong khi vẫn giữ nguyên mức độ bảo mật. Nhưng phần lớn chúng ta có thể bỏ qua điều đó và giả vờ như chúng ta đang sử dụng số học thông thường.

Có một con số mà mọi người đều biết, G. Bạn có thể nhân với G. Nhưng do bản chất của ECC, việc chia cho G là điều gần như không thể. Cách mật mã học khóa công khai thường hoạt động trong Ethereum là bạn có thể sử dụng một khóa riêng tư, Ppriv, để ký các giao dịch sau đó được xác minh bằng một khóa công khai, Ppub = GPpriv.

Alice tạo hai khóa riêng tư, KprivVpriv. Kpriv sẽ được sử dụng để tiêu tiền từ địa chỉ ẩn danh, và Vpriv để xem các địa chỉ thuộc về Alice. Sau đó, Alice công bố các khóa công khai: Kpub = GKprivVpub = GVpriv

Bill tạo một khóa riêng tư thứ ba, Rpriv, và công bố Rpub = GRpriv lên một sổ đăng ký trung tâm (Bill cũng có thể đã gửi nó cho Alice, nhưng chúng ta giả định rằng Carol đang nghe lén).

Bill tính toán RprivVpub = GRprivVpriv, mà anh ấy mong đợi Alice cũng biết (được giải thích bên dưới). Giá trị này được gọi là S, bí mật chung. Điều này cung cấp cho Bill một khóa công khai, Ppub = Kpub+G*hash(S). Từ khóa công khai này, anh ấy có thể tính toán một địa chỉ và gửi bất kỳ tài nguyên nào anh ấy muốn vào đó. Trong tương lai, nếu Alice thắng, Bill có thể nói cho cô ấy biết Rpriv để chứng minh các tài nguyên đó đến từ anh ấy.

Alice tính toán RpubVpriv = GRprivVpriv. Điều này cung cấp cho cô ấy cùng một bí mật chung, S. Vì cô ấy biết khóa riêng tư, Kpriv, cô ấy có thể tính toán Ppriv = Kpriv+hash(S). Khóa này cho phép cô ấy truy cập tài sản trong địa chỉ là kết quả từ Ppub = GPpriv = GKpriv+G*hash(S) = Kpub+G*hash(S).

Chúng ta có một khóa xem (viewing key) riêng biệt để cho phép Alice ký hợp đồng phụ với Dịch vụ Chiến dịch Thống trị Thế giới của Dave. Alice sẵn sàng cho Dave biết các địa chỉ công khai và thông báo cho cô ấy khi có thêm tiền, nhưng cô ấy không muốn anh ta tiêu tiền chiến dịch của mình.

Vì việc xem và chi tiêu sử dụng các khóa riêng biệt, Alice có thể đưa cho Dave Vpriv. Sau đó, Dave có thể tính toán S = RpubVpriv = GRprivVpriv và bằng cách đó lấy được các khóa công khai (Ppub = Kpub+G*hash(S)). Nhưng nếu không có Kpriv, Dave không thể lấy được khóa riêng tư.

Tóm lại, đây là các giá trị được biết bởi những người tham gia khác nhau.

AliceĐã công bốBillDave
GGGG
Kpriv---
Vpriv--Vpriv
Kpub = GKprivKpubKpubKpub
Vpub = GVprivVpubVpubVpub
--Rpriv-
RpubRpubRpub = GRprivRpub
S = RpubVpriv = GRprivVpriv-S = RprivVpub = GRprivVprivS = 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)
Ppriv = Kpriv+hash(S)---

Khi địa chỉ ẩn danh gặp sự cố

Không có bí mật nào trên Chuỗi khối. Mặc dù các địa chỉ ẩn danh có thể cung cấp cho bạn quyền riêng tư, nhưng quyền riêng tư đó dễ bị phân tích lưu lượng (traffic analysis). Lấy một ví dụ đơn giản, hãy tưởng tượng rằng Bill nạp tiền vào một địa chỉ và ngay lập tức gửi một giao dịch để công bố giá trị Rpub. Nếu không có Vpriv của Alice, chúng ta không thể chắc chắn rằng đây là một địa chỉ ẩn danh, nhưng đó là khả năng cao nhất. Sau đó, chúng ta thấy một giao dịch khác chuyển toàn bộ ETH từ địa chỉ đó sang địa chỉ quỹ chiến dịch của Alice. Chúng ta có thể không chứng minh được điều đó, nhưng rất có thể Bill vừa quyên góp cho chiến dịch của Alice. Carol chắc chắn sẽ nghĩ như vậy.

Rất dễ để Bill tách biệt việc công bố Rpub khỏi việc nạp tiền vào địa chỉ ẩn danh (thực hiện chúng vào các thời điểm khác nhau, từ các địa chỉ khác nhau). Tuy nhiên, điều đó là chưa đủ. Mô hình mà Carol tìm kiếm là Bill nạp tiền vào một địa chỉ, và sau đó quỹ chiến dịch của Alice rút tiền từ đó.

Một giải pháp là chiến dịch của Alice không rút tiền trực tiếp mà sử dụng nó để trả cho một bên thứ ba. Nếu chiến dịch của Alice gửi 10 ETH cho Dịch vụ Chiến dịch Thống trị Thế giới của Dave, Carol chỉ biết rằng Bill đã quyên góp cho một trong những khách hàng của Dave. Nếu Dave có đủ khách hàng, Carol sẽ không thể biết liệu Bill đã quyên góp cho Alice, người đang cạnh tranh với cô ấy, hay cho Adam, Albert, hoặc Abigail mà Carol không quan tâm. Alice có thể bao gồm một giá trị đã được băm (hashed value) cùng với khoản thanh toán, và sau đó cung cấp cho Dave tiền ảnh (preimage), để chứng minh rằng đó là khoản quyên góp của cô ấy. Ngoài ra, như đã lưu ý ở trên, nếu Alice đưa cho Dave Vpriv của cô ấy, anh ta đã biết khoản thanh toán đến từ ai.

Vấn đề chính với giải pháp này là nó yêu cầu Alice phải quan tâm đến sự bí mật khi sự bí mật đó mang lại lợi ích cho Bill. Alice có thể muốn duy trì danh tiếng của mình để bạn của Bill là Bob cũng sẽ quyên góp cho cô ấy. Nhưng cũng có khả năng cô ấy sẽ không ngại tiết lộ Bill, bởi vì khi đó anh ấy sẽ sợ hãi về những gì sẽ xảy ra nếu Carol thắng. Bill cuối cùng có thể cung cấp cho Alice nhiều sự hỗ trợ hơn nữa.

Sử dụng nhiều lớp ẩn danh

Thay vì dựa vào Alice để bảo vệ quyền riêng tư của Bill, Bill có thể tự làm điều đó. Anh ấy có thể tạo nhiều địa chỉ meta cho những người hư cấu, Bob và Bella. Sau đó, Bill gửi ETH cho Bob, và "Bob" (thực ra là Bill) gửi nó cho Bella. "Bella" (cũng là Bill) gửi nó cho Alice.

Carol vẫn có thể thực hiện phân tích lưu lượng và thấy đường ống từ Bill đến Bob đến Bella đến Alice. Tuy nhiên, nếu "Bob" và "Bella" cũng sử dụng ETH cho các mục đích khác, sẽ không có vẻ như Bill đã chuyển bất cứ thứ gì cho Alice, ngay cả khi Alice ngay lập tức rút tiền từ địa chỉ ẩn danh sang địa chỉ chiến dịch đã biết của cô ấy.

Viết một ứng dụng địa chỉ ẩn danh

Bài viết này giải thích một ứng dụng địa chỉ ẩn danh có sẵn trên GitHub (opens in a new tab).

Công cụ

một thư viện địa chỉ ẩn danh bằng TypeScript (opens in a new tab) mà chúng ta có thể sử dụng. Tuy nhiên, các hoạt động mật mã học có thể tiêu tốn nhiều CPU. Tôi thích triển khai chúng bằng một ngôn ngữ biên dịch, chẳng hạn như Rust (opens in a new tab), và sử dụng WASM (opens in a new tab) để chạy mã trong trình duyệt.

Chúng ta sẽ sử dụng Vite (opens in a new tab)React (opens in a new tab). Đây là những công cụ tiêu chuẩn trong ngành; nếu bạn không quen thuộc với chúng, bạn có thể sử dụng hướng dẫn này. Để sử dụng Vite, chúng ta cần Node.

Xem địa chỉ ẩn danh hoạt động

  1. Cài đặt các công cụ cần thiết: Rust (opens in a new tab)Node (opens in a new tab).

  2. Sao chép (clone) kho lưu trữ GitHub.

    git clone https://github.com/qbzzt/251022-stealth-addresses.git
    cd 251022-stealth-addresses
    
  3. Cài đặt các điều kiện tiên quyết và biên dịch mã Rust.

    cd src/rust-wasm
    rustup target add wasm32-unknown-unknown   
    cargo install wasm-pack   
    wasm-pack build --target web
    
  4. Khởi động máy chủ web.

    cd ../..
    npm install
    npm run dev
    
  5. Duyệt đến ứng dụng (opens in a new tab). Trang ứng dụng này có hai khung: một cho giao diện người dùng của Alice và một cho của Bill. Hai khung này không giao tiếp với nhau; chúng chỉ nằm trên cùng một trang cho thuận tiện.

  6. Với tư cách là Alice, nhấp vào Generate a Stealth Meta-Address (Tạo một địa chỉ meta ẩn danh). Thao tác này sẽ hiển thị địa chỉ ẩn danh mới và các khóa riêng tư tương ứng. Sao chép địa chỉ meta ẩn danh vào khay nhớ tạm (clipboard).

  7. Với tư cách là Bill, dán địa chỉ meta ẩn danh mới và nhấp vào Generate an address (Tạo một địa chỉ). Điều này cung cấp cho bạn địa chỉ để nạp tiền cho Alice.

  8. Sao chép địa chỉ và khóa công khai của Bill rồi dán chúng vào khu vực "Private key for address generated by Bill" (Khóa riêng tư cho địa chỉ do Bill tạo) trên giao diện người dùng của Alice. Khi các trường đó được điền, bạn sẽ thấy khóa riêng tư để truy cập tài sản tại địa chỉ đó.

  9. Bạn có thể sử dụng một máy tính trực tuyến (opens in a new tab) để đảm bảo khóa riêng tư tương ứng với địa chỉ.

Cách chương trình hoạt động

Thành phần WASM

Mã nguồn biên dịch thành WASM được viết bằng Rust (opens in a new tab). Bạn có thể xem nó trong src/rust_wasm/src/lib.rs (opens in a new tab). Mã này chủ yếu là một giao diện giữa mã JavaScript và Thư viện eth-stealth-addresses (opens in a new tab).

Cargo.toml

Cargo.toml (opens in a new tab) trong Rust tương tự như package.json (opens in a new tab) trong JavaScript. Nó chứa thông tin gói, khai báo phụ thuộc, v.v.

Gói getrandom (opens in a new tab) cần tạo các giá trị ngẫu nhiên. Điều đó không thể được thực hiện bằng các phương tiện thuật toán thuần túy; nó yêu cầu quyền truy cập vào một quá trình vật lý như một nguồn entropy. Định nghĩa này chỉ định rằng chúng ta sẽ lấy entropy đó bằng cách yêu cầu trình duyệt mà chúng ta đang chạy.

console_error_panic_hook = "0.1.7"

Thư viện này (opens in a new tab) cung cấp cho chúng ta các thông báo lỗi có ý nghĩa hơn khi mã WASM gặp lỗi nghiêm trọng (panic) và không thể tiếp tục.

[lib]
crate-type = ["cdylib", "rlib"]

Loại đầu ra cần thiết để tạo mã WASM.

lib.rs

Đây là mã Rust thực tế.

use wasm_bindgen::prelude::*;

Các định nghĩa để tạo một gói WASM từ Rust. Chúng được ghi chép tại đây (opens in a new tab).

use eth_stealth_addresses::{
    generate_stealth_meta_address,
    generate_stealth_address,
    compute_stealth_key
};

Các hàm chúng ta cần từ Thư viện eth-stealth-addresses (opens in a new tab).

use hex::{decode,encode};

Rust thường sử dụng các mảng (opens in a new tab) byte ([u8; <size>]) cho các giá trị. Nhưng trong JavaScript, chúng ta thường sử dụng các chuỗi thập lục phân. Thư viện hex (opens in a new tab) dịch cho chúng ta từ biểu diễn này sang biểu diễn khác.

#[wasm_bindgen]

Tạo các ràng buộc (bindings) WASM để có thể gọi hàm này từ JavaScript.

pub fn wasm_generate_stealth_meta_address() -> String {

Cách dễ nhất để trả về một đối tượng có nhiều trường là trả về một chuỗi JSON.

    let (address, spend_private_key, view_private_key) = 
        generate_stealth_meta_address();

generate_stealth_meta_address (opens in a new tab) trả về ba trường:

  • Địa chỉ meta (KpubVpub)
  • Khóa riêng tư để xem (Vpriv)
  • Khóa riêng tư để chi tiêu (Kpriv)

Cú pháp tuple (opens in a new tab) cho phép chúng ta tách các giá trị đó ra một lần nữa.

    format!("{{\"address\":\"{}\",\"view_private_key\":\"{}\",\"spend_private_key\":\"{}\"}}",
        encode(address),
        encode(view_private_key),
        encode(spend_private_key)
    )
}

Sử dụng macro format! (opens in a new tab) để tạo chuỗi được mã hóa JSON. Sử dụng hex::encode (opens in a new tab) để thay đổi các mảng thành chuỗi thập lục phân.

fn str_to_array<const N: usize>(s: &str) -> Option<[u8; N]> {

Hàm này biến một chuỗi thập lục phân (do JavaScript cung cấp) thành một mảng byte. Chúng ta sử dụng nó để phân tích cú pháp các giá trị do mã JavaScript cung cấp. Hàm này phức tạp vì cách Rust xử lý các mảng và vector.

Biểu thức <const N: usize> được gọi là một generic (opens in a new tab). N là một tham số kiểm soát độ dài của mảng được trả về. Hàm thực sự được gọi là str_to_array::<n>, trong đó n là độ dài mảng.

Giá trị trả về là Option<[u8; N]>, có nghĩa là mảng được trả về là tùy chọn (optional) (opens in a new tab). Đây là một mô hình điển hình trong Rust cho các hàm có thể thất bại.

Ví dụ, nếu chúng ta gọi str_to_array::10("bad060a7"), hàm được cho là sẽ trả về một mảng mười giá trị, nhưng đầu vào chỉ có bốn byte. Hàm cần phải thất bại, và nó làm như vậy bằng cách trả về None. Giá trị trả về cho str_to_array::4("bad060a7") sẽ là Some<[0xba, 0xd0, 0x60, 0xa7]>.

    // decode trả về Result<Vec<u8>, _>
    let vec = decode(s).ok()?;

Hàm hex::decode (opens in a new tab) trả về một Result<Vec<u8>, FromHexError>. Loại Result (opens in a new tab) có thể chứa một kết quả thành công (Ok(value)) hoặc một lỗi (Err(error)).

Phương thức .ok() biến Result thành một Option, có giá trị là giá trị Ok() nếu thành công hoặc None nếu không. Cuối cùng, toán tử dấu chấm hỏi (opens in a new tab) hủy bỏ các hàm hiện tại và trả về một None nếu Option trống. Nếu không, nó sẽ mở gói (unwrap) giá trị và trả về giá trị đó (trong trường hợp này, để gán một giá trị cho vec).

Điều này có vẻ giống như một phương pháp phức tạp một cách kỳ lạ để xử lý lỗi, nhưng ResultOption đảm bảo rằng tất cả các lỗi đều được xử lý, theo cách này hay cách khác.

    if vec.len() != N { return None; }

Nếu số lượng byte không chính xác, đó là một sự thất bại, và chúng ta trả về None.

    // try_into tiêu thụ vec và cố gắng tạo ra [u8; N]
    let array: [u8; N] = vec.try_into().ok()?;

Rust có hai loại mảng. Mảng (opens in a new tab) có kích thước cố định. Vector (opens in a new tab) có thể tăng và giảm kích thước. hex::decode trả về một vector, nhưng thư viện eth_stealth_addresses muốn nhận các mảng. .try_into() (opens in a new tab) chuyển đổi một giá trị thành một loại khác, ví dụ, một vector thành một mảng.

    Some(array)
}

Rust không yêu cầu bạn sử dụng từ khóa return (opens in a new tab) khi trả về một giá trị ở cuối một hàm.

#[wasm_bindgen]
pub fn wasm_generate_stealth_address(stealth_address: &str) -> Option<String> {

Hàm này nhận một địa chỉ meta công khai, bao gồm cả VpubKpub. Nó trả về địa chỉ ẩn danh, khóa công khai để công bố (Rpub), và một giá trị quét (scan value) một byte giúp tăng tốc độ xác định các địa chỉ đã công bố nào có thể thuộc về Alice.

Giá trị quét là một phần của bí mật chung (S = GRprivVpriv). Giá trị này có sẵn cho Alice, và việc kiểm tra nó nhanh hơn nhiều so với việc kiểm tra xem f(Kpub+G*hash(S)) có bằng địa chỉ đã công bố hay không.

    let (address, r_pub, scan) = 
        generate_stealth_address(&str_to_array::<66>(stealth_address)?);

Chúng ta sử dụng generate_stealth_address (opens in a new tab) của thư viện.

    format!("{{\"address\":\"{}\",\"rPub\":\"{}\",\"scan\":\"{}\"}}",
        encode(address),
        encode(r_pub),
        encode(&[scan])
    ).into()
}

Chuẩn bị chuỗi đầu ra được mã hóa JSON.

Hàm này sử dụng compute_stealth_key (opens in a new tab) của thư viện để tính toán khóa riêng tư nhằm rút tiền từ địa chỉ (Rpriv). Tính toán này yêu cầu các giá trị sau:

  • Địa chỉ (Address=f(Ppub))
  • Khóa công khai do Bill tạo (Rpub)
  • Khóa riêng tư để xem (Vpriv)
  • Khóa riêng tư để chi tiêu (Kpriv)
#[wasm_bindgen(start)]

#[wasm_bindgen(start)] (opens in a new tab) chỉ định rằng hàm được thực thi khi mã WASM được khởi tạo.

pub fn main() {
    console_error_panic_hook::set_once();
}

Mã này chỉ định rằng đầu ra lỗi nghiêm trọng (panic) sẽ được gửi đến bảng điều khiển (console) JavaScript. Để xem nó hoạt động, hãy sử dụng ứng dụng và cung cấp cho Bill một địa chỉ meta không hợp lệ (chỉ cần thay đổi một chữ số thập lục phân). Bạn sẽ thấy lỗi này trong bảng điều khiển JavaScript:

rust_wasm.js:236 panicked at /home/ori/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/subtle-2.6.1/src/lib.rs:701:9:
assertion `left == right` failed
  left: 0
 right: 1

Theo sau là một dấu vết ngăn xếp (stack trace). Sau đó, cung cấp cho Bill địa chỉ meta hợp lệ, và cung cấp cho Alice một địa chỉ không hợp lệ hoặc một khóa công khai không hợp lệ. Bạn sẽ thấy lỗi này:

rust_wasm.js:236 panicked at /home/ori/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/eth-stealth-addresses-0.1.0/src/lib.rs:78:9:
keys do not generate stealth address

Một lần nữa, theo sau là một dấu vết ngăn xếp.

Giao diện người dùng

Giao diện người dùng được viết bằng React (opens in a new tab) và được phục vụ bởi Vite (opens in a new tab). Bạn có thể tìm hiểu về chúng bằng cách sử dụng hướng dẫn này. Không cần Wagmi (opens in a new tab) ở đây vì chúng ta không tương tác trực tiếp với một Chuỗi khối hoặc một Ví.

Phần duy nhất không rõ ràng của giao diện người dùng là kết nối WASM. Đây là cách nó hoạt động.

vite.config.js

Tệp này chứa cấu hình Vite (opens in a new tab).

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import wasm from "vite-plugin-wasm";

// https://vite.dev/config/
export default defineConfig({
  plugins: [react(), wasm()],
})

Chúng ta cần hai plugin Vite: react (opens in a new tab)wasm (opens in a new tab).

App.jsx

Tệp này là thành phần chính của ứng dụng. Nó là một vùng chứa (container) bao gồm hai thành phần: AliceBill, giao diện người dùng cho những người dùng đó. Phần liên quan đến WASM là mã khởi tạo.

import init from './rust-wasm/pkg/rust_wasm.js'

Khi chúng ta sử dụng wasm-pack (opens in a new tab), nó tạo ra hai tệp mà chúng ta sử dụng ở đây: một tệp wasm với mã thực tế (ở đây là src/rust-wasm/pkg/rust_wasm_bg.wasm) và một tệp JavaScript với các định nghĩa để sử dụng nó (ở đây là src/rust_wasm/pkg/rust_wasm.js). Xuất mặc định (default export) của tệp JavaScript đó là mã cần chạy để khởi tạo WASM.

Hook useEffect (opens in a new tab) cho phép bạn chỉ định một hàm được thực thi khi các biến trạng thái thay đổi. Ở đây, danh sách các biến trạng thái trống ([]), vì vậy hàm này chỉ được thực thi một lần khi trang tải.

Hàm effect phải trả về ngay lập tức. Để sử dụng mã bất đồng bộ, chẳng hạn như init của WASM (phải tải tệp .wasm và do đó cần thời gian), chúng ta định nghĩa một hàm async (opens in a new tab) nội bộ và chạy nó mà không có await.

Bill.jsx

Đây là giao diện người dùng cho Bill. Nó có một hành động duy nhất, tạo một địa chỉ dựa trên địa chỉ meta ẩn danh do Alice cung cấp.

import { wasm_generate_stealth_address } from './rust-wasm/pkg/rust_wasm.js'

Ngoài xuất mặc định, mã JavaScript do wasm-pack tạo ra sẽ xuất một hàm cho mọi hàm trong mã WASM.

            <button onClick={() => {
              setPublicAddress(JSON.parse(wasm_generate_stealth_address(stealthMetaAddress)))
            }}>

Để gọi các hàm WASM, chúng ta chỉ cần gọi hàm được xuất bởi tệp JavaScript do wasm-pack tạo ra.

Alice.jsx

Mã trong Alice.jsx cũng tương tự, ngoại trừ việc Alice có hai hành động:

  • Tạo một địa chỉ meta
  • Lấy khóa riêng tư cho một địa chỉ do Bill công bố

Kết luận

Địa chỉ ẩn danh không phải là thuốc chữa bách bệnh; chúng phải được sử dụng đúng cách. Nhưng khi được sử dụng đúng cách, chúng có thể kích hoạt quyền riêng tư trên một Chuỗi khối công khai.

Xem thêm các bài viết khác của tôi tại đây (opens in a new tab).