Rozwój Blockchain dla przemysłu przy użyciu Go. Część 1

Od czterech miesięcy pracuję nad projektem pt. „Rozwój narzędzi do ochrony i zarządzania danymi w sektorach rządowym i przemysłowym w oparciu o blockchain”.
Teraz chciałbym opowiedzieć o tym jak zacząłem ten projekt, a teraz opiszę szczegółowo kod programu.

Rozwój Blockchain dla przemysłu przy użyciu Go. Część 1

To jest pierwszy artykuł z serii artykułów. Tutaj opisuję serwer i protokół. Tak naprawdę czytelnik może nawet napisać własne wersje tych elementów blockchain.

A oto druga część — o blockchainie i strukturach danych transakcyjnych, a także o pakiecie realizującym interakcję z bazą danych.

W zeszłym roku podczas hackatonu Digital Breakthrough wpadli na pomysł stworzenia użytecznego systemu dla przemysłu i gospodarki cyfrowej w oparciu o technologię rozproszonej księgi rachunkowej, przyznano także grant na rozwój z Funduszu Pomocy Innowacjom (powinienem napisać osobny artykuł o dotacji, dla tych, którzy dopiero zaczynają start-upy), a teraz po kolei.

Rozwój odbywa się w języku Go, a baza danych, w której przechowywane są bloki, to LevelDB.
Główne części to protokół, serwer (na którym działają TCP i WebSocket - pierwszy do synchronizacji blockchainu, drugi do łączenia klientów, wysyłania transakcji i poleceń na przykład z JavaScript).

Jak wspomniano, ten blockchain jest potrzebny przede wszystkim do automatyzacji i ochrony wymiany produktów pomiędzy dostawcami i klientami, lub obydwoma w jednej osobie. Ci ludzie nie spieszą się z zaufaniem sobie nawzajem. Ale zadaniem nie jest tylko stworzenie „książeczki czekowej” z wbudowanym kalkulatorem, ale systemu, który automatyzuje większość rutynowych zadań, które pojawiają się podczas pracy z cyklem życia produktu. Odpowiedzialny za tę sprawę kod bajtowy, jak to zwykle bywa w przypadku blockchainów, przechowywany jest na wejściach i wyjściach transakcji (same transakcje przechowywane są w blokach, bloki w LevelDB są prekodowane w formacie GOB). Najpierw porozmawiajmy o protokole i serwerze (czyli węźle).

Protokół nie jest skomplikowany, jego cała sprawa polega na tym, że w odpowiedzi na specjalną linię poleceń przechodzi w tryb ładowania niektórych danych, zwykle bloku lub transakcji, jest też potrzebny do wymiany inwentarza, aby węzeł wiedział, kogo jest podłączony i jakie mają zajęcia (węzły podłączone do sesji synchronizacji nazywane są również „sąsiadującymi”, ponieważ znany jest ich adres IP, a dane o ich stanie są przechowywane w pamięci).

Foldery (katalogi jak je nazywa Linux) w rozumieniu programistów Go nazywane są pakietami, zatem na początku każdego pliku z kodem Go z tego katalogu zapisują pakiet nazwa_folderu_gdzie_ten_plik się znajduje. W przeciwnym razie nie będziesz mógł przekazać pakietu do kompilatora. Cóż, nie jest to tajemnicą dla tych, którzy znają ten język. Oto pakiety:

  • Komunikacja sieciowa (serwer, klient, protokół)
  • Struktury przechowywanych i przesyłanych danych (blok, transakcja)
  • Baza danych (łańcuch bloków)
  • Zgoda
  • Skumulowana maszyna wirtualna (xvm)
  • Pomocnicze (krypto, typy) to wszystko na teraz.

Oto link do githuba

Jest to wersja edukacyjna, brakuje w niej interakcji międzyprocesowych i kilku elementów eksperymentalnych, ale struktura odpowiada tej, na której prowadzony jest rozwój. Jeśli masz coś do zasugerowania w komentarzach, chętnie wezmę to pod uwagę w dalszym rozwoju. A teraz wyjaśnienie serwera i protokół.

Przyjrzyjmy się najpierw serwerowi.

Podprogram serwera działa jak serwer danych, który działa na bazie protokołu TCP, korzystając ze struktur danych z pakietu protokołu.

Procedura korzysta z następujących pakietów: serwer, protokół, typy. W samym opakowaniu tcp_server.go zawiera strukturę danych Obsługiwać.

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

Może przyjąć następujące parametry:

  • Port sieciowy, przez który będą wymieniane dane
  • Plik konfiguracyjny serwera w formacie JSON
  • Flaga do działania w trybie debugowania (prywatny blockchain)

Postęp:

  • Odczytuje konfigurację z pliku JSON
  • Flaga trybu debugowania jest zaznaczona: jeśli jest ustawiona, harmonogram synchronizacji sieci nie jest uruchamiany, a łańcuch bloków nie jest ładowany
  • Inicjowanie struktury danych konfiguracyjnych i uruchamianie serwera

serwer

  • Wykonuje uruchomienie serwera TCP i interakcję sieciową zgodnie z protokołem.
  • Ma strukturę danych Serve składającą się z numeru portu, rozmiaru bufora i wskaźnika do struktury typy.Ustawienia
  • Metoda Run rozpoczyna interakcję sieciową (nasłuchuje połączeń przychodzących na danym porcie, po odebraniu nowego połączenia jego przetwarzanie jest przekazywane do metody private handle w nowym wątku)
  • В uchwyt dane z połączenia są wczytywane do bufora, konwertowane na reprezentację łańcuchową i przekazywane do protokół.Wybór
  • protokół.Wybór zwroty dalsze lub powoduje błąd. dalsze następnie przeniesiony do protokół. Interpretacjaktóry powraca wpr - obiekt typu Interpretuj danelub powoduje błąd w przetwarzaniu wyniku selekcji
  • Następnie następuje wykonanie przełącznika intrpr.Polecenia[0] który sprawdza jeden z: wynik, inw., błąd i jest odcinek domyślnym
  • W dziale dalsze przełącznik jest znajdowany według wartości intrpr.Polecenia[1] który sprawdza wartości długość bufora и wersja (w każdym przypadku wywoływana jest odpowiednia funkcja)

funkcje Pobierz wersję и Długość bufora są w pliku srvlib.go pakiet serwerowy

GetVersion(conn net.Conn, version string)

po prostu drukuje na konsoli i wysyła do klienta wersję przekazaną w parametrze:

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

.
Funkcja

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

ładuje blok, transakcję lub inne określone dane w następujący sposób:

  • Wypisuje na konsolę typ danych określony w protokole, który należy zaakceptować:
    fmt.Println("DataType:", intrpr.Commands[2])
  • Odczytuje wartość intrpr.Ciało do zmiennej numerycznej buf_len
  • Tworzy bufor nowybuf określony rozmiar:
    make([]byte, buf_len)
  • Wysyła poprawną odpowiedź:
    conn.Write([]byte("result:ok"))
  • Całkowicie wypełnia bufor ze strumienia odczytu:
    io.ReadFull(conn, newbuf)

    .

  • Wypisuje zawartość bufora na konsoli
    fmt.Println(string(newbuf))

    i liczbę odczytanych bajtów

    fmt.Println("Bytes length:", n)
  • Wysyła poprawną odpowiedź:
    conn.Write([]byte("result:ok"))

Metody z pakietu serwera są skonfigurowane tak, aby przetwarzać otrzymane dane za pomocą funkcji z pakietu protokół.

Protokół

Protokół służy jako środek reprezentujący dane w wymianie sieciowej.

Choice(str string) (str string, error) wykonuje podstawowe przetwarzanie danych otrzymanych przez serwer, otrzymuje ciąg znaków reprezentujący dane jako dane wejściowe i zwraca przygotowany ciąg znaków Interpretator:

  • Ciąg wejściowy jest dzielony na głowę i ciało za pomocą ReqParseN2(str)
  • head jest dzielony na elementy i umieszczany w wycinku poleceń za pomocą ReqParseHead(head)
  • В przełącznik(polecenia[0]) wybierz otrzymane polecenie (cmd, klucz, adres lub sekcja została uruchomiona domyślnym)
  • W cmd sprawdzane są 2 polecenia switch(commands[1]) — długość и uzyskaćwersję.
  • długość sprawdza typ danych polecenia[2] i zapisuje go w typ danych
  • Sprawdza to ciało zawiera wartość ciągu
    len(body) < 1
  • Zwraca ciąg odpowiedzi:
    "result:bufferlength:" + datatype + "/" + body
  • uzyskaćwersję zwraca ciąg
    return "result:version/auto"

Interpretator

Zawiera strukturę InterpreteData i wykonuje wtórne przetwarzanie zwracanych danych Wybór Struny i tworzenie obiektów Interpretuj dane.

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

Funkcja

Interprete(str string) (*InterpreteData, error)

akceptuje ciąg dalsze oraz tworzy i zwraca referencję do obiektu Interpretuj dane.

Postęp:

  • Podobnie Wybór Głowa i ciało są wydobywane za pomocą ReqParseN2(str)
  • głowa jest dzielona na elementy za pomocą ReqParseHead(głowa)
  • Obiekt został zainicjowany Interpretuj dane i zwracany jest wskaźnik do niego:

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

Obiekt ten jest używany w serwer.go pakiet główny.

klientem

Pakiet klienta zawiera funkcje Połączenie TCP и Dane odpowiedzi TCP.

Funkcja

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

działa w następujący sposób:

  • Nawiązywane jest połączenie z połączeniem określonym w przekazanym obiekcie ustawień
    net.Dial("tcp", s.Host + ":" + s.Port)
  • Dane przekazywane w parametrze data są przesyłane:
    conn.Write(data)
  • Odpowiedź została przeczytana
    resp, n, _ := TCPResponseData(conn, s.BufSize)

    i wydrukowane na konsoli

    fmt.Println(string(resp[:n]))
  • Jeśli przeniesiony ładowność potem przekazuje dalej
    conn.Write(payload)

    a także odczytuje odpowiedź serwera i drukuje ją na konsoli

Funkcja

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

tworzy bufor o określonej wielkości, odczytuje tam odpowiedź serwera i zwraca ten bufor oraz liczbę odczytanych bajtów, a także obiekt błędu.

Podprogram klienta

Służy do wysyłania poleceń do serwerów węzłów, a także uzyskiwania krótkich statystyk i testów.

Może akceptować następujące parametry: plik konfiguracyjny w formacie JSON, dane wysyłane do serwera w postaci ciągu znaków, ścieżkę do pliku wysyłanego do ładunku, flagę emulacji harmonogramu węzła, typ danych przesyłanych jako wartość liczbowa.

  • Pobieranie konfiguracji
    st := types.ParseConfig(*config)
  • Jeśli flaga emu zostanie przekazana, rozpoczyna się sheduler
  • Jeśli podana jest flaga f wskazująca ścieżkę do pliku, to ładujemy do niego jego dane fdb i treść jest wysyłana na serwer
    client.TCPConnect(st, []byte(CMD_BUFFER_LENGTH + ":" + strconv.Itoa(*t) + "/" + strconv.Itoa(fdblen)), fdb)
  • Jeśli plik nie zostanie określony, dane z flagi zostaną po prostu wysłane -d:
    client.TCPConnect(st, []byte(*data), nil)

Wszystko to jest uproszczoną reprezentacją pokazującą strukturę protokołu. W trakcie rozwoju do jego struktury dodawana jest niezbędna funkcjonalność.

W drugiej części opowiem o strukturach danych dla bloków i transakcji, w 3 o serwerze WebSocket do łączenia się z JavaScript, w 4 przyjrzę się harmonogramowi synchronizacji, następnie maszynie stosowej przetwarzającej kod bajtowy z wejść i wyjść, kryptografii i pule wyników.

Źródło: www.habr.com

Dodaj komentarz