1. Anasayfa
  2. Solidity

Solidity 101, Solidity Programlama Diline Giriş

Solidity 101, Solidity Programlama Diline Giriş
Solidity 101
0

Solidity 101

Solidity 101 Solidity, sözdizimi JavaScript’inkine benzer olan ve Ethereum Sanal Makinesi için kod derlemek üzere tasarlanmış üst düzey bir dildir. Bu eğitim, Solidity‘e temel bir giriş sağlar ve genel olarak Ethereum Sanal Makinesi ve programlama hakkında bazı bilgileri varsayar. Bu öğretici, doğal dil dokümantasyonu veya resmi doğrulama gibi özellikleri kapsamaz ve ayrıca dilin nihai bir spesifikasyonu anlamına gelmez.

Herhangi bir şey indirmenize veya derlemenize gerek kalmadan tarayıcınızda Solidity‘i kullanmaya başlayabilirsiniz. Bu uygulama yalnızca derlemeyi destekler – kodu çalıştırmak veya blok zincirine enjekte etmek istiyorsanız, AlethZero gibi bir istemci kullanmanız gerekir.

Basit Örnekle Başlayalım

contract SimpleStorage {
    uint storedData;
    function set(uint x) {
        storedData = x;
    }
    function get() constant returns (uint retVal) {
        return storedData;
    }
}

uint storedData storedData depolamadaki konumu derleyici tarafından otomatik olarak tahsis edilen tür uint (256 bitlik işaretsiz tamsayı) adlı bir durum değişkeni bildirir . set ve işlevleri get, değişkenin değerini değiştirmek veya almak için kullanılabilir.

Solidity Coin Örneği

contract Coin {
    address minter;
    mapping (address => uint) balances;
    function Coin() {
        minter = msg.sender;
    }
    function mint(address owner, uint amount) {
        if (msg.sender != minter) return;
        balances[owner] += amount;
    }
    function send(address receiver, uint amount) {
        if (balances[msg.sender] < amount) return;
        balances[msg.sender] -= amount;
        balances[receiver] += amount;
    }
    function queryBalance(address addr) constant returns (uint balance) {
        return balances[addr];
    }
}

Bu sözleşme bazı yeni kavramlar getiriyor. Bunlardan biri address 160 bitlik bir değer olan ve herhangi bir aritmetik işleme izin vermeyen tiptir. Ayrıca, durum değişkeni balance, adresleri işaretsiz tamsayılara eşleyen karmaşık bir veri türündedir. Eşlemeler, olası her anahtarın var olduğu ve bayt temsilinin tamamı sıfır olan bir değere eşlendiği şekilde sanal olarak başlatılan hashtable’lar olarak görülebilir. Özel işlev Coin, sözleşmenin oluşturulması sırasında çalıştırılan ve daha sonra çağrılamayan yapıcıdır. Sözleşmeyi oluşturan kişinin adresini kalıcı olarak saklar: tx ve ile birlikte blockmsg sözleşmenin dışındaki dünyaya erişime izin veren bazı özellikleri içeren sihirli bir global değişkendir. İşlev queryBalance beyan edilir constant ve bu nedenle sözleşmenin durumunu değiştirmesine izin verilmez (bunun henüz uygulanmadığını unutmayın). Solidity‘de, dönüş “parametreleri” adlandırılır ve esas olarak yerel bir değişken oluşturur. balance = balances[addr]; Yani bakiyeyi iade etmek için herhangi bir iade ifadesi olmadan da kullanabiliriz.

Yorumlar

Tek satırlı yorumlar ( //) ve çok satırlı yorumlar ( /*...*/) mümkündür, oysa ///işlev bildirimlerinin hemen önündeki üç eğik çizgili yorumlar ( ) NatSpec yorumlarını (burada ele alınmamaktadır) sunar.

Türler

Şu anda uygulanan (temel) türler, boolean ( bool), tamsayı ve sabit uzunluklu dize/bayt dizisi (bytes0 – bytes32) türleridir. Tamsayı türleri, çeşitli bit genişliklerinde ( int8uint8ila int256uint2568 bitlik adımlarla, burada uint/ , / intiçin takma adlardır ) ve adreslerde (160 bitlik) işaretli ve işaretsiz tam sayılardır.uint256int256

Karşılaştırmalar ( <=!=, , vb.) her zaman , ve ==kullanılarak birleştirilebilen boole değerleri verir . Her zamanki kısa devre kurallarının and için geçerli olduğuna dikkat edin; bu, formun ifadeleri için işlevin aslında hiçbir zaman çağrılmadığı anlamına gelir.&&||!&&||(0 < 1 || fun())

Bir işleç farklı türlere uygulanırsa, derleyici işlenenlerden birini örtük olarak diğerinin türüne dönüştürmeye çalışır (aynısı atamalar için de geçerlidir). Genel olarak, anlamsal olarak anlamlıysa ve hiçbir bilgi kaybolmadıysa, örtük bir dönüştürme mümkündür: ve ile uint8dönüştürülebilir , ancak dönüştürülemez . Ayrıca, işaretsiz tamsayılar, aynı veya daha büyük boyuttaki baytlara dönüştürülebilir, ancak bunun tersi mümkün değildir. Dönüştürülebilen herhangi bir tür de dönüştürülebilir .uint16int120int256int8uint256uint160address

Derleyici örtük dönüştürmeye izin vermiyorsa ancak ne yaptığınızı biliyorsanız, bazen açık bir tür dönüştürme mümkündür:

int8 y = -3;
uint x = uint(y);

Bu kod parçacığının sonunda, 256 bitlik ikinin tümleyen gösteriminde -3 olan xdeğere (64 onaltılık karakter) sahip olacaktır .0xfffff..fd

Kolaylık sağlamak için, bir değişkenin türünü açıkça belirtmek her zaman gerekli değildir, derleyici bunu değişkene atanan ilk ifadenin türünden otomatik olarak çıkarır:

uint20 x = 0x123;
var y = x;

Burada, türü yolacaktır uint20. Fonksiyon parametreleri veya dönüş parametreleri için kullanmak varmümkün değildir. Tamsayı ve bytesXX türlerinin durum değişkenleri sabit olarak bildirilebilir.

uint constant x = 32;
bytes3 constant text = "abc";

Tamsayı Değişmezleri

Tamsayı değişmezleri kendileriyle birleştiği sürece tamsayı değişmezlerinin türü belirlenmez. Bu muhtemelen en iyi örneklerle açıklanmıştır:

var x = 1 - 2;

1 - 2is değeri , türe -1atanır xve bu nedenle , içeren en küçük tür olan xtürü alır . Ancak aşağıdaki kod parçacığı farklı davranır:int8-1

var one = 1;
var two = 2;
var x = one - two;

Burada oneve her ikisinin de ayrıca yayılan twotürü vardır . Türün içindeki çıkarma, kaydırmaya neden olur ve bu nedenle değeri olacaktır .uint8xuint8x255

Hesaplama için yalnızca tamsayı değişmezleri kullanıldığı sürece, maksimum 256 biti geçici olarak aşmak bile mümkündür:

var x = (0xffffffffffffffffffff * 0xffffffffffffffffffff) * 0;

Burada, xdeğere 0ve dolayısıyla türe sahip olacaktır uint8.

Eter ve Zaman Birimleri

Bir sabit sayı, son eki olmayan Ether para birimi numaralarının “wei” olduğu varsayıldığında, eterin alt mezhepleri arasında dönüştürme yapmak için weifinneyszaboveya son ekini alabilir, örneğin .ether2 ether == 2000 finneytrue

Ayrıca, , , , ve sonekleri, secondssaniyelerin minutestemel hoursbirim daysolduğu weeksve yearsbirimlerin saf olarak dönüştürüldüğü zaman birimleri arasında dönüştürme yapmak için kullanılabilir (yani bir yıl her zaman tam olarak 365 gündür, vb.).

Kontrol Yapıları

C/JavaScript’teki kontrol yapılarının çoğu switch(planlanmamış) ve goto(bunun Solidity olarak adlandırıldığını unutmayın) haricinde Solidity’de mevcuttur. Yani: ifelsewhileforbreakcontinuereturn. C ve JavaScript’te olduğu gibi boole olmayan türlerden boole türlerine hiçbir tür dönüşümü olmadığını, dolayısıyla if (1) { ... }geçerli Solidity olmadığını unutmayın .

İşlev Çağrıları

Mevcut sözleşmenin işlevleri, bu saçma örnekte görüldüğü gibi, doğrudan, yinelemeli olarak da çağrılabilir:

contract c {
  function g(uint a) returns (uint ret) { return f(); }
  function f() returns (uint ret) { return g(7) + f(); }
}

İfade this.g(8);aynı zamanda geçerli bir işlev çağrısıdır, ancak bu sefer işlev doğrudan atlamalar yoluyla değil, bir mesaj çağrısı yoluyla çağrılacak. Diğer sözleşmelerin işlevlerini çağırırken, çağrı ve gazla birlikte gönderilen Wei miktarı belirtilebilir:

contract InfoFeed {
  function info() returns (uint ret) { return 42; }
}
contract Consumer {
  InfoFeed feed;
  function setFeed(address addr) { feed = InfoFeed(addr); }
  function callFeed() { feed.info.value(10).gas(800)(); }
}

İfadenin , ” InfoFeed(addr)verilen adresteki sözleşmenin türünün “olduğunu biliyoruz” şeklinde açık bir tür dönüşümü gerçekleştirdiğini InfoFeedve bunun bir kurucu yürütmediğini unutmayın. feed.info.value(10).gas(800) İşlev çağrısıyla gönderilen gazın değerini ve miktarını yalnızca (yerel olarak) ayarlamaya ve asıl çağrıyı yalnızca sondaki parantezlerin gerçekleştirmesine dikkat edin .

İşlev çağrısı bağımsız değişkenleri, herhangi bir sırada ada göre de verilebilir:

contract c {
function f(uint key, uint value) { ... }
function g() {
  f({value: 2, key: 3});
}
}

İşlev parametreleri ve dönüş parametreleri için adlar isteğe bağlıdır.

contract test {
  function func(uint k, uint) returns(uint){
    return k;
  }
}

Özel Değişkenler ve Fonksiyonlar

Global ad alanında her zaman var olan özel değişkenler ve işlevler vardır.

Blok ve İşlem Özellikleri

  • block.coinbaseaddress): mevcut blok madencisinin adresi
  • block.difficultyuint): mevcut blok zorluğu
  • block.gaslimituint): mevcut blok gaslimit
  • block.numberuint): mevcut blok numarası
  • block.blockhashfunction(uint) returns (bytes32)): verilen bloğun hash değeri
  • block.timestampuint): geçerli blok zaman damgası
  • msg.databytes): çağrı verilerini tamamla
  • msg.gasuint): kalan gaz
  • msg.senderaddress): mesajı gönderen (geçerli arama)
  • msg.valueuint): mesajla birlikte gönderilen wei sayısı
  • nowuint): geçerli blok zaman damgası ( için takma ad block.timestamp)
  • tx.gaspriceuint): işlemin gaz fiyatı
  • tx.originaddress): işlemin göndericisi (tam çağrı zinciri)

Kriptografik Fonksiyonlar

  • sha3(...) returns (bytes32): (sıkıca paketlenmiş) argümanların SHA3 karmasını hesaplayın
  • sha256(...) returns (bytes32): (sıkıca paketlenmiş) argümanların SHA256 karmasını hesaplayın
  • ripemd160(...) returns (bytes20): (sıkıca paketlenmiş) argümanların 256 RIPEMD’sini hesaplayın
  • ecrecover(bytes32, byte, bytes32, bytes32) returns (address): eliptik eğri imzasından ortak anahtarı kurtar

Yukarıda “sıkıca paketlenmiş” ifadesi, argümanların dolgu olmadan bir araya getirildiği anlamına gelir, yani sha3("ab", "c") == sha3("abc") == sha3(0x616263) == sha3(6382179) = sha3(97, 98, 99). Doldurma gerekiyorsa, açık tür dönüşümleri kullanılabilir.

  • this(mevcut sözleşmenin türü): açıkça dönüştürülebilen mevcut sözleşmeaddress
  • suicide(address): mevcut sözleşmeyi iptal et, fonlarını verilen adrese gönder

Ayrıca, mevcut sözleşmenin tüm işlevleri, mevcut işlev de dahil olmak üzere doğrudan çağrılabilir.

Adreslerdeki işlevler

Özelliği kullanarak bir adresin bakiyesini sorgulamak ve işlevi balance kullanarak bir adrese Ether (wei birimi olarak) göndermek sendmümkündür:

address x = 0x123;
if (x.balance < 10 && address(this).balance >= 10) x.send(10);

Ayrıca, ABI’ye uymayan sözleşmelerle arayüz oluşturmak için (klasik NameReg sözleşmesi gibi), callherhangi bir türden rastgele sayıda argüman alan işlev sağlanır. Bu argümanlar ABI-serileştirilir (yani ayrıca 32 bayta kadar doldurulur). Bir istisna, ilk argümanın tam olarak dört bayta kodlandığı durumdur. Bu durumda, burada fonksiyon imzalarının kullanımına izin vermek için dolgulu değildir.

address nameReg = 0x72ba7d8e73fe8eb666ea66babc8116a41bfb10e2;
nameReg.call("register", "MyName");
nameReg.call(bytes4(sha3("fun(uint256)")), a);

Sözleşmelerin tüm adres üyelerini devraldığını unutmayın, bu nedenle mevcut sözleşmenin bakiyesini kullanarak sorgulamak mümkündür this.balance.

İfadelerin Değerlendirme Sırası

İfadelerin değerlendirme sırası belirtilmez (daha resmi olarak, ifade ağacındaki bir düğümün çocuklarının değerlendirildiği sıra belirtilmez, ancak elbette düğümün kendisinden önce değerlendirilirler). Yalnızca ifadelerin sırayla yürütülmesi ve boole ifadeleri için kısa devre yapılması garanti edilir.

diziler

Hem değişken hem de sabit boyutlu diziler, depolamada ve harici işlevlerin parametreleri olarak desteklenir:

contract ArrayContract {
  uint[2**20] m_aLotOfIntegers;
  bool[2][] m_pairsOfFlags;
  function setAllFlagPairs(bool[2][] newPairs) {
    // assignment to array replaces the complete array
    m_pairsOfFlags = newPairs;
  }
  function setFlagPair(uint index, bool flagA, bool flagB) {
    // access to a non-existing index will stop execution
    m_pairsOfFlags[index][0] = flagA;
    m_pairsOfFlags[index][1] = flagB;
  }
  function changeFlagArraySize(uint newSize) {
    // if the new size is smaller, removed array elements will be cleared
    m_pairsOfFlags.length = newSize;
  }
  function clear() {
    // these clear the arrays completely
    delete m_pairsOfFlags;
    delete m_aLotOfIntegers;
    // identical effect here
    m_pairsOfFlags.length = 0;
  }
  bytes m_byteData;
  function byteArrays(bytes data) external {
    // byte arrays ("bytes") are different as they are stored without padding,
    // but can be treated identical to "uint8[]"
    m_byteData = data;
    m_byteData.length += 7;
    m_byteData[3] = 8;
    delete m_byteData[2];
  }
}

yapılar

Solidity, aşağıdaki örnekte gösterildiği gibi, yapılar biçiminde yeni türleri tanımlamanın bir yolunu sağlar:

contract CrowdFunding {
  struct Funder {
    address addr;
    uint amount;
  }
  struct Campaign {
    address beneficiary;
    uint fundingGoal;
    uint numFunders;
    uint amount;
    mapping (uint => Funder) funders;
  }
  uint numCampaigns;
  mapping (uint => Campaign) campaigns;
  function newCampaign(address beneficiary, uint goal) returns (uint campaignID) {
    campaignID = numCampaigns++; // campaignID is return variable
    Campaign c = campaigns[campaignID];  // assigns reference
    c.beneficiary = beneficiary;
    c.fundingGoal = goal;
  }
  function contribute(uint campaignID) {
    Campaign c = campaigns[campaignID];
    Funder f = c.funders[c.numFunders++];
    f.addr = msg.sender;
    f.amount = msg.value;
    c.amount += f.amount;
  }
  function checkGoalReached(uint campaignID) returns (bool reached) {
    Campaign c = campaigns[campaignID];
    if (c.amount < c.fundingGoal)
      return false;
    c.beneficiary.send(c.amount);
    c.amount = 0;
    return true;
  }
}

Sözleşme, bir kitle fonlaması sözleşmesinin tam işlevselliğini sağlamaz, ancak yapıları anlamak için gerekli temel kavramları içerir. Yapı türleri eşlemeler için değer türleri olarak kullanılabilir ve kendileri eşlemeler içerebilir (yapının kendisi bile eşlemenin değer türü olabilir, ancak bir yapıyı kendi içinde olduğu gibi dahil etmek mümkün değildir). Tüm işlevlerde, yerel bir değişkene bir yapı türünün nasıl atandığına dikkat edin. Bu, yapıyı kopyalamaz, ancak yalnızca bir referansı saklar, böylece yerel değişkenin üyelerine yapılan atamalar aslında duruma yazılır.

Numaralandırmalar

Numaralandırmalar, Solidity’de kullanıcı tanımlı bir tür oluşturmanın başka bir yoludur. Tüm tamsayı türlerine açıkça dönüştürülebilirler, ancak örtük dönüştürmeye izin verilmez. Enum türündeki değişken sabit olarak bildirilebilir.

contract test {
    enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill }
    ActionChoices choices;
    ActionChoices constant defaultChoice = ActionChoices.GoStraight;
    function setGoStraight()
    {
        choices = ActionChoices.GoStraight;
    }
    function getChoice() returns (uint)
    {
        return uint(choices);
    }
    function getDefaultChoice() returns (uint)
    {
        return uint(defaultChoice);
    }
}

Diğer Sözleşmelerle Arayüz Oluşturma

Diğer sözleşmelerle arabirim oluşturmanın iki yolu vardır: Ya adresi bilinen bir sözleşme yöntemini çağırın ya da yeni bir sözleşme oluşturun. Her iki kullanım da aşağıdaki örnekte gösterilmiştir. Oluşturulacak bir sözleşmenin kaynak kodunun (açıkça) bilinmesi gerektiğine dikkat edin, bu da onu oluşturan sözleşmeden önce gelmesi gerektiği anlamına gelir (ve yeni sözleşmenin bayt kodu fiilen içerdiğinden döngüsel bağımlılıklar mümkün değildir). oluşturma sözleşmesinin bayt kodu).

contract OwnedToken {
  // TokenCreator is a contract type that is defined below. It is fine to reference it
  // as long as it is not used to create a new contract.
  TokenCreator creator;
  address owner;
  bytes32 name;
  function OwnedToken(bytes32 _name) {
    address nameReg = 0x72ba7d8e73fe8eb666ea66babc8116a41bfb10e2;
    nameReg.call("register", _name);
    owner = msg.sender;
    // We do an explicit type conversion from `address` to `TokenCreator` and assume that the type of
    // the calling contract is TokenCreator, there is no real way to check.
    creator = TokenCreator(msg.sender);
    name = _name;
  }
  function changeName(bytes32 newName) {
    // Only the creator can alter the name -- contracts are explicitly convertible to addresses.
    if (msg.sender == address(creator)) name = newName;
  }
  function transfer(address newOwner) {
    // Only the current owner can transfer the token.
    if (msg.sender != owner) return;
    // We also want to ask the creator if the transfer is fine.
    // Note that this calls a function of the contract defined below.
    // If the call fails (e.g. due to out-of-gas), the execution here stops
    // immediately (the ability to catch this will be added later).
    if (creator.isTokenTransferOK(owner, newOwner))
      owner = newOwner;
  }
}
contract TokenCreator {
  function createToken(bytes32 name) returns (address tokenAddress) {
    // Create a new Token contract and return its address.
    return address(new OwnedToken(name));
  }
  function changeName(address tokenAddress, bytes32 name) {
    // We need an explicit type conversion because contract types are not part of the ABI.
    OwnedToken token = OwnedToken(tokenAddress);
    token.changeName(name);
  }
  function isTokenTransferOK(address currentOwner, address newOwner) returns (bool ok) {
    // Check some arbitrary condition.
    address tokenAddress = msg.sender;
    return (sha3(newOwner) & 0xff) == (bytes20(tokenAddress) & 0xff);
  }
}

Yapıcı Bağımsız Değişkenler

Bir Solidity sözleşmesi, sözleşme verilerinin kendisinin sona ermesinden sonra yapıcı argümanları bekler. Bu, argümanları derleyici tarafından olağan ABI biçiminde döndürülen derlenmiş baytlardan sonra koyarak bir sözleşmeye ilettiğiniz anlamına gelir.

Sözleşme Mirası

Solidity, polimorfizm dahil kod kopyalayarak çoklu kalıtımı destekler. Ayrıntılar aşağıdaki örnekte verilmiştir.

contract owned {
    function owned() { owner = msg.sender; }
    address owner;
}

// Use "is" to derive from another contract. Derived contracts can access all members
// including private functions and storage variables.
contract mortal is owned {
    function kill() { if (msg.sender == owner) suicide(owner); }
}

// These are only provided to make the interface known to the compiler.
contract Config { function lookup(uint id) returns (address adr) {} }
contract NameReg { function register(bytes32 name) {} function unregister() {} }

// Multiple inheritance is possible. Note that "owned" is also a base class of
// "mortal", yet there is only a single instance of "owned" (as for virtual
// inheritance in C++).
contract named is owned, mortal {
    function named(bytes32 name) {
        address ConfigAddress = 0xd5f9d8d94886e70b06e474c3fb14fd43e2f23970;
        NameReg(Config(ConfigAddress).lookup(1)).register(name);
    }

// Functions can be overridden, both local and message-based function calls take
// these overrides into account.
    function kill() {
        if (msg.sender == owner) {
            address ConfigAddress = 0xd5f9d8d94886e70b06e474c3fb14fd43e2f23970;
            NameReg(Config(ConfigAddress).lookup(1)).unregister();
// It is still possible to call a specific overridden function. 
            mortal.kill();
        }
    }
}

// If a constructor takes an argument, it needs to be provided in the header.
contract PriceFeed is owned, mortal, named("GoldFeed") {
   function updateInfo(uint newInfo) {
      if (msg.sender == owner) info = newInfo;
   }

   function get() constant returns(uint r) { return info; }

   uint info;
}

Yukarıda, mortal.kill()imha talebini “ileriye” çağırdığımızı unutmayın. Aşağıdaki örnekte görüldüğü gibi, bunun yapılma şekli sorunludur:

contract mortal is owned {
    function kill() { if (msg.sender == owner) suicide(owner); }
}
contract Base1 is mortal {
    function kill() { /* do cleanup 1 */ mortal.kill(); }
}
contract Base2 is mortal {
    function kill() { /* do cleanup 2 */ mortal.kill(); }
}
contract Final is Base1, Base2 {
}

Bir çağrı , en türetilmiş geçersiz kılma olarak Final.kill()arayacaktır , ancak bu işlev , temel olarak, hakkında bilgi sahibi olmadığı için Base2.killatlayacaktır . Bunu aşmanın yolu kullanmaktır :Base1.killBase1super

contract mortal is owned {
    function kill() { if (msg.sender == owner) suicide(owner); }
}
contract Base1 is mortal {
    function kill() { /* do cleanup 1 */ super.kill(); }
}
contract Base2 is mortal {
    function kill() { /* do cleanup 2 */ super.kill(); }
}
contract Final is Base1, Base2 {
}

Bir Base1işlevini superçağırırsa, bu işlevi temel sözleşmelerinden birinde çağırmaz, bunun yerine son kalıtım grafiğindeki bir sonraki temel sözleşmede bu işlevi çağırır, bu nedenle Base2.kill(). Super kullanılırken çağrılan asıl işlevin, türü bilinmesine rağmen kullanıldığı sınıf bağlamında bilinmediğine dikkat edin. Bu, sıradan sanal yöntem araması için benzerdir.

Çoklu Kalıtım ve Doğrusallaştırma

Çoklu mirasa izin veren diller, biri Elmas Problemi olmak üzere çeşitli problemlerle uğraşmak zorundadır . Solidity, Python’un yolunu izler ve temel sınıfların DAG’sinde belirli bir sırayı zorlamak için ” C3 Linearization ” kullanır. Bu, istenen monotonluk özelliği ile sonuçlanır, ancak bazı kalıtım grafiklerine izin vermez. Özellikle temel sınıfların isyönergede verildiği sıra önemlidir. Aşağıdaki kodda Solidity, “Kalıtım grafiğinin doğrusallaştırılması imkansız” hatası verecektir.

contract X {}
contract A is X {}
contract C is A, X {}

Bunun nedeni Cisteklerin Xoverride ( bu sırayla Abelirterek ) ancak kendisinin override isteklerinin olması çözümlenemeyecek bir çelişkidir.A, XAX

Hatırlanması gereken basit bir kural, temel sınıfları “en temele benzeyen”den “en çok türetilmiş”e doğru sıralamaktır.

Soyut Sözleşmeler

Sözleşme işlevleri, aşağıdaki örnekte olduğu gibi bir uygulamadan yoksun olabilir (işlev bildirimi başlığının tarafından sonlandırıldığına dikkat edin ;).

contract feline {
  function utterance() returns (bytes32);
}

Bu tür sözleşmeler derlenemez (uygulanmayan işlevlerin yanı sıra uygulanmış işlevler içerseler bile), ancak temel sözleşmeler olarak kullanılabilirler:

contract Cat is feline {
  function utterance() returns (bytes32) { return "miaow"; }
}

Bir sözleşme soyut bir sözleşmeden miras alırsa ve uygulanmayan tüm işlevleri geçersiz kılarak uygulamazsa, kendisi soyut olacaktır.

Görünürlük Belirteçleri

İşlevler ve depolama değişkenleri, işlevler ve depolama değişkenleri için varsayılanın olduğu yerde publicveya internalolarak belirtilebilir . Ayrıca fonksiyonlar olarak da belirtilebilir .privatepublicinternalexternal

Harici: Harici işlevler, sözleşme arabiriminin bir parçasıdır ve diğer sözleşmelerden ve işlemler aracılığıyla çağrılabilirler. Harici bir işlev fdahili olarak çağrılamaz (yani f()çalışmaz, ancak this.f()çalışır). Ayrıca, tüm fonksiyon parametreleri değişmezdir.

Genel: Genel işlevler, sözleşme arayüzünün bir parçasıdır ve dahili olarak veya mesajlar yoluyla çağrılabilir. Genel depolama değişkenleri için otomatik bir erişimci işlevi (aşağıya bakın) oluşturulur.

Devralınan: Bu işlevlere ve depolama değişkenlerine yalnızca dahili olarak erişilebilir.

Özel: Özel işlevler ve depolama değişkenleri, türetilmiş sözleşmelerde değil, yalnızca tanımlandıkları sözleşme için görünür.

contract c {
  function f(uint a) private returns (uint b) { return a + 1; }
  function setData(uint a) inherited { data = a; }
  uint public data;
}

Diğer sözleşmeler, c.data()depodaki verilerin değerini almak için arayabilir, ancak arayamaz f. Türetilen sözleşmeler , değerini değiştirmeye cçağırabilir (ancak yalnızca kendi depolarında).setDatadata

Aksesuar İşlevleri

Derleyici, tüm genel durum değişkenleri için erişimci işlevlerini otomatik olarak oluşturur. Aşağıda verilen sözleşme, dataherhangi bir argüman almayan ve durum değişkeninin değeri olan bir uint döndüren bir fonksiyona sahip olacaktır data. Durum değişkenlerinin başlatılması bildirimde yapılabilir.

contract test {
     uint public data = 42;
}

Sonraki örnek biraz daha karmaşık:

contract complex {
  struct Data { uint a; bytes3 b; mapping(uint => uint) map; }
  mapping(uint => mapping(bool => Data[])) public data;
}

Aşağıdaki formda bir fonksiyon üretecektir:

function data(uint arg1, bool arg2, uint arg3) returns (uint a, bytes3 b)
{
  a = data[arg1][arg2][arg3].a;
  b = data[arg1][arg2][arg3].b;
}

Eşleme için anahtarı sağlamanın iyi bir yolu olmadığından, yapıdaki eşlemenin atlandığına dikkat edin.

Geri Dönüş İşlevleri

Bir sözleşmenin tam olarak bir adsız işlevi olabilir. Bu işlevin argümanları olamaz ve diğer işlevlerden hiçbiri verilen işlev tanımlayıcısıyla eşleşmiyorsa (veya hiçbir veri sağlanmadıysa) sözleşmeye yapılan bir çağrıda yürütülür.

contract Test {
  function() { x = 1; }
  uint x;
}

contract Caller {
  function callTest(address testAddress) {
    Test(testAddress).send(0);
    // results in Test(testAddress).x becoming == 1.
  }
}

İşlev Değiştiriciler

Değiştiriciler, örneğin işlevi çalıştırmadan önce bir koşulu otomatik olarak kontrol etmek gibi işlevlerin davranışını kolayca değiştirmek için kullanılabilir. Bunlar, sözleşmelerin kalıtsal özellikleridir ve türetilmiş sözleşmeler tarafından geçersiz kılınabilir.

contract owned {
  function owned() { owner = msg.sender; }
  address owner;

  // This contract only defines a modifier but does not use it - it will
  // be used in derived contracts.
  // The function body is inserted where the special symbol "_" in the
  // definition of a modifier appears.
  modifier onlyowner { if (msg.sender == owner) _ }
}
contract mortal is owned {
  // This contract inherits the "onlyowner"-modifier from "owned" and
  // applies it to the "kill"-function, which causes that calls to "kill"
  // only have an effect if they are made by the stored owner.
  function kill() onlyowner {
    suicide(owner);
  }
}
contract priced {
  // Modifiers can receive arguments:
  modifier costs(uint price) { if (msg.value >= price) _ }
}
contract Register is priced, owned {
  mapping (address => bool) registeredAddresses;
  uint price;
  function Register(uint initialPrice) { price = initialPrice; }
  function register() costs(price) {
    registeredAddresses[msg.sender] = true;
  }
  function changePrice(uint _price) onlyowner {
    price = _price;
  }
}

Bir işleve, boşlukla ayrılmış bir listede belirtilerek birden çok değiştirici uygulanabilir ve sırayla değerlendirilecektir. Bir değiştiriciden veya işlev gövdesinden gelen açık dönüşler, bir işlevin veya değiştirici gövdesinin sonuna ulaşan kontrol akışı, önceki değiştiricideki “_” den sonra devam ederken, hemen tüm işlevi terk eder. Değiştirici argümanlar için keyfi ifadelere izin verilir ve bu bağlamda, fonksiyondan görünen tüm semboller değiştiricide görünür. Değiştiricide tanıtılan semboller, işlevde görünmez (geçersiz kılarak değişebilecekleri için).

Olaylar

Olaylar, EVM kayıt tesislerinin uygun kullanımına izin verir. Olaylar, sözleşmelerin kalıtsal üyeleridir. Çağrıldıklarında, argümanların işlem günlüğünde saklanmasına neden olurlar. indexedEn fazla üç parametre , ilgili argümanların veri yerine günlük konuları olarak ele alınmasına neden olacak özniteliği alabilir . Olayın imzasının hash’i, olayı anonymousbelirteci ile bildirmeniz dışında konulardan biridir. Dizine eklenmemiş tüm bağımsız değişkenler, günlüğün veri bölümünde saklanır. Örnek:

contract ClientReceipt {
  event Deposit(address indexed _from, bytes32 indexed _id, uint _value);
  function deposit(bytes32 _id) {
    Deposit(msg.sender, _id, msg.value);
  }
}

Burada, çağrı ile Depositaynı şekilde davranacaktır log3(msg.value, 0x50cb9fe53daa9737b786ab3646f04d0150dc50ef4e75f59509d83667ad5adb20, sha3(msg.sender), _id);. Büyük onaltılık sayının, olayın imzası olan “Deposit(address,bytes32,uint256)” öğesinin sha3-karmasına eşit olduğuna dikkat edin.

Depolama Düzeni

Statik olarak boyutlandırılmış değişkenler (eşleme ve dinamik olarak boyutlandırılmış dizi türleri dışında her şey) position ‘dan başlayarak depolamada bitişik olarak düzenlenir 0. 32 bayttan daha azına ihtiyaç duyan birden çok öğe, mümkünse aşağıdaki kurallara göre tek bir depolama yuvasına paketlenir:

  • Bir depolama yuvasındaki ilk öğe, alt sıra hizalı olarak depolanır.
  • Temel türler, yalnızca bunları depolamak için gerekli olan baytları kullanır.
  • Temel tip, bir depolama yuvasının kalan kısmına uymuyorsa, bir sonraki depolama yuvasına taşınır.
  • Yapılar ve dizi verileri her zaman yeni bir yuva başlatır ve tüm yuvaları kaplar (ancak bir yapı veya dizi içindeki öğeler bu kurallara göre sıkıca paketlenir).

Yapıların ve dizilerin öğeleri, sanki açıkça verilmiş gibi birbiri ardına depolanır.

Tahmin edilemeyen boyutları nedeniyle, eşleme ve dinamik olarak boyutlandırılmış dizi türleri sha3 , değerin veya dizi verilerinin başlangıç ​​konumunu bulmak için bir hesaplama kullanır. Bu başlangıç ​​pozisyonları her zaman tam yığın yuvalarıdır.

pEşleme veya dinamik dizinin kendisi , yukarıdaki kurala göre (veya eşlemelere veya dizi dizilerine eşlemeler için bu kuralı yinelemeli olarak uygulayarak ) bir konumda depolamada (doldurulmamış) bir yuva işgal eder . Dinamik bir dizi için bu yuva, dizideki öğelerin sayısını depolar. Bir eşleme için yuva kullanılmaz (ancak birbiri ardına iki eşit eşlemenin farklı bir karma dağılımı kullanması için gereklidir). Dizi verileri, sha3(p)bir eşleme anahtarına karşılık gelen değer , birleştirmenin olduğu yerde kbulunur . Değer yine temel olmayan bir türse, konumlar ofset eklenerek bulunur .sha3(k . p).sha3(k . p)

Yani aşağıdaki sözleşme pasajı için:

contract c {
  struct S { uint a; uint b; }
  uint x;
  mapping(uint => mapping(uint => S)) data;
}

konumundadır . data[4][9].b_sha3(uint256(9) . sha3(uint256(4) . uint(256(1))) + 1

Ezoterik Özellikler

Solidity’nin tip sisteminde, söz diziminde karşılığı olmayan bazı tipler vardır. Bu türlerden biri de fonksiyon türleridir. Ancak yine de, varbu tür yerel değişkenlere sahip olmak mümkündür:

contract FunctionSelector {
  function select(bool useB, uint x) returns (uint z) {
    var f = a;
    if (useB) f = b;
    return f(x);
  }
  function a(uint x) returns (uint z) {
    return x * x;
  }
  function b(uint x) returns (uint z) {
    return 2 * x;
  }
}

Çağrı select(false, x)hesaplayacak x * xve select(true, x)hesaplayacaktır 2 * x.

Dahili – Optimize Edici

Solidity optimizer, montaj üzerinde çalışır, bu nedenle diğer diller tarafından da kullanılabilir ve kullanılabilir. Talimat dizisini, taşınması zor noktalarda temel bloklara böler. Bunlar temel olarak, kontrol akışını değiştiren (atlamalar, çağrılar, vb.) komutların tümü, MSTOREve dışında yan etkileri olan komutlardır SSTORE( , gibi LOGiEXTCODECOPYancak aynı zamandaCALLDATALOADve diğerleri). Böyle bir bloğun içinde, talimatlar analiz edilir ve yığında, bellekte veya depolamada yapılan her değişiklik, bir talimattan ve esasen diğer ifadelere işaret eden bir argüman listesinden oluşan bir ifade olarak kaydedilir. Şimdi ana fikir, her zaman eşit (veya her girdi) olan ifadeleri bulmak ve bunları bir ifade sınıfında birleştirmek. Optimize edici önce her yeni ifadeyi zaten bilinen ifadeler listesinde bulmaya çalışır. Bu işe yaramazsa, ifade constantconstantsum_of_constantsveya X* 1 = gibi kurallara göre basitleştirilir.X. Bu özyinelemeli olarak yapıldığından, ikinci faktörün her zaman bir olarak değerlendirileceğini bildiğimiz daha karmaşık bir ifadeyse, ikinci kuralı da uygulayabiliriz. Depolama ve bellek konumlarında yapılan değişiklikler, farklı olduğu bilinmeyen depolama ve bellek konumları hakkındaki bilgileri silmek zorundadır: Önce x konumuna sonra y konumuna yazarsak ve her ikisi de girdi değişkenleriyse, ikincisi ilkinin üzerine yazabilir, bu nedenle biz aslında y’ye yazdıktan sonra x’te ne saklandığını bilmiyoruz. Öte yandan, x – y ifadesinin bir sadeleştirmesi sıfırdan farklı bir sabit olarak değerlendirilirse, x’te nelerin depolandığıyla ilgili bilgimizi koruyabileceğimizi biliyoruz.

Bu işlemin sonunda, sonunda hangi ifadelerin yığında olması gerektiğini ve bellek ve depolamada yapılan değişikliklerin listesini biliyoruz. Aslında ihtiyaç duyulan bu ifadelerden bir bağımlılık grafiği oluşturulur ve bu grafiğin parçası olmayan her işlem esasen çıkarılır. Şimdi, değişiklikleri orijinal kodda yapıldıkları sırayla (gerekli olmayan değişiklikleri bırakarak) belleğe ve depolamaya uygulayan ve son olarak, yığında olması gereken tüm değerleri üreten yeni kod oluşturulur. doğru yer.

Bu adımlar her temel bloğa uygulanır ve yeni oluşturulan kod daha küçükse yedek olarak kullanılır. Bir temel blok bir JUMPI’de bölünürse ve analiz sırasında koşul bir sabit olarak değerlendirilirse, JUMPI sabitin değerine bağlı olarak değiştirilir ve bu nedenle aşağıdaki gibi kodlanır.

var x = 7;
data[7] = 9;
if (data[x] != x + 2) 
  return 2;
else
  return 1;

ayrıca derlenebilen koda basitleştirilmiştir.

data[7] = 9;
return 1;

Talimatlar başlangıçta bir sıçrama içeriyor olsa da.

Solidity hakkında daha fazla kaynak için Solidity 101 içeriklerini ve Solidity kategorisini takip edebilirsiniz.

Aşağıdaki içeriklere muhakkak göz atınız!

Bu makaleyi okuduğunuz için teşekkürler! Bize destek olmak isterseniz aşağıdaki şekilde yapabilirsiniz:

Bizi Twitter‘dan takip etmeyi ve link ağacımızı radarına almayı UNUTMA!

solidity101 - Solidity, 2015 yılında Christian Reitwiessner liderliğinde piyasaya sürülen, büyük harf kullanımına göre ikinci en büyük kripto para piyasası olan Ethereum tarafından oluşturulan yepyeni bir programlama dilidir.

Yazarın Profili

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir