Vývoj blockchainu pro průmysl pomocí Go. Část 1

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.

Vývoj blockchainu pro průmysl pomocí Go. Část 1

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.

A tady je druhá část — o blockchainu a transakčních datových strukturách a také o balíčku, který implementuje interakci s databází.

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.

Zde je odkaz na github

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

Přidat komentář