Už štyri mesiace pracujem na projekte s názvom „Vývoj nástrojov na ochranu a správu údajov vo vládnom a priemyselnom sektore založenom na blockchaine“.
Teraz by som vám rád povedal, ako som začal s týmto projektom, a teraz podrobne opíšem kód programu.
Toto je prvý článok zo série článkov. Tu popisujem server a protokol. V skutočnosti môže čitateľ dokonca napísať svoje vlastné verzie týchto prvkov blockchainu.
Minulý rok na hackathone Digital Breakthrough prišli s nápadom urobiť užitočný systém pre priemysel a digitálnu ekonomiku pomocou technológie distribuovanej účtovnej knihy, na vývoj bol udelený grant aj Fondom pomoci pri inováciách (mal by som napísať samostatný článok o grante, pre tých, ktorí začínajú robiť startupy ), a teraz po poriadku.
Vývoj prebieha v jazyku Go a databáza, v ktorej sú bloky uložené, je LevelDB.
Hlavnými časťami sú protokol, server (na ktorom beží TCP a WebSocket – prvý na synchronizáciu blockchainu, druhý na pripojenie klientov, odosielanie transakcií a príkazov z JavaScriptu napr.
Ako už bolo spomenuté, tento blockchain je potrebný predovšetkým na automatizáciu a ochranu výmeny produktov medzi dodávateľmi a zákazníkmi, prípadne oboma v jednej osobe. Títo ľudia sa neponáhľajú, aby si navzájom dôverovali. Úlohou však nie je len vytvoriť „šekovú knižku“ so vstavanou kalkulačkou, ale systém, ktorý automatizuje väčšinu rutinných úloh, ktoré vznikajú pri práci so životným cyklom produktu. Bytekód, ktorý je za túto záležitosť zodpovedný, ako je pri blockchainoch zvykom, je uložený vo vstupoch a výstupoch transakcií (samotné transakcie sú uložené v blokoch, bloky v LevelDB sú predkódované vo formáte GOB). Najprv si povedzme o protokole a serveri (známy ako uzol).
Protokol nie je zložitý, jeho zmyslom je prepnutie do režimu načítania nejakých údajov, zvyčajne bloku alebo transakcie, v reakcii na špeciálny príkazový riadok a je potrebný aj na výmenu zásob, aby uzol vedel, koho má ku ktorému je pripojený a ako majú robiť (uzly pripojené na synchronizačnú reláciu sa tiež nazývajú „susedné“, pretože ich IP je známa a ich stavové údaje sú uložené v pamäti).
Priečinky (adresáre ako ich Linux nazýva) v ponímaní programátorov Go sa nazývajú balíčky, takže na začiatok každého súboru s kódom Go z tohto adresára napíšu balíček názov_priečinku_kde_tento_súbor sa nachádza. V opačnom prípade nebudete môcť odovzdať balík kompilátoru. Pre tých, ktorí poznajú tento jazyk, to nie je žiadne tajomstvo. Ide o tieto balíčky:
- Sieťová komunikácia (server, klient, protokol)
- Štruktúry uložených a prenášaných údajov (blok, transakcia)
- Databáza (blockchain)
- Konsenzus
- Skladaný virtuálny počítač (xvm)
- Pomocné (kryptomety, typy), to je zatiaľ všetko.
Toto je vzdelávacia verzia, chýba jej medziprocesová interakcia a niekoľko experimentálnych komponentov, ale štruktúra zodpovedá tej, na ktorej prebieha vývoj. Ak máte čo navrhnúť v komentároch, rád to zohľadním pri ďalšom vývoji. A teraz vysvetlenie k serveru a protokol.
Najprv sa pozrime na server.
Podprogram servera funguje ako údajový server, ktorý beží nad protokolom TCP pomocou dátových štruktúr z balíka protokolov.
Rutina používa nasledujúce balíky: server, protokol, Typy. V samotnom balení tcp_server.go obsahuje dátovú štruktúru Slúžiť.
type Serve struct {
Port string
BufSize int
ST *types.Settings
}
Môže akceptovať nasledujúce parametre:
- Sieťový port, cez ktorý sa budú vymieňať údaje
- Konfiguračný súbor servera vo formáte JSON
- Príznak pre spustenie v režime ladenia (súkromný blockchain)
Pokrok:
- Načíta konfiguráciu zo súboru JSON
- Je skontrolovaný príznak režimu ladenia: ak je nastavený, plánovač synchronizácie siete sa nespustí a blockchain sa nenačíta
- Inicializácia štruktúry konfiguračných údajov a spustenie servera
server
- Vykonáva spustenie TCP servera a sieťovej interakcie v súlade s protokolom.
- Má dátovú štruktúru Serve pozostávajúcu z čísla portu, veľkosti vyrovnávacej pamäte a ukazovateľa na štruktúru typy.Nastavenia
- Metóda Run spúšťa sieťovú interakciu (počúvanie prichádzajúcich spojení na danom porte, pri prijatí nového spojenia sa jeho spracovanie prenesie do metódy private handle v novom vlákne)
- В rukoväť údaje z pripojenia sú načítané do vyrovnávacej pamäte, konvertované na reťazcovú reprezentáciu a odovzdané do protokol.Výber
- protokol.Výber sa vracia následok alebo spôsobí chybu. následok potom prenesený do protokol.Interpretovaťktorý sa vracia intrpr - objekt druhu InterpreteDataalebo spôsobí chybu pri spracovaní výsledku výberu
- Potom sa prepínač vykoná intrpr.Commands[0] ktorý kontroluje jeden z: výsledok, inv, chyba a je tam sekcia štandardné
- V sekcii následok prepínač sa nájde podľa hodnoty intrpr.Commands[1] ktorý kontroluje hodnoty dĺžka vyrovnávacej pamäte и verzia (v každom prípade sa volá zodpovedajúca funkcia)
Funkcia GetVersion и BufferLength sú v súbore srvlib.go serverový balík
GetVersion(conn net.Conn, version string)
jednoducho vytlačí do konzoly a odošle verziu odovzdanú v parametri klientovi:
conn.Write([]byte("result:" + version))
.
Funkcia
BufferLength(conn net.Conn, intrpr *protocol.InterpreteData)
načíta blok, transakciu alebo iné špecifické údaje takto:
- Vytlačí do konzoly typ údajov špecifikovaný v protokole, ktorý je potrebné akceptovať:
fmt.Println("DataType:", intrpr.Commands[2])
- Číta hodnotu intrpr.Telo na číselnú premennú buf_len
- Vytvára vyrovnávaciu pamäť newbuf špecifikovaná veľkosť:
make([]byte, buf_len)
- Odošle odpoveď v poriadku:
conn.Write([]byte("result:ok"))
- Úplne vyplní vyrovnávaciu pamäť z čítaného toku:
io.ReadFull(conn, newbuf)
.
- Vytlačí obsah vyrovnávacej pamäte do konzoly
fmt.Println(string(newbuf))
a počet prečítaných bajtov
fmt.Println("Bytes length:", n)
- Odošle odpoveď v poriadku:
conn.Write([]byte("result:ok"))
Metódy zo serverového balíka sú nakonfigurované na spracovanie prijatých údajov pomocou funkcií z balíka protokol.
Protokol
Protokol slúži ako prostriedok, ktorý reprezentuje dáta v sieťovej výmene.
Choice(str reťazec) (reťazec, chyba) vykoná primárne spracovanie údajov prijatých serverom, prijme reťazcovú reprezentáciu údajov ako vstup a vráti reťazec pripravený pre Tlmočník:
- Vstupný reťazec je rozdelený na hlavu a telo pomocou ReqParseN2(str)
- hlava je rozdelená na prvky a umiestnená do segmentu príkazov pomocou ReqParseHead(head)
- В switch(príkazy[0]) vyberte prijatý príkaz (cmd, kľúč, adresa alebo sa sekcia spustí štandardné)
- V cmd sa kontrolujú 2 príkazy switch(príkazy[1]) — dĺžka и getversion.
- dĺžka skontroluje typ údajov príkazy[2] a uloží ho do Dátový typ
- Kontroluje to telo obsahuje hodnotu reťazca
len(body) < 1
- Vráti reťazec odpovede:
"result:bufferlength:" + datatype + "/" + body
- getversion vráti reťazec
return "result:version/auto"
Tlmočník
Obsahuje štruktúru InterpreteData a vykonáva sekundárne spracovanie údajov vrátených z voľba reťazcov a formovanie objektov InterpreteData.
type InterpreteData struct {
Head string
Commands []string
Body string
IsErr bool
ErrCode int
ErrMessage string
}
Funkcia
Interprete(str string) (*InterpreteData, error)
prijíma reťazec následok a vytvorí a vráti odkaz na objekt InterpreteData.
Pokrok:
- podobne voľba hlava a telo sa extrahujú pomocou ReqParseN2(str)
- hlava je rozdelená na prvky pomocou ReqParseHead(head)
- Objekt sa inicializuje InterpreteData a vráti sa naň ukazovateľ:
res := &InterpreteData{
Head: head,
Commands: commands,
Body: body,
}
return res, nil
Tento objekt sa používa v server.go hlavný balík.
Zákazník
Klientsky balík obsahuje funkcie TCPConnect и TCPResponseData.
Funkcia
TCPConnect(s *types.Settings, data []byte, payload []byte)
funguje takto:
- Vytvorí sa pripojenie k pripojeniu špecifikovanému v odovzdanom objekte nastavení
net.Dial("tcp", s.Host + ":" + s.Port)
- Údaje odovzdané v parametri údajov sa prenesú:
conn.Write(data)
- Odpoveď je prečítaná
resp, n, _ := TCPResponseData(conn, s.BufSize)
a vytlačené na konzole
fmt.Println(string(resp[:n]))
- Ak sa prenesie užitočné zaťaženie potom to odovzdá
conn.Write(payload)
a tiež prečíta odpoveď servera a vytlačí ju do konzoly
Funkcia
TCPResponseData(conn net.Conn, bufsiz int) ([]byte, int, error)
vytvorí vyrovnávaciu pamäť zadanej veľkosti, načíta tam odpoveď servera a vráti túto vyrovnávaciu pamäť a počet prečítaných bajtov, ako aj chybový objekt.
Klientsky podprogram
Slúži na odosielanie príkazov na uzlové servery, ako aj na získanie krátkych štatistík a testovania.
Môže akceptovať nasledujúce parametre: konfiguračný súbor vo formáte JSON, údaje, ktoré sa majú odoslať na server ako reťazec, cesta k súboru, ktorý sa má odoslať do užitočného zaťaženia, príznak emulácie plánovača uzla, typ prenášaných údajov ako číselná hodnota.
- Získanie konfigurácie
st := types.ParseConfig(*config)
- Ak prejde príznak emu, spustí sa plánovač
- Ak je zadaný príznak f označujúci cestu k súboru, načítame jeho údaje do fdb a obsah sa odošle na server
client.TCPConnect(st, []byte(CMD_BUFFER_LENGTH + ":" + strconv.Itoa(*t) + "/" + strconv.Itoa(fdblen)), fdb)
- Ak súbor nie je zadaný, údaje z príznaku sa jednoducho odošlú -d:
client.TCPConnect(st, []byte(*data), nil)
Toto všetko je zjednodušená reprezentácia zobrazujúca štruktúru protokolu. Počas vývoja sa do jeho štruktúry pridáva potrebná funkcionalita.
V druhej časti budem hovoriť o dátových štruktúrach pre bloky a transakcie, v 3 o WebSocket serveri na pripojenie z JavaScriptu, v 4 sa pozriem na plánovač synchronizácie, potom na zásobníkový stroj, ktorý spracováva bytekód zo vstupov a výstupov, kryptografiu a bazény pre výstupy.
Zdroj: hab.com