Již čtyři měsíce pracuji na projektu s názvem „Vývoj nástrojů pro ochranu a správu dat ve vládních a průmyslových sektorech založených na blockchainu“.
Nyní bych vám rád řekl, jak jsem s tímto projektem začal, a nyní podrobně popíšu programový kód.
Toto je první článek ze série článků. Zde popisuji server a protokol. Ve skutečnosti může čtenář dokonce napsat své vlastní verze těchto prvků blockchainu.
Loni na hackathonu Digital Breakthrough přišli s nápadem udělat užitečný systém pro průmysl a digitální ekonomiku s využitím technologie distribuované účetní knihy, na vývoj byl vydán grant i Fond na podporu inovací (měl bych napsat samostatný článek o grantu, pro ty, kteří začínají dělat startupy ), a teď v pořádku.
Vývoj probíhá v jazyce Go a databáze, ve které jsou bloky uloženy, je LevelDB.
Hlavními částmi jsou protokol, server (na kterém běží TCP a WebSocket - první pro synchronizaci blockchainu, druhý pro připojení klientů, odesílání transakcí a příkazů například z JavaScriptu.
Jak bylo zmíněno, tento blockchain je potřeba především k automatizaci a ochraně výměny produktů mezi dodavateli a zákazníky, případně oběma v jedné osobě. Tito lidé nespěchají, aby si navzájem důvěřovali. Úkolem však není pouze vytvořit „šekovou knížku“ s vestavěným kalkulátorem, ale systém, který automatizuje většinu rutinních úkolů, které vznikají při práci s životním cyklem produktu. Bytekód, který je za tuto záležitost zodpovědný, jak je u blockchainů zvykem, je uložen ve vstupech a výstupech transakcí (samotné transakce jsou uloženy v blocích, bloky v LevelDB jsou předkódovány ve formátu GOB). Nejprve si promluvme o protokolu a serveru (neboli uzlu).
Protokol není složitý, jeho smyslem je přepnout se do režimu načítání nějakých dat, obvykle bloku nebo transakce, v reakci na speciální příkazový řádek, a je také potřeba pro výměnu zásob, aby uzel věděl, koho má je připojen a jak mají na práci (uzly připojené k synchronizační relaci se také nazývají „sousední“, protože jejich IP je známá a jejich stavová data jsou uložena v paměti).
Složky (adresáře, jak je Linux nazývá) v chápání programátorů Go se nazývají balíčky, takže na začátek každého souboru s kódem Go z tohoto adresáře píšou balíček název_složky_kde_tento_soubor se nachází. V opačném případě nebudete moci balíček odeslat do kompilátoru. Pro ty, kteří znají tento jazyk, to není žádné tajemství. Jedná se o tyto balíčky:
- Síťová komunikace (server, klient, protokol)
- Struktury uložených a přenášených dat (blok, transakce)
- Databáze (blockchain)
- Konsensus
- Skládaný virtuální počítač (xvm)
- Pomocné (kryptomety, typy), to je prozatím vše.
Toto je výuková verze, postrádá meziprocesovou interakci a několik experimentálních komponent, ale struktura odpovídá té, na které se vývoj provádí. Pokud máte v komentářích cokoli navrhnout, rád to vezmu v úvahu při dalším vývoji. A nyní k vysvětlení serveru a protokol.
Nejprve se podíváme na server.
Podprogram serveru funguje jako datový server, který běží nad protokolem TCP pomocí datových struktur z balíku protokolů.
Rutina používá následující balíčky: Server, protokol, Typy. V samotném balení tcp_server.go obsahuje datovou strukturu Sloužit.
type Serve struct {
Port string
BufSize int
ST *types.Settings
}
Může přijmout následující parametry:
- Síťový port, přes který se budou vyměňovat data
- Konfigurační soubor serveru ve formátu JSON
- Příznak pro spuštění v režimu ladění (soukromý blockchain)
Pokrok:
- Načte konfiguraci ze souboru JSON
- Je zkontrolován příznak režimu ladění: pokud je nastaven, plánovač síťové synchronizace se nespustí a blockchain se nenačte
- Inicializace struktury konfiguračních dat a spuštění serveru
Server
- Provádí spuštění TCP serveru a síťové interakce v souladu s protokolem.
- Má datovou strukturu Serve sestávající z čísla portu, velikosti vyrovnávací paměti a ukazatele na strukturu typy.Nastavení
- Metoda Run spouští síťovou interakci (naslouchání příchozím spojením na daném portu, při přijetí nového spojení se jeho zpracování přenese do metody private handle v novém vlákně)
- В zacházet s data z připojení jsou načtena do vyrovnávací paměti, převedena na řetězcovou reprezentaci a předána do protokol.Výběr
- protokol.Výběr se vrací následek nebo způsobí chybu. následek poté přenesen do protokol.Interpretovatkterá se vrací intrpr - objekt typu InterpreteDatanebo způsobí chybu při zpracování výsledku výběru
- Poté se provede přepnutí intrpr.Commands[0] který kontroluje jedno z: výsledek, inv, chyba a je tam oddíl standardní
- V sekci následek přepínač je nalezen podle hodnoty intrpr.Commands[1] která kontroluje hodnoty délka vyrovnávací paměti и verze (v každém případě je volána odpovídající funkce)
funkce GetVersion и BufferLength jsou v souboru srvlib.go serverový balíček
GetVersion(conn net.Conn, version string)
jednoduše vytiskne do konzole a odešle verzi předanou v parametru klientovi:
conn.Write([]byte("result:" + version))
.
Funkce
BufferLength(conn net.Conn, intrpr *protocol.InterpreteData)
načte blok, transakci nebo jiná specifická data následovně:
- Vytiskne do konzole typ dat zadaný v protokolu, který je třeba přijmout:
fmt.Println("DataType:", intrpr.Commands[2])
- Přečte hodnotu intrpr.Tělo na číselnou proměnnou buf_len
- Vytvoří vyrovnávací paměť newbuf specifikovaná velikost:
make([]byte, buf_len)
- Odešle odpověď OK:
conn.Write([]byte("result:ok"))
- Zcela zaplní vyrovnávací paměť ze čteného streamu:
io.ReadFull(conn, newbuf)
.
- Vytiskne obsah vyrovnávací paměti do konzoly
fmt.Println(string(newbuf))
a počet přečtených bajtů
fmt.Println("Bytes length:", n)
- Odešle odpověď OK:
conn.Write([]byte("result:ok"))
Metody ze serverového balíčku jsou nakonfigurovány pro zpracování přijatých dat pomocí funkcí z balíčku protokol.
Protokol
Protokol slouží jako prostředek, který reprezentuje data v síťové výměně.
Choice(str string) (řetězec, chyba) provádí primární zpracování dat přijatých serverem, přijímá řetězcovou reprezentaci dat jako vstup a vrací řetězec připravený pro Tlumočník:
- Vstupní řetězec je rozdělen na hlavu a tělo pomocí ReqParseN2(str)
- hlava je rozdělena na prvky a umístěna do řezu příkazů pomocí ReqParseHead(head)
- В switch(příkazy[0]) vyberte přijatý příkaz (cmd, klíč, adresa nebo se sekce spustí standardní)
- 2 příkazy jsou kontrolovány v cmd switch(příkazy[1]) — délka и getversion.
- délka zkontroluje datový typ příkazy[2] a uloží to do datový typ
- Kontroluje to tělo obsahuje hodnotu řetězce
len(body) < 1
- Vrátí řetězec odpovědi:
"result:bufferlength:" + datatype + "/" + body
- getversion vrátí řetězec
return "result:version/auto"
Tlumočník
Obsahuje strukturu InterpreteData a provádí sekundární zpracování dat vrácených z Výběr struny a formování objektů InterpreteData.
type InterpreteData struct {
Head string
Commands []string
Body string
IsErr bool
ErrCode int
ErrMessage string
}
Funkce
Interprete(str string) (*InterpreteData, error)
přijímá řetězec následek a vytvoří a vrátí odkaz na objekt InterpreteData.
Pokrok:
- Stejně tak, Výběr hlava a tělo jsou extrahovány pomocí ReqParseN2(str)
- hlava je rozdělena na prvky pomocí ReqParseHead(head)
- Objekt je inicializován InterpreteData a vrátí se ukazatel na něj:
res := &InterpreteData{
Head: head,
Commands: commands,
Body: body,
}
return res, nil
Tento objekt se používá v server.go hlavní balíček.
Klient
Klientský balíček obsahuje funkce TCPConnect и TCPResponseData.
Funkce
TCPConnect(s *types.Settings, data []byte, payload []byte)
funguje takto:
- Je vytvořeno připojení k připojení uvedenému v předaném objektu nastavení
net.Dial("tcp", s.Host + ":" + s.Port)
- Data předaná v parametru data se přenášejí:
conn.Write(data)
- Odpověď je přečtena
resp, n, _ := TCPResponseData(conn, s.BufSize)
a vytištěné na konzole
fmt.Println(string(resp[:n]))
- Pokud se přenese užitečné zatížení pak to předá dál
conn.Write(payload)
a také přečte odpověď serveru a vytiskne ji do konzoly
Funkce
TCPResponseData(conn net.Conn, bufsiz int) ([]byte, int, error)
vytvoří vyrovnávací paměť zadané velikosti, načte tam odpověď serveru a vrátí tuto vyrovnávací paměť a počet přečtených bajtů a také chybový objekt.
Klientský podprogram
Slouží k odesílání příkazů uzlovým serverům a také k získávání stručných statistik a testování.
Může přijímat následující parametry: konfigurační soubor ve formátu JSON, data, která mají být odeslána na server jako řetězec, cesta k souboru, který má být odeslán do užitečného zatížení, příznak emulace plánovače uzlů, typ přenášených dat jako číselná hodnota.
- Získání konfigurace
st := types.ParseConfig(*config)
- Pokud je předán příznak emu, spustí se rozvrhář
- Pokud je zadán příznak f označující cestu k souboru, načteme jeho data do fdb a obsah je odeslán na server
client.TCPConnect(st, []byte(CMD_BUFFER_LENGTH + ":" + strconv.Itoa(*t) + "/" + strconv.Itoa(fdblen)), fdb)
- Pokud není soubor zadán, pak se jednoduše odešlou data z příznaku -d:
client.TCPConnect(st, []byte(*data), nil)
To vše je zjednodušená reprezentace ukazující strukturu protokolu. Během vývoje se do jeho struktury přidává potřebná funkčnost.
Ve druhé části budu hovořit o datových strukturách pro bloky a transakce, ve 3 o serveru WebSocket pro připojení z JavaScriptu, ve 4 se podívám na plánovač synchronizace, dále na zásobníkový stroj, který zpracovává bytekód ze vstupů a výstupů, kryptografii a bazény pro výstupy.
Zdroj: www.habr.com