Lanjut ke konten utama

Memperkecil ukuran kontrak untuk mengatasi batasan ukuran kontrak

soliditykontrak pintarpenyimpanantruffle
Tingkat menengah
Markus Waas
soliditydeveloper.com(opens in a new tab)
26 Juni 2020
6 bacaan singkat minute read

Mengapa ada batasan?

Pada tanggal 22 November 2016(opens in a new tab) fork keras Sprurious Dragon memperkenalkan EIP-170(opens in a new tab) yang menambah batas ukuran kontrak pintar sebesar 24,576 kb. Bagi Anda sebagai pengembang Solidity, ini berarti ketika Anda menambahkan lebih banyak fungsionalitas ke kontrak, pada titik tertentu Anda akan mencapai batas dan ketika menggunakannya akan melihat kesalahan:

Peringatan: Ukuran kode kontrak melebih 24576 bita (batasan yang diperkenalkan dalam Spurious Dragon). This contract may not be deployable on Mainnet. Pertimbangkan untuk mengaktifkan pengoptimisasi (dengan nilai "berjalan" yang rendah!), yang menghentikan string pembalikan atau menggunakan pustaka.

Batasan ini diperkenalkan untuk mencegah serangan layanan penolakan (DOS). Setiap pemanggilan terhadap kontrak relatif berbiaya gas murah. Namun, dampak pemanggilan kontrak bagi node Ethereum meningkat secara tidak proporsional bergantung pada ukuran kode kontrak yang dipanggil (pembacaan kode dari disk, pemrosesan kode sebelumnya, penambahan data ke bukti Merkle). Setiap kali Anda berhadapan dengan situasi semacam itu, di mana penyerang membutuhkan sedikit sumber daya untuk menghasilkan banyak pekerjaan bagi yang lain, Anda berpotensi menghadapi serangan DOS.

Pada awalnya ini tidak menjadi masalah, karena batasan ukuran kontrak dasar seseorang adalah batasan gas blok. Jelas sebuah kontrak perlu digunakan dalam transaksi yang menampung semua kode bita kontrak. Jika Anda kemudian memasukkan hanya satu transaksi tersebut ke dalam sebuah blok, Anda dapat menggunakan semua gas tersebut, tapi itu tak terbatas. Namun, masalah dalam kasus ini adalah bahwa batasan gas blok berubah dari waktu ke waktu dan secara teori tidak terbatas. Saat peluncuran EIP-170, batasan gas blok hanya 4,7 juta. Sekarang batasan gas blok baru meningkat lagi(opens in a new tab) pada bulan lalu ke angka 11,9 juta.

Menghadapi pertempuran

Sayangnya, tidak ada cara mudah untuk mendapatkan ukuran kode bita kontrak Anda. Sebuat peralatan hebat yang dapat membantu Anda untuk ini adalah plugin truffle-contract-size(opens in a new tab) jika Anda menggunakan Truffle.

  1. npm install truffle-contract-size
  2. Tambahkan plugin ke truffle-config.js: plugins: ["truffle-contract-size"]
  3. Jalankan truffle run contract-size

Ini akan membantu Anda menemukan bagaimana perubahan Anda berdampak terhadap ukuran kontrak total.

Berikutnya kita akan melihat beberapa metode yang diurutkan berdasarkan potensi dampaknya. Bayangkan ini seperti upaya mengurangi berat badan. Strategi terbaik bagi seseorang untuk mencapai berat badan idealnya (dalam kasus kita 24kb) adalah memusatkan perhatian pada metode yang berdampak besar terlebih dahulu. Dalam kebanyakan kasus, hanya memperbaiki pola makan Anda akan membawa Anda pada sasaran, tetapi terkadang Anda memerlukan upaya yang sedikit lebih banyak. Lalu Anda mungkin menambahkan beberapa jenis latihan (dampak sedang) atau bahkan mengonsumsi suplemen (dampak kecil).

Dampak besar

Pisahkan kontrak Anda

Ini harus selalu menjadi pendekatan pertama Anda. Bagaimana cara Anda memisahkan kontrak menjadi beberapa kontrak yang lebih kecil? Ini umumnya memaksa Anda untuk menemukan arsitektur yang baik untuk kontrak Anda. Kontrak yang lebih kecil selalu lebih disukai jika dilhat dari aspek keterbacaan kode. Untuk memecahkan kontrak, tanyakan pada diri Anda sendiri:

  • Fungsi mana yang saling terkait? Setiap rangkaian fungsi mungkin berfungsi paling baik dalam kontraknya masing-masing.
  • Fungsi mana yang tidak memerlukan pembacaan state kontrak atau yang hanya memerlukan subset state tertentu?
  • Bisakah Anda memisahkan penyimpanan dan fungsionalitas?

Pustaka

Satu cara sederhana untuk memindahkan kode fungsionalitas dari penyimpanannya adalah dengan menggunakan pustaka(opens in a new tab). Jangan mendeklarasikan fungsi pustaka sebagai fungsi internal karena itu akan ditambahkan ke kontrak(opens in a new tab) secara lansung pada saat pengompilasian. Tetapi jika Anda menggunakan fungsi publik, pada dasarnya fungsi itu akan ada dalam kontrak pustaka terpisah. Pertimbangkan tujuan penggunaan(opens in a new tab) untuk membuat penggunaan pustaka menjadi lebih mudah.

Proksi

Strategi yang lebih canggih adalah sistem proksi. Pustaka menggunakan DELEGATECALL di latar belakang yang hanya mengeksekusi fungsi kontrak lain dengan state kontrak yang memanggil. Lihat postingan blog ini(opens in a new tab) untuk mempelajari selengkapnya tentang sistem proksi. Sistem ini memberi Anda lebih banyak fungsionalitas, misalnya memungkinkan kemampuan untuk melakukan peningkatan, tetapi juga menambah banyak kompleksitas. Saya tidak akan menambahkan sistem itu hanya untuk mengurangi ukuran kontrak, kecuali itulah satu-satunya opsi yang tersedia dengan alasan apa pun.

Dampak sedang

Menghapus fungsi

Ini harus jelas. Fungsi sedikit menambah ukuran kontrak.

  • Eksternal: Sering kali kita menambah banyak fungsi tampilan untuk alasan kenyamanan. Itu sangat baik sampai Anda mencapai batasan ukuran kontrak. Kemudian Anda mungkin merasa benar-benar ingin menghapus semua fungsi dengan mengecualikan fungsi yang benar-benar penting.
  • Internal: Anda juga dapat menghapus fungsi internal/privat dan hanya memasukkan kode asalkan fungsinya dipanggil hanya satu kali.

Hindari penggunaan variabel tambahan

Sebuah perubahan sederhana seperti ini:

1function get(uint id) returns (address,address) {
2 MyStruct memory myStruct = myStructs[id];
3 return (myStruct.addr1, myStruct.addr2);
4}
Salin
1function get(uint id) returns (address,address) {
2 return (myStructs[id].addr1, myStructs[id].addr2);
3}
Salin

menghasilkan perbedaan sebesar 0,28kb. Kemungkinannya Anda dapat menghadapi banyak situasi serupa dalam kontrak Anda dan itu dapat benar-benar menambah ukuran dalam jumlah yang besar.

Perpendek pesan kesalahan

Pesan pembalikan yang panjang dan khususnya beragam pesan pembalikan dapat membengkakkan ukuran kontrak. Sebagai gantinya, gunakan kode-kode kesalahan yang pendek dan kodekan dalam kontrak Anda. Sebuah pesan yang panjang dapat menjadi jauh lebih pendek:

1require(msg.sender == owner, "Only the owner of this contract can call this function");
2
Salin
1require(msg.sender == owner, "OW1");
Salin

Pertimbangkan menggunakan nilai berjalan yang rendah dalam pengoptimisasi

Anda juga dapat mengubah pengaturan pengoptimisasinya. Nilai default 200 berarti mencoba mengoptimalkan kode bita seolah-olah sebuah fungsi dipanggil 200 kali. Jika Anda mengubahnya menjadi 1, pada dasarnya Anda memberi tahu pengoptimisasi untuk melakukan pengoptimalan dalam hal menjalankan setiap fungsi hanya sekali. Sebuah fungsi yang dioptimalkan agar berjalan hanya satu kali berarti fungsi itu dioptimalkan untuk penggunaan itu sendiri. Ketahuilah bahwa ini meningkatkan biaya gas untuk menjalankan fungsi, sehingga Anda mungkin tidak ingin melakukannya.

Dampak kecil

Hindari meneruskan struct ke fungsi

Jika Anda menggunakan ABIEncoderV2(opens in a new tab), ini dapat membantu agar tidak meneruskan struct ke sebuah fungsi. Sebagai ganti meneruskan parameter sebagai struct...

1function get(uint id) returns (address,address) {
2 return _get(myStruct);
3}
4
5function _get(MyStruct memory myStruct) private view returns(address,address) {
6 return (myStruct.addr1, myStruct.addr2);
7}
Salin
1function get(uint id) returns(address,address) {
2 return _get(myStructs[id].addr1, myStructs[id].addr2);
3}
4
5function _get(address addr1, address addr2) private view returns(address,address) {
6 return (addr1, addr2);
7}
Salin

... teruskan parameter yang diperlukan secara langsung. Dalam contoh ini, kita menghemat ukuran sebesar 0,1kb.

Deklarasikan visibilitas yang benar untuk fungsi dan variabel

  • Fungsi atau variabel yang hanya dipanggil dari luar? Deklarasikan mereka sebagai eksternal ketimbang publik.
  • Fungsi atau variabel yang hanya dipanggil dari dalam kontrak? Deklarasikan mereka sebagai privat atau internal ketimbang publik.

Menghapus pengubah

Pengubah, khususnya jika dipakai secara intens, dapat berdampak signifikan terhadap ukuran kontrak. Pertimbangkan untuk menghapusnya dan sebagai gantinya gunakan fungsi.

1modifier checkStuff() {}
2
3function doSomething() checkStuff {}
Salin
1function checkStuff() private {}
2
3function doSomething() { checkStuff(); }
Salin

Tips tersebut akan membantu Anda mengurangi ukuran kontrak secara signifikan. Sekali lagi, ini sangat penting, selalu fokus pada pemisahan kontrak jika memungkinkan untuk mendapatkan hasil terbaik.

Masa depan batasan ukuran kontrak

Ada proposal terbuka(opens in a new tab) untuk menghapus batasan ukuran kontrak. Ide ini pada dasarnya untuk membuat pemanggilan kontrak lebih mahal untuk kontrak dengan ukuran sangat besar. Ini tidak akan terlalu sulit untuk diimplementasikan, memiliki kompatibilitas ke belakang yang sederhana (menempatkan semua kontrak yang telah digunakan sebelumnya dalam kategori termurah), tetapi tidak semua orang diyakinkan oleh ide ini(opens in a new tab).

Hanya waktu yang akan menentukan apakah batasan itu akan berubah di masa depan, reaksinya (lihat gambar di sebelah kanan) secara pasti menunjukkan persyaratan yang jelas bagi kita para pengembang. Sayangnya, ini bukanlah sesuatu yang dapat Anda harapkan dalam waktu dekat ini.

Terakhir diedit: @yeremiaryangunadi(opens in a new tab), 15 Agustus 2023

Apakah tutorial ini membantu?