Blockchain izstrāde rūpniecībai, izmantojot Go. 1. daļa

Jau četrus mēnešus esmu strādājis pie projekta “Datu aizsardzības un pārvaldības rīku izstrāde valdības un rūpniecības sektoros, pamatojoties uz blokķēdi”.
Tagad es vēlētos jums pastāstīt par to, kā es sāku šo projektu, un tagad es detalizēti aprakstīšu programmas kodu.

Blockchain izstrāde rūpniecībai, izmantojot Go. 1. daļa

Šis ir pirmais raksts rakstu sērijā. Šeit es aprakstu serveri un protokolu. Patiesībā lasītājs pat var uzrakstīt savas versijas par šiem blokķēdes elementiem.

Un šeit ir otrā daļa — par blokķēdes un darījumu datu struktūrām, kā arī par paketi, kas realizē mijiedarbību ar datu bāzi.

Pērn Digital Breakthrough hakatonā radās ideja izveidot nozarei un digitālajai ekonomikai noderīgu sistēmu, izmantojot sadalītās virsgrāmatas tehnoloģiju, izstrādei tika piešķirta arī Inovāciju palīdzības fonda dotācija (jāraksta atsevišķi). raksts par dotāciju tiem, kas tikko sāk jaunuzņēmumus ), un tagad kārtībā.

Izstrāde notiek Go valodā, un datu bāze, kurā tiek glabāti bloki, ir LevelDB.
Galvenās daļas ir protokols, serveris (kurā darbojas TCP un WebSocket - pirmais blokķēdes sinhronizēšanai, otrais klientu savienošanai, transakciju un komandu nosūtīšanai no JavaScript, piemēram.

Kā jau minēts, šī blokķēde ir nepieciešama galvenokārt, lai automatizētu un aizsargātu preču apmaiņu starp piegādātājiem un klientiem vai abus vienā personā. Šie cilvēki nesteidzas uzticēties viens otram. Taču uzdevums ir ne tikai izveidot “čeku grāmatiņu” ar iebūvētu kalkulatoru, bet gan sistēmu, kas automatizē lielāko daļu rutīnas uzdevumu, kas rodas, strādājot ar produkta dzīves ciklu. Baitkods, kas ir atbildīgs par šo lietu, kā tas ir ierasts ar blokķēdēm, tiek saglabāts transakciju ieejās un izvadēs (paši darījumi tiek glabāti blokos, LevelDB bloki ir iepriekš kodēti GOB formātā). Vispirms pastāstīsim par protokolu un serveri (aka mezglu).

Protokols nav sarežģīts, tā būtība ir pārslēgties uz dažu datu, parasti bloka vai transakcijas, ielādes režīmu, reaģējot uz īpašu komandrindu, un tas ir nepieciešams arī krājumu apmaiņai, lai mezgls zinātu, kas to dara. ir savienots un kā viņiem ir darīšana (sinhronizācijas sesijai pievienotos mezglus sauc arī par “kaimiņos”, jo to IP ir zināms un to stāvokļa dati tiek glabāti atmiņā).

Mapes (direktorijus, kā tos sauc Linux) Go programmētāju izpratnē sauc par pakotnēm, tāpēc katra faila sākumā ar Go kodu no šī direktorija viņi raksta pakotni mape_nosaukums_kur_atrodas šis_fails. Pretējā gadījumā jūs nevarēsit ievadīt pakotni kompilatoram. Nu, tas nav noslēpums tiem, kas zina šo valodu. Šīs ir paketes:

  • Tīkla komunikācija (serveris, klients, protokols)
  • Uzglabāto un pārsūtīto datu struktūras (bloks, transakcija)
  • Datu bāze (blokķēde)
  • Vienprātība
  • Stacked virtuālā mašīna (xvm)
  • Palīgrīki (kriptogrāfija, veidi) - tas ir viss.

Šeit ir saite uz github

Šī ir izglītojoša versija, tai trūkst starpprocesu mijiedarbības un vairāku eksperimentālu komponentu, bet struktūra atbilst tai, uz kuras tiek veikta izstrāde. Ja jums ir ko ieteikt komentāros, ar prieku ņemšu vērā turpmākajā attīstībā. Un tagad par servera skaidrojumu un protokols.

Vispirms apskatīsim serveri.

Servera apakšprogramma darbojas kā datu serveris, kas darbojas virs TCP protokola, izmantojot datu struktūras no protokola pakotnes.

Rutīna izmanto šādas pakotnes: serveris, protokols, veidi. Pašā iepakojumā tcp_server.go satur datu struktūru Pasniedziet.

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

Tas var pieņemt šādus parametrus:

  • Tīkla ports, caur kuru tiks veikta datu apmaiņa
  • Servera konfigurācijas fails JSON formātā
  • Atzīmēt, lai darbotos atkļūdošanas režīmā (privātā blokķēde)

Progress:

  • Nolasa konfigurāciju no JSON faila
  • Atkļūdošanas režīma karodziņš ir pārbaudīts: ja tas ir iestatīts, tīkla sinhronizācijas plānotājs netiek palaists un blokķēde netiek ielādēta
  • Konfigurācijas datu struktūras inicializācija un servera palaišana

Server

  • Veic TCP servera palaišanu un tīkla mijiedarbību saskaņā ar protokolu.
  • Tam ir Serve datu struktūra, kas sastāv no porta numura, bufera lieluma un rādītāja uz struktūru veidi.Iestatījumi
  • Metode Run sāk tīkla mijiedarbību (noklausās ienākošos savienojumus noteiktā portā, kad tiek saņemts jauns savienojums, tā apstrāde tiek pārsūtīta uz privātā roktura metodi jaunā pavedienā)
  • В rokturis dati no savienojuma tiek nolasīti buferī, pārveidoti virknes attēlojumā un nosūtīti uz protokols.Izvēle
  • protokols.Izvēle atgriežas radīt vai rada kļūdu. radīt pēc tam pārsūtīts uz protokols.Interpretētkas atgriežas intrpr - tipa objekts InterpreteData, vai rada kļūdu atlases rezultāta apstrādē
  • Pēc tam tiek izpildīts slēdzis intrpr.Commands[0] kas pārbauda vienu no: rezultāts, inv, kļūda un ir sadaļa noklusējuma
  • Sadaļā radīt slēdzis tiek atrasts pēc vērtības intrpr.Commands[1] kas pārbauda vērtības bufera garums и versija (katrā gadījumā tiek izsaukta atbilstošā funkcija)

Funkcijas GetVersion и Bufera garums atrodas failā srvlib.go servera pakotne

GetVersion(conn net.Conn, version string)

tas vienkārši izdrukā uz konsoli un nosūta klientam parametrā nodoto versiju:

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

.
Funkcija

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

ielādē bloku, transakciju vai citus konkrētus datus šādi:

  • Izdrukā konsolei protokolā norādīto datu veidu, kas ir jāpieņem:
    fmt.Println("DataType:", intrpr.Commands[2])
  • Nolasa vērtību intrpr.Body uz skaitlisko mainīgo buf_len
  • Izveido buferi newbuf norādītais izmērs:
    make([]byte, buf_len)
  • Nosūta ok atbildi:
    conn.Write([]byte("result:ok"))
  • Pilnībā aizpilda buferi no lasīšanas straumes:
    io.ReadFull(conn, newbuf)

    .

  • Izdrukā bufera saturu konsolē
    fmt.Println(string(newbuf))

    un nolasīto baitu skaits

    fmt.Println("Bytes length:", n)
  • Nosūta ok atbildi:
    conn.Write([]byte("result:ok"))

Servera pakotnes metodes ir konfigurētas, lai apstrādātu saņemtos datus, izmantojot funkcijas no pakotnes protokols.

Protokols

Protokols kalpo kā līdzeklis, kas attēlo datus tīkla apmaiņā.

Izvēle (virkne) (virkne, kļūda) veic servera saņemto datu primāro apstrādi, saņem datu virknes attēlojumu kā ievadi un atgriež virkni, kas sagatavota Tulks:

  • Ievades virkne tiek sadalīta galvā un ķermenī, izmantojot ReqParseN2(str)
  • galva tiek sadalīta elementos un ievietota komandu slānī, izmantojot ReqParseHead(head)
  • В slēdzis (komandas[0]) izvēlieties saņemto komandu (cmd, atslēga, adrese vai sadaļa tiek aktivizēta noklusējuma)
  • 2 komandas tiek pārbaudītas cmd slēdzis(komandas[1]) — garums и getversion.
  • garums pārbauda ievadīto datu veidu komandas[2] un saglabā to datu tips
  • Pārbauda to ķermenis satur virknes vērtību
    len(body) < 1
  • Atgriež atbildes virkni:
    "result:bufferlength:" + datatype + "/" + body
  • getversion atgriež virkni
    return "result:version/auto"

Tulks

Satur InterpreteData struktūru un veic atgriezto datu sekundāro apstrādi Izvēle stīgas un objektu veidošana InterpreteData.

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

Funkcija

Interprete(str string) (*InterpreteData, error)

pieņem virkni radīt un izveido un atgriež atsauci uz objektu InterpreteData.

Progress:

  • Līdzīgi Izvēle galva un ķermenis tiek ekstrahēti, izmantojot ReqParseN2(str)
  • galva tiek sadalīta elementos, izmantojot ReqParseHead(galva)
  • Objekts tiek inicializēts InterpreteData un tiek atgriezts rādītājs uz to:

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

Šis objekts tiek izmantots server.go galvenā pakete.

Klients

Klienta pakotne satur funkcijas TCPConnect и TCPResponseData.

Funkcija

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

darbojas šādi:

  • Tiek izveidots savienojums ar savienojumu, kas norādīts nodoto iestatījumu objektā
    net.Dial("tcp", s.Host + ":" + s.Port)
  • Datu parametrā nodotie dati tiek pārsūtīti:
    conn.Write(data)
  • Atbilde tiek lasīta
    resp, n, _ := TCPResponseData(conn, s.BufSize)

    un uzdrukāts uz konsoles

    fmt.Println(string(resp[:n]))
  • Ja tiek pārsūtīts celtspēja tad nodod to tālāk
    conn.Write(payload)

    un arī nolasa servera atbildi, izdrukājot to konsolē

Funkcija

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

izveido norādītā izmēra buferi, tur nolasa servera atbildi un atgriež šo buferi un nolasīto baitu skaitu, kā arī kļūdas objektu.

Klienta apakšprogramma

Kalpo, lai nosūtītu komandas uz mezglu serveriem, kā arī iegūtu īsu statistiku un testēšanu.

Var pieņemt šādus parametrus: konfigurācijas fails JSON formātā, dati, kas jānosūta uz serveri kā virkne, ceļš uz failu, kas jānosūta uz lietderīgo slodzi, mezgla plānotāja emulācijas karogs, pārsūtīto datu veids kā skaitliskā vērtība.

  • Konfigurācijas iegūšana
    st := types.ParseConfig(*config)
  • Ja tiek nodots emu karogs, tas sākas plānotāja
  • Ja tiek nodrošināts f karogs, kas norāda ceļu uz failu, mēs ielādējam tā datus fdb un saturs tiek nosūtīts uz serveri
    client.TCPConnect(st, []byte(CMD_BUFFER_LENGTH + ":" + strconv.Itoa(*t) + "/" + strconv.Itoa(fdblen)), fdb)
  • Ja fails nav norādīts, dati no karoga tiek vienkārši nosūtīti -d:
    client.TCPConnect(st, []byte(*data), nil)

Tas viss ir vienkāršots attēlojums, kas parāda protokola struktūru. Izstrādes laikā tās struktūrai tiek pievienota nepieciešamā funkcionalitāte.

Otrajā daļā es runāšu par datu struktūrām blokiem un transakcijām, 3 par WebSocket serveri savienojuma izveidei no JavaScript, 4 apskatīšu sinhronizācijas plānotāju, tad steka mašīnu, kas apstrādā baitkodu no ieejām un izejām, kriptogrāfiju un baseini izvadiem.

Avots: www.habr.com

Pievieno komentāru