Terima kasih khusus kepada Yoav Weiss, Dan Finlay, Martin Koppelmann, dan tim Arbitrum, Optimism, Polygon, Scroll, dan SoulWallet atas umpan balik dan tinjauannya.
Dalam postingan tentang Tiga Transisi ini, saya menguraikan beberapa alasan utama mengapa penting untuk mulai berpikir secara eksplisit tentang dukungan L1 + cross-L2, keamanan dompet, dan privasi sebagai fitur dasar yang diperlukan dari tumpukan ekosistem, daripada membangun masing-masing hal tersebut sebagai add-on yang dapat dirancang secara terpisah oleh masing-masing dompet.
Artikel ini akan lebih fokus pada aspek teknis dari satu sub-masalah spesifik: bagaimana cara mempermudah membaca L1 dari L2, L2 dari L1, atau L2 dari L2 lainnya. Memecahkan masalah ini sangat penting untuk mengimplementasikan arsitektur pemisahan aset / keystore, tetapi juga memiliki kasus penggunaan yang berharga di area lain, terutama mengoptimalkan panggilan lintas-L2 yang andal, termasuk kasus penggunaan seperti memindahkan aset antara L1 dan L2.
Setelah L2 menjadi lebih umum, pengguna akan memiliki aset di beberapa L2, dan mungkin juga L1. Ketika dompet kontrak pintar (multisig, pemulihan sosial, atau lainnya) menjadi arus utama, kunci yang dibutuhkan untuk mengakses beberapa akun akan berubah seiring waktu, dan kunci yang lama tidak lagi valid. Ketika kedua hal ini terjadi, seorang pengguna harus memiliki cara untuk mengubah kunci yang memiliki otoritas untuk mengakses banyak akun yang berada di berbagai tempat, tanpa melakukan transaksi dalam jumlah yang sangat besar.
Secara khusus, kita membutuhkan cara untuk menangani alamat-alamat kontrafaktual: alamat-alamat yang belum "terdaftar" dengan cara apa pun secara on-chain, tetapi tetap perlu menerima dan menyimpan dana dengan aman. Kita semua bergantung pada alamat kontrafaktual: ketika Anda menggunakan Ethereum untuk pertama kalinya, Anda dapat membuat alamat ETH yang dapat digunakan seseorang untuk membayar Anda, tanpa "mendaftarkan" alamat tersebut secara on-chain (yang akan membutuhkan pembayaran txfee, dan dengan demikian Anda telah memiliki sejumlah ETH).
Dengan EOA, semua alamat dimulai sebagai alamat kontrafaktual. Dengan dompet smart contract, alamat kontrafaktual masih dimungkinkan, sebagian besar berkat CREATE2, yang memungkinkan Anda untuk memiliki alamat ETH yang hanya dapat diisi oleh smart contract yang memiliki kode yang cocok dengan hash tertentu.
Algoritme penghitungan alamat EIP-1014 (CREATE2).
Akan tetapi, dompet kontrak pintar memperkenalkan sebuah tantangan baru: kemungkinan kunci akses berubah. Alamat, yang merupakan hash dari initcode, hanya dapat berisi kunci verifikasi awal dompet. Kunci verifikasi saat ini akan disimpan di dalam penyimpanan dompet, tetapi catatan penyimpanan tersebut tidak secara ajaib menyebar ke L2 lainnya.
Jika seorang pengguna memiliki banyak alamat di banyak L2, termasuk alamat yang (karena bersifat kontrafaktual) tidak diketahui oleh L2 tempat ia berada, maka sepertinya hanya ada satu cara untuk mengizinkan pengguna mengganti kunci mereka: arsitektur pemisahan aset / keystore. Setiap pengguna memiliki (i) "kontrak keystore" (pada L1 atau pada satu L2 tertentu), yang menyimpan kunci verifikasi untuk semua dompet beserta aturan untuk mengubah kunci, dan (ii) "kontrak dompet" pada L1 dan banyak L2, yang membaca rantai silang untuk mendapatkan kunci verifikasi.
Ada dua cara untuk mengimplementasikan hal ini:
Untuk menunjukkan kompleksitas penuh, kita akan mengeksplorasi kasus yang paling sulit: di mana keystore berada di satu L2, dan dompet berada di L2 yang berbeda. Jika keystore atau wallet berada di L1, maka hanya setengah dari desain ini yang diperlukan.
Mari kita asumsikan bahwa keystore ada di Linea, dan dompet ada di Kakarot. Bukti lengkap dari kunci-kunci dompet terdiri dari:
Ada dua pertanyaan implementasi utama yang rumit di sini:
Ada lima opsi utama:
Dalam hal pekerjaan infrastruktur yang diperlukan dan biaya bagi pengguna, saya mengurutkannya secara kasar sebagai berikut:
"Agregasi" mengacu pada ide untuk menggabungkan semua bukti yang diberikan oleh pengguna di dalam setiap blok ke dalam sebuah meta-bukti besar yang menggabungkan semuanya. Hal ini memungkinkan untuk SNARK, dan untuk KZG, tetapi tidak untuk cabang Merkle (anda dapat menggabungkan cabang Merkle sedikit, tetapi hanya menghemat log(txs per blok)/log(jumlah total keystore), mungkin 15-30% pada praktiknya, jadi mungkin tidak sebanding dengan biayanya).
Agregasi hanya menjadi layak setelah skema ini memiliki sejumlah besar pengguna, jadi secara realistis tidak masalah bagi implementasi versi-1 untuk tidak menggunakan agregasi, dan mengimplementasikannya untuk versi 2.
Yang ini sederhana: ikuti diagram di bagian sebelumnya secara langsung. Lebih tepatnya, setiap "bukti" (dengan asumsi kasus tingkat kesulitan maksimum untuk membuktikan satu L2 menjadi L2 lainnya) akan mengandung:
Sayangnya, pembuktian status Ethereum cukup rumit, tetapi ada library untuk memverifikasinya, dan jika Anda menggunakan library ini, mekanisme ini tidak terlalu rumit untuk diterapkan.
Masalah yang lebih besar adalah biaya. Bukti Merkle sangat panjang, dan pohon Patricia sayangnya ~3.9x lebih panjang dari yang seharusnya (tepatnya: sebuah bukti Merkle yang ideal untuk sebuah pohon yang menyimpan N objek memiliki panjang 32 log2(N) byte, dan karena pohon Patricia milik Ethereum memiliki 16 daun per anak, maka bukti untuk pohon-pohon tersebut memiliki panjang 32 15 log16(N) ~= 125 log2(N) byte). Dalam sebuah negara dengan sekitar 250 juta (~2²⁸) akun, ini membuat setiap bukti menjadi 125 * 28 = 3500 byte, atau sekitar 56.000 gas, ditambah biaya tambahan untuk memecahkan kode dan memverifikasi hash.
Dua bukti yang digabungkan akan menghabiskan biaya sekitar 100.000 hingga 150.000 gas (tidak termasuk verifikasi tanda tangan jika digunakan per transaksi) - jauh lebih mahal daripada biaya dasar 21.000 gas per transaksi. Namun, perbedaannya akan semakin parah jika bukti tersebut diverifikasi pada L2. Komputasi di dalam L2 tidak mahal, karena komputasi dilakukan secara off-chain dan di dalam ekosistem dengan jumlah node yang jauh lebih sedikit daripada L1. Data, di sisi lain, harus dikirim ke L1. Oleh karena itu, perbandingannya bukan 21.000 gas vs 150.000 gas; melainkan 21.000 L2 gas vs 100.000 L1 gas.
Kita dapat menghitung apa artinya ini dengan melihat perbandingan antara biaya gas L1 dan biaya gas L2:
L1 saat ini sekitar 15-25x lebih mahal daripada L2 untuk pengiriman sederhana, dan 20-50x lebih mahal untuk pertukaran token. Pengiriman sederhana relatif berat secara data, tetapi pertukaran jauh lebih berat secara komputasi. Oleh karena itu, swap adalah tolok ukur yang lebih baik untuk memperkirakan biaya komputasi L1 vs komputasi L2. Dengan mempertimbangkan semua ini, jika kita mengasumsikan rasio biaya 30x antara biaya komputasi L1 dan biaya komputasi L2, hal ini mengimplikasikan bahwa menempatkan bukti Merkle pada L2 akan memakan biaya yang setara dengan lima puluh transaksi biasa.
Tentu saja, menggunakan pohon Merkle biner dapat memangkas biaya hingga ~4x lipat, tetapi tetap saja, biayanya dalam banyak kasus akan terlalu tinggi - dan jika kita bersedia berkorban untuk tidak lagi kompatibel dengan pohon status heksar Ethereum saat ini, sebaiknya kita mencari opsi yang lebih baik lagi.
Secara konseptual, penggunaan ZK-SNARK juga mudah dimengerti: Anda cukup mengganti bukti Merkle pada diagram di atas dengan ZK-SNARK yang membuktikan bahwa bukti Merkle tersebut ada. Sebuah ZK-SNARK membutuhkan ~400.000 gas untuk komputasi, dan sekitar 400 byte (bandingkan: 21.000 gas dan 100 byte untuk transaksi dasar, di masa depan dapat dikurangi menjadi ~25 byte dengan kompresi). Oleh karena itu, dari perspektif komputasi, ZK-SNARK berharga 19x lipat dari biaya transaksi dasar hari ini, dan dari perspektif data, ZK-SNARK berharga 4x lipat dari transaksi dasar hari ini, dan 16x lipat dari harga transaksi dasar di masa depan.
Angka-angka ini merupakan peningkatan besar atas bukti Merkle, tetapi masih cukup mahal. Ada dua cara untuk meningkatkan hal ini: (i) pembuktian KZG dengan tujuan khusus, atau (ii) agregasi, mirip dengan agregasi ERC-4337 tetapi menggunakan matematika yang lebih rumit. Kita bisa melihat keduanya.
Peringatan, bagian ini jauh lebih rumit daripada bagian lainnya. Hal ini karena kami melampaui alat serba guna dan membangun sesuatu yang memiliki tujuan khusus agar lebih murah, jadi kami harus lebih banyak "bekerja di bawah tenda". Jika Anda tidak menyukai matematika yang rumit, langsung saja ke bagian berikutnya.
Pertama, rekapitulasi cara kerja komitmen KZG:
Beberapa properti utama yang penting untuk dipahami adalah:
Oleh karena itu, kami memiliki struktur di mana kami dapat terus menambahkan nilai pada akhir daftar yang terus bertambah, meskipun dengan batas ukuran tertentu (secara realistis, ratusan juta dapat dilakukan). Kami kemudian menggunakannya sebagai struktur data kami untuk mengelola (i) komitmen terhadap daftar kunci pada setiap L2, yang disimpan di L2 tersebut dan dicerminkan ke L1, dan (ii) komitmen terhadap daftar komitmen kunci L2, yang disimpan di L1 Ethereum dan dicerminkan ke setiap L2.
Menjaga komitmen tetap diperbarui dapat menjadi bagian dari logika inti L2, atau dapat diimplementasikan tanpa perubahan protokol inti L2 melalui jembatan setor dan tarik.
Oleh karena itu, diperlukan bukti yang lengkap:
Sebenarnya memungkinkan untuk menggabungkan dua bukti KZG menjadi satu, sehingga kita mendapatkan ukuran total hanya 100 byte.
Perhatikan satu hal yang perlu diperhatikan: karena daftar kunci adalah sebuah daftar, dan bukan peta kunci/nilai seperti state, maka daftar kunci harus menetapkan posisi secara berurutan. Kontrak komitmen kunci akan berisi registri internalnya sendiri yang memetakan setiap keystore ke sebuah ID, dan untuk setiap kunci, ia akan menyimpan hash (kunci, alamat keystore), bukan hanya kunci, untuk secara jelas mengkomunikasikan ke L2 lain keystore mana yang sedang dibicarakan oleh sebuah entri.
Kelebihan dari teknik ini adalah, bahwa teknik ini bekerja dengan sangat baik pada L2. Data berukuran 100 byte, ~4x lebih pendek daripada ZK-SNARK dan jauh lebih pendek daripada Merkle proof. Biaya komputasi sebagian besar adalah satu pemeriksaan pasangan ukuran-2, atau sekitar 119.000 gas. Pada L1, data tidak terlalu penting dibandingkan dengan komputasi, dan sayangnya KZG sedikit lebih mahal dibandingkan dengan bukti Merkle.
Pohon Verkle pada dasarnya melibatkan penumpukan komitmen KZG (atau komitmen IPA, yang dapat lebih efisien dan menggunakan kriptografi yang lebih sederhana) di atas satu sama lain: untuk menyimpan nilai 2⁴⁸, Anda dapat membuat komitmen KZG ke daftar nilai 2²⁴, yang masing-masingnya merupakan komitmen KZG untuk nilai 2²⁴. Pohon verkle sedang <a href="https://notes.ethereum.org/@vbuterin/verkle_tree_eip"> sangat dipertimbangkan untuk pohon negara Ethereum, karena pohon Verkle dapat digunakan untuk menyimpan peta nilai-kunci dan bukan hanya daftar (pada dasarnya, Anda dapat membuat pohon ukuran-2²⁵⁶ tetapi memulainya dengan kosong, hanya mengisi bagian-bagian tertentu dari pohon setelah Anda benar-benar perlu mengisinya).
Seperti apa rupa pohon Verkle. Dalam praktiknya, Anda dapat memberikan setiap simpul dengan lebar 256 == 2⁸ untuk pohon berbasis IPA, atau 2²⁴ untuk pohon berbasis KZG.
Bukti-bukti dalam pohon Verkle agak lebih panjang daripada KZG; panjangnya mungkin beberapa ratus byte. Mereka juga sulit untuk diverifikasi, terutama jika Anda mencoba menggabungkan banyak bukti menjadi satu.
Secara realistis, pohon Verkle harus dianggap seperti pohon Merkle, tetapi lebih layak tanpa SNARKing (karena biaya data yang lebih rendah), dan lebih murah dengan SNARKing (karena biaya prover yang lebih rendah).
Keuntungan terbesar dari pohon Verkle adalah kemungkinan untuk menyelaraskan struktur data: Bukti Verkle dapat digunakan secara langsung di atas status L1 atau L2, tanpa struktur overlay, dan menggunakan mekanisme yang sama persis untuk L1 dan L2. Ketika komputer kuantum menjadi sebuah masalah, atau ketika pembuktian cabang-cabang Merkle menjadi cukup efisien, pohon Verkle dapat digantikan dengan pohon hash biner dengan fungsi hash yang sesuai dengan SNARK.
Jika N pengguna membuat N transaksi (atau lebih realistisnya, N ERC-4337 UserOperations) yang perlu membuktikan N klaim cross-chain, kita dapat menghemat banyak gas dengan menggabungkan bukti-bukti tersebut: pembangun yang akan menggabungkan transaksi-transaksi tersebut ke dalam sebuah blok atau bundel yang masuk ke dalam sebuah blok dapat membuat satu bukti yang membuktikan semua klaim secara bersamaan.
Ini bisa berarti:
Dalam ketiga kasus tersebut, biaya pembuktiannya hanya beberapa ratus ribu saja. Pembangun perlu membuat salah satu dari ini di setiap L2 untuk pengguna di L2 tersebut; oleh karena itu, agar ini berguna untuk dibangun, skema ini secara keseluruhan harus memiliki penggunaan yang cukup sehingga sering kali setidaknya ada beberapa transaksi dalam blok yang sama di beberapa L2 utama.
Jika ZK-SNARK digunakan, biaya marjinal utama hanyalah "logika bisnis" untuk mengoper angka di antara kontrak, jadi mungkin hanya beberapa ribu gas L2 per pengguna. Jika multi-bukti KZG digunakan, prover perlu menambahkan 48 gas untuk setiap L2 yang memegang keystore yang digunakan di dalam blok tersebut, sehingga biaya marjinal dari skema per pengguna akan menambahkan ~800 gas L1 per L2 (bukan per pengguna) di atasnya. Tetapi biaya ini jauh lebih rendah daripada biaya untuk tidak melakukan agregasi, yang pasti melibatkan lebih dari 10.000 gas L1 dan ratusan ribu gas L2 per pengguna. Untuk pohon Verkle, Anda dapat menggunakan Verkle multi-proof secara langsung, menambahkan sekitar 100-200 byte per pengguna, atau Anda dapat membuat ZK-SNARK dari Verkle multi-proof, yang memiliki biaya yang sama dengan ZK-SNARK cabang Merkle tetapi jauh lebih murah untuk dibuktikan.
Dari perspektif implementasi, mungkin yang terbaik adalah bundler mengumpulkan bukti lintas rantai melalui standar abstraksi akun ERC-4337. ERC-4337 sudah memiliki mekanisme bagi pembangun untuk mengumpulkan bagian-bagian UserOperations dengan cara khusus. Bahkan ada <a href="https://hackmd.io/@voltrevo/BJ0QBy3zi"> implementasi ini untuk agregasi tanda tangan BLS, yang dapat mengurangi biaya gas pada L2 sebesar 1,5x hingga 3x tergantung pada bentuk kompresi lain yang disertakan.
Diagram dari <a href="https://hackmd.io/@voltrevo/BJ0QBy3zi"> pos implementasi dompet BLS yang menunjukkan alur kerja tanda tangan agregat BLS dalam versi ERC-4337 sebelumnya. Alur kerja untuk mengumpulkan bukti lintas rantai kemungkinan akan terlihat sangat mirip.
Kemungkinan terakhir, dan yang hanya dapat digunakan untuk L2 yang membaca L1 (dan bukan L1 yang membaca L2), adalah memodifikasi L2 agar dapat melakukan panggilan statis ke kontrak pada L1 secara langsung.
Hal ini dapat dilakukan dengan sebuah opcode atau precompile, yang memungkinkan pemanggilan ke L1 di mana Anda memberikan alamat tujuan, gas dan calldata, dan mengembalikan output, meskipun karena pemanggilan ini adalah pemanggilan statis, mereka tidak dapat benar-benar mengubah status L1. L2 harus sudah mengetahui L1 untuk memproses deposit, jadi tidak ada hal mendasar yang menghentikan hal tersebut untuk diimplementasikan; ini terutama merupakan tantangan implementasi teknis (lihat: RFP ini dari Optimism untuk mendukung panggilan statis ke L1).
Perhatikan bahwa jika keystore berada di L1, dan L2 mengintegrasikan fungsionalitas static-call L1, maka tidak ada bukti yang diperlukan sama sekali! Namun, jika L2 tidak mengintegrasikan panggilan statis L1, atau jika keystore berada di L2 (yang pada akhirnya mungkin harus demikian, ketika L1 menjadi terlalu mahal untuk digunakan oleh pengguna meskipun hanya sedikit), maka pembuktian akan diperlukan.
Semua skema di atas membutuhkan L2 untuk mengakses baik root status L1 terbaru, atau seluruh status L1 terbaru. Untungnya, semua L2 sudah memiliki beberapa fungsi untuk mengakses status L1 terkini. Hal ini karena mereka membutuhkan fungsi seperti itu untuk memproses pesan yang masuk dari L1 ke L2, terutama deposito.
Dan memang, jika L2 memiliki fitur setoran, maka Anda dapat menggunakan L2 tersebut sebagaimana adanya untuk memindahkan akar status L1 ke dalam kontrak di L2: cukup dengan membuat kontrak di L1 yang memanggil opcode BLOCKHASH, dan meneruskannya ke L2 sebagai pesan setoran. Header blok penuh dapat diterima, dan akar statusnya diekstraksi, pada sisi L2. Namun, akan jauh lebih baik jika setiap L2 memiliki cara eksplisit untuk mengakses status L1 terkini, atau akar status L1 terkini, secara langsung.
Tantangan utama dalam mengoptimalkan cara L2 menerima akar status L1 terbaru adalah secara bersamaan mencapai keamanan dan latensi yang rendah:
Selain itu, pada arah yang berlawanan (L1 membaca L2):
Beberapa dari kecepatan ini untuk operasi lintas rantai yang tidak dapat dipercaya ini sangat lambat untuk banyak kasus penggunaan defi; untuk kasus-kasus tersebut, Anda membutuhkan jembatan yang lebih cepat dengan model keamanan yang lebih tidak sempurna. Namun, untuk kasus penggunaan memperbarui kunci dompet, penundaan yang lebih lama lebih dapat diterima: Anda tidak menunda transaksi dalam hitungan jam, Anda hanya menunda perubahan kunci. Anda hanya perlu menyimpan kunci lama lebih lama. Jika Anda mengganti kunci karena kuncinya dicuri, maka Anda memiliki periode kerentanan yang signifikan, tetapi hal ini dapat dikurangi, misalnya dengan dompet yang memiliki fungsi pembekuan.
Pada akhirnya, solusi meminimalkan latensi terbaik adalah agar L2 mengimplementasikan pembacaan langsung akar status L1 dengan cara yang optimal, di mana setiap blok L2 (atau log komputasi akar status) berisi penunjuk ke blok L1 yang terbaru, sehingga jika L1 mundur, L2 juga dapat mundur. Kontrak keystore harus ditempatkan di mainnet, atau di L2 yang merupakan ZK-rollup sehingga dapat dengan cepat melakukan komit ke L1.
Blok-blok rantai L2 dapat memiliki ketergantungan tidak hanya pada blok L2 sebelumnya, tetapi juga pada blok L1. Jika L1 kembali melewati tautan tersebut, L2 juga akan kembali. Perlu dicatat bahwa ini juga merupakan cara kerja sharding versi sebelumnya (sebelum Dank); lihat di sini untuk kode.
Yang mengejutkan, tidak sebanyak itu. Sebenarnya tidak perlu berupa rollup: jika itu adalah L3, atau validium, maka tidak masalah untuk menyimpan dompet di sana, selama Anda menyimpan keystore di L1 atau di rollup ZK. Hal yang Anda perlukan adalah rantai memiliki akses langsung ke akar negara Ethereum, dan komitmen teknis dan sosial untuk bersedia melakukan reorganisasi jika Ethereum melakukan reorganisasi, dan melakukan hard fork jika Ethereum melakukan hard fork.
Salah satu masalah penelitian yang menarik adalah mengidentifikasi sejauh mana sebuah rantai dapat memiliki bentuk koneksi ke beberapa rantai lainnya (misalnya. Ethereum dan Zcash). Melakukannya secara naif mungkin saja dilakukan: chain Anda dapat setuju untuk melakukan reorganisasi jika Ethereum atau Zcash melakukan reorganisasi (dan melakukan hard fork jika Ethereum atau Zcash melakukan hard fork), tetapi kemudian operator node dan komunitas Anda secara umum memiliki ketergantungan teknis dan politis yang berlipat ganda. Oleh karena itu, teknik seperti itu dapat digunakan untuk menghubungkan ke beberapa rantai lain, tetapi dengan biaya yang lebih tinggi. Skema yang didasarkan pada jembatan ZK memiliki sifat teknis yang menarik, tetapi memiliki kelemahan utama yaitu tidak kuat terhadap serangan 51% atau hard fork. Mungkin ada solusi yang lebih cerdas.
Idealnya, kami juga ingin menjaga privasi. Jika Anda memiliki banyak dompet yang dikelola oleh keystore yang sama, maka kami ingin memastikannya:
Hal ini menimbulkan beberapa masalah:
Dengan SNARK, solusinya secara konseptual mudah: bukti-bukti menyembunyikan informasi secara default, dan agregator perlu membuat SNARK rekursif untuk membuktikan SNARK.
Tantangan utama dari pendekatan ini saat ini adalah bahwa agregasi mengharuskan agregator untuk membuat SNARK rekursif, yang saat ini cukup lambat.
Dengan KZG, kita dapat menggunakan <a href="https://notes.ethereum.org/@vbuterin/non_index_revealing_proof"> ini bekerja pada bukti-bukti KZG yang tidak mengungkapkan indeks (lihat juga: versi yang lebih formal dari pekerjaan tersebut dalam makalah Caulk) sebagai titik awal. Agregasi bukti-bukti yang dibutakan, bagaimanapun juga, merupakan masalah terbuka yang membutuhkan lebih banyak perhatian.
Sayangnya, pembacaan L1 secara langsung dari dalam L2 tidak menjaga privasi, meskipun mengimplementasikan fungsionalitas pembacaan langsung masih sangat berguna, baik untuk meminimalkan latensi maupun karena kegunaannya untuk aplikasi lain.
مشاركة
المحتوى
Terima kasih khusus kepada Yoav Weiss, Dan Finlay, Martin Koppelmann, dan tim Arbitrum, Optimism, Polygon, Scroll, dan SoulWallet atas umpan balik dan tinjauannya.
Dalam postingan tentang Tiga Transisi ini, saya menguraikan beberapa alasan utama mengapa penting untuk mulai berpikir secara eksplisit tentang dukungan L1 + cross-L2, keamanan dompet, dan privasi sebagai fitur dasar yang diperlukan dari tumpukan ekosistem, daripada membangun masing-masing hal tersebut sebagai add-on yang dapat dirancang secara terpisah oleh masing-masing dompet.
Artikel ini akan lebih fokus pada aspek teknis dari satu sub-masalah spesifik: bagaimana cara mempermudah membaca L1 dari L2, L2 dari L1, atau L2 dari L2 lainnya. Memecahkan masalah ini sangat penting untuk mengimplementasikan arsitektur pemisahan aset / keystore, tetapi juga memiliki kasus penggunaan yang berharga di area lain, terutama mengoptimalkan panggilan lintas-L2 yang andal, termasuk kasus penggunaan seperti memindahkan aset antara L1 dan L2.
Setelah L2 menjadi lebih umum, pengguna akan memiliki aset di beberapa L2, dan mungkin juga L1. Ketika dompet kontrak pintar (multisig, pemulihan sosial, atau lainnya) menjadi arus utama, kunci yang dibutuhkan untuk mengakses beberapa akun akan berubah seiring waktu, dan kunci yang lama tidak lagi valid. Ketika kedua hal ini terjadi, seorang pengguna harus memiliki cara untuk mengubah kunci yang memiliki otoritas untuk mengakses banyak akun yang berada di berbagai tempat, tanpa melakukan transaksi dalam jumlah yang sangat besar.
Secara khusus, kita membutuhkan cara untuk menangani alamat-alamat kontrafaktual: alamat-alamat yang belum "terdaftar" dengan cara apa pun secara on-chain, tetapi tetap perlu menerima dan menyimpan dana dengan aman. Kita semua bergantung pada alamat kontrafaktual: ketika Anda menggunakan Ethereum untuk pertama kalinya, Anda dapat membuat alamat ETH yang dapat digunakan seseorang untuk membayar Anda, tanpa "mendaftarkan" alamat tersebut secara on-chain (yang akan membutuhkan pembayaran txfee, dan dengan demikian Anda telah memiliki sejumlah ETH).
Dengan EOA, semua alamat dimulai sebagai alamat kontrafaktual. Dengan dompet smart contract, alamat kontrafaktual masih dimungkinkan, sebagian besar berkat CREATE2, yang memungkinkan Anda untuk memiliki alamat ETH yang hanya dapat diisi oleh smart contract yang memiliki kode yang cocok dengan hash tertentu.
Algoritme penghitungan alamat EIP-1014 (CREATE2).
Akan tetapi, dompet kontrak pintar memperkenalkan sebuah tantangan baru: kemungkinan kunci akses berubah. Alamat, yang merupakan hash dari initcode, hanya dapat berisi kunci verifikasi awal dompet. Kunci verifikasi saat ini akan disimpan di dalam penyimpanan dompet, tetapi catatan penyimpanan tersebut tidak secara ajaib menyebar ke L2 lainnya.
Jika seorang pengguna memiliki banyak alamat di banyak L2, termasuk alamat yang (karena bersifat kontrafaktual) tidak diketahui oleh L2 tempat ia berada, maka sepertinya hanya ada satu cara untuk mengizinkan pengguna mengganti kunci mereka: arsitektur pemisahan aset / keystore. Setiap pengguna memiliki (i) "kontrak keystore" (pada L1 atau pada satu L2 tertentu), yang menyimpan kunci verifikasi untuk semua dompet beserta aturan untuk mengubah kunci, dan (ii) "kontrak dompet" pada L1 dan banyak L2, yang membaca rantai silang untuk mendapatkan kunci verifikasi.
Ada dua cara untuk mengimplementasikan hal ini:
Untuk menunjukkan kompleksitas penuh, kita akan mengeksplorasi kasus yang paling sulit: di mana keystore berada di satu L2, dan dompet berada di L2 yang berbeda. Jika keystore atau wallet berada di L1, maka hanya setengah dari desain ini yang diperlukan.
Mari kita asumsikan bahwa keystore ada di Linea, dan dompet ada di Kakarot. Bukti lengkap dari kunci-kunci dompet terdiri dari:
Ada dua pertanyaan implementasi utama yang rumit di sini:
Ada lima opsi utama:
Dalam hal pekerjaan infrastruktur yang diperlukan dan biaya bagi pengguna, saya mengurutkannya secara kasar sebagai berikut:
"Agregasi" mengacu pada ide untuk menggabungkan semua bukti yang diberikan oleh pengguna di dalam setiap blok ke dalam sebuah meta-bukti besar yang menggabungkan semuanya. Hal ini memungkinkan untuk SNARK, dan untuk KZG, tetapi tidak untuk cabang Merkle (anda dapat menggabungkan cabang Merkle sedikit, tetapi hanya menghemat log(txs per blok)/log(jumlah total keystore), mungkin 15-30% pada praktiknya, jadi mungkin tidak sebanding dengan biayanya).
Agregasi hanya menjadi layak setelah skema ini memiliki sejumlah besar pengguna, jadi secara realistis tidak masalah bagi implementasi versi-1 untuk tidak menggunakan agregasi, dan mengimplementasikannya untuk versi 2.
Yang ini sederhana: ikuti diagram di bagian sebelumnya secara langsung. Lebih tepatnya, setiap "bukti" (dengan asumsi kasus tingkat kesulitan maksimum untuk membuktikan satu L2 menjadi L2 lainnya) akan mengandung:
Sayangnya, pembuktian status Ethereum cukup rumit, tetapi ada library untuk memverifikasinya, dan jika Anda menggunakan library ini, mekanisme ini tidak terlalu rumit untuk diterapkan.
Masalah yang lebih besar adalah biaya. Bukti Merkle sangat panjang, dan pohon Patricia sayangnya ~3.9x lebih panjang dari yang seharusnya (tepatnya: sebuah bukti Merkle yang ideal untuk sebuah pohon yang menyimpan N objek memiliki panjang 32 log2(N) byte, dan karena pohon Patricia milik Ethereum memiliki 16 daun per anak, maka bukti untuk pohon-pohon tersebut memiliki panjang 32 15 log16(N) ~= 125 log2(N) byte). Dalam sebuah negara dengan sekitar 250 juta (~2²⁸) akun, ini membuat setiap bukti menjadi 125 * 28 = 3500 byte, atau sekitar 56.000 gas, ditambah biaya tambahan untuk memecahkan kode dan memverifikasi hash.
Dua bukti yang digabungkan akan menghabiskan biaya sekitar 100.000 hingga 150.000 gas (tidak termasuk verifikasi tanda tangan jika digunakan per transaksi) - jauh lebih mahal daripada biaya dasar 21.000 gas per transaksi. Namun, perbedaannya akan semakin parah jika bukti tersebut diverifikasi pada L2. Komputasi di dalam L2 tidak mahal, karena komputasi dilakukan secara off-chain dan di dalam ekosistem dengan jumlah node yang jauh lebih sedikit daripada L1. Data, di sisi lain, harus dikirim ke L1. Oleh karena itu, perbandingannya bukan 21.000 gas vs 150.000 gas; melainkan 21.000 L2 gas vs 100.000 L1 gas.
Kita dapat menghitung apa artinya ini dengan melihat perbandingan antara biaya gas L1 dan biaya gas L2:
L1 saat ini sekitar 15-25x lebih mahal daripada L2 untuk pengiriman sederhana, dan 20-50x lebih mahal untuk pertukaran token. Pengiriman sederhana relatif berat secara data, tetapi pertukaran jauh lebih berat secara komputasi. Oleh karena itu, swap adalah tolok ukur yang lebih baik untuk memperkirakan biaya komputasi L1 vs komputasi L2. Dengan mempertimbangkan semua ini, jika kita mengasumsikan rasio biaya 30x antara biaya komputasi L1 dan biaya komputasi L2, hal ini mengimplikasikan bahwa menempatkan bukti Merkle pada L2 akan memakan biaya yang setara dengan lima puluh transaksi biasa.
Tentu saja, menggunakan pohon Merkle biner dapat memangkas biaya hingga ~4x lipat, tetapi tetap saja, biayanya dalam banyak kasus akan terlalu tinggi - dan jika kita bersedia berkorban untuk tidak lagi kompatibel dengan pohon status heksar Ethereum saat ini, sebaiknya kita mencari opsi yang lebih baik lagi.
Secara konseptual, penggunaan ZK-SNARK juga mudah dimengerti: Anda cukup mengganti bukti Merkle pada diagram di atas dengan ZK-SNARK yang membuktikan bahwa bukti Merkle tersebut ada. Sebuah ZK-SNARK membutuhkan ~400.000 gas untuk komputasi, dan sekitar 400 byte (bandingkan: 21.000 gas dan 100 byte untuk transaksi dasar, di masa depan dapat dikurangi menjadi ~25 byte dengan kompresi). Oleh karena itu, dari perspektif komputasi, ZK-SNARK berharga 19x lipat dari biaya transaksi dasar hari ini, dan dari perspektif data, ZK-SNARK berharga 4x lipat dari transaksi dasar hari ini, dan 16x lipat dari harga transaksi dasar di masa depan.
Angka-angka ini merupakan peningkatan besar atas bukti Merkle, tetapi masih cukup mahal. Ada dua cara untuk meningkatkan hal ini: (i) pembuktian KZG dengan tujuan khusus, atau (ii) agregasi, mirip dengan agregasi ERC-4337 tetapi menggunakan matematika yang lebih rumit. Kita bisa melihat keduanya.
Peringatan, bagian ini jauh lebih rumit daripada bagian lainnya. Hal ini karena kami melampaui alat serba guna dan membangun sesuatu yang memiliki tujuan khusus agar lebih murah, jadi kami harus lebih banyak "bekerja di bawah tenda". Jika Anda tidak menyukai matematika yang rumit, langsung saja ke bagian berikutnya.
Pertama, rekapitulasi cara kerja komitmen KZG:
Beberapa properti utama yang penting untuk dipahami adalah:
Oleh karena itu, kami memiliki struktur di mana kami dapat terus menambahkan nilai pada akhir daftar yang terus bertambah, meskipun dengan batas ukuran tertentu (secara realistis, ratusan juta dapat dilakukan). Kami kemudian menggunakannya sebagai struktur data kami untuk mengelola (i) komitmen terhadap daftar kunci pada setiap L2, yang disimpan di L2 tersebut dan dicerminkan ke L1, dan (ii) komitmen terhadap daftar komitmen kunci L2, yang disimpan di L1 Ethereum dan dicerminkan ke setiap L2.
Menjaga komitmen tetap diperbarui dapat menjadi bagian dari logika inti L2, atau dapat diimplementasikan tanpa perubahan protokol inti L2 melalui jembatan setor dan tarik.
Oleh karena itu, diperlukan bukti yang lengkap:
Sebenarnya memungkinkan untuk menggabungkan dua bukti KZG menjadi satu, sehingga kita mendapatkan ukuran total hanya 100 byte.
Perhatikan satu hal yang perlu diperhatikan: karena daftar kunci adalah sebuah daftar, dan bukan peta kunci/nilai seperti state, maka daftar kunci harus menetapkan posisi secara berurutan. Kontrak komitmen kunci akan berisi registri internalnya sendiri yang memetakan setiap keystore ke sebuah ID, dan untuk setiap kunci, ia akan menyimpan hash (kunci, alamat keystore), bukan hanya kunci, untuk secara jelas mengkomunikasikan ke L2 lain keystore mana yang sedang dibicarakan oleh sebuah entri.
Kelebihan dari teknik ini adalah, bahwa teknik ini bekerja dengan sangat baik pada L2. Data berukuran 100 byte, ~4x lebih pendek daripada ZK-SNARK dan jauh lebih pendek daripada Merkle proof. Biaya komputasi sebagian besar adalah satu pemeriksaan pasangan ukuran-2, atau sekitar 119.000 gas. Pada L1, data tidak terlalu penting dibandingkan dengan komputasi, dan sayangnya KZG sedikit lebih mahal dibandingkan dengan bukti Merkle.
Pohon Verkle pada dasarnya melibatkan penumpukan komitmen KZG (atau komitmen IPA, yang dapat lebih efisien dan menggunakan kriptografi yang lebih sederhana) di atas satu sama lain: untuk menyimpan nilai 2⁴⁸, Anda dapat membuat komitmen KZG ke daftar nilai 2²⁴, yang masing-masingnya merupakan komitmen KZG untuk nilai 2²⁴. Pohon verkle sedang <a href="https://notes.ethereum.org/@vbuterin/verkle_tree_eip"> sangat dipertimbangkan untuk pohon negara Ethereum, karena pohon Verkle dapat digunakan untuk menyimpan peta nilai-kunci dan bukan hanya daftar (pada dasarnya, Anda dapat membuat pohon ukuran-2²⁵⁶ tetapi memulainya dengan kosong, hanya mengisi bagian-bagian tertentu dari pohon setelah Anda benar-benar perlu mengisinya).
Seperti apa rupa pohon Verkle. Dalam praktiknya, Anda dapat memberikan setiap simpul dengan lebar 256 == 2⁸ untuk pohon berbasis IPA, atau 2²⁴ untuk pohon berbasis KZG.
Bukti-bukti dalam pohon Verkle agak lebih panjang daripada KZG; panjangnya mungkin beberapa ratus byte. Mereka juga sulit untuk diverifikasi, terutama jika Anda mencoba menggabungkan banyak bukti menjadi satu.
Secara realistis, pohon Verkle harus dianggap seperti pohon Merkle, tetapi lebih layak tanpa SNARKing (karena biaya data yang lebih rendah), dan lebih murah dengan SNARKing (karena biaya prover yang lebih rendah).
Keuntungan terbesar dari pohon Verkle adalah kemungkinan untuk menyelaraskan struktur data: Bukti Verkle dapat digunakan secara langsung di atas status L1 atau L2, tanpa struktur overlay, dan menggunakan mekanisme yang sama persis untuk L1 dan L2. Ketika komputer kuantum menjadi sebuah masalah, atau ketika pembuktian cabang-cabang Merkle menjadi cukup efisien, pohon Verkle dapat digantikan dengan pohon hash biner dengan fungsi hash yang sesuai dengan SNARK.
Jika N pengguna membuat N transaksi (atau lebih realistisnya, N ERC-4337 UserOperations) yang perlu membuktikan N klaim cross-chain, kita dapat menghemat banyak gas dengan menggabungkan bukti-bukti tersebut: pembangun yang akan menggabungkan transaksi-transaksi tersebut ke dalam sebuah blok atau bundel yang masuk ke dalam sebuah blok dapat membuat satu bukti yang membuktikan semua klaim secara bersamaan.
Ini bisa berarti:
Dalam ketiga kasus tersebut, biaya pembuktiannya hanya beberapa ratus ribu saja. Pembangun perlu membuat salah satu dari ini di setiap L2 untuk pengguna di L2 tersebut; oleh karena itu, agar ini berguna untuk dibangun, skema ini secara keseluruhan harus memiliki penggunaan yang cukup sehingga sering kali setidaknya ada beberapa transaksi dalam blok yang sama di beberapa L2 utama.
Jika ZK-SNARK digunakan, biaya marjinal utama hanyalah "logika bisnis" untuk mengoper angka di antara kontrak, jadi mungkin hanya beberapa ribu gas L2 per pengguna. Jika multi-bukti KZG digunakan, prover perlu menambahkan 48 gas untuk setiap L2 yang memegang keystore yang digunakan di dalam blok tersebut, sehingga biaya marjinal dari skema per pengguna akan menambahkan ~800 gas L1 per L2 (bukan per pengguna) di atasnya. Tetapi biaya ini jauh lebih rendah daripada biaya untuk tidak melakukan agregasi, yang pasti melibatkan lebih dari 10.000 gas L1 dan ratusan ribu gas L2 per pengguna. Untuk pohon Verkle, Anda dapat menggunakan Verkle multi-proof secara langsung, menambahkan sekitar 100-200 byte per pengguna, atau Anda dapat membuat ZK-SNARK dari Verkle multi-proof, yang memiliki biaya yang sama dengan ZK-SNARK cabang Merkle tetapi jauh lebih murah untuk dibuktikan.
Dari perspektif implementasi, mungkin yang terbaik adalah bundler mengumpulkan bukti lintas rantai melalui standar abstraksi akun ERC-4337. ERC-4337 sudah memiliki mekanisme bagi pembangun untuk mengumpulkan bagian-bagian UserOperations dengan cara khusus. Bahkan ada <a href="https://hackmd.io/@voltrevo/BJ0QBy3zi"> implementasi ini untuk agregasi tanda tangan BLS, yang dapat mengurangi biaya gas pada L2 sebesar 1,5x hingga 3x tergantung pada bentuk kompresi lain yang disertakan.
Diagram dari <a href="https://hackmd.io/@voltrevo/BJ0QBy3zi"> pos implementasi dompet BLS yang menunjukkan alur kerja tanda tangan agregat BLS dalam versi ERC-4337 sebelumnya. Alur kerja untuk mengumpulkan bukti lintas rantai kemungkinan akan terlihat sangat mirip.
Kemungkinan terakhir, dan yang hanya dapat digunakan untuk L2 yang membaca L1 (dan bukan L1 yang membaca L2), adalah memodifikasi L2 agar dapat melakukan panggilan statis ke kontrak pada L1 secara langsung.
Hal ini dapat dilakukan dengan sebuah opcode atau precompile, yang memungkinkan pemanggilan ke L1 di mana Anda memberikan alamat tujuan, gas dan calldata, dan mengembalikan output, meskipun karena pemanggilan ini adalah pemanggilan statis, mereka tidak dapat benar-benar mengubah status L1. L2 harus sudah mengetahui L1 untuk memproses deposit, jadi tidak ada hal mendasar yang menghentikan hal tersebut untuk diimplementasikan; ini terutama merupakan tantangan implementasi teknis (lihat: RFP ini dari Optimism untuk mendukung panggilan statis ke L1).
Perhatikan bahwa jika keystore berada di L1, dan L2 mengintegrasikan fungsionalitas static-call L1, maka tidak ada bukti yang diperlukan sama sekali! Namun, jika L2 tidak mengintegrasikan panggilan statis L1, atau jika keystore berada di L2 (yang pada akhirnya mungkin harus demikian, ketika L1 menjadi terlalu mahal untuk digunakan oleh pengguna meskipun hanya sedikit), maka pembuktian akan diperlukan.
Semua skema di atas membutuhkan L2 untuk mengakses baik root status L1 terbaru, atau seluruh status L1 terbaru. Untungnya, semua L2 sudah memiliki beberapa fungsi untuk mengakses status L1 terkini. Hal ini karena mereka membutuhkan fungsi seperti itu untuk memproses pesan yang masuk dari L1 ke L2, terutama deposito.
Dan memang, jika L2 memiliki fitur setoran, maka Anda dapat menggunakan L2 tersebut sebagaimana adanya untuk memindahkan akar status L1 ke dalam kontrak di L2: cukup dengan membuat kontrak di L1 yang memanggil opcode BLOCKHASH, dan meneruskannya ke L2 sebagai pesan setoran. Header blok penuh dapat diterima, dan akar statusnya diekstraksi, pada sisi L2. Namun, akan jauh lebih baik jika setiap L2 memiliki cara eksplisit untuk mengakses status L1 terkini, atau akar status L1 terkini, secara langsung.
Tantangan utama dalam mengoptimalkan cara L2 menerima akar status L1 terbaru adalah secara bersamaan mencapai keamanan dan latensi yang rendah:
Selain itu, pada arah yang berlawanan (L1 membaca L2):
Beberapa dari kecepatan ini untuk operasi lintas rantai yang tidak dapat dipercaya ini sangat lambat untuk banyak kasus penggunaan defi; untuk kasus-kasus tersebut, Anda membutuhkan jembatan yang lebih cepat dengan model keamanan yang lebih tidak sempurna. Namun, untuk kasus penggunaan memperbarui kunci dompet, penundaan yang lebih lama lebih dapat diterima: Anda tidak menunda transaksi dalam hitungan jam, Anda hanya menunda perubahan kunci. Anda hanya perlu menyimpan kunci lama lebih lama. Jika Anda mengganti kunci karena kuncinya dicuri, maka Anda memiliki periode kerentanan yang signifikan, tetapi hal ini dapat dikurangi, misalnya dengan dompet yang memiliki fungsi pembekuan.
Pada akhirnya, solusi meminimalkan latensi terbaik adalah agar L2 mengimplementasikan pembacaan langsung akar status L1 dengan cara yang optimal, di mana setiap blok L2 (atau log komputasi akar status) berisi penunjuk ke blok L1 yang terbaru, sehingga jika L1 mundur, L2 juga dapat mundur. Kontrak keystore harus ditempatkan di mainnet, atau di L2 yang merupakan ZK-rollup sehingga dapat dengan cepat melakukan komit ke L1.
Blok-blok rantai L2 dapat memiliki ketergantungan tidak hanya pada blok L2 sebelumnya, tetapi juga pada blok L1. Jika L1 kembali melewati tautan tersebut, L2 juga akan kembali. Perlu dicatat bahwa ini juga merupakan cara kerja sharding versi sebelumnya (sebelum Dank); lihat di sini untuk kode.
Yang mengejutkan, tidak sebanyak itu. Sebenarnya tidak perlu berupa rollup: jika itu adalah L3, atau validium, maka tidak masalah untuk menyimpan dompet di sana, selama Anda menyimpan keystore di L1 atau di rollup ZK. Hal yang Anda perlukan adalah rantai memiliki akses langsung ke akar negara Ethereum, dan komitmen teknis dan sosial untuk bersedia melakukan reorganisasi jika Ethereum melakukan reorganisasi, dan melakukan hard fork jika Ethereum melakukan hard fork.
Salah satu masalah penelitian yang menarik adalah mengidentifikasi sejauh mana sebuah rantai dapat memiliki bentuk koneksi ke beberapa rantai lainnya (misalnya. Ethereum dan Zcash). Melakukannya secara naif mungkin saja dilakukan: chain Anda dapat setuju untuk melakukan reorganisasi jika Ethereum atau Zcash melakukan reorganisasi (dan melakukan hard fork jika Ethereum atau Zcash melakukan hard fork), tetapi kemudian operator node dan komunitas Anda secara umum memiliki ketergantungan teknis dan politis yang berlipat ganda. Oleh karena itu, teknik seperti itu dapat digunakan untuk menghubungkan ke beberapa rantai lain, tetapi dengan biaya yang lebih tinggi. Skema yang didasarkan pada jembatan ZK memiliki sifat teknis yang menarik, tetapi memiliki kelemahan utama yaitu tidak kuat terhadap serangan 51% atau hard fork. Mungkin ada solusi yang lebih cerdas.
Idealnya, kami juga ingin menjaga privasi. Jika Anda memiliki banyak dompet yang dikelola oleh keystore yang sama, maka kami ingin memastikannya:
Hal ini menimbulkan beberapa masalah:
Dengan SNARK, solusinya secara konseptual mudah: bukti-bukti menyembunyikan informasi secara default, dan agregator perlu membuat SNARK rekursif untuk membuktikan SNARK.
Tantangan utama dari pendekatan ini saat ini adalah bahwa agregasi mengharuskan agregator untuk membuat SNARK rekursif, yang saat ini cukup lambat.
Dengan KZG, kita dapat menggunakan <a href="https://notes.ethereum.org/@vbuterin/non_index_revealing_proof"> ini bekerja pada bukti-bukti KZG yang tidak mengungkapkan indeks (lihat juga: versi yang lebih formal dari pekerjaan tersebut dalam makalah Caulk) sebagai titik awal. Agregasi bukti-bukti yang dibutakan, bagaimanapun juga, merupakan masalah terbuka yang membutuhkan lebih banyak perhatian.
Sayangnya, pembacaan L1 secara langsung dari dalam L2 tidak menjaga privasi, meskipun mengimplementasikan fungsionalitas pembacaan langsung masih sangat berguna, baik untuk meminimalkan latensi maupun karena kegunaannya untuk aplikasi lain.