Blokkjedeutvikling for industri ved hjelp av Go. Del 1

I fire måneder nå har jeg jobbet med et prosjekt kalt "Utvikling av databeskyttelse og styringsverktøy i statlige og industrielle sektorer basert på blockchain."
Nå vil jeg gjerne fortelle deg om hvordan jeg startet dette prosjektet, og nå vil jeg beskrive programkoden i detalj.

Blokkjedeutvikling for industri ved hjelp av Go. Del 1

Dette er den første artikkelen i en serie artikler. Her beskriver jeg serveren og protokollen. Faktisk kan leseren til og med skrive sine egne versjoner av disse blokkjedeelementene.

Og her er den andre delen — om blokkjede- og transaksjonsdatastrukturer, samt om pakken som implementerer interaksjon med databasen.

I fjor, på Digital Breakthrough hackathon, kom de med en idé om å lage et nyttig system for industri og digital økonomi ved hjelp av distribuert reskontroteknologi; det ble også gitt tilskudd til utviklingen av Innovasjonsstøttefondet (jeg burde skrive en egen artikkel om tilskuddet, for de som nettopp har startet oppstart ), og nå i orden.

Utviklingen skjer på Go-språket, og databasen som blokkene er lagret i er LevelDB.
Hoveddelene er protokollen, serveren (som kjører TCP og WebSocket - den første for å synkronisere blokkjeden, den andre for å koble klienter, sende transaksjoner og kommandoer fra JavaScript, for eksempel.

Som nevnt er denne blokkjeden først og fremst nødvendig for å automatisere og beskytte utvekslingen av produkter mellom leverandører og kunder, eller begge deler i én person. Disse menneskene har ikke hastverk med å stole på hverandre. Men oppgaven er ikke bare å lage et «sjekkhefte» med en innebygd kalkulator, men et system som automatiserer de fleste rutineoppgavene som oppstår når man arbeider med produktets livssyklus. Bytekoden som er ansvarlig for denne saken, som vanlig med blokkjeder, lagres i inngangene og utgangene til transaksjoner (selve transaksjonene er lagret i blokker, blokkene i LevelDB er forhåndskodet i GOB-formatet). La oss først snakke om protokollen og serveren (aka node).

Protokollen er ikke komplisert, hele poenget er å bytte til modusen for å laste inn noen data, vanligvis en blokk eller transaksjon, som svar på en spesiell kommandolinje, og den er også nødvendig for å utveksle inventar, slik at noden vet hvem den er koblet til og hvordan de har forretninger å gjøre (nodene som er koblet til synkroniseringsøkten kalles også "naboer" fordi IP-en deres er kjent og tilstandsdataene deres er lagret i minnet).

Mapper (kataloger som Linux kaller dem) i forståelsen av Go-programmerere kalles pakker, så i begynnelsen av hver fil med Go-kode fra denne katalogen skriver de pakken folder_name_where_this_file er plassert. Ellers vil du ikke kunne mate pakken til kompilatoren. Vel, dette er ingen hemmelighet for de som kan dette språket. Dette er pakkene:

  • Nettverkskommunikasjon (server, klient, protokoll)
  • Strukturer av lagrede og overførte data (blokkering, transaksjon)
  • Database (blokkjede)
  • Konsensus
  • Stablet virtuell maskin (xvm)
  • Auxiliary (krypto, typer) det er alt for nå.

Her er lenken til github

Dette er en pedagogisk versjon, den mangler inter-prosess interaksjon og flere eksperimentelle komponenter, men strukturen tilsvarer den som utviklingen utføres på. Hvis du har noe å foreslå i kommentarene, tar jeg gjerne hensyn til det i videre utvikling. Og nå for en forklaring på serveren og protokollen.

La oss først se på serveren.

Serversubrutinen fungerer som en dataserver som kjører på toppen av TCP-protokollen ved hjelp av datastrukturer fra protokollpakken.

Rutinen bruker følgende pakker: server, protokollen, typer. I selve pakken tcp_server.go inneholder datastruktur Tjene.

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

Den kan godta følgende parametere:

  • Nettverksport som data vil bli utvekslet gjennom
  • Serverkonfigurasjonsfil i JSON-format
  • Flagg for å kjøre i feilsøkingsmodus (privat blokkjede)

Framgang:

  • Leser konfigurasjon fra JSON-fil
  • Feilsøkingsmodusflagget er sjekket: hvis det er satt, startes ikke nettverkssynkroniseringsplanleggeren og blokkjeden lastes ikke inn
  • Initialisere konfigurasjonsdatastrukturen og starte serveren

Server

  • Utfører lanseringen av TCP-serveren og nettverksinteraksjon i samsvar med protokollen.
  • Den har en Serve-datastruktur som består av et portnummer, en bufferstørrelse og en peker til strukturen typer.Innstillinger
  • Kjør-metoden starter nettverksinteraksjon (lytter etter innkommende tilkoblinger på en gitt port, når en ny tilkobling mottas, overføres behandlingen til den private håndteringsmetoden i en ny tråd)
  • В håndtere data fra forbindelsen leses inn i en buffer, konverteres til en strengrepresentasjon og sendes til protokoll.Valg
  • protokoll.Valg returnerer resultere eller forårsaker en feil. resultere deretter overført til protokoll. Tolkesom returnerer intrpr - type objekt Tolk Data, eller forårsaker en feil under behandling av utvalgsresultatet
  • Deretter utføres bryteren intrpr.Commands[0] som sjekker en av: resultat, inv, feil og det er en seksjon standard~~POS=TRUNC
  • I seksjonen resultere bryteren er funnet etter verdi intrpr.Commands[1] som sjekker verdiene bufferlengde и versjon (i hvert tilfelle kalles den tilsvarende funksjonen)

funksjoner GetVersion и Bufferlengde er i filen srvlib.go serverpakken

GetVersion(conn net.Conn, version string)

den skriver ganske enkelt ut til konsollen og sender versjonen som er sendt i parameteren til klienten:

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

.
Funksjon

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

laster en blokk, transaksjon eller andre spesifikke data som følger:

  • Skriver ut til konsollen typen data spesifisert i protokollen som må godtas:
    fmt.Println("DataType:", intrpr.Commands[2])
  • Leser verdien intrpr.Body til en numerisk variabel buff_len
  • Oppretter en buffer newbuf spesifisert størrelse:
    make([]byte, buf_len)
  • Sender et ok svar:
    conn.Write([]byte("result:ok"))
  • Fyller bufferen fra lesestrømmen fullstendig:
    io.ReadFull(conn, newbuf)

    .

  • Skriver ut innholdet i bufferen til konsollen
    fmt.Println(string(newbuf))

    og antall leste byte

    fmt.Println("Bytes length:", n)
  • Sender et ok svar:
    conn.Write([]byte("result:ok"))

Metoder fra serverpakken er konfigurert til å behandle mottatte data ved å bruke funksjoner fra pakken protokollen.

Protokoll

En protokoll fungerer som et middel som representerer data i nettverksutveksling.

Valg(str streng) (streng, feil) utfører primærbehandling av data mottatt av serveren, mottar en strengrepresentasjon av dataene som input og returnerer en streng forberedt for Tolk:

  • Inndatastrengen deles opp i hode og kropp ved hjelp av ReqParseN2(str)
  • hodet deles opp i elementer og plasseres i et kommandostykke ved å bruke ReqParseHead(head)
  • В bryter(kommandoer[0]) velg den mottatte kommandoen (cmd, nøkkel, adresse eller seksjonen utløses standard~~POS=TRUNC)
  • 2 kommandoer er sjekket i cmd bryter(kommandoer[1]) — lengde и getversjon.
  • lengde sjekker datatypen inn kommandoer[2] og lagrer den inn data-type
  • Sjekker det kroppen inneholder en strengverdi
    len(body) < 1
  • Returnerer svarstrengen:
    "result:bufferlength:" + datatype + "/" + body
  • getversjon returnerer en streng
    return "result:version/auto"

Tolk

Inneholder InterpreteData-strukturen og utfører sekundær behandling av dataene som returneres fra Choice strenger og objektdannelse Tolk Data.

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

Funksjon

Interprete(str string) (*InterpreteData, error)

godtar en streng resultere og oppretter og returnerer en referanse til objektet Tolk Data.

Framgang:

  • Tilsvar Choice hode og kropp trekkes ut ved hjelp av ReqParseN2(str)
  • hodet deles opp i elementer ved hjelp av ReqParseHead(hode)
  • Objektet initialiseres Tolk Data og en peker til den returneres:

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

Dette objektet brukes i server.go hovedpakke.

kunde

Klientpakken inneholder funksjonene TCPConnect и TCPResponseData.

Funksjon

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

fungerer som følger:

  • En tilkobling opprettes til tilkoblingen spesifisert i det beståtte innstillingsobjektet
    net.Dial("tcp", s.Host + ":" + s.Port)
  • Dataene som sendes i dataparameteren overføres:
    conn.Write(data)
  • Svaret er lest
    resp, n, _ := TCPResponseData(conn, s.BufSize)

    og trykt på konsollen

    fmt.Println(string(resp[:n]))
  • Hvis overført nyttelast sender den videre
    conn.Write(payload)

    og leser også serversvaret og skriver det ut til konsollen

Funksjon

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

oppretter en buffer av spesifisert størrelse, leser serverresponsen der og returnerer denne bufferen og antall leste byte, samt et feilobjekt.

Klientsubrutine

Tjener til å sende kommandoer til nodeservere, samt få kort statistikk og testing.

Kan godta følgende parametere: konfigurasjonsfil i JSON-format, data som skal sendes til serveren som en streng, bane til filen som skal sendes til nyttelast, nodeplanlegger-emuleringsflagg, type data som overføres som en numerisk verdi.

  • Henter konfigurasjonen
    st := types.ParseConfig(*config)
  • Hvis emu-flagget passeres, starter det sheduler
  • Hvis f-flagget som indikerer banen til filen er oppgitt, laster vi inn dataene FDB og innholdet sendes til serveren
    client.TCPConnect(st, []byte(CMD_BUFFER_LENGTH + ":" + strconv.Itoa(*t) + "/" + strconv.Itoa(fdblen)), fdb)
  • Hvis filen ikke er spesifisert, blir dataene fra flagget ganske enkelt sendt -d:
    client.TCPConnect(st, []byte(*data), nil)

Alt dette er en forenklet representasjon som viser strukturen til protokollen. Under utviklingen legges den nødvendige funksjonaliteten til strukturen.

I den andre delen skal jeg snakke om datastrukturer for blokker og transaksjoner, i 3 om WebSocket-serveren for tilkobling fra JavaScript, i 4 skal jeg se på synkroniseringsplanleggeren, deretter en stabelmaskin som behandler bytekode fra innganger og utganger, kryptografi og bassenger for utganger.

Kilde: www.habr.com

Legg til en kommentar