Go'yu kullanarak endüstri için Blockchain geliştirme. Bölüm 1

Dört aydır “Kamu ve sanayi sektörlerinde blockchain tabanlı veri koruma ve yönetim araçlarının geliştirilmesi” adlı bir proje üzerinde çalışıyorum.
Şimdi sizlere bu projeye nasıl başladığımı anlatmak istiyorum ve şimdi programın kodunu detaylı bir şekilde anlatacağım.

Go'yu kullanarak endüstri için Blockchain geliştirme. Bölüm 1

Bu bir dizi makalenin ilk makalesidir. Burada sunucuyu ve protokolü anlatacağım. Aslında okuyucu bu blockchain öğelerinin kendi versiyonlarını bile yazabilir.

Ve işte ikinci bölüm — blockchain ve işlem veri yapılarının yanı sıra veritabanıyla etkileşimi uygulayan paket hakkında.

Geçen yıl, Dijital Atılım hackathon'unda, dağıtılmış defter teknolojisini kullanarak endüstri ve dijital ekonomi için faydalı bir sistem yapma fikri ortaya çıktı; ayrıca İnovasyon Yardım Vakfı tarafından geliştirilmesi için bir hibe verildi (ayrıca yazmalıyım) Startup yapmaya yeni başlayanlar için hibeyle ilgili makale) ve şimdi sırayla.

Geliştirme Go dilinde gerçekleşir ve blokların depolandığı veritabanı LevelDB'dir.
Ana parçalar protokol ve sunucudur (TCP ve WebSocket'i çalıştırır; birincisi blok zincirini senkronize etmek için, ikincisi istemcileri bağlamak, örneğin JavaScript'ten işlem ve komut göndermek için kullanılır).

Daha önce de belirtildiği gibi, bu blok zincirine öncelikle tedarikçiler ve müşteriler arasında veya her ikisi arasında tek bir kişi arasında ürün alışverişini otomatikleştirmek ve korumak için ihtiyaç duyulmaktadır. Bu insanların birbirlerine güvenmek için aceleleri yok. Ancak görev yalnızca yerleşik bir hesap makinesine sahip bir "çek defteri" oluşturmak değil, aynı zamanda ürün yaşam döngüsüyle çalışırken ortaya çıkan rutin görevlerin çoğunu otomatikleştiren bir sistem oluşturmaktır. Bu konudan sorumlu olan bayt kodu, blockchainlerde alışılageldiği gibi işlemlerin giriş ve çıkışlarında depolanır (işlemlerin kendisi bloklar halinde saklanır, LevelDB'deki bloklar GOB formatında önceden kodlanmıştır). Öncelikle protokolden ve sunucudan (diğer adıyla node) bahsedelim.

Protokol karmaşık değildir, asıl amacı, özel bir komut satırına yanıt olarak genellikle bir blok veya işlem olmak üzere bazı verileri yükleme moduna geçmektir ve aynı zamanda envanter alışverişi için de gereklidir, böylece düğümün kim olduğunu bilmesi sağlanır. bağlı olup olmadığı ve nasıl iş yaptıkları (senkronizasyon oturumu için bağlanan düğümlere aynı zamanda "komşu" da denir çünkü IP'leri bilinir ve durum verileri bellekte saklanır).

Go programcılarının anlayışında klasörlere (Linux'un dediği gibi dizinler) paket adı verilir, bu nedenle her dosyanın başında Go koduyla bu dizinden paket klasör_adı_bura_bu_dosyanın bulunduğu yere yazarlar. Aksi takdirde paketi derleyiciye besleyemezsiniz. Bu dili bilenler için bu bir sır değil. Bunlar paketler:

  • Ağ iletişimi (sunucu, istemci, protokol)
  • Saklanan ve iletilen verilerin yapıları (blok, işlem)
  • Veritabanı (blok zinciri)
  • Uzlaşma
  • Yığılmış sanal makine (xvm)
  • Yardımcı (kripto, türler) şimdilik bu kadar.

İşte github'a bağlantı

Bu eğitici bir versiyondur, süreçler arası etkileşimden ve çeşitli deneysel bileşenlerden yoksundur, ancak yapı, geliştirmenin gerçekleştirildiği yapıya karşılık gelir. Yorumlarda önereceğiniz bir şey varsa, bunu daha sonraki geliştirmelerde dikkate almaktan memnuniyet duyarım. Şimdi sunucunun açıklamasına geçelim ve protokol.

Önce sunucuya bakalım.

Sunucu alt yordamı, protokol paketindeki veri yapılarını kullanarak TCP protokolünün üzerinde çalışan bir veri sunucusu görevi görür.

Rutin aşağıdaki paketleri kullanır: sunucu, protokol, türleri. Paketin kendisinde tcp_server.go veri yapısını içerir Servis.

type Serve struct {
	Port string
	BufSize int
	ST *types.Settings
}

Aşağıdaki parametreleri kabul edebilir:

  • Veri alışverişinin yapılacağı ağ bağlantı noktası
  • JSON formatında sunucu yapılandırma dosyası
  • Hata ayıklama modunda çalıştırma bayrağı (özel blockchain)

İlerlemek:

  • JSON dosyasından yapılandırmayı okur
  • Hata ayıklama modu bayrağı kontrol edilir: ayarlandıysa ağ senkronizasyon planlayıcısı başlatılmaz ve blok zinciri yüklenmez
  • Yapılandırma veri yapısını başlatma ve sunucuyu başlatma

sunucu

  • TCP sunucusunun başlatılmasını ve ağ etkileşimini protokole uygun olarak gerçekleştirir.
  • Bağlantı noktası numarası, arabellek boyutu ve yapıya işaretçiden oluşan bir Serve veri yapısına sahiptir. türleri.Ayarlar
  • Run yöntemi ağ etkileşimini başlatır (belirli bir bağlantı noktasındaki gelen bağlantıları dinler, yeni bir bağlantı alındığında, işlenmesi yeni bir iş parçacığında özel tanıtıcı yöntemine aktarılır)
  • В sap bağlantıdan gelen veriler bir ara belleğe okunur, dize temsiline dönüştürülür ve aktarılır protokol.Seçim
  • protokol.Seçim döner sonuç veya hataya neden olur. sonuç daha sonra transfer edildi protokol.Yorumlahangisi geri döner içeri giren - türün nesnesi Verileri Yorumlaveya seçim sonucunun işlenmesinde bir hataya neden oluyor
  • Daha sonra anahtar yürütülür intrpr.Komutlar[0] aşağıdakilerden birini kontrol eder: sonuç, yatırım, hata ve bir bölüm var varsayılan
  • Bölümde sonuç anahtar değere göre bulunur intrpr.Komutlar[1] değerleri kontrol eden arabellek uzunluğu и versiyon (her durumda karşılık gelen işlev çağrılır)

fonksiyonlar Sürümü Al и Tampon Uzunluğu dosyada var srvlib.go sunucu paketi

GetVersion(conn net.Conn, version string)

yalnızca konsola yazdırır ve parametrede iletilen sürümü istemciye gönderir:

conn.Write([]byte("result:" + version))

.
Fonksiyon

BufferLength(conn net.Conn, intrpr *protocol.InterpreteData)

bir bloğu, işlemi veya diğer belirli verileri aşağıdaki gibi yükler:

  • Kabul edilmesi gereken protokolde belirtilen veri türünü konsola yazdırır:
    fmt.Println("DataType:", intrpr.Commands[2])
  • Değeri okur iç gövde sayısal bir değişkene buf_len
  • Bir tampon oluşturur yeni belirtilen boyut:
    make([]byte, buf_len)
  • Tamam yanıtı gönderir:
    conn.Write([]byte("result:ok"))
  • Tamponu okuma akışından tamamen doldurur:
    io.ReadFull(conn, newbuf)

    .

  • Arabellek içeriğini konsola yazdırır
    fmt.Println(string(newbuf))

    ve okunan bayt sayısı

    fmt.Println("Bytes length:", n)
  • Tamam yanıtı gönderir:
    conn.Write([]byte("result:ok"))

Sunucu paketindeki yöntemler, paketteki işlevleri kullanarak alınan verileri işleyecek şekilde yapılandırılmıştır protokol.

Protokol

Bir protokol, ağ alışverişindeki verileri temsil eden bir araç görevi görür.

Seçim(str dize) (dize, hata) sunucu tarafından alınan verilerin birincil işlenmesini gerçekleştirir, girdi olarak verilerin dize temsilini alır ve bunun için hazırlanmış bir dize döndürür. Çevirmen:

  • Giriş dizesi kullanılarak baş ve gövdeye bölünür. ReqParseN2(str)
  • head, ReqParseHead(head) kullanılarak öğelere bölünür ve bir komut dilimine yerleştirilir.
  • В anahtar(komutlar[0]) alınan komutu seçin (cmd, anahtar, adres veya bölüm tetiklenir varsayılan)
  • Cmd'de 2 komut kontrol edilir switch(komutlar[1]) — uzunluk и dönüşüm.
  • uzunluk veri türünü kontrol eder komutlar[2] ve onu kaydeder veri tipi
  • Bunu kontrol eder vücut bir dize değeri içerir
    len(body) < 1
  • Yanıt dizesini döndürür:
    "result:bufferlength:" + datatype + "/" + body
  • dönüşüm bir dize döndürür
    return "result:version/auto"

Çevirmen

InterpreteData yapısını içerir ve döndürülen verilerin ikincil işlenmesini gerçekleştirir. Seçim dizeler ve nesne oluşumu Verileri Yorumla.

type InterpreteData struct {
	Head string
	Commands []string
	Body string
	IsErr bool
	ErrCode int 
	ErrMessage string
}

Fonksiyon

Interprete(str string) (*InterpreteData, error)

bir dizeyi kabul eder sonuç ve nesneye bir referans oluşturur ve döndürür Verileri Yorumla.

İlerlemek:

  • Benzer şekilde, Seçim baş ve vücut kullanılarak çıkarılır ReqParseN2(str)
  • kafa kullanılarak elemanlara bölünür ReqParseHead(baş)
  • Nesne başlatıldı Verileri Yorumla ve ona bir işaretçi döndürülür:

res := &InterpreteData{
	Head: head,
	Commands: commands,
	Body: body,
}
return res, nil

Bu nesne şuralarda kullanılır: sunucu.go paket ana.

müşteri

İstemci paketi işlevleri içerir TCP Bağlantısı и TCPYanıtVerileri.

Fonksiyon

TCPConnect(s *types.Settings, data []byte, payload []byte)

aşağıdaki gibi çalışır:

  • İletilen ayarlar nesnesinde belirtilen bağlantıya bağlantı kuruldu
    net.Dial("tcp", s.Host + ":" + s.Port)
  • Veri parametresinde iletilen veriler iletilir:
    conn.Write(data)
  • Cevap okundu
    resp, n, _ := TCPResponseData(conn, s.BufSize)

    ve konsola yazdırıldı

    fmt.Println(string(resp[:n]))
  • Aktarılırsa yük sonra aktarır
    conn.Write(payload)

    ve ayrıca sunucu yanıtını okur ve bunu konsola yazdırır

Fonksiyon

 TCPResponseData(conn net.Conn, bufsiz int) ([]byte, int, error)

belirtilen boyutta bir arabellek oluşturur, oradaki sunucu yanıtını okur ve bu arabelleği, okunan bayt sayısını ve bir hata nesnesini döndürür.

İstemci alt programı

Düğüm sunucularına komut göndermenin yanı sıra kısa istatistikler ve testler elde etmeye de hizmet eder.

Şu parametreleri kabul edebilir: JSON formatındaki yapılandırma dosyası, sunucuya dize olarak gönderilecek veriler, yüke gönderilecek dosyanın yolu, düğüm zamanlayıcı emülasyon bayrağı, sayısal değer olarak aktarılan veri türü.

  • Yapılandırmayı alma
    st := types.ParseConfig(*config)
  • Emu bayrağı geçilirse başlar planlayıcı
  • Dosyanın yolunu gösteren f bayrağı sağlanmışsa, verilerini fdb ve içerik sunucuya gönderilir
    client.TCPConnect(st, []byte(CMD_BUFFER_LENGTH + ":" + strconv.Itoa(*t) + "/" + strconv.Itoa(fdblen)), fdb)
  • Dosya belirtilmemişse bayraktaki veriler basitçe gönderilir -d:
    client.TCPConnect(st, []byte(*data), nil)

Bütün bunlar protokolün yapısını gösteren basitleştirilmiş bir gösterimdir. Geliştirme sırasında yapısına gerekli işlevsellik eklenir.

İkinci bölümde bloklar ve işlemler için veri yapılarından bahsedeceğim, 3'te JavaScript'ten bağlanmak için WebSocket sunucusundan bahsedeceğim, 4'te senkronizasyon zamanlayıcıya, ardından giriş ve çıkışlardan bayt kodunu işleyen bir yığın makinesine, kriptografiye ve çıkışlar için havuzlar.

Kaynak: habr.com

Yorum ekle