Bitcoin’i Kodlamak — 1 (Golang ile)

Faruk Terzioğlu
9 min readApr 2, 2019

--

Blockchain üzerine uygulama geliştirmek istediğimizde ilk olarak akla gelen seçenekler Ethereum veya Neo gibi platformlarda smart contract geliştirerek blockchain üzerinde çalıştırmak oluyor.

Nitekim benim de blockchain’e girişim C# ile Neo smart contract’ları yazarak oldu ve Solidity ile Ethereum smart contract’ları yazarak devam ettim.
Bir süredir de Golang ile Bitcoin blockchaini üzerine kodladığım için edindiğin bilgi birikimini birkaç yazıda aktarmaya çalışacağım.

Bitcoin blockchain’i gibi protokol seviyesinde kodlamak istediğinizde muazzam bir code base ile ve standart yazılım geliştirme süreçlerinde bilgi ve tecrübe sahibi olunmayacak alanlar ile karşılaşıyoruz.
Neyin nasıl yapıldığını, kodun neresinde ne olduğunu anlamak için baya bi kod okumak ve blockchain, cryptography, hashing, public key infrastructure gibi konularda bilgi sahibi olmak gerekiyor.
Bunlarda ağırlıklı olarak C/C++ ile yazıldığı için ekstra geliştirmeler yapabilmek oldukça uzmanlık gerektiriyor.

Image by tumbledore from Pixabay

Bitcoin’in referans uygulaması sayılan ve Satoshi Nakamoto tarafından yayınlanan ilk Bitcoin node uygulaması “Bitcoin Core” C++ ile yazılmıştı. Transferler alıp gönderebileceğimiz cüzdan özelliklerini de barındıran Bitcoin Core, bitcoin ağına bağlanarak full node görevini görür ve tüm transactionların doğrulanmasını sağlar (Wikipedia).
Bitcoin Core’un 0.1 versiyonu ilk olarak 9 Ocak 2009 da yayınlandı. Bugün itibariyle 19.000'in üzerinde commit’i var ve aktif olarak da geliştirilmeye devam ediliyor. https://github.com/bitcoin/bitcoin (İlk commit).

Alternatif bitcoin node’u btcd, Golang dili ile yazıldı ve aktif olarak kullanılmakta ve geliştirilmekte. (https://github.com/btcsuite/btcd). Halen beta olan BTCD, 2013 den beri stable olarak production da kullanılmaktadır. Diğer bitcoin node’larına bağlanarak blokların indirilmesi ve doğrulanmasını sağlayarak full bir bitcoin node görevi görmekte. Golang dilinin performans, test edilebilirlik, geliştirici yaygınlığı gibi artıları ile orijinal Bitcoin node’unun ciddi bir alternatifi. Bitcoin Core’dan farklı olarak cüzdan işlemleri ve blockchain kurallarının işletilmesi ayrı uygulamalara konulmuş. Bitcoin Core içine dahil olan yeni transaction oluşturma, account’ların kaydının tutulması gibi cüzdan işlemleri ayrı bir uygulama olan btcwallet uygulaması içerisine taşınmış.

https://blog.conformal.com/btcd-not-your-moms-bitcoin-daemon/

Bu yazı içerisinde btcd ile gelen btcctl uygulamasını kullanarak blockchain transaction’larına dair uygulamalı örnekler yapacağım. Yeni bir transaction’ın oluşturulması, gönderilmesi, blok içerisine dahil edilmesi, bloğun maden edilmesi, harcanmamış işlem çıktılarının (UTXO) listelenmesi gibi işlemleri rpc istekleri yaparak uygulamalı olarak anlatacağım. Bu kısım kodlama yapacağımız ikinci kısım için altyapı olacaktır. Bitcoin ve blockchain’in nasıl çalıştığına dair oldukça fikir sahibi olabilirsiniz. Bu yazı içerisinde anlatılanlar için Golang diline hakim olmanız gerekmiyor. Terminal içinden komutları gönderebiliyor olmak yeterli.

Yazı içerisinde bahsedeceğim ve uygulayacağım bir çok kavramı şu iki yazı içerisinde okuyabilirsiniz.

Kurulum

Kullanacağımız ve ikinci kısımda kodlarında değişiklik yapacağımız btcd ve btcwallet uygulamaları Golang dili ile yazıldığı için Go geliştirme ortamının kurulu olması gerekiyor. İşletim sisteminize uygun yükleme adımları için şuraya bakabilirsiniz. https://golang.org/doc/install
Go ortamının kurulu olduğunu aşağıdaki komutlar ile doğrulayalabilirsiniz. Minimum Go versiyonu 1.11 olmalıdır. btcd ve btcwallet uygulamalarına komut satırından çalıştırabilmek için $GOPATH/bin dizinin PATH’e eklendiğinden emin olun.

$ sudo apt install golang 
$ echo 'export GOPATH=$HOME/go' >> ~/.bashrc
$ echo 'export PATH=${PATH}:${GOPATH}/bin' >> ~/.bashrc
$ source ~/.bashrc
$ go version
$ go env GOROOT GOPATH

btcd

btcd’yi yükleyip çalıştırabilmek için kaynak kodlarını indirelim ve Go ile yükleyelim. ‘go install’ komutu ile daha önce PATH e eklediğimiz ‘${GOPATH}/bin’ dizini altına btcd’nin gelmiş olduğunu görebiliriz.

$ git clone https://github.com/btcsuite/btcd $GOPATH/src/github.com/btcsuite/btcd$ cd $GOPATH/src/github.com/btcsuite/btcd$ GO111MODULE=on go install -v . ./cmd/...

Buraya kadar olan kısımda btcd bilgisayarınızda kurulmuş ve kullanıma hazırdır. Eğer komut satırından ‘btcd’ komutunu çalıştırırsanız Bitcoin main net e bağlandığını ve blokları indirmeye başladığını görebilirsiniz. Eğer uygulamayı durdurmazsanız tüm bitcoin verisini (~200GB) bilgisayarınıza indirir. Bu uygulama ile gerçek bitcoin ağı üzerinde işlemler yapabilirsiniz.

Mainnet’te çalışan btcd

Mainnet’te çalışmak için hem büyük boyutta veri indirilmesi gerektiği için hem de işlem yapmak için gerçek bitcoin gerektiği için işlemlerin tamamına simulasyon ağında yapacağız. Bunun için btcd’yi çalıştırırken “ — simnet” parametresini ekleyeceğiz. Simulasyon ağının gerçek bitcoin ağına ve test ağına göre farklılıkları var. Bunlardan bazıları;

  • Blok zorluğu CPU madenciliğe destek vermesi için oldukça düşüktür.
  • Ağın gizli kalması için node keşfine dair kodlar devre dışıdır.
  • Node keşfine dair portlar ve RPC portları farklıdır.
  • Main net bloklarıyla karışmaması için farklı bir byte dizilimi kullanılmakta.
  • Adresler main net’den farklı ön ekler ile üretilir (S-s)
  • Farklı bir genesis blok

btcwallet

İkinci olarak cüzdan işlemlerini yapabileceğiz ‘btcwallet’ uygulamasını kuracağız. Bu işlem sonucunda da yeni cüzdanlar tanımlayıp, transaction’lar gönderip alabileceğiz.

$ git clone https://github.com/btcsuite/btcwallet  $GOPATH/src/github.com/btcsuite/btcwallet$ cd $GOPATH/src/github.com/btcsuite/btcwallet$ GO111MODULE=on go install -v . ./cmd/...

Cüzdan uygulaması btcwallet, btcd uygulaması ile rpc üzerinden haberleşmektedir. Bunun için btcd’yi rpc isteklerini kabul edecek şekilde tekrar başlatmalıyız. Kullanıcı adı için ‘ — rpcuser’ ve şifre için ‘rpcpass’ parametrelerini ekliyoruz. Ayriyeten simulasyon ağında işlem yapacağımız için de ‘ — simnet’ parametresini ekliyoruz.

$ btcd --simnet --rpcuser=myuser --rpcpass=SomeDecentp4ssw0rd

Aynı rpc kullanıcı adı ve şifresi ile btcwallet uygulamasını başlatacağız. Cüzdan işlemleri yapabilmek için btcd’de olduğu gibi btcwallet uygulaması da çalışır halde kalmalıdır. Aynı şekilde bu da simulasyon ağında çalışacaktır.
btcwallet’ı çalıştırmak için öncelikli olarak yeni bir cüzdan oluşturulmalıdır.

$ btcwallet --simnet --username=myuser --password=SomeDecentp4ssw0rd --create

Komutu çalıştırdıktan sonra terminal üzerindeki adımlar izlenerek cüzdan şifresini, ekstra şifreleme istenip istenmediği, varsa mevcut bir cüzdan seed’i girilmesi gerekmektedir. Cüzdan şifresi daha sonraki komutlarda sorulacağı için not edilmelidir ([WALLETPASS]). Aynı şekilde, eğer sıfırdan cüzdan oluşturuluyorsa üretilen cüzdan seed’i de daha sonra kullanmak üzere bir yere not edilmelidir. Bu işlemler tamamlandıktan sonra cüzdan yaratılmış olacak. Aynı komutu ‘ — create’ parametresi olmadan çalıştıralım ve btcwallet uygulaması çalışır halde bırakalım.

$ btcwallet --simnet --username=myuser --password=SomeDecentp4ssw0rd

Her iki uygulama için de her seferinde parametreleri girmemek için config dosyalarına gerekli değerleri girelim. btcd için aşağıdaki komutları çalıştırıp, Home dizininde ‘.btcd’ klasörü ve altında da ‘btcd.conf’ dosyasını oluşturalım. Btcd çalışırken varsayılan olarak bu dosyayı okuyor ve içindekilere göre çalışıyor.

$ mkdir  ~/.btcd
$ nano ~/.btcd/btcd.conf
// Dosya içerisine aşağıdaki yapıştırıp kaydedelim
rpcuser=myuser
rpcpass=SomeDecentp4ssw0rd
simnet=1

btcwallet uygulaması için de aşağıdakileri çalıştıralım. Bu sayede btcd ve btcwallet uygulamalarına parametre vermeden çalıştırabiliriz. Config dosyalarını okuyup simülasyon ağında çalışacak ve tanımladığımız şifreler sayesinde birbiri ile iletişim kurabilecekler.

$ mkdir ~/.btcwallet 
$ nano ~/.btcwallet/btcwallet.conf
//Dosya içerisine aşağıdaki yapıştırıp kaydedelim
simnet=1
username=myuser
password=SomeDecentp4ssw0rd
btcdusername=myuser
btcdpassword=SomeDecentp4ssw0rd

Önce btcd’yi ve sonrasında btcwallet’ı ayrı ayrı terminallerde çalıştırırsak simülasyon ağı üzerinde çalışıp birbirine bağlandıklarını ve komut almaya hazır olduklarını görebiliriz.

btcd ve btcwallet simülasyon ağında çalışırken

btcctl

Çalışan btcd ve btcwallet üzerinde işlem yapmak için btcctl ile rpc istekleri göndermemiz gerekiyor. Yukarıda olduğu gibi btcctl için de config dosyasını oluşturalım.

$ mkdir ~/.btcctl
$ nano ~/.btcctl/btcctl.conf
// Dosya içerisine aşağıdaki yapıştırıp kaydedelim
rpcuser=myuser
rpcpass=SomeDecentp4ssw0rd
simnet=1

‘btcctl -l’ komutu ile çalıştırılabilir tüm konutları listeleyebiliriz. Komutların ikinci kısmında Wallet Server Commands ( — wallet) başlığı altında listelenen komutlar cüzdan işlemleri için kullanılacak komutlardır ve bunları çağırabilmek için ‘ — wallet’ parametresini eklememiz gerekmektedir.

btcctl ile btcwallet üzerinde işlem yapabilmek için cüzdan aşağıdaki komut ile açılmalıdır. Buradaki şifre cüzdan oluştururken girdiğimiz şifredir. 600 değeri ile de cüzdan 600 saniyeliğine açık tutulmaktadır. Bu komutu çalıştırdıktan sonra çalışmakta olan btcwallet üzerinde ‘The wallet has been temporarily unlocked’ mesajı görülebilir.

$ btcctl — wallet walletpassphrase “[WALLETPASS]” 600

Transaction, bloklar ve madencilik

Yazının bundan sonraki kısmında btcctl ile btcwallet’a komutlar göndererek farklı adresler tanımlayacak, yeni bir transaction oluşturacak ve oluşan transaction’ı bir blok içerisine dahil etmek için yeni bir bloğun maden edilmesini sağlayacağız.

https://medium.com/hepsiburadatech/blokzincirin-temelleri-e1bdf3f7e90c

Simülasyon ağını yeni başlattığımız için henüz üretilmiş hiç bir blok yok ve dolayısıyla hiç bir blok ödülü de olmadığı için simülasyon bitcoin blokchain’imizde tanımlı hiç bitcoin yoktur. Kendi adresimize tanımlı bitcoin’lerimiz olması için madencilik yapmalı ve blok ödüllerini almamız gerekmektedir.

Madencilik ile bloklar üretmek ve ürettiğimiz bloklar sonucunda verilecek bitcoin ödüllerini (50 BTC) sahiplenebilmek için btcd uygulamasını madencilik modunda başlatmamız gerekmektedir. Blok ödüllerini almak için yeni bir adres tanımlamamız gerekmektedir.

$ btcctl --wallet getnewaddress
>> Sb4WWUPuqAx4dXVxESdWwiMqsvKRRzTDFR

btcd uygulamasını durdurup üretilen adresi madencilik adresi olarak ayarlayıp tekrar başlatalım.

$ btcd --miningaddr=Sb4WWUPuqAx4dXVxESdWwiMqsvKRRzTDFR

Şu anda btcd yeni blok üretme komutlarını kabul eder ve üretilen blokların ödüllerini de tanımladığımız Sb4WWUPuqAx4dXVxESdWwiMqsvKRRzTDFR adresine tanımlar. Yeni bir blok üretmek için;

$ btcctl generate 1
>> 28edddc954e4d13c6df80f856554a347a132d3d3d5c56097bbf1e0440491e4fc

Bu komut sonucunda bir blok üretilmiştir ve üretilen blok hash’i de çıktı olarak verilmiştir. Ben çalıştırdığımda üretilen blok hash’i ‘28edddc954e4d13c6df80f856554a347a132d3d3d5c56097bbf1e0440491e4fc’ şeklindedir. Eğer btcd uygulamasına bakarsanız aşağıdaki gibi bir mesaj yazdığını görebiliriz.

[INF] MINR: Block submitted via CPU miner accepted (hash 28edddc954e4d13c6df80f856554a347a132d3d3d5c56097bbf1e0440491e4fc, amount 50 BTC)

Burada yazdığı gibi CPU madencisi ile yeni bir blok üretilmiş ve kabul edilmiştir. Blok ödülü de 50 BTC dir. Asıl Bitcoin ağında 2009–01–09 tarihinde üretilen ilk blok ödülü de aynı şekilde 50 BTC idi;

İlk Bitcoin bloğu

Üretilen blok detayına bakmak için aşağıdaki komutu çalıştıralım. Çıktıda da görüldüğü üzere blok içerisinde sadece bir transaction var ve bu da blok ödülü olan transaction dır.

$ btcctl --wallet getblock 28edddc954e4d13c6df80f856554a347a132d3d3d5c56097bbf1e0440491e4fc
Blok detayları

Transaction detayına bakmak için aşağıdaki komutu çalıştıralım.

$ btcctl --wallet gettransaction e0707d5ce9a17c8cbfbfea1eb03ee934f7de21af6b977054be5750cc2d3cbd7a
Transaction detayları

Yukarıdaki transaction detaylarında birkaç noktaya dikkat çekmek istiyorum. Görüldüğü üzere miktarı 50 BTC dir. Asıl Bitcoin ağında da olduğu gibi ilk başta blok ödülü 50 BTC olarak başlamakta. Tanımlandığı adres ise bizim btcd yi başlatırken verdiğimiz madencilik adresi. Bir diğer önemli nokta ise transaction’ın immature olması. Yani kullanıma hazır değil. Başka bir transaction’a girdi olarak veremeyiz.

İlk bloğu maden etmemize ve üzerimize tanımlı BTC olmasına rağmen eğer cüzdan bakiyesini sorgularsak bakiyemizin 0 olduğu görürürüz. Çünkü blok ödülünü kullanabilmemiz için en az 100 confirmation alması gerekmektedir. Bunun için toplu olarak 99 blok daha üretiyoruz.

$ btcctl --wallet getbalance
>> 0
$ btcctl generate 99

Eğer şimdi bakiyemiz sorgularsak ilk blokdan kazandığımız 50 BTC nin hesabımıza tanımlandığını görebiliriz.

$ btcctl --wallet getbalance
>> 50

Aynı transaction’ı sorgularsak artık 100 confirmation’a ulaştığını ve immature statüsünden çıktığını görebiliriz. Bu transaction’ı başka birinde girdi olarak kullanabiliriz, yani BTC gönderimi yapabiliriz.

Gerekli olgunluğa erişmiş bir transaction

Bitcoin Gönderimi

Artık 50 BTC’miz olduğuna göre bunu başkasına gönderebiliriz. Göndereceğimiz adres için yeni bir account oluşturalım ve bunun altında yeni bir adres yaratalım.

$ btcctl --wallet createnewaccount account1
$ btcctl --wallet getnewaddress account1
>> Sg6NCtYtfAScMtKgE4W7DEXgqdghX4wqbx

Açık olan cüzdanımızdan yeni yarattığımız adrese 20 BTC transfer edelim.

$ btcctl --wallet sendtoaddress Sg6NCtYtfAScMtKgE4W7DEXgqdghX4wqbx 20
>> 82d731fbaa886499c6be6ae5772dd9e0c40d420bc7c93ac786234094f34d2931

Üretilen transaction’ın detaylarına bakarsak henüz hiçbir confirmation almadığını ve bir blok içerisine dahil olmadığını görebiliriz. Detaylardaki işlem ücreti olan 0.00000224 BTC çıktıyı da not alalım, ilerde bahsedeceğim.

Yeni bir blok oluştursak bu transaction’ın da blok içerisine dahil olduğunu görebiliriz.

$ btcctl --wallet generate 1
>> 035aa643fd500e5fd630c1342f22025624e65d50de70fe91a86289131d647ed4
$ btcctl --wallet getblock 035aa643fd500e5fd630c1342f22025624e65d50de70fe91a86289131d647ed4

Aşağıdaki görselde görüldüğü üzere blok içerisinde iki transaction var; biri bizim yukarıda gönderdiğimiz transaction (82d73…) diğeri de yeni blok ödülü olan 50 BTC. Bu transaction’ların detayına da yukarıdaki gibi bakabiliriz.

Unspent Transaction Output

Blockchain deki en önemli konulardan biri unspent transation output’dur (UTXO). Harcanmamış işlem çıktıları olarak tarif edebileceğimiz UTXO lar, Bitcoin bakiyesini oluşturan çıktılardır. Bir adresin sahip olduğu toplam BTC bakiyesi, o adrese tanımlı harcanmamış işlem çıktılarının toplamıdır.

UTXO ları detaylı olarak şu yazıda açıklamaya çalıştım.

Örnek UTXO’lar

Yaptığımız transfer işlemleri ve kazandığımız blok ödülleri sonucunda sahip olduğumuz UTXO ları listelemek için şu komutu çalıştıralım.

$ btcctl --wallet listunspent

Aşağıdaki çıktıda görebileceğimiz üzere üç adet UTXO vardır; ilki yukarıdaki örneklerde account1 hesabına ait ‘Sg6N…’ adresine gönderdiğimiz 20 BTC lik transaction, ikincisi 20 BTC lik transfer için kullanılan 50 BTC lik transaction’dan kalan para üstü (30 BTC eksi işlem ücreti), üçüncüsü ise yeni bir blok maden ettiğimiz için yeterli olgunluğa (100 onay) erişmiş blok ödülü olan 50 BTC.

Yeni bir transfer işlemi daha yapıp UTXO’ları listelersek (yeni blok üretmeden) içlerinden birinin eksildiğini görebiliriz.

Yazı içerisinde kullanılan tüm komutları şurada toplu olarak görebilirsiniz.

Biraz da kod…

Yazının ikinci kısmında transaction’larla ilgili olarak, mevcut bitcoin de olmayan basit bir yeni özelliği kodlayacağım. Hem btcd hem de btcwallet uygulamalarında kod yazacak, mevcut kodları referans alarak, neyin nerede olduğunu ve niye kullanıldığını anlatarak uygulamaya çalışacağım.

Soru ve yorumlarınız için Discord üzerinden haberleşebiliriz.

--

--