15 Dakikada İlk Akıllı Sözleşmemizi Yazalım

Biliyorum oldukça uzun bir zaman önce (15 Dakikada Ethereum Ağı Oluşturabilmek yazısı) akıllı sözleşme (smart contract) yazma konusuna değineceğimi belirtmiştim ve işte o an bu an :)

Bu yazıda üzerinde çalışacağımız akıllı sözleşmeyi Visual Studio Code (Solidity extension’i yüklü) ve daha önce oluşturduğumuz Ethereum ağı ile hazırlayacağız ama yazının sonunda başka bir alternatif paylaşacağım.

İsterseniz hemen başlayalım.


1- Öncelikle Visual Studio Code makinemizde yoksa kurup üzerine Solidity extension’i (JuanBlanco.solidity) kuruyoruz.

2- Tahmin edebileceğiniz gibi amacımız bir “Hello World” akıllı sözleşmesi yazmak olacak. Burada akışa biraz akıllılık katmak amacı ile sözleşmemiz şu özelliklere sahip olacak:

  • Sözleşme İngilizce, Fransızca ve Almanca olarak “Merhaba Dünya” cümlesinin karşılıklarını bilecek
  • Kendisi ile etkileşime geçen kullanıcının dil tercihine göre uygun karşılığı dönecek

Yani aslında sözleşme yapımız çok basit olsa da üzerinde veri taşıyabilen (aynı cümlenin değişik dillerdeki karşılıkları) ve bu veriyi mantıksal bir akış içerisinde (kullanıcının dil tercihi) kullanan bir yapıda olacak.

3- Visual Studio Code üzerinde “HelloWorld.sol” isminde bir dosya oluşturuyoruz. Buradaki “sol” uzantısı uygulamaya bu dosyanın bir Solidity dosyası olduğunu ifade ediyor.

4- İlk başta akıllı sözleşmemizin desteklediği Solidity compiler versiyonunu belirtiyoruz. Burada kullandığımız yapı minimum Solidity compiler’i olarak 0.4.18'u istediğimizi ve 0.5.0 sonrası versiyonları desteklemediğimizi belirtiyor.

5- “contract” anahtar kelimesini kullanarak “HelloWorld” adlı akıllı sözleşmemizi oluşturacak bloğu tanımlayalım.

6- Her üç dilde “Merhaba Dunya” cümlesinin karşılıklarını tanımlayalım. Burada “string” veri tipini kullanacağız. Bu şekilde sözleşme kapsamında tanımlanan değişkenlere “durum değişkenleri” (state variables) denilmektedir, bu değişkenlerin değerleri sözleşmenin blockchain üzerindeki saklama alanında kalıcı bir şekilde tutulmaktadır.

7- Şimdi ise bu sözleşme için “yapıcı” (constructor) fonksiyonu tanımlayacağız (yapıcı fonksiyon tanımı sözleşmeler için zorunlu değildir). Bu fonksiyon sözleşmenin blockchain üzerinde yaratılma anında bir defalık çalıştırılır. Sözleşme ile aynı ismi taşımalıdır. Burada içi boş (yani çalıştırılma anında herhangi bir şey yapmayan) bir fonksiyon tanımı yapıyoruz.

Fonksiyon tanımları “function” anahtar kelimesi ile başlar. Arkasından fonksiyon ismi ve çalışması için gereken değerler (fonksiyon parametreleri) gelir. Burada ayrıca “public” tanımını belirterek bu fonksiyonun blockchain içerisinden ve dışarısından çağrılabileceğini belirtmiş oluyoruz.

8- Asıl fonksiyonumuzu tanımlamaya geçebiliriz. İlk olarak akışımızı daha basit bir şekilde düşünelim, çağıracağımız fonksiyon (bunu sözleşmenin sağladığı servis olarak düşünebiliriz) geri dönüş değeri olarak İngilizce “Merhaba Dünya” bilgisini geri dönüyor olsun.

Burada fonksiyon tanımına bir adet düzenleyici (modifier) eklememiz gerekiyor: “view”. Bu düzenleyici fonksiyon içerisinde durum değişkenlerine eriştiğimizi (burada “english” adlı string değişken) ancak değerini değiştirmediğimizi ifade ediyor.

Geri dönüş bilgisi ise “returns” yapısı içerisinde belirtilir, burada fonksiyonumuz “string” türünde bir değer dönüşü yapıyor.

9- Ama ilk başta belirttiğimiz gibi sözleşmemizin “akıllı” olmasını istiyoruz, bundan dolayı “sayHello” fonksiyonunun bir parametre alıp (hangi dilde karşılık istediğini belirtmeli) buna göre içerisinde bir kontrol akışı olmasını ve bu akış kapsamında ilgili string değerini dönmesini sağlamalıyız.

Burada Solidity dilinin “Enums” desteğini kullanarak sözleşme içerisinde dilleri ifade eden bir yapı oluşturacağız. Sonrasında bu yapının tanımladığımız “sayHello” fonksiyonuna bir girdi değeri olarak verilebilinmesini sağlayıp, verilen bu değere göre fonksiyon içerisinde “eğer bu ise şunu yap” şeklinde bir akış oluşturacağız.

LANGUAGE adlı bir enum oluşturup alabileceği değerleri tanımlıyoruz:

enum LANGUAGE {EN, FR, DE}

“sayHello” fonksiyon tanımı yukarıda oluşturduğumuz LANGUAGE tipinde bir girdi alacak şekilde güncelliyoruz:

function sayHello(LANGUAGE lang) public view returns(string)

Son olarak Solidity dilinin desteklediği “if-else” yapısı ile akışı oluşturuyoruz:

10- Evet, akıllı sözleşmemizin yazılması bitmiş durumda. Bundan sonra bu sözleşmeyi derleyip, kullanmak istediğimiz Ethereum ağına yüklenmesini gerçekleştireceğiz.

Öncelikli olarak Visual Code Studio’da F5 tuşuna basarak sözleşmemizi derleyelim.

11- Başarılı bir derleme işlemi sonrasında “bin” dizini ve altında 3 adet dosyanın oluştuğunu görüyoruz. “bin” uzantılı dosya sözleşmemize ait binary kodları içerirken , “abi” uzantılı dosya içerisinde sözleşmeye ait açık değişken ve metodlara ait bilgiler bulunmaktadır (ABI — Application Binary Interface). “json” uzantılı dosya içerisinde ise tüm bu bilgilerin yanı sıra sunulan metodların çalıştırılması için gereken gaz miktarı (bu değeri gaz birim fiyatı ile çarparak tahmini Ether maliyetini hesaplayabilirsiniz) gibi yan bilgiler bulunmaktadır.

12- Bu noktadan sonra sözleşmemizi bir Ethereum node’una yüklememiz gerekecek, burada daha önceki yazımda oluşturduğum “nodeminer” adlı node’u kullanacağım. Bu node’u öncelikle ayağa kaldıralım.

13- İlk olarak ABI dosyasındaki bilgileri kullanarak bir contract yapısı oluşturmamız gerekiyor. Bunun için çalıştırılması gereken komut yapısı şu şekildedir:

var helloContract = eth.contract(ABI_DOSYASI_ICERIGI)

14- BIN dosyasının içeriğini kullanmak amacı ile bir değişkene atıyoruz:

var helloCompiled = “0x” + “BIN_DOSYASI_ICERIGI”

15- Bu noktada yarattığımız contract yapısı üzerinde “new” methodunu çalıştırarak sözleşmemizi Ethereum node’una yüklemeyi başlatabiliriz:

16- Sözleşme yaratımı temelde bir transaction olduğundan dolayı blockchain üzerinde yer alması için madencilik (mining) akışı içerisinde bir blok kapsamında değerlendirilmesi gerekmektedir. Bunun için Ethereum node’umuz üzerinde “miner.start” komutu ile madencilik işlemini başlatmamız yeterlidir:

17- Bu noktadan sonra sözleşmemiz üzerindeki açık değişken ve metodlara erişebiliriz. Hazırladığımı “sayHello” methodu sözleşme içerisinde tanımladığımı “enum LANGUAGE” tipinde bir girdi değeri istiyordu. “enum” yapılarına tanımlandıkları sıra ile sayısal değerleri ile erişmemiz mümkündür. Yani bizim örneğimizde “EN” yerine 0, “FR” yerine 1 ve “DE” yerine 2 değerlerini kullanabiliriz.

18- Evet, sözleşmemizi Ethereum ağımız üzerine yükledik ve üzerindeki açık metoda erişim sağladık. Ve bu method yapısı da tanımladığımız mantıksal akış kapsamında istediğimiz sonucu geri döndü.

19- Burada aklımızda şu şekilde bir soru oluşmuş olmalı, yukarıdaki işlemleri tek node’luk bir yapıda denedik, diğer node’lar üzerinde bu sözleşme nasıl çalışıyor. Bunun için daha önceki yazımızda oluşturduğumuz ve “nodeminer”ı bir peer (eş) olarak gören “node2”yi ayağa kaldıralım. Açılışta bir süre blockchain üzerindeki blok yapılarının senkronize olmasını beklemeniz gerekebilir.

20- Ethereum blockchain yapısı, ABI tanımını ve adresini bildiğiniz herhangi bir sözleşmeye herhangi bir node üzerinden erişmenize olanak sağlar. Bunun için aşağıdaki komut yapısını kullanabiliriz ve sonrasında erişmek istediğimiz metodları çalıştırabiliriz.

var contract = eth.contract(ABI_ICERIGI).at(SOZLESME_ADRESI)

Evet, ilk akıllı sözleşmemizi yazdık, derledik, yükledik ve kullandık.

Hatırlarsanız yazının başlangıcında bir alternatif paylaşacağımı belirtmiştim. Ethereum platformu basit sözleşmeleri tasarlamak, yaratmak ve test etmek amacı ile “Remix” adlı browser tabanlı bir IDE (Integrated Development Environment) sunmaktadır. Remix’e aşağıdaki adresten erişebilirsiniz:

https://remix.ethereum.org/

Burada hızlı bir şekilde yeni bir dosya yaratıp, yukarıda tanımladığımız sözleşme içeriğini kopyalayıp, sözleşmemizi derleyebiliriz (Buradaki statik analiz ile ilgili uyarıları şimdilik göz ardı edebiliriz):

Başarılı bir derlemeden sonra “Run” kısmına geçip bu derlenmiş sözleşmeyi browser üzerinde çalışan bir Ethereum node yapısı üzerinde hızlıca test edebiliriz. Bunun için “Environment” ve sözleşme bilgisini seçip “Create” tuşuna basmamız yeterlidir:

Yaratılan sözleşmeyi, sözleşmeye bağlı erişilebilir değişken ve fonksiyonları ekranın sağ alt kısmında görebiliriz. Bu fonksiyonları gerekli girdileri sağlayarak çalıştırabiliriz:


Bu yazıda değer ve mantıksal bir akış tutan bir sözleşme yarattık, bu sözleşmeyi derleyip bir Ethereum ağına yükledik ve ağ üzerindeki herhangi bir node üzerinden sözleşmeye erişerek açık olan fonksiyonları çalıştırdık. Yani aslında bir akıllı sözleşmeden beklenen temel işlevlerin önemli bir kısmını yerine getirmiş olduk.

İlerideki yazılarda Web3.js kütüphanesinin kullanımı, ERC20 standartlarında token yazımı gibi çeşitli konulara değinmeyi planlıyorum (ama bu yazıların öncesinde muhtemelen şu sıralar büyük keyifle okuduğum “Machine Platform Crowd” ile çıkmasını dört gözle beklediğim “Digital Human” kitapları hakkında birşeyler karalayacağım, bunun dışında Kotlin ve Android uygulama geliştirme konularında da düzenlenmeyi bekleyen çeşitli notlarım var). Ama yazıları beklemeden de geçenlerde Boğaziçi TechSummit kapsamında düzenlediğim workshop sonrasında oluşturmaya başladığım Github projesini takip etmeye başlayabilirsiniz, amacım sizlerinde geri bildirimleri ile birlikte açık, temel ve orta seviyeye hitap eden bir Ethereum ve Solidity eğitim/öğrenim içeriği oluşturmaktır.

Görüşmek üzere :)