Razvoj blokovnih verig za industrijo z uporabo Go. 1. del

Že štiri mesece delam na projektu z naslovom »Razvoj orodij za zaščito in upravljanje podatkov v vladnih in industrijskih sektorjih, ki temeljijo na verigi blokov«.
Zdaj bi vam rad povedal, kako sem se lotil tega projekta, zdaj pa bom podrobno opisal programsko kodo.

Razvoj blokovnih verig za industrijo z uporabo Go. 1. del

To je prvi članek v seriji člankov. Tukaj opisujem strežnik in protokol. Pravzaprav lahko bralec celo napiše svoje različice teh elementov blockchaina.

In tukaj je drugi del — o blockchainu in transakcijskih podatkovnih strukturah ter o paketu, ki izvaja interakcijo z bazo podatkov.

Lani so na hackathonu Digital Breakthrough prišli na idejo, da bi s tehnologijo distributed ledger naredili uporaben sistem za industrijo in digitalno ekonomijo, za razvoj je bila izdana tudi nepovratna sredstva s strani Innovation Assistance Fund (moram napisati ločeno članek o nepovratnih sredstvih, za tiste, ki šele ustanavljate startupe ), zdaj pa po vrsti.

Razvoj poteka v jeziku Go, baza podatkov, v kateri so shranjeni bloki, pa je LevelDB.
Glavni deli so protokol, strežnik (ki poganja TCP in WebSocket – prvi za sinhronizacijo verige blokov, drugi za povezovanje strank, pošiljanje transakcij in ukazov iz JavaScripta, npr.

Kot smo že omenili, je ta blockchain potreben predvsem za avtomatizacijo in zaščito izmenjave izdelkov med dobavitelji in kupci ali obojimi v eni osebi. Ti ljudje se ne mudi zaupati drug drugemu. Toda naloga ni samo ustvariti "čekovno knjižico" z vgrajenim kalkulatorjem, temveč sistem, ki avtomatizira večino rutinskih opravil, ki se pojavijo pri delu z življenjskim ciklom izdelka. Bajtna koda, ki je odgovorna za to zadevo, je, kot je to običajno pri verigah blokov, shranjena v vhodih in izhodih transakcij (same transakcije so shranjene v blokih, bloki v LevelDB so predkodirani v formatu GOB). Najprej se pogovorimo o protokolu in strežniku (aka vozlišče).

Protokol ni zapleten, njegov cel namen je, da preklopi v način nalaganja nekega podatka, običajno bloka ali transakcije, kot odgovor na posebno ukazno vrstico, potreben pa je tudi za izmenjavo inventarja, da vozlišče ve, kdo je je povezan in kako morajo opraviti (vozlišča, povezana za sinhronizacijsko sejo, se imenujejo tudi »sosednja«, ker je njihov IP znan in so njihovi podatki o stanju shranjeni v pomnilniku).

Mape (imeniki, kot jih imenuje Linux) v razumevanju Go programerjev imenujemo paketi, zato na začetku vsake datoteke z Go kodo iz tega imenika zapišejo ime_paketa_paketa_kjer_je_ta_datoteka. V nasprotnem primeru ne boste mogli podati paketa v prevajalnik. No, to ni skrivnost za tiste, ki poznajo ta jezik. To so paketi:

  • Omrežna komunikacija (strežnik, odjemalec, protokol)
  • Strukture shranjenih in prenesenih podatkov (blok, transakcija)
  • Baza podatkov (veriga blokov)
  • Soglasje
  • Zložen virtualni stroj (xvm)
  • Pomožni (kripto, tipi), to je zaenkrat vse.

Tukaj je povezava do githuba

To je izobraževalna različica, nima medprocesne interakcije in več eksperimentalnih komponent, vendar struktura ustreza tisti, na kateri poteka razvoj. Če imate kaj predlagati v komentarjih, bom z veseljem to upošteval pri nadaljnjem razvoju. In zdaj za razlago strežnika in protokol.

Najprej si poglejmo strežnik.

Podprogram strežnika deluje kot podatkovni strežnik, ki deluje na vrhu protokola TCP z uporabo podatkovnih struktur iz paketa protokola.

Rutina uporablja naslednje pakete: strežnik, protokol, Vrste. V samem paketu tcp_server.go vsebuje strukturo podatkov Postrezite.

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

Sprejme lahko naslednje parametre:

  • Omrežna vrata, prek katerih se izmenjujejo podatki
  • Konfiguracijska datoteka strežnika v formatu JSON
  • Zastavica za izvajanje v načinu za odpravljanje napak (zasebna veriga blokov)

Napredek:

  • Prebere konfiguracijo iz datoteke JSON
  • Zastavica načina odpravljanja napak je preverjena: če je nastavljena, se načrtovalnik omrežne sinhronizacije ne zažene in veriga blokov ni naložena
  • Inicializacija konfiguracijske podatkovne strukture in zagon strežnika

Server

  • Izvaja zagon strežnika TCP in omrežno interakcijo v skladu s protokolom.
  • Ima podatkovno strukturo Serve, ki jo sestavljajo številka vrat, velikost medpomnilnika in kazalec na strukturo vrste.Nastavitve
  • Metoda Run zažene omrežno interakcijo (poslušanje dohodnih povezav na danih vratih, ko je prejeta nova povezava, se njena obdelava prenese na metodo zasebnega ročaja v novi niti)
  • В ročaj podatki iz povezave se preberejo v medpomnilnik, pretvorijo v predstavitev niza in posredujejo vanj protokol.Izbira
  • protokol.Izbira vrne povzroči ali povzroči napako. povzroči nato prenesli na protokol.Tolmačiki se vrača intrpr - predmet vrste InterpreteData, ali povzroči napako pri obdelavi rezultata izbire
  • Nato se izvede preklop intrpr.Ukazi[0] ki preverja enega od: rezultat, inv, napaka in obstaja razdelek privzeto
  • V razdelku povzroči stikalo najdemo po vrednosti intrpr.Ukazi[1] ki preverja vrednosti dolžina medpomnilnika и različica (v vsakem primeru se pokliče ustrezna funkcija)

Funkcije GetVersion и BufferLength so v datoteki srvlib.go strežniški paket

GetVersion(conn net.Conn, version string)

preprosto natisne na konzolo in odjemalcu pošlje različico, posredovano v parametru:

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

.
Funkcija

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

naloži blok, transakcijo ali druge specifične podatke, kot sledi:

  • V konzolo natisne vrsto podatkov, podanih v protokolu, ki jih je treba sprejeti:
    fmt.Println("DataType:", intrpr.Commands[2])
  • Prebere vrednost intrpr.Telo na številsko spremenljivko buf_len
  • Ustvari medpomnilnik newbuf določena velikost:
    make([]byte, buf_len)
  • Pošlje ok odgovor:
    conn.Write([]byte("result:ok"))
  • Popolnoma zapolni medpomnilnik iz bralnega toka:
    io.ReadFull(conn, newbuf)

    .

  • Natisne vsebino medpomnilnika na konzolo
    fmt.Println(string(newbuf))

    in število prebranih bajtov

    fmt.Println("Bytes length:", n)
  • Pošlje ok odgovor:
    conn.Write([]byte("result:ok"))

Metode iz strežniškega paketa so konfigurirane za obdelavo prejetih podatkov z uporabo funkcij iz paketa protokol.

Protokol

Protokol služi kot sredstvo, ki predstavlja podatke v omrežni izmenjavi.

Izbira(str niz) (niz, napaka) izvede primarno obdelavo podatkov, ki jih prejme strežnik, prejme nizovno predstavitev podatkov kot vhod in vrne niz, pripravljen za Tolmač:

  • Vhodni niz je razdeljen na glavo in telo z uporabo ReqParseN2(str)
  • glava je razdeljena na elemente in postavljena v rezino ukazov z uporabo ReqParseHead(head)
  • В stikalo (ukazi [0]) izberite prejeti ukaz (cmd, ključ, naslov ali se odsek sproži privzeto)
  • 2 ukaza sta preverjena v cmd stikalo(ukazi[1]) — dolžina и getversion.
  • dolžina preveri vrsto podatkov ukazi [2] in ga shrani v Podatkovni tip
  • To preverja telo vsebuje vrednost niza
    len(body) < 1
  • Vrne odgovorni niz:
    "result:bufferlength:" + datatype + "/" + body
  • getversion vrne niz
    return "result:version/auto"

Tolmač

Vsebuje strukturo InterpreteData in izvaja sekundarno obdelavo podatkov, vrnjenih iz Izbira strune in oblikovanje objektov InterpreteData.

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

Funkcija

Interprete(str string) (*InterpreteData, error)

sprejme niz povzroči ter ustvari in vrne sklic na objekt InterpreteData.

Napredek:

  • Podobno Izbira glava in telo sta ekstrahirana z uporabo ReqParseN2(str)
  • glava je razdeljena na elemente z uporabo ReqParseHead(head)
  • Objekt je inicializiran InterpreteData in vrne se kazalec nanj:

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

Ta predmet se uporablja v server.go glavni paket.

Pomoč

Paket odjemalca vsebuje funkcije TCPConnect и TCPResponseData.

Funkcija

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

deluje na naslednji način:

  • Vzpostavi se povezava s povezavo, podano v predmetu posredovanih nastavitev
    net.Dial("tcp", s.Host + ":" + s.Port)
  • Podatki, posredovani v podatkovnem parametru, se prenašajo:
    conn.Write(data)
  • Odgovor se glasi
    resp, n, _ := TCPResponseData(conn, s.BufSize)

    in natisnjena na konzoli

    fmt.Println(string(resp[:n]))
  • Če se prenese tovor potem ga posreduje naprej
    conn.Write(payload)

    in tudi prebere odgovor strežnika ter ga natisne na konzolo

Funkcija

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

ustvari vmesni pomnilnik podane velikosti, tam prebere odgovor strežnika in vrne ta vmesni pomnilnik in število prebranih bajtov ter objekt napake.

Odjemalski podprogram

Služi za pošiljanje ukazov strežnikom vozlišč, pa tudi za pridobivanje kratkih statističnih podatkov in testiranja.

Lahko sprejme naslednje parametre: konfiguracijsko datoteko v formatu JSON, podatke, ki bodo poslani strežniku kot niz, pot do datoteke, ki bo poslana koristnemu tovoru, zastavica emulacije načrtovalnika vozlišča, vrsta podatkov, prenesenih kot številska vrednost.

  • Pridobivanje konfiguracije
    st := types.ParseConfig(*config)
  • Če je zastavica emu posredovana, se zažene sheduler
  • Če je podana zastavica f, ki označuje pot do datoteke, naložimo njene podatke fdb in vsebina je poslana na strežnik
    client.TCPConnect(st, []byte(CMD_BUFFER_LENGTH + ":" + strconv.Itoa(*t) + "/" + strconv.Itoa(fdblen)), fdb)
  • Če datoteka ni navedena, se podatki iz zastavice preprosto pošljejo -d:
    client.TCPConnect(st, []byte(*data), nil)

Vse to je poenostavljena predstavitev, ki prikazuje strukturo protokola. Med razvojem se njegovi strukturi doda potrebna funkcionalnost.

V drugem delu bom govoril o podatkovnih strukturah za bloke in transakcije, v 3. o strežniku WebSocket za povezovanje iz JavaScripta, v 4. delu si bom ogledal sinhronizacijski razporejevalnik, nato stack machine, ki obdeluje bajtno kodo iz vhodov in izhodov, kriptografijo in bazeni za izhode.

Vir: www.habr.com

Dodaj komentar