Već četiri mjeseca radim na projektu pod nazivom “Razvoj alata za zaštitu i upravljanje podacima u državnom i industrijskom sektoru temeljen na blockchainu”.
Sada bih vam želio ispričati kako sam započeo ovaj projekt, a sada ću detaljno opisati programski kod.
Ovo je prvi članak u nizu članaka. Ovdje opisujem poslužitelj i protokol. Zapravo, čitatelj čak može napisati vlastitu verziju ovih elemenata blockchaina.
Prošle godine na hackathonu Digital Breakthrough došli su na ideju napraviti koristan sustav za industriju i digitalnu ekonomiju koristeći distributed ledger tehnologiju, također je izdana bespovratna sredstva za razvoj od strane Innovation Assistance Fund (trebao bih napisati zasebno članak o bespovratnim sredstvima, za one koji se tek počinju baviti startupima ), a sada po redu.
Razvoj se odvija u Go jeziku, a baza podataka u kojoj se pohranjuju blokovi je LevelDB.
Glavni dijelovi su protokol, poslužitelj (koji pokreće TCP i WebSocket – prvi za sinkronizaciju blockchaina, drugi za povezivanje klijenata, slanje transakcija i naredbi iz JavaScripta, na primjer.
Kao što je spomenuto, ovaj blockchain je prvenstveno potreban za automatizaciju i zaštitu razmjene proizvoda između dobavljača i kupaca, ili oboje u jednoj osobi. Ovi ljudi ne žure da vjeruju jedni drugima. Ali zadatak nije samo stvoriti "čekovnu knjižicu" s ugrađenim kalkulatorom, već sustav koji automatizira većinu rutinskih zadataka koji se javljaju pri radu sa životnim ciklusom proizvoda. Bytecode koji je odgovoran za tu stvar, kao što je uobičajeno kod blockchaina, pohranjen je u ulazima i izlazima transakcija (same transakcije su pohranjene u blokovima, blokovi u LevelDB-u su unaprijed kodirani u GOB formatu). Prvo, razgovarajmo o protokolu i poslužitelju (aka čvor).
Protokol nije kompliciran, cijela mu je poanta da se kao odgovor na posebnu naredbenu liniju prebaci u mod učitavanja nekog podatka, obično bloka ili transakcije, a potreban je i za razmjenu inventara, kako bi čvor znao tko je s kojim je povezan i kako imaju posla (čvorovi povezani za sesiju sinkronizacije nazivaju se i "susjedima" jer je njihov IP poznat, a podaci o stanju pohranjeni su u memoriji).
Mape (direktorije kako ih Linux naziva) u shvaćanju Go programera nazivaju se paketima, pa na početku svake datoteke s Go kodom iz ove mape napišu naziv mape_paketa_gdje_se_ova_datoteka nalazi. U suprotnom, nećete moći ubaciti paket u kompajler. Pa, to nije tajna za poznavaoce ovog jezika. Ovo su paketi:
- Mrežna komunikacija (poslužitelj, klijent, protokol)
- Strukture pohranjenih i prenesenih podataka (blok, transakcija)
- Baza podataka (blockchain)
- Konsenzus
- Složeni virtualni stroj (xvm)
- Pomoćni (kripto, vrste) to je sve za sada.
Ovo je edukativna verzija, nedostaje joj međuprocesna interakcija i nekoliko eksperimentalnih komponenti, ali struktura odgovara onoj na kojoj se odvija razvoj. Ako imate što predložiti u komentarima, rado ću to uzeti u obzir u daljnjem razvoju. A sada objašnjenje poslužitelja i protokol.
Pogledajmo prvo poslužitelj.
Podrutina poslužitelja djeluje kao podatkovni poslužitelj koji radi povrh TCP protokola koristeći podatkovne strukture iz paketa protokola.
Rutina koristi sljedeće pakete: server, protokol, vrste. U samom paketu tcp_server.go sadrži strukturu podataka Poslužiti.
type Serve struct {
Port string
BufSize int
ST *types.Settings
}
Može prihvatiti sljedeće parametre:
- Mrežni priključak preko kojeg će se razmjenjivati podaci
- Konfiguracijska datoteka poslužitelja u JSON formatu
- Oznaka za pokretanje u načinu rada za otklanjanje pogrešaka (privatni blockchain)
Napredak:
- Čita konfiguraciju iz JSON datoteke
- Oznaka načina otklanjanja pogrešaka je provjerena: ako je postavljena, planer mrežne sinkronizacije se ne pokreće i blockchain se ne učitava
- Inicijalizacija konfiguracijske strukture podataka i pokretanje poslužitelja
Server
- Provodi pokretanje TCP poslužitelja i mrežne interakcije u skladu s protokolom.
- Ima Serve podatkovnu strukturu koja se sastoji od broja porta, veličine međuspremnika i pokazivača na strukturu vrste.Postavke
- Metoda Run pokreće mrežnu interakciju (osluškuje dolazne veze na danom portu, kada se primi nova veza, njezina obrada se prenosi na metodu privatnog rukovanja u novoj niti)
- В rukovati podaci iz veze se čitaju u međuspremnik, pretvaraju u prikaz niza i prosljeđuju protokol.Izbor
- protokol.Izbor vraća rezultirati ili uzrokuje grešku. rezultirati zatim prebačen u protokol.Tumačitikoji se vraća intrpr - objekt vrste InterpreteData, ili uzrokuje pogrešku u obradi rezultata odabira
- Zatim se izvrši prekidač intrpr.Naredbe[0] koji provjerava jedno od: rezultat, inv, greška i postoji odjeljak zadani
- U odjeljku rezultirati prekidač se nalazi prema vrijednosti intrpr.Naredbe[1] koji provjerava vrijednosti duljina međuspremnika и verzija (u svakom slučaju poziva se odgovarajuća funkcija)
Funkcije GetVersion и BufferLength su u datoteci srvlib.go poslužiteljski paket
GetVersion(conn net.Conn, version string)
jednostavno ispisuje na konzolu i klijentu šalje verziju proslijeđenu u parametru:
conn.Write([]byte("result:" + version))
.
Funkcija
BufferLength(conn net.Conn, intrpr *protocol.InterpreteData)
učitava blok, transakciju ili druge specifične podatke na sljedeći način:
- Ispisuje na konzolu vrstu podataka navedenih u protokolu koje treba prihvatiti:
fmt.Println("DataType:", intrpr.Commands[2])
- Čita vrijednost intrpr.Tijelo na numeričku varijablu buf_len
- Stvara međuspremnik newbuf navedena veličina:
make([]byte, buf_len)
- Šalje ok odgovor:
conn.Write([]byte("result:ok"))
- Potpuno ispunjava međuspremnik iz toka čitanja:
io.ReadFull(conn, newbuf)
.
- Ispisuje sadržaj međuspremnika na konzolu
fmt.Println(string(newbuf))
i broj pročitanih bajtova
fmt.Println("Bytes length:", n)
- Šalje ok odgovor:
conn.Write([]byte("result:ok"))
Metode iz poslužiteljskog paketa konfigurirane su za obradu primljenih podataka pomoću funkcija iz paketa protokol.
Protokol
Protokol služi kao sredstvo koje predstavlja podatke u mrežnoj razmjeni.
Choice(str string) (string, error) obavlja primarnu obradu podataka primljenih od strane poslužitelja, prima string reprezentaciju podataka kao ulaz i vraća niz pripremljen za Tumač:
- Ulazni niz je podijeljen na glavu i tijelo korištenjem ReqParseN2(str)
- glava se dijeli na elemente i postavlja u isječak naredbi pomoću ReqParseHead(head)
- В prekidač (naredbe[0]) odaberite primljenu naredbu (cmd, ključ, adresa ili se dionica aktivira zadani)
- 2 naredbe se provjeravaju u cmd-u prekidač(naredbe[1]) — duljina и dobiti verziju.
- dužina provjerava tip podataka naredbe[2] i sprema ga u tip podataka
- Provjerava to tijelo sadrži vrijednost niza
len(body) < 1
- Vraća niz odgovora:
"result:bufferlength:" + datatype + "/" + body
- dobiti verziju vraća niz
return "result:version/auto"
Tumač
Sadrži strukturu InterpreteData i izvodi sekundarnu obradu podataka vraćenih iz Izbor nizovi i formiranje objekata InterpreteData.
type InterpreteData struct {
Head string
Commands []string
Body string
IsErr bool
ErrCode int
ErrMessage string
}
Funkcija
Interprete(str string) (*InterpreteData, error)
prihvaća niz rezultirati te stvara i vraća referencu na objekt InterpreteData.
Napredak:
- slično Izbor glava i tijelo se izvlače pomoću ReqParseN2(str)
- glava je podijeljena na elemente pomoću ReqParseHead(head)
- Objekt je inicijaliziran InterpreteData i vraća se pokazivač na njega:
res := &InterpreteData{
Head: head,
Commands: commands,
Body: body,
}
return res, nil
Ovaj objekt se koristi u server.go glavni paket.
Klijent
Paket klijenta sadrži funkcije TCPConnect и TCPResponseData.
Funkcija
TCPConnect(s *types.Settings, data []byte, payload []byte)
djeluje na sljedeći način:
- Veza se uspostavlja s vezom navedenom u proslijeđenom objektu postavki
net.Dial("tcp", s.Host + ":" + s.Port)
- Podaci proslijeđeni u parametru podataka prenose se:
conn.Write(data)
- Odgovor se čita
resp, n, _ := TCPResponseData(conn, s.BufSize)
i ispisuje se na konzoli
fmt.Println(string(resp[:n]))
- Ako se prenese korisna nosivost zatim ga prosljeđuje dalje
conn.Write(payload)
i također čita odgovor poslužitelja ispisujući ga na konzolu
Funkcija
TCPResponseData(conn net.Conn, bufsiz int) ([]byte, int, error)
stvara međuspremnik navedene veličine, tamo čita odgovor poslužitelja i vraća ovaj međuspremnik i broj pročitanih bajtova, kao i objekt pogreške.
Podrutina klijenta
Služi za slanje naredbi poslužiteljima čvorova, kao i za dobivanje kratkih statistika i testiranja.
Može prihvatiti sljedeće parametre: konfiguracijsku datoteku u JSON formatu, podatke koji se šalju poslužitelju kao niz, stazu do datoteke koja se šalje u korisni teret, oznaku emulacije planera čvora, vrstu podataka koji se prenose kao numeričku vrijednost.
- Dobivanje konfiguracije
st := types.ParseConfig(*config)
- Ako je proslijeđena emu zastavica, počinje sheduler
- Ako je navedena zastavica f koja označava stazu do datoteke, tada učitavamo njezine podatke fdb a sadržaj se šalje na poslužitelj
client.TCPConnect(st, []byte(CMD_BUFFER_LENGTH + ":" + strconv.Itoa(*t) + "/" + strconv.Itoa(fdblen)), fdb)
- Ako datoteka nije navedena, podaci iz oznake se jednostavno šalju -d:
client.TCPConnect(st, []byte(*data), nil)
Sve ovo je pojednostavljeni prikaz koji prikazuje strukturu protokola. Tijekom razvoja njegovoj se strukturi dodaje potrebna funkcionalnost.
U drugom dijelu govorit ću o podatkovnim strukturama za blokove i transakcije, u 3. o WebSocket poslužitelju za povezivanje iz JavaScripta, u 4. ću se osvrnuti na planer sinkronizacije, zatim stack stroj koji obrađuje bytecode iz ulaza i izlaza, kriptografiju i bazeni za izlaze.
Izvor: www.habr.com