Ana içeriğe geç

Sözleşme boyutu sınırıyla mücadele etmek için sözleşmelerin küçültülmesi

solidityakıllı kontratlardepolamatruffle
Orta düzey
Markus Waas
soliditydeveloper.com(opens in a new tab)
26 Haziran 2020
5 dakikalık okuma minute read

Neden bir sınır var?

22 Kasım 2016(opens in a new tab)'da Spurious Dragon sert çatalı 24,576 kb akıllı sözleşme boyutu sınırı ekleyen EIP-170(opens in a new tab)'i tanıttı. Bir Solidity geliştiricisi olarak sizin için bu, sözleşmenize giderek daha fazla işlevsellik eklediğinizde, bir noktada sınıra ulaşacağınız ve dağıtım sırasında şu hatayı göreceğiniz anlamına gelir:

Warning: Contract code size exceeds 24576 bytes (a limit introduced in Spurious Dragon). Bu sözleşme Mainnet'te dağıtılamayabilir. Consider enabling the optimizer (with a low "runs" value!), turning off revert strings, or using libraries.

Bu sınır, hizmet reddi (DOS) saldırılarını önlemek için getirildi. Bir sözleşmeye yapılan herhangi bir çağrı, gaz açısından nispeten ucuzdur. Bununla birlikte, Ethereum düğümleri için bir sözleşme çağrısının etkisi, çağrılan sözleşme kodunun boyutuna bağlı olarak orantısız bir şekilde artar (kodu diskten okumak, kodu önceden işlemek, Merkle kanıtına veri eklemek). Saldırganın başkaları için çok iş yapmak için az kaynağa ihtiyaç duyduğu böyle bir durumunuz olduğunda, DOS saldırıları potansiyeli elde edersiniz.

Bir doğal sözleşme boyutu limiti, blok gaz limiti olduğu için başlangıçta bu çok da büyük bir problem değildi. Açıkça görülüyor ki bir sözleşmenin, sözleşmenin tüm bit kodunu tutan bir işlem içinde dağıtılması gerekir. Bir bloğa yalnızca bir işlemi dahil ederseniz bu gazın tamamını kullanabilirsiniz, ancak bu sonsuz değildir. Londra Yükseltmesi'nden bu yana blok gaz limiti, ağ talebine bağlı olarak 15 milyon ile 30 milyon birim arasında değişti.

Mücadeleye girişmek

Ne yazık ki, sözleşmelerinizin bayt kodu boyutunu almanın kolay bir yolu yok. Truffle kullanıyorsanız truffle-contract-size(opens in a new tab) eklentisi size yardımcı olacak iyi bir araçtır.

  1. npm install truffle-contract-size
  2. Eklentiyi truffle-config.js öğesine ekleyin: plugins: ["truffle-contract-size"]
  3. truffle run contract-size komutunu çalıştırın

Bu, değişikliklerinizin toplam sözleşme boyutlarını nasıl etkilediğini anlamanıza yardımcı olacaktır.

Aşağıda, potansiyel etkilerine göre sıralanan bazı yöntemlere bakacağız. Bunu, kilo verme gibi düşünün. Birinin hedef kilosuna (bizim durumumuzda 24 kb) ulaşması için en iyi strateji, önce büyük etkiye sahip yöntemlere odaklanmaktır. Çoğu zaman sadece diyeti düzeltmek amaca ulaştırır ancak bazen biraz daha fazlası gerekir. Sonra biraz egzersiz (orta etki) veya hatta takviye besinler (küçük etki) ekleyebilirsiniz.

Büyük etki

Sözleşmelerinizi ayırın

Bu her zaman ilk yaklaşımınız olmalıdır. Sözleşmeyi birden çok küçük sözleşmeye nasıl ayırabilirsiniz? Genellikle sizi sözleşmeleriniz için iyi bir mimari bulmaya zorlar. Daha küçük sözleşmeler her zaman kod okunabilirliği açısından tercih edilir. Sözleşmeleri bölmek için kendinize şunları sorun:

  • Hangi fonksiyonlar birlikte olmalıdır? Her fonksiyon seti, en çok kendi sözleşmesine uyacaktır.
  • Hangi fonksiyonlar, sözleşme durumunun okunmasını veya yalnızca durumun belirli bir alt kümesini gerektirmez?
  • Depolamayı ve işlevselliği bölebilir misiniz?

Kütüphaneler

Fonksiyon kodunu depolama alanından uzaklaştırmanın basit bir yolu, bir kütüphane(opens in a new tab) kullanmaktır. Kütüphane fonksiyonları derleme esnasında doğrudan sözleşmeye ekleneceği(opens in a new tab) için onları dahili olarak duyurmayın. Ancak genel fonksiyonları kullanırsanız, bunlar aslında ayrı bir kütüphane sözleşmesinde olacaktır. Kütüphanelerin kullanımını daha uygun hâle getirmek için using for(opens in a new tab)'u göz önüne alın.

Proxy'ler

Proxy sistemi, daha gelişmiş bir stratejidir. Kütüphaneler arka planda, çağıran sözleşmenin durumuyla başka bir sözleşmenin fonksiyonunu yürüten DELEGATECALL kullanır. Proxy'ler hakkında dahasını öğrenmek için bu blog gönderisine(opens in a new tab) bakın. Yükseltilebilirliği sağlamak gibi daha fazla işlevsellik sağlarlar ancak aynı zamanda çok fazla karmaşıklık da eklerler. Herhangi bir nedenle tek seçeneğiniz olmadıkça, bunları yalnızca sözleşme boyutlarını azaltmak için eklenmesini tavsiye etmem.

Orta etki

Fonksiyonları kaldırın

Bu bariz bir yöntem. Fonksiyonlar, sözleşme boyutunu biraz artırır.

  • Harici: Çoğu zaman kolaylık sağlamak için çok sayıda görüntüleme fonksiyonu ekleriz. Boyut sınırına ulaşana kadar bu gayet iyi bir yöntemdir. O zaman kesinlikle gerekli olanlar hariç hepsini kaldırmayı gerçekten düşünmek isteyebilirsiniz.
  • Dahili: Ayrıca dahili/özel fonksiyonları kaldırabilir ve fonksiyon yalnızca bir kez çağrıldığı sürece kodu satır içine alabilirsiniz.

Ek değişkenlerden kaçının

Bunun gibi küçük bir değişim:

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

0,28kb'lık bir fark yaratır. Muhtemelen sözleşmelerinizde birçok benzer durum vardır ve bunlar gerçekten önemli miktarlara ulaşabilir.

Hata mesajını kısaltın

Uzun geri dönüş mesajları ve özellikle birçok farklı geri dönüş mesajı, sözleşmeyi şişirebilir. Bunun yerine kısa hata kodları kullanın ve bunları sözleşmenizde çözün. Uzun bir mesaj çok daha kısa olabilir:

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

Hata mesajları yerine özel hatalar kullanın

Özel hatalar Solidity 0.8.4(opens in a new tab)'te tanıtılmıştır. Bu hatalar, sözleşmelerinizin boyutunu azaltmanın harika bir yoludur, çünkü seçiciler olarak ABI kodludur (tıpkı işlevler gibi).

1error Unauthorized();
2
3if (msg.sender != owner) {
4 revert Unauthorized();
5}
Kopyala

Optimize edicide düşük bir çalıştırma değerini göz önünde bulundurun

Optimize edici ayarlarını da değiştirebilirsiniz. 200 varsayılan değeri, bit kodunu bir fonksiyon 200 kez çağrılmış gibi optimize etmeye çalıştığı anlamına gelir. 1 olarak değiştirirseniz, temel olarak optimize ediciye her fonksiyonu yalnızca bir kez çalıştırma durumu için optimize etmesini söylersiniz. Yalnızca bir kez çalışmak için optimize edilmiş bir fonksiyon, dağıtımın kendisi için optimize edildiği anlamına gelir. Bunun, işlevleri çalıştırmak için gereken gaz maliyetlerini artırdığını unutmayın, yani bunu yapmamak daha iyi olabilir.

Küçük etki

Fonksiyonlara yapılar aktarmaktan kaçının

Eğer ABIEncoderV2(opens in a new tab) kullanıyorsanız bu, fonksiyonlara yapı aktarmamanıza yardımcı olabilir. Parametreyi bir yapı olarak aktarmaktansa...

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}
Kopyala
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}
Kopyala

...gerekli parametreleri doğrudan aktarın. Bu örnekte 0,1 kb daha kazandık.

Fonksiyonlar ve değişkenler için doğru görünürlük duyurun

  • Yalnızca dışarıdan çağrılan fonksiyonlar veya değişkenler ne olacak? Onları public yerine external olarak duyurun.
  • Yalnızca sözleşmenin içinden çağrılan fonksiyonlar veya değişkenler ne olacak? Onları public yerine private veya external olarak duyurun.

Niteleyicileri kaldırın

Niteleyiciler, özellikle yoğun olarak kullanıldığında sözleşme boyutu üzerinde önemli bir etkiye sahip olabilir. Onları kaldırmayı ve yerine fonksiyon kullanmayı göz önünde bulundurun.

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

Bu ipuçları, sözleşme boyutunu önemli ölçüde azaltmanıza yardımcı olacaktır. Bir kez daha, en büyük etki için mümkünse her zaman sözleşmeleri bölmeye odaklanmanızı söylemem gerekiyor.

Bu rehber yararlı oldu mu?