Blockchain kūrimas pramonei naudojant Go. 1 dalis

Jau keturis mėnesius dirbu prie projekto, pavadinto „Duomenų apsaugos ir valdymo įrankių kūrimas vyriausybės ir pramonės sektoriuose, remiantis blokų grandine“.
Dabar norėčiau papasakoti apie tai, kaip pradėjau šį projektą, o dabar išsamiai aprašysiu programos kodą.

Blockchain kūrimas pramonei naudojant Go. 1 dalis

Tai pirmasis straipsnis iš straipsnių serijos. Čia aprašysiu serverį ir protokolą. Tiesą sakant, skaitytojas netgi gali parašyti savo šių blokų grandinės elementų versijas.

Ir štai antroji dalis - apie blokų grandinės ir operacijų duomenų struktūras, taip pat apie paketą, kuris įgyvendina sąveiką su duomenų baze.

Praėjusiais metais hakatone „Digital Breakthrough“ jiems kilo mintis sukurti pramonei ir skaitmeninei ekonomikai naudingą sistemą, naudojant paskirstytosios knygos technologiją, plėtrai buvo skirta ir Inovacijų paramos fondo dotacija (turėčiau parašyti atskirą). straipsnis apie dotaciją, tiems, kurie tik pradeda kurti startuolius ), ir dabar tvarka.

Kūrimas vyksta Go kalba, o duomenų bazė, kurioje saugomi blokai, yra LevelDB.
Pagrindinės dalys yra protokolas, serveris (kuriame veikia TCP ir „WebSocket“ – pirmasis skirtas „blockchain“ sinchronizavimui, antrasis – klientų prijungimui, operacijų ir komandų siuntimui iš „JavaScript“, pavyzdžiui.).

Kaip minėta, ši blokų grandinė pirmiausia reikalinga tam, kad būtų automatizuotas ir apsaugotas prekių mainai tarp tiekėjų ir klientų arba abiejų viename asmenyje. Šie žmonės neskuba pasitikėti vienas kitu. Tačiau užduotis yra ne tik sukurti „čekių knygelę“ su įmontuotu skaičiuotuvu, bet ir sistemą, kuri automatizuoja daugumą įprastų užduočių, kylančių dirbant su produkto gyvavimo ciklu. Už šį reikalą atsakingas baitų kodas, kaip įprasta su blokų grandinėmis, yra saugomas operacijų įvesties ir išvesties vietose (pačios operacijos saugomos blokuose, blokai LevelDB yra iš anksto užkoduoti GOB formatu). Pirmiausia pakalbėkime apie protokolą ir serverį (dar žinomą kaip mazgas).

Protokolas nesudėtingas, jo esmė yra perjungti į kai kurių duomenų, dažniausiai bloko ar operacijos, įkėlimo režimą, reaguojant į specialią komandinę eilutę, taip pat jis reikalingas apsikeitimui atsargomis, kad mazgas žinotų, kas jį turi. yra prijungtas ir kaip jie turi reikalų (mazgai, prijungti prie sinchronizacijos seanso, taip pat vadinami „kaimyniniais“, nes žinomas jų IP, o jų būsenos duomenys saugomi atmintyje).

Aplankai (katalogai, kaip juos vadina Linux) Go programuotojų supratimu, vadinami paketais, todėl kiekvieno failo su Go kodu iš šio katalogo pradžioje jie rašo paketo aplanko_pavadinimas_kur_kur yra šis_failas. Priešingu atveju negalėsite pateikti paketo kompiliatoriui. Na, tai ne paslaptis tiems, kurie moka šią kalbą. Tai yra paketai:

  • Tinklo ryšys (serveris, klientas, protokolas)
  • Saugomų ir perduodamų duomenų struktūros (blokas, operacija)
  • Duomenų bazė (blockchain)
  • Sutarimas
  • Sudėtinė virtuali mašina (xvm)
  • Pagalbinė priemonė (kriptovaliuta, tipai) kol kas viskas.

Čia yra nuoroda į github

Tai mokomoji versija, jai trūksta tarpprocesų sąveikos ir kelių eksperimentinių komponentų, tačiau struktūra atitinka tą, kurioje vykdoma plėtra. Jei turite ką pasiūlyti komentaruose, mielai atsižvelgsiu į tai toliau plėtojant. O dabar paaiškinimas apie serverį ir protokolas.

Pirmiausia pažiūrėkime į serverį.

Serverio paprogramė veikia kaip duomenų serveris, kuris veikia TCP protokolo viršuje, naudojant duomenų struktūras iš protokolo paketo.

Rutina naudoja šiuos paketus: serveris, protokolas, tipai. Pačioje pakuotėje tcp_server.go yra duomenų struktūra Tarnauti.

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

Jis gali priimti šiuos parametrus:

  • Tinklo prievadas, per kurį bus keičiamasi duomenimis
  • Serverio konfigūracijos failas JSON formatu
  • Vėliava, skirta veikti derinimo režimu (privati ​​blokų grandinė)

Progresas:

  • Nuskaito konfigūraciją iš JSON failo
  • Pažymima derinimo režimo vėliavėlė: jei ji nustatyta, tinklo sinchronizavimo planuoklis nepaleidžiamas ir blokų grandinė neįkeliama
  • Konfigūracijos duomenų struktūros inicijavimas ir serverio paleidimas

serverio

  • Vykdo TCP serverio paleidimą ir tinklo sąveiką pagal protokolą.
  • Ji turi aptarnavimo duomenų struktūrą, kurią sudaro prievado numeris, buferio dydis ir rodyklė į struktūrą tipai.Nustatymai
  • Vykdymo metodas pradeda tinklo sąveiką (klausomas įeinančių jungčių tam tikrame prievade, kai gaunamas naujas ryšys, jo apdorojimas perkeliamas į privačios rankenos metodą naujoje gijoje)
  • В tvarkyti duomenys iš ryšio nuskaitomi į buferį, konvertuojami į eilutės atvaizdavimą ir perduodami į protokolas.Pasirinkimas
  • protokolas.Pasirinkimas grįžta rezultatas arba sukelia klaidą. rezultatas tada perkelta į protokolas.Ištarkkuris grįžta intrpr - tipo objektas InterpreteData, arba sukelia klaidą apdorojant atrankos rezultatą
  • Tada jungiklis vykdomas intrpr.Commands[0] kuris patikrina vieną iš: rezultatas, inv, klaida ir yra skyrius numatytasis
  • Skyriuje rezultatas jungiklis randamas pagal vertę intrpr.Commands[1] kuri tikrina vertes buferio ilgis и versija (kiekvienu atveju iškviečiama atitinkama funkcija)

Funkcijos Gauti versiją и Buferio ilgis yra faile srvlib.go serverio paketą

GetVersion(conn net.Conn, version string)

jis tiesiog atspausdina į konsolę ir siunčia parametre perduotą versiją klientui:

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

.
Funkcija

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

įkelia bloką, operaciją ar kitus konkrečius duomenis taip:

  • Į konsolę atspausdinami protokole nurodyti duomenys, kuriuos reikia priimti:
    fmt.Println("DataType:", intrpr.Commands[2])
  • Skaito vertę intrpr.Kūnas į skaitinį kintamąjį buf_len
  • Sukuria buferį newbuf nurodytas dydis:
    make([]byte, buf_len)
  • Išsiunčia atsakymą gerai:
    conn.Write([]byte("result:ok"))
  • Visiškai užpildo skaitymo srauto buferį:
    io.ReadFull(conn, newbuf)

    .

  • Spausdina buferio turinį į konsolę
    fmt.Println(string(newbuf))

    ir nuskaitytų baitų skaičius

    fmt.Println("Bytes length:", n)
  • Išsiunčia atsakymą gerai:
    conn.Write([]byte("result:ok"))

Metodai iš serverio paketo sukonfigūruoti apdoroti gautus duomenis naudojant paketo funkcijas protokolas.

Protokolas

Protokolas yra priemonė, vaizduojanti duomenis keičiantis tinkle.

Pasirinkimas (eilutės eilutė) (eilutė, klaida) atlieka pirminį serverio gautų duomenų apdorojimą, gauna duomenų eilutės atvaizdavimą kaip įvestį ir grąžina eilutę, paruoštą Vertėjas:

  • Įvesties eilutė yra padalinta į galvą ir kūną naudojant ReqParseN2(str)
  • galvutė suskaidoma į elementus ir įtraukiama į komandų skiltį naudojant ReqParseHead(head)
  • В jungiklis (komandos[0]) pasirinkite gautą komandą (cmd, raktas, adresas arba sekcija suveikia numatytasis)
  • 2 komandos yra patikrintos cmd jungiklis(komandos[1]) — ilgis и gautiversija.
  • ilgis patikrina duomenų tipą komandos[2] ir išsaugo jį duomenų tipas
  • Patikrina tai kūnas yra eilutės reikšmė
    len(body) < 1
  • Grąžina atsako eilutę:
    "result:bufferlength:" + datatype + "/" + body
  • gautiversija grąžina eilutę
    return "result:version/auto"

Vertėjas

Turi InterpreteData struktūrą ir atlieka antrinį duomenų, grąžintų iš, apdorojimą Pasirinkimas stygos ir objektų formavimas InterpreteData.

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

Funkcija

Interprete(str string) (*InterpreteData, error)

priima eilutę rezultatas ir sukuria bei grąžina nuorodą į objektą InterpreteData.

Progresas:

  • Panašiai Pasirinkimas galva ir kūnas ištraukiami naudojant ReqParseN2(str)
  • galva yra padalinta į elementus naudojant ReqParseHead(head)
  • Objektas inicijuojamas InterpreteData ir grąžinama žymeklis į jį:

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

Šis objektas naudojamas server.go pagrindinis paketas.

klientas

Kliento pakete yra funkcijos TCPConnect и TCPResponseData.

Funkcija

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

veikia taip:

  • Užmezgamas ryšys su perduotų nustatymų objekte nurodytu ryšiu
    net.Dial("tcp", s.Host + ":" + s.Port)
  • Duomenų parametre perduoti duomenys perduodami:
    conn.Write(data)
  • Atsakymas skaitomas
    resp, n, _ := TCPResponseData(conn, s.BufSize)

    ir atspausdinta ant konsolės

    fmt.Println(string(resp[:n]))
  • Jei perkeliama Naudingoji apkrova tada perduoda
    conn.Write(payload)

    taip pat nuskaito serverio atsakymą, išspausdindamas jį į konsolę

Funkcija

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

sukuria nurodyto dydžio buferį, ten nuskaito serverio atsakymą ir grąžina šį buferį bei perskaitytų baitų skaičių bei klaidos objektą.

Kliento paprogramė

Tarnauja komandoms siųsti į mazgų serverius, taip pat gauti trumpą statistiką ir testavimą.

Gali priimti šiuos parametrus: konfigūracijos failas JSON formatu, duomenys, kurie turi būti siunčiami į serverį kaip eilutė, kelias į failą, siunčiamas į naudingą apkrovą, mazgo planavimo priemonės emuliacijos vėliavėlė, duomenų tipas, perduodamas kaip skaitinė reikšmė.

  • Konfigūracijos gavimas
    st := types.ParseConfig(*config)
  • Jei emu vėliavėlė perduodama, ji pradedama planuotojas
  • Jei pateikiama f vėliavėlė, nurodanti kelią į failą, tada įkeliame jo duomenis fdb ir turinys siunčiamas į serverį
    client.TCPConnect(st, []byte(CMD_BUFFER_LENGTH + ":" + strconv.Itoa(*t) + "/" + strconv.Itoa(fdblen)), fdb)
  • Jei failas nenurodytas, tada vėliavos duomenys tiesiog siunčiami -d:
    client.TCPConnect(st, []byte(*data), nil)

Visa tai yra supaprastintas vaizdas, parodantis protokolo struktūrą. Kūrimo metu į jo struktūrą pridedamas reikiamas funkcionalumas.

Antroje dalyje kalbėsiu apie duomenų struktūras blokams ir operacijoms, 3 apie WebSocket serverį, skirtą prisijungti iš JavaScript, 4-oje pažvelgsiu į sinchronizacijos planuoklį, tada dėklo mašiną, kuri apdoroja baitinį kodą iš įėjimų ir išėjimų, kriptografiją ir baseinai išėjimui.

Šaltinis: www.habr.com

Добавить комментарий