Python ile Blockchain Nasıl Oluşturulur 1. Bölüm

1 Yorum, 09 Aralık 2017 04:16, by Akif Çavdar, in

Blockchain Nasıl Oluşturulur 1. Bölüm

Oluşturma, D​epolama, Senkronize Etme, Gösterme, Madencilik ve İspat 

by 

 

2012 yılına döndüğümde, kayıt olduğum Coinbase hesabıma bakarak Bitcoin cüzdan geçmişimden işlem hareketlerini görebilirim. O zaman Bitcoin yaklaşık olarak 6.50 dolara işlem görüyordu. Eğer 0.1 BTC hala elimde olsaydı bu yazı yazıldığı sırada 500 dolardan fazla değere sahip olacaktı.

Bitcoin’den haberim olsada, pek ilgilenmemiştim. Dolar ile Bitcoin oranındaki yükselişleri ve düşüşleri görüyordum. İnsanların Bitcoin’in gelecekteki değeri hakkında konuşmalarını ve Bitcoin’in anlamsız olduğuna dair bir kaç makale görmüştüm. Onun hakkında hiç bir fikrim yoktu, sadece biraz takip ediyordum.

Aniden blockchain’i öğrenmeye karar verdim. İnternetteki blockchain makalelerini bularak onun hakkında araştırma yapmaya başladım. Bazısı iyi, bazısı kötü, bazıları da üst düzeydeydi.

Okumaya devam ettikçe biliyordum ki programlama ile elde edeceğim bilgi beni daha çok bilgiye yakınlaştırcaktı. O sebepten dolayı kendi basit yerel blok zincirimi yazmaya karar verdim.

Dikkat edilmesi gereken şey burada anlattığım basit blockchain ile profesyonel blockchain arasında bir takım farklar olacağıdır. Bu zincir, bir kripto para birimi oluşturmayacak. Blockchain ticarette kullanılan veya değiştirilebilen bir para üretmez. Blockchain bilgiyi saklamak ve doğrulamak için kullanılır. Paralar, teşvik düğümlerinin (incentive node) doğrulamaya katılmasında yardımcı olur, ancak paranın olmasına da gerek yoktur.

Bu yazının yazılma amacı insanların blockchaini öğrenebilmelerini sağlamak ve benim kodu sadece yazarak değil, kodu açıklayarak daha çok şey öğrenebilmem içindir.

Bu ilk yazıda, blockchain verilerinin saklanmasını, başlangıç bloğunun oluşturulmasını, yerel blockchain verileri ile bir düğümün nasıl senkronize olabileceğini, blockchainin nasıl görüntüleneceğini ve nasıl geçerli bloklar oluşturulacağını göstereceğim.

Süper yüksek seviyede bir blockchain, katılan herkesin verilerini saklayabileceği, görüntüleyebileceği, doğrulayabileceği ve asla silemeyeceği bir veritabanıdır.

Biraz daha alt düzeyde, bu bloktaki veriler, blockchain izin verdiği sürece herhangi bir şey olabilir. Örneğin, Bitcoin blok zincirindeki veriler, yalnızca hesaplar arasındaki Bitcoin işlemleridir. Ethereum block zinciri aynı şekilde, Ether işlemeleri ve ayrıca kod çalıştırmak için kullanılan işlemlere izin verir.

Bir blok, oluşturulmadan ve blok zincirine bağlanmadan önceki seviyede, düğüm diye adlandırılan bir çoğunluk tarafından doğrulanır. Gerçek blockchain, düğümlerin çoğunluğu tarafında doğru şekilde onaylanan, çok sayıda blok içeren bir zincirdir. Bunun anlamı eğer bir düğüm bir önceki bloğun verilerini değiştirmeyi denerse, düğümler uygunsuz bloğun verisine güvenmeyecek ve yeni blok geçerli olmayacak demektir.

Eğer part 1’in kodlarına bakmak isterseniz GitHub’dan ulaşabilirsiniz. Twitter adresimden de iletişime geçebilirsiniz.

 

1. Adım – Sınıflar ve Dosyalar

İlk adım, bir düğüm çalışırken blokları kullanan bir sınıf yazmaktır. Bu sınıfı Block olarak adlandıracağım. Aslında bu sınıfta pek bir şey yok, sadece __init__ fonksiyonunda gerekli bilgilerin bir dictionary içerisinde tutulmasını sağlayacağız. Bir üretim blockchain'i yazıyor olsaydım bu yaptığım pek akıllıca olmazdı, ancak kodu yazan bir tek ben olduğum için bu kod yeterli olacaktır. Ayrıca, önemli blok bilgilerini bir dict içerisine yazan bir method yazmak istiyorum ve eğer terminalden bir blok yazarsam, blok içerisindeki bilgileri göstermenin daha güzel bir yoluna sahip olacağım.

class Block(object):
  def __init__(self, dictionary):
  '''
    We're looking for index, timestamp, data, prev_hash, nonce
  '''
  for k, v in dictionary.items():
    setattr(self, k, v)
  if not hasattr(self, 'hash'): #in creating the first block, needs to be removed in future
    self.hash = self.create_self_hash()

  def __dict__(self):
    info = {}
    info['index'] = str(self.index)
    info['timestamp'] = str(self.timestamp)
    info['prev_hash'] = str(self.prev_hash)
    info['hash'] = str(self.hash)
    info['data'] = str(self.data)
    return info

  def __str__(self):
    return "Block<prev_hash: %s,hash: %s>" % (self.prev_hash, self.hash)

İlk bloğumuzu yarattığımızda, aşağıdaki bu basit kodu çalıştırabiliriz.

def create_first_block():
  # index zero and arbitrary previous hash
  block_data = {}
  block_data['index'] = 0
  block_data['timestamp'] = date.datetime.now()
  block_data['data'] = 'First block data'
  block_data['prev_hash'] = None
  block = Block(block_data)
  return block

Güzel. Bu bölümdeki son soru, verilerimizi dosya sisteminde nereye kaydedeceğiz. Bunu istememizin nedeni düğüm kapandığında yerel blok verilerimizi kaybetmemek.

Bir miktar Etherium Mist dosya şemasından kopya alarak, klasörün adını ‘chaindata’ verileri olarak adlandıracağım. Her blok indeksine dayalı olarak dosyalarının adlandırılmasına izin verilecek. Blokların sayısal sırada olması için ilk sıradaki dosya adının sıfır ile başlayıp başlamadığından emin olmalıyız.

Yukarıdaki kod ile ilk blok için aşağıdakini yazmalıyım.

#check if chaindata folder exists.
chaindata_dir = 'chaindata'
if not os.path.exists(chaindata_dir):
  #make chaindata dir
  os.mkdir(chaindata_dir)
  #check if dir is empty from just creation, or empty before
if os.listdir(chaindata_dir) == []:
  #create first block
  first_block = create_first_block()
  first_block.self_save()

 

2. Adım – Blockchain'i Local olarak Senkronize Etme

Bir düğüm başlattığınızda, madencilik ve veri yorumlamaya veya zincire yeni veri oluşturmaya/göndermeye başlamadan önce düğümü senkronize etmeniz gerekir. Başka bir düğüm olmamasından, lokal dosyadaki blokların okunmasından bahsediyorum. İleride, dosyalardan okuma, senkronizasyonun bir parçası olacak, ayrıca sizin düğümünüz çalışmıyorken, üretilen blokları toplamak için eşler (peer to peer) konuşuyor olacak.

def sync():
  node_blocks = []
  #We're assuming that the folder and at least initial block exists
  chaindata_dir = 'chaindata'
  if os.path.exists(chaindata_dir):
    for filename in os.listdir(chaindata_dir):
      if filename.endswith('.json'): #.DS_Store sometimes screws things up
        filepath = '%s/%s' % (chaindata_dir, filename)
        with open(filepath, 'r') as block_file:
          block_info = json.load(block_file)
          block_object = Block(block_info) #since we can init a Block object with just a dict
          node_blocks.append(block_object)
return node_blocks

Şimdilik güzel ve sade. String değerleri okurken ve onları veri yapıları içine yüklerken aşırı karmaşık kod gerekmiyor. Şİmdilik çalışıyor, ama sonraki postlarımda, sync fonksiyonu, farklı düğümlerin iletişim kurabilme yeteneğine kavuştuğunda daha karmaşık hale gelecek.

 

3.Adım – Blockchain'i Görüntüleme

Artı hafızamızda blockchain var ve ben zinciri tarayıcımızda göstermek istiyorum. Bunu yapmamın iki nedeni var. ilk olarak bir değişiklik olduğunda tarayıcıda onu doğrulamak. Ikicisi ise tarayıcı kullanarak işlemleri gönderme ve cüzdan yönetimi gibi şeyleri blockchainde görüntülemek ve onun üzerinde yeni eylemler gerçekleştirmek.

Flask’ı başlatmak etkileyici bir şekilde kolay olduğu için burada kullanıyorum.

Burada blockchain json kodu gösteriliyor. Yer kazanmak için 'import' satırlarını es geçtim.

node = Flask(__name__)

node_blocks = sync.sync() #inital blocks that are synced

@node.route('/blockchain.json', methods=['GET'])
def blockchain():
  '''
  Shoots back the blockchain, which in our case, is a json list of hashes
  with the block information which is:
  index
  timestamp
  data
  hash
  prev_hash
  '''
  node_blocks = sync.sync() #regrab the nodes if they've changed
  # Convert our blocks into dictionaries
  # so we can send them as json objects later
  python_blocks = []
  for block in node_blocks:
    python_blocks.append(block.__dict__())
  json_blocks = json.dumps(python_blocks)
  return json_blocks

if __name__ == '__main__':
  node.run()

Bu kodu çalıştırarak ve localhost:3000/blockchain.json sayfasını ziyaret ederek mevcut blokları görebilirsiniz.

 

4. Adım - “Madencilik”, ayrıca blok yaratmak olarak da bilinir

Tek bir başlangıç bloğumuz var. Eğer bizim saklamak ve dağıtmak için daha çok verimiz varsa, onları yeni bir blok içine dahil etmenin bir yolunu bulmamız gerekir. Soru, bir önceki bloğa bağlıyken nasıl yeni bir blok oluşturacağız.

Bitcoin dökümanında (whitepaper), Satoshi bunu şöyle tanımlıyor. Not: Burada ‘timestamp sunucusu’na ‘düğüm’ olarak değinilmektedir.

“Önerdiğimiz çözüm, bir zaman damgası (timestamp) sunucusu ile başlar. Bir timestamp sunucusu, bir blok öğeden oluşan hash değerini alarak ve hash değerini geniş ölçüde yayınlayarak çalışır ... Zaman damgası, verilerin o zaman içinde var olması gerektiğini kanıtlar. Her bir timestamp, zincir oluşturan bir önceki zaman damgasını içerir ve her timestamp kendisinden önce olanları ekler.”

İşte tanımlamanın bir ekran görüntüsü.

Bu bölümün bir özeti, blokları birbirine bağlamak için, blok oluşturma zamanı, önceki bloğun hash’i ve bloktaki bilgileri içeren yeni bir blok bilgisi hash’i oluşturuyoruz. Bu bilgi grubunu bloğun 'başlığı (header)' olarak adlandıracağım. Bu şekilde, bir bloğun doğruluğunu, önündeki tüm hashler üzerinden geçirip sırayı onaylayarak doğrulayabiliriz.

Oluşturduğum header şunları kapsıyor:

1. Index, blok numarasını belirtir

2. Bir önceki bloğun hash değeri

3. Veri, burada rasgele üretilmiş bir string değerdir. Bitcoin'de işlemler ile ilgili bilgi içerir ve Merkle root olarak adlandırılır.

4. Bloğun ne zaman oluşturulduğu (timestamp).

def generate_header(index, prev_hash, data, timestamp):
  return str(index) + prev_hash + data + str(timestamp)

Bilgileri hashlemeden önce string tipinde bilgi içeren bir başlık koymaya gerek yoktur. Çünkü herkes bir bloğun başlığının nasıl oluşturulacağını ve başlığın içinde bir önceki bloğun hash değerinin olması gerekliliğini bilir. Bu, herkesin yeni bloğun hash değerini doğrulayabilmesi ve iki blok arasındaki bağlantıyı onaylayabilmesi içindir.

Bitcoin header’ı, stringleri birleştirmekten çok daha karmaşıktır. Verilerin ve zamanın hash değerlerini kullanır ve baytların bilgisayar belleğinde nasıl depolandığına değinir. Ancak şu an için stringleri eklemek yeterlidir.

Bir başlığa sahibiz ve artık hash değerini hesaplamak istiyoruz. Benim hash hesaplama şeklim Bitcoin’nin yönteminden biraz farklı ama hala blok başlığını sha256 fonksiyonu ile hesaplıyorum.

def calculate_hash(index, prev_hash, data, timestamp, nonce):
  header_string = generate_header(index, prev_hash, data, timestamp, nonce)
  sha = hashlib.sha256()
  sha.update(header_string)
  return sha.hexdigest()

Son olarak, aşağıdaki fonksiyonu kullanarak yeni blok için hash değerini elde eder ve hash değerini yeni blokta saklarız, sonra bloğu chaindata klasörümüze kaydederiz.

node_blocks = sync.sync()

def mine(last_block):
  index = int(last_block.index) + 1
  timestamp = date.datetime.now()
  data = "I block #%s" % (int(last_block.index) + 1) #random string for now, not transactions
  prev_hash = last_block.hash
  block_hash = calculate_hash(index, prev_hash, data, timestamp)

  block_data = {}
  block_data['index'] = int(last_block.index) + 1
  block_data['timestamp'] = date.datetime.now()
  block_data['data'] = "I block #%s" % last_block.index
  block_data['prev_hash'] = last_block.hash
  block_data['hash'] = block_hash
  return Block(block_data)

def save_block(block):
  chaindata_dir = 'chaindata'
  filename = '%s/%s.json' % (chaindata_dir, block.index)
  with open(filename, 'w') as block_file:
    print new_block.__dict__()
    json.dump(block.__dict__(), block_file)

if __name__ == '__main__':
  last_block = node_blocks[-1]
  new_block = mine(last_block)
  save_block(new_block)

Bu şekilde bir blok oluşturmamıza rağmen, çok hızlı bir CPU’ya sahip olanlar, diğer düğümlerin gerçek olarak düşünebilecekleri fazlasıyla uzun bir zincir yaratabilirler. Blok oluşturmayı yavaşlatmanın bir yolunu bulmamız gerekli böylece sonraki bloğa geçmeden önce birbirimizi teyit edebiliriz.

 

5. Adım – Proof-of-Work

Yavaşlamak için, Bitcoin'in yaptığı gibi Proof-of-Work'i atıyorum. Bunu yapmanın yolu, bir bloğun hash değerinin belirli özelliklere sahip olma gereksinimini ayarlamaktır. Bitcoin'da olduğu gibi, bir sonraki karaktere geçmeden önce hash değerinin belirli sayıda sıfırdan başladığına emin olacağım. Bunu yapmanın yolu başlığa bir parça daha bilgi eklemektir - bir ‘nonce’.

def generate_header(index, prev_hash, data, timestamp, nonce):
  return str(index) + prev_hash + data + str(timestamp) + str(nonce)

Şimdi mine fonksiyonu hash değeri oluşturmak için ayarlanır, ancak bloğun hash değeri yeterli sıfırlarla sonuçlanmazsa, nonce değerini artırır, yeni header oluşturur, yeni hash değeri hesaplar ve yeterli sayıda sıfır olup olmadığını kontrol eder.

NUM_ZEROS = 4

def mine(last_block):
  index = int(last_block.index) + 1
  timestamp = date.datetime.now()
  data = "I block #%s" % (int(last_block.index) + 1) #random string for now, not transactions
  prev_hash = last_block.hash
  nonce = 0

  block_hash = calculate_hash(index, prev_hash, data, timestamp, nonce)
  while str(block_hash[0:NUM_ZEROS]) != '0' * NUM_ZEROS:
    nonce += 1
    block_hash = calculate_hash(index, prev_hash, data, timestamp, nonce)
  block_data = {}
  block_data['index'] = int(last_block.index) + 1
  block_data['timestamp'] = date.datetime.now()
  block_data['data'] = "I block #%s" % last_block.index
  block_data['prev_hash'] = last_block.hash
  block_data['hash'] = block_hash
  block_data['nonce'] = nonce
  return Block(block_data)

Mükemmel. Bu yeni blok geçerli nonce değerini içerir, böylece diğer düğümler hash değerini doğrulayabilir. Bu yeni bloğu geri kalanlar için üretebilir, kaydedebilir ve dağıtabiliriz.

 

Özet

Ve bu kadar! Şimdilik. Blockchain'e ilişkin dahil etmediğim tonlarca soru ve özellik var.

Örneğin, diğer düğümler nasıl dahil olur? Düğümler, bir bloğa dahil edilmek istedikleri verileri nasıl aktarırlar? Bilgileri sadece dev bir string dışında nasıl blokta saklayabiliriz? Dev string verileri içermeyen daha iyi bir tür başlık var mı?

Bu soruların çözümünde ilerleyeceğim serinin daha fazla kısmı olacak. Bu nedenle, hangi adımları görmek istediğinize dair önerileriniz varsa, twitter üzerinden bildirin, bu yazı hakkında yorum yapın veya iletişim kurun!

 

Kaynak: bigishdata.com

Yazar Hakkında

Akif Çavdar Akif Çavdar - Hacettepe Üniversitesi Bilgisayar Mühendisliği bölümünde öğrenim görmektedir.

1 Yorum

16 Ocak 2018 11:54

Blockchain ile ilgili çok bilgilendirici bir yazı olmuş. Teşekkürler.

Yorumunuzu Paylaşırsanız seviniriz.

Girdiğiniz e-posta adresi kimseyle paylaşılmaz. Sadece kimlik olarak kullanılır.