Menggunakan Alamat Siluman
Anda adalah Bill. Untuk alasan yang tidak akan kita bahas, Anda ingin berdonasi ke kampanye "Alice untuk Ratu Dunia" dan ingin Alice tahu bahwa Anda berdonasi sehingga dia akan memberi Anda imbalan jika dia menang. Sayangnya, kemenangannya tidak dijamin. Ada kampanye pesaing, "Carol untuk Permaisuri Tata Surya". Jika Carol menang, dan dia mengetahui bahwa Anda berdonasi kepada Alice, Anda akan mendapat masalah. Jadi, Anda tidak bisa begitu saja mentransfer 200 ETH dari akun Anda ke akun Alice.
ERC-5564 (opens in a new tab) memiliki solusinya. ERC ini menjelaskan cara menggunakan alamat siluman (opens in a new tab) untuk transfer anonim.
Peringatan: Kriptografi di balik alamat siluman, sejauh yang kami tahu, aman. Namun, ada potensi serangan saluran sampingan (side-channel attacks). Di bawah ini, Anda akan melihat apa yang dapat Anda lakukan untuk mengurangi risiko ini.
Bagaimana cara kerja alamat siluman
Artikel ini akan mencoba menjelaskan alamat siluman dengan dua cara. Yang pertama adalah cara menggunakannya. Bagian ini cukup untuk memahami sisa artikel. Kemudian, ada penjelasan tentang matematika di baliknya. Jika Anda tertarik dengan kriptografi, bacalah bagian ini juga.
Versi sederhana (cara menggunakan alamat siluman)
Alice membuat dua kunci privat dan memublikasikan kunci publik yang sesuai (yang dapat digabungkan menjadi satu alamat meta dengan panjang ganda). Bill juga membuat kunci privat dan memublikasikan kunci publik yang sesuai.
Dengan menggunakan kunci publik satu pihak dan kunci privat pihak lain, Anda dapat memperoleh rahasia bersama yang hanya diketahui oleh Alice dan Bill (ini tidak dapat diperoleh dari kunci publik saja). Dengan menggunakan rahasia bersama ini, Bill mendapatkan alamat siluman dan dapat mengirimkan aset ke alamat tersebut.
Alice juga mendapatkan alamat dari rahasia bersama tersebut, tetapi karena dia mengetahui kunci privat dari kunci publik yang dia publikasikan, dia juga bisa mendapatkan kunci privat yang memungkinkannya untuk menarik dana dari alamat tersebut.
Matematika (mengapa alamat siluman bekerja seperti ini)
Alamat siluman standar menggunakan kriptografi kurva eliptik (ECC) (opens in a new tab) untuk mendapatkan performa yang lebih baik dengan bit kunci yang lebih sedikit, sambil tetap menjaga tingkat keamanan yang sama. Namun sebagian besar kita dapat mengabaikannya dan berpura-pura bahwa kita menggunakan aritmatika biasa.
Ada sebuah angka yang diketahui semua orang, G. Anda dapat mengalikannya dengan G. Namun karena sifat ECC, secara praktis tidak mungkin untuk membaginya dengan G. Cara kerja kriptografi kunci publik secara umum di Ethereum adalah Anda dapat menggunakan kunci privat, Ppriv, untuk menandatangani transaksi yang kemudian diverifikasi oleh kunci publik, Ppub = GPpriv.
Alice membuat dua kunci privat, Kpriv dan Vpriv. Kpriv akan digunakan untuk membelanjakan uang dari alamat siluman, dan Vpriv untuk melihat alamat yang menjadi milik Alice. Alice kemudian memublikasikan kunci publik: Kpub = GKpriv dan Vpub = GVpriv
Bill membuat kunci privat ketiga, Rpriv, dan memublikasikan Rpub = GRpriv ke registri pusat (Bill juga bisa saja mengirimkannya ke Alice, tetapi kita asumsikan Carol sedang mendengarkan).
Bill menghitung RprivVpub = GRprivVpriv, yang dia harapkan Alice juga mengetahuinya (dijelaskan di bawah). Nilai ini disebut S, rahasia bersama. Ini memberi Bill sebuah kunci publik, Ppub = Kpub+G*hash(S). Dari kunci publik ini, dia dapat menghitung sebuah alamat dan mengirimkan sumber daya apa pun yang dia inginkan ke alamat tersebut. Di masa depan, jika Alice menang, Bill dapat memberitahunya Rpriv untuk membuktikan bahwa sumber daya tersebut berasal darinya.
Alice menghitung RpubVpriv = GRprivVpriv. Ini memberinya rahasia bersama yang sama, S. Karena dia mengetahui kunci privat, Kpriv, dia dapat menghitung Ppriv = Kpriv+hash(S). Kunci ini memungkinkannya mengakses aset di alamat yang dihasilkan dari Ppub = GPpriv = GKpriv+G*hash(S) = Kpub+G*hash(S).
Kita memiliki kunci peninjauan (viewing key) terpisah untuk memungkinkan Alice melakukan subkontrak ke Layanan Kampanye Dominasi Dunia Dave. Alice bersedia memberi tahu Dave alamat publik dan menginformasikannya ketika ada lebih banyak uang yang tersedia, tetapi dia tidak ingin Dave membelanjakan uang kampanyenya.
Karena peninjauan dan pembelanjaan menggunakan kunci yang terpisah, Alice dapat memberikan Vpriv kepada Dave. Kemudian Dave dapat menghitung S = RpubVpriv = GRprivVpriv dan dengan cara itu mendapatkan kunci publik (Ppub = Kpub+G*hash(S)). Tetapi tanpa Kpriv Dave tidak bisa mendapatkan kunci privat.
Singkatnya, ini adalah nilai-nilai yang diketahui oleh berbagai partisipan.
| Alice | Dipublikasikan | 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 = RprivVpub = GRprivVpriv | S = RpubVpriv = GRprivVpriv |
| Ppub = Kpub+G*hash(S) | - | Ppub = Kpub+G*hash(S) | Ppub = Kpub+G*hash(S) |
| Alamat=f(Ppub) | - | Alamat=f(Ppub) | Alamat=f(Ppub) |
| Ppriv = Kpriv+hash(S) | - | - | - |
Ketika alamat siluman bermasalah
Tidak ada rahasia di rantai blok. Meskipun alamat siluman dapat memberi Anda privasi, privasi tersebut rentan terhadap analisis lalu lintas. Sebagai contoh sederhana, bayangkan Bill mendanai sebuah alamat dan segera mengirimkan transaksi untuk memublikasikan nilai Rpub. Tanpa Vpriv milik Alice, kita tidak dapat memastikan bahwa ini adalah alamat siluman, tetapi kemungkinan besar memang begitu. Kemudian, kita melihat transaksi lain yang mentransfer semua ETH dari alamat tersebut ke alamat dana kampanye Alice. Kita mungkin tidak dapat membuktikannya, tetapi kemungkinan besar Bill baru saja berdonasi ke kampanye Alice. Carol pasti akan berpikir demikian.
Sangat mudah bagi Bill untuk memisahkan publikasi Rpub dari pendanaan alamat siluman (melakukannya pada waktu yang berbeda, dari alamat yang berbeda). Namun, itu tidak cukup. Pola yang dicari Carol adalah Bill mendanai sebuah alamat, dan kemudian dana kampanye Alice menarik dana darinya.
Salah satu solusinya adalah kampanye Alice tidak menarik uang secara langsung, melainkan menggunakannya untuk membayar pihak ketiga. Jika kampanye Alice mengirimkan 10 ETH ke Layanan Kampanye Dominasi Dunia Dave, Carol hanya tahu bahwa Bill berdonasi ke salah satu pelanggan Dave. Jika Dave memiliki cukup banyak pelanggan, Carol tidak akan dapat mengetahui apakah Bill berdonasi kepada Alice yang bersaing dengannya, atau kepada Adam, Albert, atau Abigail yang tidak dipedulikan Carol. Alice dapat menyertakan nilai yang di-hash dengan pembayaran tersebut, dan kemudian memberikan prapeta (preimage) kepada Dave, untuk membuktikan bahwa itu adalah donasinya. Sebagai alternatif, seperti yang dicatat di atas, jika Alice memberikan Vpriv miliknya kepada Dave, dia sudah tahu dari mana pembayaran itu berasal.
Masalah utama dengan solusi ini adalah bahwa hal itu mewajibkan Alice untuk peduli tentang kerahasiaan ketika kerahasiaan itu menguntungkan Bill. Alice mungkin ingin mempertahankan reputasinya sehingga teman Bill, Bob, juga akan berdonasi kepadanya. Tetapi mungkin juga dia tidak keberatan mengekspos Bill, karena dengan begitu Bill akan takut dengan apa yang akan terjadi jika Carol menang. Bill mungkin pada akhirnya akan memberikan lebih banyak dukungan kepada Alice.
Menggunakan beberapa lapisan siluman
Alih-alih mengandalkan Alice untuk menjaga privasi Bill, Bill dapat melakukannya sendiri. Dia dapat menghasilkan beberapa alamat meta untuk orang fiktif, Bob dan Bella. Bill kemudian mengirimkan ETH ke Bob, dan "Bob" (yang sebenarnya adalah Bill) mengirimkannya ke Bella. "Bella" (juga Bill) mengirimkannya ke Alice.
Carol masih dapat melakukan analisis lalu lintas dan melihat jalur Bill-ke-Bob-ke-Bella-ke-Alice. Namun, jika "Bob" dan "Bella" juga menggunakan ETH untuk tujuan lain, tidak akan terlihat bahwa Bill mentransfer apa pun ke Alice, bahkan jika Alice segera menarik dana dari alamat siluman ke alamat kampanyenya yang diketahui.
Menulis aplikasi alamat siluman
Artikel ini menjelaskan aplikasi alamat siluman yang tersedia di GitHub (opens in a new tab).
Alat
Ada pustaka alamat siluman TypeScript (opens in a new tab) yang bisa kita gunakan. Namun, operasi kriptografi dapat memakan banyak CPU. Saya lebih suka mengimplementasikannya dalam bahasa yang dikompilasi, seperti Rust (opens in a new tab), dan menggunakan WASM (opens in a new tab) untuk menjalankan kode di peramban.
Kita akan menggunakan Vite (opens in a new tab) dan React (opens in a new tab). Ini adalah alat standar industri; jika Anda tidak familier dengannya, Anda dapat menggunakan tutorial ini. Untuk menggunakan Vite, kita memerlukan Node.
Melihat alamat siluman beraksi
-
Instal alat yang diperlukan: Rust (opens in a new tab) dan Node (opens in a new tab).
-
Klon repositori GitHub.
git clone https://github.com/qbzzt/251022-stealth-addresses.git cd 251022-stealth-addresses -
Instal prasyarat dan kompilasi kode Rust.
cd src/rust-wasm rustup target add wasm32-unknown-unknown cargo install wasm-pack wasm-pack build --target web -
Mulai server web.
cd ../.. npm install npm run dev -
Buka aplikasi (opens in a new tab). Halaman aplikasi ini memiliki dua bingkai: satu untuk antarmuka pengguna Alice dan yang lainnya untuk Bill. Kedua bingkai tersebut tidak berkomunikasi; keduanya hanya berada di halaman yang sama untuk kenyamanan.
-
Sebagai Alice, klik Generate a Stealth Meta-Address. Ini akan menampilkan alamat siluman baru dan kunci privat yang sesuai. Salin alamat meta siluman ke papan klip.
-
Sebagai Bill, tempel alamat meta siluman yang baru dan klik Generate an address. Ini memberi Anda alamat untuk didanai bagi Alice.
-
Salin alamat dan kunci publik Bill lalu tempelkan di area "Private key for address generated by Bill" pada antarmuka pengguna Alice. Setelah bidang tersebut diisi, Anda akan melihat kunci privat untuk mengakses aset di alamat tersebut.
-
Anda dapat menggunakan kalkulator daring (opens in a new tab) untuk memastikan kunci privat sesuai dengan alamat tersebut.
Bagaimana program ini bekerja
Komponen WASM
Kode sumber yang dikompilasi ke dalam WASM ditulis dalam Rust (opens in a new tab). Anda dapat melihatnya di src/rust_wasm/src/lib.rs (opens in a new tab). Kode ini pada dasarnya adalah antarmuka antara kode JavaScript dan pustaka eth-stealth-addresses (opens in a new tab).
Cargo.toml
Cargo.toml (opens in a new tab) di Rust analog dengan package.json (opens in a new tab) di JavaScript. Ini berisi informasi paket, deklarasi dependensi, dll.
[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"] }
Paket getrandom (opens in a new tab) perlu menghasilkan nilai acak. Hal itu tidak dapat dilakukan dengan cara algoritmik murni; ini memerlukan akses ke proses fisik sebagai sumber Entropi. Definisi ini menentukan bahwa kita akan mendapatkan Entropi tersebut dengan meminta peramban tempat kita menjalankannya.
console_error_panic_hook = "0.1.7"
Pustaka ini (opens in a new tab) memberi kita pesan kesalahan yang lebih bermakna ketika kode WASM mengalami panic dan tidak dapat dilanjutkan.
[lib]
crate-type = ["cdylib", "rlib"]
Jenis keluaran yang diperlukan untuk menghasilkan kode WASM.
lib.rs
Ini adalah kode Rust yang sebenarnya.
use wasm_bindgen::prelude::*;
Definisi untuk membuat paket WASM dari Rust. Semuanya didokumentasikan di sini (opens in a new tab).
use eth_stealth_addresses::{
generate_stealth_meta_address,
generate_stealth_address,
compute_stealth_key
};
Fungsi yang kita butuhkan dari pustaka eth-stealth-addresses (opens in a new tab).
use hex::{decode,encode};
Rust biasanya menggunakan larik (array) (opens in a new tab) bita ([u8; <size>]) untuk nilai. Tetapi di JavaScript, kita biasanya menggunakan string heksadesimal. Pustaka hex (opens in a new tab) menerjemahkannya untuk kita dari satu representasi ke representasi lainnya.
#[wasm_bindgen]
Hasilkan pengikatan (bindings) WASM agar dapat memanggil fungsi ini dari JavaScript.
pub fn wasm_generate_stealth_meta_address() -> String {
Cara termudah untuk mengembalikan objek dengan beberapa bidang adalah dengan mengembalikan string JSON.
let (address, spend_private_key, view_private_key) =
generate_stealth_meta_address();
generate_stealth_meta_address (opens in a new tab) mengembalikan tiga bidang:
- Alamat meta (Kpub dan Vpub)
- Kunci privat peninjauan (Vpriv)
- Kunci privat pembelanjaan (Kpriv)
Sintaks tupel (opens in a new tab) memungkinkan kita memisahkan nilai-nilai tersebut lagi.
format!("{{\"address\":\"{}\",\"view_private_key\":\"{}\",\"spend_private_key\":\"{}\"}}",
encode(address),
encode(view_private_key),
encode(spend_private_key)
)
}
Gunakan makro format! (opens in a new tab) untuk menghasilkan string yang dienkode JSON. Gunakan hex::encode (opens in a new tab) untuk mengubah larik menjadi string hex.
fn str_to_array<const N: usize>(s: &str) -> Option<[u8; N]> {
Fungsi ini mengubah string hex (yang disediakan oleh JavaScript) menjadi larik bita. Kita menggunakannya untuk mengurai nilai yang disediakan oleh kode JavaScript. Fungsi ini rumit karena cara Rust menangani larik dan vektor.
Ekspresi <const N: usize> disebut generik (opens in a new tab). N adalah parameter yang mengontrol panjang larik yang dikembalikan. Fungsi ini sebenarnya dipanggil str_to_array::<n>, di mana n adalah panjang larik.
Nilai kembaliannya adalah Option<[u8; N]>, yang berarti larik yang dikembalikan bersifat opsional (opens in a new tab). Ini adalah pola khas di Rust untuk fungsi yang mungkin gagal.
Misalnya, jika kita memanggil str_to_array::10("bad060a7"), fungsi tersebut seharusnya mengembalikan larik sepuluh nilai, tetapi masukannya hanya empat bita. Fungsi tersebut harus gagal, dan ia melakukannya dengan mengembalikan None. Nilai kembalian untuk str_to_array::4("bad060a7") akan menjadi Some<[0xba, 0xd0, 0x60, 0xa7]>.
// decode mengembalikan Result<Vec<u8>, _>
let vec = decode(s).ok()?;
Fungsi hex::decode (opens in a new tab) mengembalikan Result<Vec<u8>, FromHexError>. Tipe Result (opens in a new tab) dapat berisi hasil yang berhasil (Ok(value)) atau kesalahan (Err(error)).
Metode .ok() mengubah Result menjadi Option, yang nilainya adalah nilai Ok() jika berhasil atau None jika tidak. Terakhir, operator tanda tanya (opens in a new tab) membatalkan fungsi saat ini dan mengembalikan None jika Option kosong. Jika tidak, ia akan membuka (unwrap) nilai tersebut dan mengembalikannya (dalam hal ini, untuk menetapkan nilai ke vec).
Ini terlihat seperti metode yang sangat berbelit-belit untuk menangani kesalahan, tetapi Result dan Option memastikan bahwa semua kesalahan ditangani, dengan satu atau lain cara.
if vec.len() != N { return None; }
Jika jumlah bita tidak benar, itu adalah kegagalan, dan kita mengembalikan None.
// try_into mengonsumsi vec dan mencoba membuat [u8; N]
let array: [u8; N] = vec.try_into().ok()?;
Rust memiliki dua tipe larik. Larik (opens in a new tab) memiliki ukuran tetap. Vektor (opens in a new tab) dapat membesar dan mengecil. hex::decode mengembalikan vektor, tetapi pustaka eth_stealth_addresses ingin menerima larik. .try_into() (opens in a new tab) mengonversi nilai ke tipe lain, misalnya, vektor menjadi larik.
Some(array)
}
Rust tidak mewajibkan Anda untuk menggunakan kata kunci return (opens in a new tab) saat mengembalikan nilai di akhir fungsi.
#[wasm_bindgen]
pub fn wasm_generate_stealth_address(stealth_address: &str) -> Option<String> {
Fungsi ini menerima alamat meta publik, yang mencakup Vpub dan Kpub. Fungsi ini mengembalikan alamat siluman, kunci publik untuk dipublikasikan (Rpub), dan nilai pemindaian satu bita yang mempercepat identifikasi alamat mana yang dipublikasikan yang mungkin milik Alice.
Nilai pemindaian adalah bagian dari rahasia bersama (S = GRprivVpriv). Nilai ini tersedia untuk Alice, dan memeriksanya jauh lebih cepat daripada memeriksa apakah f(Kpub+G*hash(S)) sama dengan alamat yang dipublikasikan.
let (address, r_pub, scan) =
generate_stealth_address(&str_to_array::<66>(stealth_address)?);
Kita menggunakan generate_stealth_address (opens in a new tab) dari pustaka tersebut.
format!("{{\"address\":\"{}\",\"rPub\":\"{}\",\"scan\":\"{}\"}}",
encode(address),
encode(r_pub),
encode(&[scan])
).into()
}
Siapkan string keluaran yang dienkode JSON.
#[wasm_bindgen]
pub fn wasm_compute_stealth_key(
address: &str,
bill_pub_key: &str,
view_private_key: &str,
spend_private_key: &str
) -> Option<String> {
.
.
.
}
Fungsi ini menggunakan compute_stealth_key (opens in a new tab) dari pustaka untuk menghitung kunci privat guna menarik dana dari alamat tersebut (Rpriv). Perhitungan ini memerlukan nilai-nilai berikut:
- Alamat (Alamat=f(Ppub))
- Kunci publik yang dihasilkan oleh Bill (Rpub)
- Kunci privat peninjauan (Vpriv)
- Kunci privat pembelanjaan (Kpriv)
#[wasm_bindgen(start)]
#[wasm_bindgen(start)] (opens in a new tab) menentukan bahwa fungsi tersebut dieksekusi saat kode WASM diinisialisasi.
pub fn main() {
console_error_panic_hook::set_once();
}
Kode ini menentukan bahwa keluaran panic dikirim ke konsol JavaScript. Untuk melihatnya beraksi, gunakan aplikasi dan berikan Bill alamat meta yang tidak valid (cukup ubah satu digit heksadesimal). Anda akan melihat kesalahan ini di konsol 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
Diikuti oleh jejak tumpukan (stack trace). Kemudian berikan Bill alamat meta yang valid, dan berikan Alice alamat yang tidak valid atau kunci publik yang tidak valid. Anda akan melihat kesalahan ini:
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
Sekali lagi, diikuti oleh jejak tumpukan.
Antarmuka Pengguna
Antarmuka pengguna ditulis menggunakan React (opens in a new tab) dan disajikan oleh Vite (opens in a new tab). Anda dapat mempelajarinya menggunakan tutorial ini. Tidak perlu Wagmi (opens in a new tab) di sini karena kita tidak berinteraksi langsung dengan rantai blok atau dompet.
Satu-satunya bagian yang tidak jelas dari antarmuka pengguna adalah konektivitas WASM. Berikut adalah cara kerjanya.
vite.config.js
Berkas ini berisi konfigurasi 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()],
})
Kita memerlukan dua plugin Vite: react (opens in a new tab) dan wasm (opens in a new tab).
App.jsx
Berkas ini adalah komponen utama aplikasi. Ini adalah wadah yang mencakup dua komponen: Alice dan Bill, antarmuka pengguna untuk pengguna tersebut. Bagian yang relevan untuk WASM adalah kode inisialisasi.
import init from './rust-wasm/pkg/rust_wasm.js'
Saat kita menggunakan wasm-pack (opens in a new tab), ia membuat dua berkas yang kita gunakan di sini: berkas wasm dengan kode sebenarnya (di sini, src/rust-wasm/pkg/rust_wasm_bg.wasm) dan berkas JavaScript dengan definisi untuk menggunakannya (di sini, src/rust_wasm/pkg/rust_wasm.js). Ekspor bawaan dari berkas JavaScript tersebut adalah kode yang perlu dijalankan untuk memulai WASM.
function App() {
.
.
.
useEffect(() => {
const loadWasm = async () => {
try {
await init();
setWasmReady(true)
} catch (err) {
console.error('Error loading wasm:', err)
alert("Wasm error: " + err)
}
}
loadWasm()
}, []
)
Hook useEffect (opens in a new tab) memungkinkan Anda menentukan fungsi yang dieksekusi saat variabel state berubah. Di sini, daftar variabel state kosong ([]), jadi fungsi ini hanya dieksekusi sekali saat halaman dimuat.
Fungsi efek harus segera dikembalikan. Untuk menggunakan kode asinkron, seperti init WASM (yang harus memuat berkas .wasm dan karenanya membutuhkan waktu) kita mendefinisikan fungsi async (opens in a new tab) internal dan menjalankannya tanpa await.
Bill.jsx
Ini adalah antarmuka pengguna untuk Bill. Ini memiliki satu tindakan, membuat alamat berdasarkan alamat meta siluman yang disediakan oleh Alice.
import { wasm_generate_stealth_address } from './rust-wasm/pkg/rust_wasm.js'
Selain ekspor bawaan, kode JavaScript yang dihasilkan oleh wasm-pack mengekspor fungsi untuk setiap fungsi dalam kode WASM.
<button onClick={() => {
setPublicAddress(JSON.parse(wasm_generate_stealth_address(stealthMetaAddress)))
}}>
Untuk memanggil fungsi WASM, kita cukup memanggil fungsi yang diekspor oleh berkas JavaScript yang dibuat oleh wasm-pack.
Alice.jsx
Kode di Alice.jsx adalah analog, kecuali bahwa Alice memiliki dua tindakan:
- Menghasilkan alamat meta
- Mendapatkan kunci privat untuk alamat yang dipublikasikan oleh Bill
Kesimpulan
Alamat siluman bukanlah obat mujarab; alamat tersebut harus digunakan dengan benar. Namun jika digunakan dengan benar, alamat tersebut dapat mengaktifkan privasi di rantai blok publik.
Lihat di sini untuk karya saya yang lain (opens in a new tab).