Bitcoin İle Konuşan Uygulamalar
Bitcoin Block Explorer Geliştiriyoruz
Bu yazı içerisinde Bitcoin blockchain’i ile veri alışverişi için gerekli olan adımlara örnekler vereceğim ve bitcoin verisini okuyan iki farklı uygulama geliştireceğim.
Yazı içerisindeki adımların takip edilebilmesi ve uygulamaların çalıştırılabilmesi için gereksinimler; Golang geliştirme ortamının kurulması, bir Bitcoin node çalıştırmak (btcd, bitcoind) ve yeni transaction ile yeni block’lar oluşturabiliyor olmak ve opsiyonel olarak Elastic Search çalıştırmak. Golang ile yazılmış Bitcoin full node uygulaması btcd’nin kurulumu ve kullanımına dair detaylı yazımı şurada bulabilirsiniz;
btcd’nin kurulumuna dair toplu olarak komutları şurada da bulabilirsiniz: https://github.com/farukterzioglu/btc-scraper/blob/master/setup_notes.md
Özetle yapılacak olan: Golang dilinin kurulumu, btcd, btcwallet ve btcctl uygulamalarının kurulması, btcd’nin mining mod ve transaction index mod ile çalıştırılması, rpc ile sorgulamak üzere yeni transaction ve block oluşturmak.
‘btcd’ çalıştırıldıktan sonra örnek olarak ‘btcctl’ uygulaması ile komut satırından rpc isteği gönderelim;
➜ / btcctl getinfo
{
"version": 120000,
"protocolversion": 70002,
"blocks": 1,
"timeoffset": 0,
"connections": 0,
"proxy": "",
"difficulty": 1,
"testnet": false,
"relayfee": 0.00001,
"errors": ""
}
Aynı isteği https ile göndereceğiz. ‘btcd’ uygulaması varsayılan olarak TSL sertifikası gerektirmektedir. Bunun için ‘btcd’ uygulamasının ürettiği sertifikayı kullanacağız; ~/.btcd/rpc.cer
Bu dosyayı https isteklerine TSL sertifikası olarak eklemeliyiz. Kullanım kolaylığı için sertifikayı kopyalayalım. $ cp ~/.btcd/rpc.cert /
Rpc istekleri yapabilmek için Basic Authentication kullanmalıyız ve kullanıcı adı/şifre olarak da ‘btcd’ kurulumu sırasında tanımladığımız şifreyi kullanıyoruz. (myuser/SomeDecentp4ssw0rd)
‘btcd’ yi simülasyon ağında çalıştırdığımız için varsayılan olarak ‘18556’ portundan rpc isteklerini kabul etmektedir. Portu config dosyası içinde değiştirebilirsiniz.
Http istekleri yapmak için herhangi Postman gibi bir aracı kullanabilirsiniz. Ben oldukça kullanışlı olan VS Code eklentisi REST Client’ı kullanacağım. ‘getinfo’ metodu için örnek bir http post isteği ve cevabı;
Yukarıdaki rpc isteğini özetlersek; https://127.0.0.1:18556 adresine, btcd’nin ürettiği tsl sertifikası ve basic authentication bilgileri ile POST isteği attık ve body içerisinde de btcd’nin kabul ettiği jsonrpc formatında ‘getinfo’ metodunu sorguladık. Sağda görüldüğü üzere, komut satırından aldığımız cevabın aynısı aldık. curl isteği de şu şekilde olacak;
➜ / curl --cacert /rpc.cert --user myuser:SomeDecentp4ssw0rd --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "getinfo", "params": [] }'
-H 'content-type: text/plain;' https://127.0.0.1:18556/
Btcd Rpc Client
Yukarıdaki rpc isteklerini kod içerisinden yapmak için Golang ile uygulamalar geliştireceğiz. Bir web-api ve bir de konsol uygulaması şeklinde iki uygulama olacak. Web-api uygulaması ile bitcoin node’una rpc istekleri atacak, en sonunda da Elastic Search üzerinde depolanmış bitcoin verisini sorgulayacağız. Konsol uygulaması ile de bitcoin blockchain’ini 7/24 dinleyecek, yeni blok ve transaction’ları Elastic Search veritabanına kaydedeceğim.
Bitcoin node’una (btcd) rpc istekleri atmak için btcd projesine dahil olan ‘rpcclient’ paketini kullanacağım;
https://github.com/btcsuite/btcd/tree/master/rpcclient
https://github.com/btcsuite/btcd/blob/master/docs/json_rpc_api.md
Aşağıdaki örnekte rpcclient paketini kullanarak bitcoin node’una ‘GetInfo’ isteğini atıyorum. Yukarıda bahsettiğim gibi isteklere tls sertifikasını eklememiz gerekiyor. Bunun için btcd’nin konumunu alıyorum ve bu dizindeki ‘rpc.cert’ dosyasını rpc isteklerinde kullanmak üzere ayarlıyorum.
(Basitlik açısından tüm örneklerde error handling’i kaldırdım.)
Block Explorer
Geliştireceğimiz web-api uygulaması hem bitcoin node’una rpc istekleri yapacak hem de elastic search veritabanı üzerinde bitcoin verisini sorgulayacak. Geliştirilen uygulama herhangi bir blockchain explorer için back-end olarak kullanılabilir.
Benzer olarak ‘Blockchain Explorer’ sitesi içerisinde blokları, transaction’ları ve adresleri sorgulayabilirsiniz;
Golang ile geliştirdiğim uygulamada http istekleri için Gorilla Mux paketini, endpoint’leri listemek için arayüz olarak da Swagger ı kullandım. Golang üzerinde swagger ile web-api geliştirmeye dair daha detaylı bir örneği şurada inceleyebilirsiniz;
Block Explorer uygulamasında ilk olarak şu endpointler olacak;
İlk endpoint, son blokdan başlayarak tüm blok hash’lerini dönecek olan
‘/btc-rpc/block’ endpoint’i.
Şimdiye kadar kaç adet blok üretilmiş öğrenmek için rpcclient üzerinden ‘GetBlockCount()’ metodunu çağırıyoruz. Daha sonra bu blokların hash’ini öğrenmek üzere ‘GetBlockHash(height)’ metodunu çağırıyoruz. Parametre olarak da hash’ini öğrenmek istediğimiz bloğun sıra sayısını veriyoruz.
Herhangi bir bloğun detayını (içindeki transaction’lar, üretildiği zaman, doğrulanma adeti vs.) öğrenmek için çağıracağımız endpoint
‘/btc-rpc/block/{BlockID}’. ‘BlockID’ değeri olarak, bir önceki endpoint ile elde ettiğimiz ve detayını öğrenmek istediğimiz bloğun hash’ini yazıyoruz. Bunun için yazacağımız kodlar aşağıdaki gibi. rpcclient üzerinden ‘GetBlockVerbose(hash)’ metodunu çağırıyoruz.
Sık sık kullanılan bir metod da ‘chainhash’ kütüphanesinden ‘.NewHashFromStr()’ metodu. String olarak verdiğimiz değeri metodlara gönderdiğimiz ‘Hash’ tipine çevirir.
Rpc isteği gönderdiğimiz son endpoint, hash’i bilinen bir transaction(tx)’ın detayları getirmekte. ‘/btc-rpc/tx/{TxHash}’ endpoint’i parametre olarak detaylarını görmek istediğimiz tx’ın hash’ini alıyor. Dönüş değer olarak da, tx’ın input, output, oluşturulma zamanı ve doğrulanma sayısı gibi değerlerini dönüyor. Bu metod içerisinde de yukarıda olduğu gibi string değerden hash elde etmek için ‘.NewHashFromStr(txHashStr)’ ve hash’i verilen tx’In detaylarını almak için de ‘.GetRawTransactionVerbose(txHash)’ metodunu çağırıyoruz.
Buraya kadar olan kısmın kodlarını şu commit ve dosyada bulabilirsiniz;
Block explorer’ı çalıştırmak için kodu indirip aşağıdaki komutlar ile çalıştırabilirsiniz. (btcd’nin çalışıyor olması gerekiyor)
$ git clone https://github.com/farukterzioglu/btc-scraper.git
$ cd btc-scraper
$ git checkout 05b33a60b68dab2b1ec282c1a6195703b13eb9f6
$ cd block-explorer
$ go run .
Bu adımlardan sonra http://localhost:8000/swaggerui/#/ linkine gidip endpointleri sorgulayabilirsiniz.
Diğer block explorer’lar yukarıda örneğini yaptığım gibi direkt rpc istekleri göndermemektedir. Daha öncesinde blockchain verisini herhangi bir veritabanında index’lemiştir ve istekleri blockchain yerine bu veritabanı üzerinden karşılamaktadır.
İkinci olarak örneğini yapacağım uygulama, Bitcoin blockchain’ini 7/24 dinleyen ve her bir blok üretildiğinde ilgili verileri Elastic Search veritabanına yazan bir uygulama olacak.
Bitcoin Indexer
Bitcoin blockchain’inde meydana gelen olaylarda (mempool’a yeni tx eklenmesi, yeni blok yaratılması gibi) bildirim almak için rpcclient’a web socket bağlantısı yaparak bazı notification’lar tanımlıyoruz.
Aşağıdaki örnekte gördüğümüz üzere, yeni bir blok yaratıldığında bildirim almak için ‘OnFilteredBlockConnected’ isminde bir notification handler tanımlıyoruz. Bu bildirimleri almak istediğimizi belirtmek için de rpcclient üzerinden ‘.NotifyBlocks()’ metodunu çağırıyoruz. Artık yeni bir blok üretildiğinde handler içerisindeki kod çalışacaktır.
İkinci olarak, mempool’a yeni bir tx eklendiğinde bildirim almak üzere ‘OnTxAcceptedVerbose’ handler’ını tanımlıyoruz. Bu tür bildirimleri almak için de ‘.NotifyNewTransactions(true)’ metodunu çağırıyoruz. Metot parametresi olarak gönderdiğimiz true değeri, dönüş değeri olarak aldığımız tx’ı verbose yani detaylı isteyip istemediğimizi belirtiyoruz. Eğer verbose=false olarak gönderirsek ‘OnTxAcceptedVerbose‘ yerine ‘OnTxAccepted’ bildirimini alacağız.
Son olarak da sadece ilgilendiğimiz adreslere bir tx gönderilirse veya belli output’lar harcanırsa bildirim almak üzere ‘OnRelevantTxAccepted’ handler’ını tanımlıyoruz. Bu bildirimleri almak üzere ‘LoadTxFilter’ metodunu çağırıyoruz. Bu metoda parametre olarak ilgilendiğimiz adreslerin listesini veya harcanma durumlarını takip etmek istediğimiz output’ların listesini gönderiyoruz. Aşağıdaki örnekte görüldüğü üzere sadece ‘SP6pS3u7RbtafiVKB9MAUNSPXaMc9U2YF5’ adresine gelen tx’ların bildirimini OnRelevantTxAccepted handler’ı ile alabileceğiz. Aşağıdaki örnekde dikkat ederseniz rpcclient ayarlarında ‘Enpoint’ değerini ‘ws’ olarak ayarladık, yani web socket (ws) bağlantısı yapıyoruz. Gerçek zamanlı bildirimleri ws ile alabiliyoruz.
Uygulamayı çalıştıralım ve örnek olarak yeni bir tx gönderip bir blok kazalım;
$ git clone https://github.com/farukterzioglu/btc-scraper.git
$ cd btc-scraper
$ git checkout ef3f0d8dd29da68b2d1eb8b13af5ef7f3a2ad63a
$ cd btc-indexer
$ go run .$ btcctl --wallet sendtoaddress SP6pS3u7RbtafiVKB9MAUNSPXaMc9U2YF5 1 7312cba0a25bdb3eff65740bc52356ff320d93419d210cc794002e69f8c7ce76 $ btcctl --wallet generate 1 [
"7cafa992f5950b653287f74986df436524942db50b453e304846281aaae1674b"
]
Yukarıda görüldüğü üzere ‘7312c…’ hash’li bir tx oluşturduk ve ‘7cafa…’ hash’li yeni bir blok ürettik. Bu işlemler için yakaladığımız bildirimler;
mempool’a yeni tx eklendiğinde, takip ettiğimiz adrese ait bit tx tanımlandığında ve yeni bir blok üretildiğinde 3 farklı bildirim aldık ve handler içerisinde tanımlanan komutlar çalıştı. btc-indexer’ın kodlarını şurada görebilirsiniz;
Blockchain verisinin Elastic Search veritabanına indexlemek için yeni blok üretilme bildirimlerini takip etmemiz gerekiyor. Yeni bir blok bildirimi aldığımızda kendi tanımladığım ‘BlockNotification’ tipindeki bildirimi bir channel üzerinden göndereceğim. Başka bir go routine içerisinde de channel üzerinden bu bildirimleri alıp, rpc client ile blockchain üzerinden daha fazla bilgiyi alarak Elastic üzerinde depolayacağım.
Yukarıdaki kodda gördüğümüz üzere, yeni blok bildirimlerini ‘blockChannel’ üzerinden gönderiyoruz. Bu bildirimi alan başka bir go routine, blockchain’e giderek daha detaylı bilgileri alıyor. Bunun sebebi, bildirim ile gelen veride detaylı tx bilgileri yer almaması. Tüm tx’ların input ve output’ları ile beraber blok detayını çekmek için ‘client.GetBlockVerboseTx(&hash)’ metodunu çağırıyoruz.
Buradan sonraki kısımda, elde ettiğimiz blok ve tx bilgilerini Elastic üzerinde depolamalıyız. Yazı içeriği çok karmaşık ve uzun olmaması için Elastic ile ilgili kısımlardan bahsetmiyorum. Kodların tamamlanmış halini Github üzerinde bulabilirsiniz. Aşağıdaki komutlar ile bitcoin-indexer uygulamasını çalıştırabiliriz;
$ git clone https://github.com/farukterzioglu/btc-scraper.git
$ cd btc-scraper
$ git checkout c4096a0f3504b684ee188abfb1df01c11655269a
$ docker-compose up -d Creating elasticsearch-container ... done$ cd btc-indexer
$ go run .Elasticsearch returned with code 200 and version 7.2.0
2019/07/16 23:00:53 Started to consume blocks...
2019/07/16 23:00:53 Started to consume transactions...
2019/07/16 23:00:53 Started to consume relevant transactions...
2019/07/16 23:00:53 Best block: 2, last processed block: 0
2019/07/16 23:00:53 processing block 1...
2019/07/16 23:00:53 processing block 2...
2019/07/16 23:00:53 Block connected: 70835e6d055257043d509300a708d58e365eeb9fe6b3a3affc0af478b72ec784, height: 1, tx count: 0
2019/07/16 23:00:54 Block connected: 28971a5c0f1c47d8f77281bbf96db9e420623968a5dad5177a879ff83a4c3234, height: 2, tx count: 0
2019/07/16 23:00:54 Last processed height : 1
2019/07/16 23:00:54 Last processed height : 2
2019/07/16 23:00:59 Updated db. Last processed : 2
Elastic search get api’si üzerinden sorguladığımızda aşağıdaki sonuçları alırız;
GET http://localhost:9200/btc-block/_search?size=1
GET http://localhost:9200/btc-transaction/_search?size=1
Kodların son halinde, önceki endpointlere ek olarak direkt olarak Elastic üzerinden blok ve tx’leri sorgulayabildiğimiz endpointler de ekledim. Block explorer’ın son halini çalıştırıp ‘http://localhost:8000/swaggerui/#/’ linkine gidersek ek olarak aşağıdaki endpointlerin (BTC başlığı) geldiğini görebiliriz.
‘/btc/*’ şeklindeki endpointler, Bitcoin client’a rpc istekleri göndermek yerine Elastic Search üzerinde index’lediğimiz verilerden sorgulamakta;
Tüm notification listesine ve dinlemek için gerekli metodların listesine şuradan erişebilirsiniz; https://gist.github.com/farukterzioglu/e4e71aa49e144b665f4ba6178fcf68f5#Notifications
Uygulamanın tamamlanmış hali;
Sorularınız ve iletişim için;
faruk.terzioglu [@] hotmail [.] com
https://twitter.com/farukterzioglu