(Bu yazıda Blockchain ve Ethereum ile ilgili olarak temel bilgilere sahip olduğunuzu varsayıyorum, eğer bu noktada soru işaretleriniz varsa öncesinde Ahmet Usta ile birlikte hazırladığımız ve Bankalararası Kart Merkezi tarafından yayınlanan “Blockchain 101” adlı kitabımızı okumanızı öneriyorum).

Geçenlerde oldukça faydalı olduğunu düşündüğüm bir Ethereum çalışmasına katılma şansım oldu ve burada kullanılan “Go-Ethereum” adlı Go tabanlı Ethereum uygulamasının yalın yapısı ile Ethereum platformu üzerinde çalışmayı/deney yapmayı oldukça kolaylaştırdığını gözlemledim. Bu yazıda bu tarz çalışmalar için 15 dakika gibi kısa bir sürede özel (private) bir Ethereum yapısı nasıl oluşturulur ondan bahsetmek istiyorum.


Öncelikle bir çalışma dizini oluşturup (Mac kullanıyorum ve makinem üzerinde bu yazı için hazırladığım dizin “/Users/serkand/Work/Ethereum” şeklinde) bu dizinine Geth projesine ait git repository’sini aktarıyoruz:

git clone https://github.com/ethereum/go-ethereum

Geth, Go ile hazırlanmış olduğundan dolayı devam edebilmek için Go derleyicisine ihtiyaç duymaktayız. Eğer makinenizde bulunmuyorsa ve MacOs kullanıyorsanız Homebrew kullanarak kolaylıkla kurabilirsiniz (alternatifler için GoLang sitesine bakabilirsiniz):

brew install go

Tüm bu adımlardan sonra tek yapılması gereken lokal repository dizininde “make” komutunu calıştırarak Geth’i build etmek olacak:

make geth

Build işlemi başarı ile bittiği zaman lokal repository dizini altındaki “/build/bin” dizini içerisinde “geth” komutunu çalıştırabilirsiniz. Ben kolaylık olması açısından (sadece “geth” diyerek çalıştırabilmek) bu path bilgisini makinemdeki PATH çevre değişkenine (environment variable) ekleyerek devam ediyor olacağım.

Başarılı bir şekilde Geth kurulumu yaptıkdan sonra artık kendi özel Ethereum ağımızı oluşturmak için hiç bir engelimiz kalmıyor.

Bu yazıdaki senaryoda toplam 3 adet node’dan oluşan bir ağ oluşturacağız, bu node’lardan bir tanesi ayrıca “miner” rolünü üstleniyor olacak. Bu node yapılarını “node1”, “node2” ve “nodeminer” olarak adlandırıp her birisi için birer alt dizin (bu alt dizinlerde ilgili node’a ait olan veritabanı ve keystore tutulacak) oluşturuyoruz (yazı boyunca ekranlar soldan sağa sırası ile node1, node2 ve nodeminer’ı göstermektedir):

Normal koşullarda node’ların çalışmasından sonra hesap (account) yaratımlarının yapılması beklenmektedir, ancak bu örnekte bazı hesapları önce hazırlayıp blockchain’i bu hesaplar üzerinde Ether bakiyesi (balance) içeren şekilde oluşturmak ilerideki denemelerimizi kolaylaştıracaktır. Bu örnekte “node1” üzerinde iki, “node2” üzerinde ise bir adet hesap oluşturalım. Bunun için kullanılan komut şu şekildedir:

geth --datadir=<hazırladığımız ilgili alt dizin> account new

Hesaplar bu aşamada belirtilmesi istenen bir şifre ile kilitlenmiş olarak yaratılırlar. Hesap yaratım sonunda hesaba ilişkin adres bilgisi gösterilir.

Bu örnekte hesaplar şu şekilde oluşturulmuştur:

node 1 :

  • 0cdac2b760949841a79e4e23eac13ed753aabef5
  • 90bd89cd6da9187b8e34fe22cce38a73be5ead8a

node 2 :

  • b85663113b9ace88693a4089e26bb87f58b2850c

Şimdi ise bu hesapları içeren ve diğer ana özellikleri bir genesis blok tanımı oluşturmamız gerekiyor. Geth kapsamında bunu bir json dosyası üzerinden gerçekleştirebiliyoruz:

Bu tanım içerisindeki “alloc” alanında tanımladığımız hesap bilgileri için başlangıç bakiyelerini tanımlamış oluyoruz (wei cinsinden). Ayrıca buradaki “difficulty” alanına dikkat edilmesi gerekiyor, eğer yüksek bir zorluk derecesi verilirse mining işlemi sırasında blok üretimi için oldukça uzun süreler beklemek gerekecektir.

Bu dosyayı oluşturduktan sonra “genesis.json” adı ile çalışma dizinimize kaydedelim.

Genesis blok tanımımız da hazır olduğuna göre tüm node’larımı bu tanım üzerinden başlangıç durumuna getirebiliriz. Bunun için kullanılan komut şu şekildedir:

geth --datadir=<hazırladığımız ilgili alt dizin> init genesis.json

Bundan sonra aşağıdaki komut yapısını kullanarak node’larımızı çalıştırabiliriz. Bu noktada aynı makine üzerinde çalışmalarda (ki bu örnekte bu şekilde) çakışma olmaması için port değerlerine dikkat edilmesi gerekmektedir:

geth --datadir <hazırladığımız ilgili alt dizin> --networkid <genesis.json içerisindeki chainId değeri> --maxpeers 25 --identity <node adı> --verbosity <log seviyesi> --rpcport <http-rpc port değeri> --port <ağ dinleme port değeri> --nodiscover --ipcpath <ipc dosya dizini> console

Komut sonunda “console” parametresini belirttiğimizden dolayı node’lar çalışmaya başladıktan sonra Geth JavaScript konsolu devreye girerek çeşitli komutları çalıştırmamıza olanak sağlamaktadır:

Aşağıdaki komutu kullanarak node’lar üzerinde tanımlı olan hesap bilgilerini görüntüleyebiliriz (node1'de 2, node2'de 1, nodeminer’da ise hiç hesap tanımı olmaması gerekmektedir):

eth.accounts

Hesaplara ait bakiye bilgilerini yine ilgili komutu kullanarak görüntüleyebiliriz:

eth.getBalance(<hesaba ait adres bilgisi>) -- wei birim

Genel anlamda node ile ilgili bilgilere erişmek için aşağıdaki komutlar kullanılabilir:

admin
admin.nodeInfo

Gelen bilgi yapısı içerisinde “enode” değeri bu node’a diğer node yapılarından erişmek istenilirse kullanılacak adres bilgisini göstermektedir. Burada dikkat edilmesi gereken alanlardan bir tanesi “peers” bilgisidir, şu andaki örneğimizde node’lar arasında bir ilişki bulunmamaktadır (her node bağımsız olarak çalışmaktadır). Burada çözümlerden bir tanesi tüm node’ların bağlanıp birbirlerini bulabilecekleri bir “bootnode” çalıştırmak olabileceği gibi, node’ların adres bilgilerini kullanarak birbirlerini görmesi sağlanabilir. Bu örnekte bu şekilde ilerleyeceğiz, node’ların alt dizinlerinde “static-nodes.json” adlı bir dosya oluşturup (dosyanın ismi bu şekilde olmak zorundadır), bağlanmak istedikleri node’lara ait adres bilgilerini paylaşacağız.

Öncelikle “exit” komutunu kullanarak node2 ve nodeminer’ı kapatalım.

Sonrasında node2'ye ait alt dizin altında node1'i, nodeminer’a ait alt dizin altında ise node1 ve node2'yi gösteren “static-nodes.json” dosyalarını hazırlayalım. Bu dosyanın formatı şu şekildedir:

[ “ilgili node adresi”, “ilgili node adresi”, … ]

Sonrasında node2 ve nodeminer’ı tekrardan çalıştıralım. Çalıştırdıktan sonra node’lar üzerinde “admin.peers” komutunu çalıştırırsak tüm node’ların artık birbileri ile ilişkili olduklarını görebiliriz.

Artık ağ üzerindeki tüm node’lar birbirleri ile bağlı hale geldiklerine göre mining işlemine başlayabiliriz. Bunun için mining yapmasını istediğimiz node üzerinde aşağıdaki komutu çalıştırmamız yeterli olacaktır:

miner.start(<kullanılması istenen thread sayısı>)

Bu örnekte hem tek makine üzerinde hem de “difficulty” değeri oldukça düşük bir blockchain üzerinde çalıştığımızdan dolayı tek thread ile mining işlemini başlatabiliriz.

Sizinde görebileceğiniz gibi “nodeminer” üzerinde mining işlemini başlatmaya çalıştığımız zaman hata alıyoruz. Bunun nedeni mining işlemi sırasında kullanılması gereken “coinbase” yani blok ödül adresi için node üzerinde bir hesap tanımı bulunmamasıdır. Geth üzerindeki mining işlemi bu durumda varsayılan değer olarak node üzerinde tanımlanan ilk hesap adresi bilgisini kullanmaktadır. Projenin başında node1 ve node2 için hesap yaratımları gerçekleştirmiştik, nodeminer içinde Geth JavaScript konsolu üzerinde hesap yaratımı gerçekleştirip, mining işlemini tekrar başlatabiliriz:

personal.newAccount(<hesap için kullanılacak şifre>)

Görülebileceği gibi mining işlemi sadece nodeminer üzerinde gerçekleşse de sonrasında mutabakat (consensus) mekanizması kapsamında node1 ve node2'ye iletilmektedir.

Bir süre bekleyip yeterli sayıda blok oluşumunu gözlemlediğimizde aşağıdaki komutu kullanarak mining işlemini sonlandıralım:

miner.stop()

Nodeminer üzerindeki coinbase hesabını kontrol edelim. Bunun için aşağıdaki komutları kullanabiliriz:

eth.coinbase
eth.getBalance(eth.coinbase) -- wei birim
web3.fromWei(eth.getBalance(eth.coinbase), “ether”) -- ether birim

Şimdi bu ether’lerimizi harcamaya başlayabiliriz. Örneğin nodeminer’daki coinbase hesabından node1 üzerindeki herhangi bir hesaba 1 Ether gönderelim. Bunun için öncelikle gönderim yapacağımız hesabı yaratırken verdiğimiz şifre ile açmalı (unlock), sonrasında ise kaynak ve hedef hesapları içeren bir işlem (transaction) kaydı oluşturmamız gerekecektir:

personal.unlockAccount(<hesap adresi>, <şifre>)
eth.sendTransaction({from: <kaynak hesap>, to: <hedef hesap>, value: <gönderilecek miktar>})

Oluşturulan işlem kayıtları bir blok içerisine yerleştirilmeden önce bekleyen işlemler (pending transactions) listesinde tutulurlar. Bir node üzerinde bekleyen işlem kayıtlarını görüntülemek için aşağıdaki komut kullanılır:

eth.pendingTransactions

Bekleyen bir işlemin gerçekleşmesi için mining işlemi sırasında değerlendirilip, geçerli bir blok içerisinde yer alması gerekmektedir. Bundan dolayı nodeminer üzerinde mining işlemini tekrar başlatıp (miner.start), bir kaç blok üretimi yaptıktan sonra kapatalım (miner.stop).

Node1 ve nodeminer üzerinde işlem içerisinde kullanılan hesapların bakiyesini kontrol edelim (nodeminer yeni bloklar oluşturup blok başına 5 Ether yaratım ödülü kazandığından dolayı hesapta azalma değil artış görülmesi normal bir davranış şeklidir).

İşlemimize ait adres bilgisini (hash) kullanarak durumunu kontrol etmemiz mümkündür:

eth.getTransaction(<işleme ait adres bilgisi>)

Bunun yanı sıra blockchain üzerindeki herhangi bir bloğu yada son bloğu incelemekte mümkündür:

eth.getBlock(<blok no>)
eth.getBlock(<blok hash değeri>)
eth.getBlock("latest") -- son blok

Sanırsam 15 dakikamızın sonuna geldik :) Yakın zamanda bu konuda özellikle akıllı sözleşmeler (smart contracts) açısından daha fazla yazmayı planlıyorum. Bu sırada Geth konusunda daha fazla bilgi edinmek, denemeler yapmak isterseniz projenin Wiki sayfasını kullanabilirsiniz.

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.