I fire måneder nu har jeg arbejdet på et projekt kaldet "Udvikling af databeskyttelses- og styringsværktøjer i offentlige og industrielle sektorer baseret på blockchain."
Nu vil jeg gerne fortælle dig om, hvordan jeg startede dette projekt, og nu vil jeg beskrive programkoden i detaljer.
Dette er den første artikel i en serie af artikler. Her beskriver jeg serveren og protokollen. Faktisk kan læseren endda skrive sine egne versioner af disse blockchain-elementer.
Sidste år, på Digital Breakthrough hackathon, kom de med en idé om at lave et nyttigt system til industrien og den digitale økonomi ved hjælp af distribueret hovedbogsteknologi; der blev også udstedt en bevilling til udviklingen af Innovation Assistance Foundation (jeg burde skrive en separat artikel om bevillingen, for dem der lige er begyndt at lave startups ), og nu i orden.
Udviklingen foregår i Go-sproget, og databasen, som blokkene er gemt i, er LevelDB.
Hoveddelene er protokollen, serveren (som kører TCP og WebSocket - den første til synkronisering af blockchain, den anden til at forbinde klienter, sende transaktioner og kommandoer fra f.eks. JavaScript.
Som nævnt er denne blockchain primært nødvendig for at automatisere og beskytte udvekslingen af produkter mellem leverandører og kunder, eller begge dele i én person. Disse mennesker har ikke travlt med at stole på hinanden. Men opgaven er ikke kun at lave et "checkhæfte" med en indbygget lommeregner, men et system, der automatiserer de fleste af de rutineopgaver, der opstår, når man arbejder med produktets livscyklus. Bytekoden, der er ansvarlig for denne sag, som det er sædvanligt med blockchains, lagres i input og output af transaktioner (selve transaktionerne er lagret i blokke, blokkene i LevelDB er præ-kodet i GOB-formatet). Lad os først fortælle dig om protokollen og serveren (alias node).
Protokollen er ikke kompliceret, hele dens pointe er at skifte til tilstanden til at indlæse nogle data, normalt en blok eller transaktion, som svar på en speciel kommandolinje, og den er også nødvendig for at udveksle inventar, så noden ved, hvem den er forbundet til, og hvordan de har forretninger at gøre (noderne forbundet til synkroniseringssessionen kaldes også "naboer", fordi deres IP er kendt, og deres tilstandsdata er gemt i hukommelsen).
Mapper (mapper som Linux kalder dem) i forståelsen af Go-programmører kaldes pakker, derfor skriver de i begyndelsen af hver fil med Go-kode fra denne mappe pakken folder_name_where_this_file er placeret. Ellers vil du ikke være i stand til at sende pakken til compileren. Nå, dette er ingen hemmelighed for dem, der kender dette sprog. Dette er pakkerne:
- Netværkskommunikation (server, klient, protokol)
- Strukturer af lagrede og transmitterede data (blokering, transaktion)
- Database (blockchain)
- Konsensus
- Stablet virtuel maskine (xvm)
- Auxiliary (krypto, typer) det er alt for nu.
Dette er en pædagogisk version, den mangler inter-proces interaktion og flere eksperimentelle komponenter, men strukturen svarer til den, som udviklingen udføres på. Hvis du har noget at foreslå i kommentarerne, vil jeg gerne tage det i betragtning i den videre udvikling. Og nu til en forklaring af serveren og protokol.
Lad os først se på serveren.
Serverunderrutinen fungerer som en dataserver, der kører oven på TCP-protokollen ved hjælp af datastrukturer fra protokolpakken.
Rutinen bruger følgende pakker: server, protokol, typer. I selve pakken tcp_server.go indeholder datastruktur Tjene.
type Serve struct {
Port string
BufSize int
ST *types.Settings
}
Den kan acceptere følgende parametre:
- Netværksport, hvorigennem data vil blive udvekslet
- Serverkonfigurationsfil i JSON-format
- Flag for at køre i fejlretningstilstand (privat blockchain)
Fremskridt:
- Læser konfiguration fra JSON-fil
- Fejlfindingstilstandsflaget er markeret: hvis det er indstillet, startes netværkssynkroniseringsplanlæggeren ikke, og blockchainen indlæses ikke
- Initialisering af konfigurationsdatastrukturen og start af serveren
Server
- Udfører lanceringen af TCP-serveren og netværksinteraktion i overensstemmelse med protokollen.
- Den har en Serve-datastruktur bestående af et portnummer, en bufferstørrelse og en pegepind til strukturen typer.Indstillinger
- Kør-metoden starter netværksinteraktion (lytter efter indgående forbindelser på en given port, når en ny forbindelse modtages, overføres dens behandling til den private håndteringsmetode i en ny tråd)
- В håndtere data fra forbindelsen læses ind i en buffer, konverteres til en strengrepræsentation og sendes til protokol.Valg
- protokol.Valg vender tilbage resultere eller forårsager en fejl. resultere derefter overført til protokol. Fortolkesom vender tilbage intrpr - typeobjekt Fortolke Data, eller forårsager en fejl i behandlingen af udvælgelsesresultatet
- Derefter udføres skiftet intrpr.Commands[0] som tjekker en af: resultat, inv, fejl og der er et afsnit standard
- I afsnittet resultere switch findes efter værdi intrpr.Commands[1] som tjekker værdierne bufferlængde и udgave (i hvert tilfælde kaldes den tilsvarende funktion)
Funktioner GetVersion и Bufferlængde er i filen srvlib.go serverpakke
GetVersion(conn net.Conn, version string)
den udskriver simpelthen til konsollen og sender den version, der er sendt i parameteren, til klienten:
conn.Write([]byte("result:" + version))
.
Funktion
BufferLength(conn net.Conn, intrpr *protocol.InterpreteData)
indlæser en blok, transaktion eller andre specifikke data som følger:
- Udskriver til konsollen den type data, der er angivet i protokollen, der skal accepteres:
fmt.Println("DataType:", intrpr.Commands[2])
- Læser værdien intrpr.Body til en numerisk variabel buff_len
- Opretter en buffer newbuf specificeret størrelse:
make([]byte, buf_len)
- Sender et ok svar:
conn.Write([]byte("result:ok"))
- Fylder bufferen fra læsestrømmen fuldstændigt:
io.ReadFull(conn, newbuf)
.
- Udskriver indholdet af bufferen til konsollen
fmt.Println(string(newbuf))
og antallet af læste bytes
fmt.Println("Bytes length:", n)
- Sender et ok svar:
conn.Write([]byte("result:ok"))
Metoder fra serverpakken er konfigureret til at behandle modtagne data ved hjælp af funktioner fra pakken protokol.
protokol
En protokol tjener som et middel, der repræsenterer data i netværksudveksling.
Valg(str streng) (streng, fejl) udfører primær behandling af data modtaget af serveren, modtager en strengrepræsentation af dataene som input og returnerer en streng forberedt til Tolk:
- Indtastningsstrengen opdeles i hoved og krop vha ReqParseN2(str)
- hovedet opdeles i elementer og placeres i et kommandoudsnit ved hjælp af ReqParseHead(head)
- В switch(kommandoer[0]) vælg den modtagne kommando (cmd, nøgle, adresse eller afsnittet udløses standard)
- 2 kommandoer er afkrydset i cmd switch(kommandoer[1]) — længde и getversion.
- længde tjekker datatypen ind kommandoer[2] og gemmer den ind datatype
- Tjekker det krop indeholder en strengværdi
len(body) < 1
- Returnerer svarstrengen:
"result:bufferlength:" + datatype + "/" + body
- getversion returnerer en streng
return "result:version/auto"
Tolk
Indeholder InterpreteData-strukturen og udfører sekundær behandling af de data, der returneres fra Valg strenge og objektdannelse Fortolke Data.
type InterpreteData struct {
Head string
Commands []string
Body string
IsErr bool
ErrCode int
ErrMessage string
}
Funktion
Interprete(str string) (*InterpreteData, error)
accepterer en streng resultere og opretter og returnerer en reference til objektet Fortolke Data.
Fremskridt:
- Tilsvarende Valg hoved og krop udvindes vha ReqParseN2(str)
- hovedet opdeles i elementer vha ReqParseHead(hoved)
- Objektet initialiseres Fortolke Data og en pointer til det returneres:
res := &InterpreteData{
Head: head,
Commands: commands,
Body: body,
}
return res, nil
Dette objekt bruges i server.go hovedpakke.
Klient
Klientpakken indeholder funktionerne TCPConnect и TCPResponseData.
Funktion
TCPConnect(s *types.Settings, data []byte, payload []byte)
virker sådan her:
- Der oprettes forbindelse til den forbindelse, der er angivet i det beståede indstillingsobjekt
net.Dial("tcp", s.Host + ":" + s.Port)
- De data, der sendes i dataparameteren, overføres:
conn.Write(data)
- Svaret er læst
resp, n, _ := TCPResponseData(conn, s.BufSize)
og trykt på konsollen
fmt.Println(string(resp[:n]))
- Hvis overført nyttelast så sender det videre
conn.Write(payload)
og læser også serversvaret og udskriver det til konsollen
Funktion
TCPResponseData(conn net.Conn, bufsiz int) ([]byte, int, error)
opretter en buffer af den angivne størrelse, læser serversvaret der og returnerer denne buffer og antallet af læste bytes samt et fejlobjekt.
Klientunderrutine
Tjener til at sende kommandoer til nodeservere, samt opnå korte statistikker og test.
Kan acceptere følgende parametre: konfigurationsfil i JSON-format, data, der skal sendes til serveren som en streng, sti til filen, der skal sendes til nyttelast, nodeplanlægnings-emuleringsflag, type data, der overføres som en numerisk værdi.
- Henter konfigurationen
st := types.ParseConfig(*config)
- Hvis emu-flaget passeres, starter det sheduler
- Hvis f-flaget, der angiver stien til filen, er leveret, indlæser vi dens data i fdb og indholdet sendes til serveren
client.TCPConnect(st, []byte(CMD_BUFFER_LENGTH + ":" + strconv.Itoa(*t) + "/" + strconv.Itoa(fdblen)), fdb)
- Hvis filen ikke er angivet, sendes dataene fra flaget simpelthen -d:
client.TCPConnect(st, []byte(*data), nil)
Alt dette er en forenklet repræsentation, der viser strukturen af protokollen. Under udviklingen tilføjes den nødvendige funktionalitet til dens struktur.
I anden del vil jeg tale om datastrukturer for blokke og transaktioner, i 3 om WebSocket-serveren til tilslutning fra JavaScript, i 4 vil jeg se på synkroniseringsplanlæggeren, derefter en stackmaskine, der behandler bytekode fra input og output, kryptografi og puljer til output.
Kilde: www.habr.com