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 block
, msg
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 ( int8
/ uint8
ila int256
/ uint256
8 bitlik adımlarla, burada uint
/ , / int
için takma adlardır ) ve adreslerde (160 bitlik) işaretli ve işaretsiz tam sayılardır.uint256
int256
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 uint8
dö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 .uint16
int120
int256
int8
uint256
uint160
address
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 x
değ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ü y
olacaktır uint20
. Fonksiyon parametreleri veya dönüş parametreleri için kullanmak var
mü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 - 2
is değeri , türe -1
atanır x
ve bu nedenle , içeren en küçük tür olan x
tü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 one
ve her ikisinin de ayrıca yayılan two
türü vardır . Türün içindeki çıkarma, kaydırmaya neden olur ve bu nedenle değeri olacaktır .uint8
x
uint8
x
255
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, x
değere 0
ve 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 wei
, finney
, szabo
veya son ekini alabilir, örneğin .ether
2 ether == 2000 finney
true
Ayrıca, , , , ve sonekleri, seconds
saniyelerin minutes
temel hours
birim days
olduğu weeks
ve years
birimlerin 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: if
, else
, while
, for
, break
, continue
, return
. 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 InfoFeed
ve 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.coinbase
(address
): mevcut blok madencisinin adresiblock.difficulty
(uint
): mevcut blok zorluğublock.gaslimit
(uint
): mevcut blok gaslimitblock.number
(uint
): mevcut blok numarasıblock.blockhash
(function(uint) returns (bytes32)
): verilen bloğun hash değeriblock.timestamp
(uint
): geçerli blok zaman damgasımsg.data
(bytes
): çağrı verilerini tamamlamsg.gas
(uint
): kalan gazmsg.sender
(address
): mesajı gönderen (geçerli arama)msg.value
(uint
): mesajla birlikte gönderilen wei sayısınow
(uint
): geçerli blok zaman damgası ( için takma adblock.timestamp
)tx.gasprice
(uint
): işlemin gaz fiyatıtx.origin
(address
): işlemin göndericisi (tam çağrı zinciri)
Kriptografik Fonksiyonlar
sha3(...) returns (bytes32)
: (sıkıca paketlenmiş) argümanların SHA3 karmasını hesaplayınsha256(...) returns (bytes32)
: (sıkıca paketlenmiş) argümanların SHA256 karmasını hesaplayınripemd160(...) returns (bytes20)
: (sıkıca paketlenmiş) argümanların 256 RIPEMD’sini hesaplayınecrecover(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.
Sözleşmeyle İlgili
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 send
mü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), call
herhangi 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.kill
atlayacaktır . Bunu aşmanın yolu kullanmaktır :Base1.kill
Base1
super
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 Base1
iş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 is
yö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 C
isteklerin X
override ( bu sırayla A
belirterek ) ancak kendisinin override isteklerinin olması çözümlenemeyecek bir çelişkidir.A, X
A
X
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 public
veya internal
olarak belirtilebilir . Ayrıca fonksiyonlar olarak da belirtilebilir .private
public
internal
external
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 f
dahili 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).setData
data
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, data
herhangi 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. indexed
En 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ı anonymous
belirteci 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 Deposit
aynı ş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.
p
Eş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 k
bulunur . 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, var
bu 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 * x
ve 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ü, MSTORE
ve dışında yan etkileri olan komutlardır SSTORE
( , gibi LOGi
, EXTCODECOPY
ancak aynı zamandaCALLDATALOAD
ve 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 constant
+ constant
= sum_of_constants
veya 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!
- Blockchain Developer Olmak İçin Yol Haritası 2022
- Solidity nedir? Ethereum Akıllı Sözleşmelerinin Dili Rehberi
- Yeni Başlayanlar için Solidity – Akıllı Sözleşme Geliştirme Hızlandırılmış Kursu
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!