Zhvillimi i Blockchain për industrinë duke përdorur Go. Pjesa 1

Prej katër muajsh kam punuar në një projekt të quajtur “Zhvillimi i mjeteve të mbrojtjes dhe menaxhimit të të dhënave në sektorët qeveritarë dhe industrialë bazuar në blockchain”.
Tani do të doja t'ju tregoja se si e fillova këtë projekt, dhe tani do të përshkruaj kodin e programit në detaje.

Zhvillimi i Blockchain për industrinë duke përdorur Go. Pjesa 1

Ky është artikulli i parë në një seri artikujsh. Këtu unë përshkruaj serverin dhe protokollin. Në fakt, lexuesi mund të shkruajë edhe versionet e tij të këtyre elementeve të blockchain.

Dhe këtu është pjesa e dytë — për strukturat e të dhënave blockchain dhe transaksioneve, si dhe për paketën që zbaton ndërveprimin me bazën e të dhënave.

Vitin e kaluar, në hackathon Digital Breakthrough, ata dolën me një ide për të bërë një sistem të dobishëm për industrinë dhe ekonominë dixhitale duke përdorur teknologjinë e librit të shpërndarë; gjithashtu u dha një grant për zhvillimin nga Fondi i Ndihmës për Inovacionin (duhet të shkruaj një të veçantë artikull për grantin, për ata që sapo kanë filluar startup-et ), dhe tani në rregull.

Zhvillimi bëhet në gjuhën Go, dhe baza e të dhënave në të cilën ruhen blloqet është LevelDB.
Pjesët kryesore janë protokolli, serveri (i cili drejton TCP dhe WebSocket - i pari për sinkronizimin e blockchain, i dyti për lidhjen e klientëve, dërgimin e transaksioneve dhe komandave nga JavaScript, për shembull.

Siç u përmend, ky blockchain nevojitet kryesisht për të automatizuar dhe mbrojtur shkëmbimin e produkteve midis furnitorëve dhe klientëve, ose të dyja në një person. Këta njerëz nuk nxitojnë t'i besojnë njëri-tjetrit. Por detyra nuk është vetëm krijimi i një "libër kontrolli" me një kalkulator të integruar, por një sistem që automatizon shumicën e detyrave rutinë që lindin kur punoni me ciklin e jetës së produktit. Bajtkodi që është përgjegjës për këtë çështje, siç është zakon me blockchains, ruhet në hyrjet dhe daljet e transaksioneve (vetë transaksionet ruhen në blloqe, blloqet në LevelDB janë të koduar paraprakisht në formatin GOB). Së pari, le të flasim për protokollin dhe serverin (aka node).

Protokolli nuk është i komplikuar, e gjithë qëllimi i tij është të kalojë në mënyrën e ngarkimit të disa të dhënave, zakonisht një bllok ose transaksion, në përgjigje të një linje komandimi të veçantë, dhe gjithashtu nevojitet për shkëmbimin e inventarit, në mënyrë që nyja të dijë se kush është. është i lidhur dhe si duhet të bëjnë biznesin (nyjet e lidhura për seancën e sinkronizimit quhen gjithashtu "fqinj" sepse IP-ja e tyre njihet dhe të dhënat e gjendjes së tyre ruhen në memorie).

Dosjet (direktoritë siç i quan Linux) në kuptimin e programuesve Go quhen paketa, kështu që në fillim të çdo skedari me kodin Go nga kjo direktori shkruajnë folder_name_where_this_file. Përndryshe, nuk do të jeni në gjendje ta ushqeni paketën te përpiluesi. Epo, kjo nuk është sekret për ata që e njohin këtë gjuhë. Këto janë paketat:

  • Komunikimi në rrjet (server, klient, protokoll)
  • Strukturat e të dhënave të ruajtura dhe të transmetuara (blloku, transaksioni)
  • Baza e të dhënave (blockchain)
  • Konsensusi
  • Makina virtuale e grumbulluar (xvm)
  • Ndihmës (kripto, lloje) kjo është e gjitha për momentin.

Këtu është lidhja me github

Ky është një version edukativ, i mungon ndërveprimi ndër-procesor dhe disa komponentë eksperimentalë, por struktura korrespondon me atë në të cilën po zhvillohet zhvillimi. Nëse keni diçka për të sugjeruar në komente, do të jem i lumtur ta marr parasysh në zhvillimin e mëtejshëm. Dhe tani për një shpjegim të serverit dhe protokoll.

Le të shohim së pari serverin.

Nënprogrami i serverit vepron si një server i të dhënave që funksionon në krye të protokollit TCP duke përdorur strukturat e të dhënave nga paketa e protokollit.

Rutina përdor paketat e mëposhtme: server, protokoll, Llojet. Në vetë paketimin tcp_server.go përmban strukturën e të dhënave Shërbej.

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

Mund të pranojë parametrat e mëposhtëm:

  • Porta e rrjetit përmes së cilës do të shkëmbehen të dhënat
  • Skedari i konfigurimit të serverit në formatin JSON
  • Flamuri për ekzekutimin në modalitetin e korrigjimit (blockchain privat)

Progresi:

  • Lexon konfigurimin nga skedari JSON
  • Flamuri i modalitetit të korrigjimit kontrollohet: nëse është vendosur, planifikuesi i sinkronizimit të rrjetit nuk niset dhe zinxhiri i bllokimit nuk është i ngarkuar
  • Inicializimi i strukturës së të dhënave të konfigurimit dhe fillimi i serverit

server

  • Kryen nisjen e serverit TCP dhe ndërveprimin e rrjetit në përputhje me protokollin.
  • Ajo ka një strukturë të dhënash Serve që përbëhet nga një numër porti, një madhësi buferi dhe një tregues për strukturën llojet.Cilësimet
  • Metoda Run fillon ndërveprimin e rrjetit (dëgjimi i lidhjeve hyrëse në një port të caktuar, kur merret një lidhje e re, përpunimi i saj transferohet në metodën e dorezës private në një thread të ri)
  • В trajtuar të dhënat nga lidhja lexohen në një buffer, konvertohen në një paraqitje të vargut dhe kalohen në protokolli.Zgjedhja
  • protokolli.Zgjedhja kthehet rezultat ose shkakton një gabim. rezultat më pas transferohet në protokolli.Interpretonie cila kthehet intrpr - objekt i llojit InterpreteData, ose shkakton një gabim në përpunimin e rezultatit të përzgjedhjes
  • Pastaj kaloni ekzekutohet intrpr.Komanda[0] i cili kontrollon një nga: rezultat, inv, gabim dhe ka një seksion parazgjedhur
  • Në seksionin rezultat kaloni gjendet sipas vlerës intrpr.Komanda[1] i cili kontrollon vlerat gjatësia e tamponit и version (në çdo rast thirret funksioni përkatës)

Funksionet GetVersion и Gjatësia e tamponit janë në dosje srvlib.go paketën e serverit

GetVersion(conn net.Conn, version string)

ai thjesht printon në tastierë dhe dërgon versionin e kaluar në parametrin te klienti:

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

.
Funksion

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

ngarkon një bllok, transaksion ose të dhëna të tjera specifike si më poshtë:

  • Printon në tastierë llojin e të dhënave të specifikuara në protokoll që duhet të pranohen:
    fmt.Println("DataType:", intrpr.Commands[2])
  • Lexon vlerën intrpr.Trupi në një ndryshore numerike buf_len
  • Krijon një tampon i ri madhësia e specifikuar:
    make([]byte, buf_len)
  • Dërgon një përgjigje në rregull:
    conn.Write([]byte("result:ok"))
  • Plotëson plotësisht buferin nga transmetimi i lexuar:
    io.ReadFull(conn, newbuf)

    .

  • Printon përmbajtjen e tamponit në tastierë
    fmt.Println(string(newbuf))

    dhe numri i bajteve të lexuara

    fmt.Println("Bytes length:", n)
  • Dërgon një përgjigje në rregull:
    conn.Write([]byte("result:ok"))

Metodat nga paketa e serverit janë konfiguruar për të përpunuar të dhënat e marra duke përdorur funksionet nga paketa protokoll.

Protokoll

Një protokoll shërben si një mjet që përfaqëson të dhënat në shkëmbimin e rrjetit.

Zgjedhja (string str) (string, gabim) kryen përpunimin parësor të të dhënave të marra nga serveri, merr një paraqitje të vargut të të dhënave si hyrje dhe kthen një varg të përgatitur për Përkthyes:

  • Vargu i hyrjes ndahet në kokë dhe trup duke përdorur ReqParseN2(rr)
  • koka ndahet në elementë dhe vendoset në një pjesë të komandave duke përdorur ReqParseHead(head)
  • В ndërprerës (komandat[0]) zgjidhni komandën e marrë (cmd, çelësi, adresa ose seksioni është shkaktuar parazgjedhur)
  • 2 komanda kontrollohen në cmd switch(komandat[1]) - gjatësia и getversion.
  • gjatësi kontrollon llojin e të dhënave komandat[2] dhe e ruan atë brenda tipi
  • Kontrollon atë trup përmban një vlerë vargu
    len(body) < 1
  • Kthen vargun e përgjigjes:
    "result:bufferlength:" + datatype + "/" + body
  • getversion kthen një varg
    return "result:version/auto"

Përkthyes

Përmban strukturën InterpreteData dhe kryen përpunim dytësor të të dhënave të kthyera nga Zgjedhje vargjet dhe formimi i objekteve InterpreteData.

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

Funksion

Interprete(str string) (*InterpreteData, error)

pranon një varg rezultat dhe krijon dhe kthen një referencë për objektin InterpreteData.

Progresi:

  • Аналогично Zgjedhje koka dhe trupi nxirren duke përdorur ReqParseN2(rr)
  • koka është e ndarë në elementë duke përdorur ReqParseHead(koka)
  • Objekti është inicializuar InterpreteData dhe një tregues për të kthehet:

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

Ky objekt përdoret në server.go kryesore e paketës.

klient

Paketa e klientit përmban funksionet TCPConnect и TCPRsponseData.

Funksion

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

funksionon si më poshtë:

  • Bëhet një lidhje me lidhjen e specifikuar në objektin e cilësimeve të kaluara
    net.Dial("tcp", s.Host + ":" + s.Port)
  • Të dhënat e kaluara në parametrin e të dhënave transmetohen:
    conn.Write(data)
  • Përgjigja lexohet
    resp, n, _ := TCPResponseData(conn, s.BufSize)

    dhe të printuara në tastierë

    fmt.Println(string(resp[:n]))
  • Nëse transferohet payload pastaj e kalon atë
    conn.Write(payload)

    dhe gjithashtu lexon përgjigjen e serverit, duke e printuar atë në tastierë

Funksion

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

krijon një buffer të madhësisë së specifikuar, lexon përgjigjen e serverit atje dhe kthen këtë buffer dhe numrin e bajteve të lexuara, si dhe një objekt gabimi.

Nënprogrami i klientit

Shërben për të dërguar komanda në serverët e nyjeve, si dhe për të marrë statistika dhe testime të shkurtra.

Mund të pranojë parametrat e mëposhtëm: skedari i konfigurimit në formatin JSON, të dhënat që do të dërgohen në server si një varg, shtegu drejt skedarit që do të dërgohet në ngarkesë, flamuri i emulimit të programuesit të nyjeve, lloji i të dhënave të transferuara si vlerë numerike.

  • Marrja e konfigurimit
    st := types.ParseConfig(*config)
  • Nëse kalohet flamuri emu, ai fillon sheduler
  • Nëse jepet flamuri f që tregon rrugën drejt skedarit, atëherë ne i ngarkojmë të dhënat e tij fdb dhe përmbajtja dërgohet në server
    client.TCPConnect(st, []byte(CMD_BUFFER_LENGTH + ":" + strconv.Itoa(*t) + "/" + strconv.Itoa(fdblen)), fdb)
  • Nëse skedari nuk është i specifikuar, atëherë të dhënat nga flamuri thjesht dërgohen -d:
    client.TCPConnect(st, []byte(*data), nil)

E gjithë kjo është një paraqitje e thjeshtuar që tregon strukturën e protokollit. Gjatë zhvillimit, strukturës së tij i shtohet funksionaliteti i nevojshëm.

Në pjesën e dytë do të flas për strukturat e të dhënave për blloqet dhe transaksionet, në 3 për serverin WebSocket për lidhje nga JavaScript, në 4 do të shikoj planifikuesin e sinkronizimit, më pas një makineri stack që përpunon bytekodin nga hyrjet dhe daljet, kriptografinë dhe pishina për rezultate.

Burimi: www.habr.com

Shto një koment