Blockchain-ontwikkeling voor de industrie met behulp van Go. Deel 1

Sinds vier maanden werk ik aan een project genaamd “Ontwikkeling van gegevensbeschermings- en beheertools in overheids- en industriële sectoren op basis van blockchain.”
Nu wil ik je vertellen hoe ik aan dit project ben begonnen, en nu zal ik de programmacode in detail beschrijven.

Blockchain-ontwikkeling voor de industrie met behulp van Go. Deel 1

Dit is het eerste artikel in een reeks artikelen. Hier beschrijf ik de server en het protocol. In feite kan de lezer zelfs zijn eigen versies van deze blockchain-elementen schrijven.

En hier is het tweede deel — over blockchain- en transactiedatastructuren, maar ook over het pakket dat de interactie met de database implementeert.

Vorig jaar kwamen ze tijdens de Digital Breakthrough hackathon met een idee om een ​​bruikbaar systeem voor de industrie en de digitale economie te maken met behulp van gedistribueerde grootboektechnologie; er werd ook subsidie ​​verleend voor de ontwikkeling door het Innovation Assistance Fund (ik zou een apart artikel moeten schrijven artikel over de subsidie, voor degenen die net beginnen met startups), en nu op volgorde.

De ontwikkeling vindt plaats in de Go-taal en de database waarin de blokken zijn opgeslagen is LevelDB.
De belangrijkste onderdelen zijn het protocol, de server (die TCP en WebSocket uitvoert - de eerste voor het synchroniseren van de blockchain, de tweede voor het verbinden van clients, het verzenden van transacties en opdrachten vanuit bijvoorbeeld JavaScript.

Zoals gezegd is deze blockchain vooral nodig om de uitwisseling van producten tussen leveranciers en klanten, of beide in één persoon, te automatiseren en te beschermen. Deze mensen hebben geen haast om elkaar te vertrouwen. Maar de taak is niet alleen het creëren van een ‘chequeboek’ met een ingebouwde rekenmachine, maar een systeem dat de meeste routinetaken automatiseert die zich voordoen bij het werken met de levenscyclus van een product. De bytecode die hiervoor verantwoordelijk is, zoals gebruikelijk bij blockchains, wordt opgeslagen in de in- en uitgangen van transacties (de transacties zelf worden in blokken opgeslagen, de blokken in LevelDB zijn vooraf gecodeerd in het GOB-formaat). Laten we het eerst hebben over het protocol en de server (ook wel knooppunt genoemd).

Het protocol is niet ingewikkeld, het hele punt is om over te schakelen naar de modus voor het laden van bepaalde gegevens, meestal een blok of transactie, als reactie op een speciale opdrachtregel, en het is ook nodig voor het uitwisselen van inventaris, zodat het knooppunt weet wie het is. waarmee ze zijn verbonden en hoe ze zaken moeten doen (de knooppunten die zijn verbonden voor de synchronisatiesessie worden ook wel ‘naburige’ genoemd omdat hun IP bekend is en hun statusgegevens in het geheugen worden opgeslagen).

Mappen (directories zoals Linux ze noemt) worden in de opvatting van Go-programmeurs pakketten genoemd, dus aan het begin van elk bestand met Go-code uit deze map schrijven ze pakketmapnaam_waar_dit_bestand zich bevindt. Anders kunt u het pakket niet aan de compiler doorgeven. Welnu, dit is geen geheim voor degenen die deze taal kennen. Dit zijn de pakketten:

  • Netwerkcommunicatie (server, client, protocol)
  • Structuren van opgeslagen en verzonden gegevens (blok, transactie)
  • Database (blockchain)
  • Overeenstemming
  • Gestapelde virtuele machine (xvm)
  • Hulpmiddelen (crypto, typen), dat is alles voor nu.

Hier is de link naar github

Dit is een educatieve versie, er ontbreekt interactie tussen processen en verschillende experimentele componenten, maar de structuur komt overeen met die waarop de ontwikkeling wordt uitgevoerd. Als u iets te melden heeft in de opmerkingen, zal ik daar bij de verdere ontwikkeling graag rekening mee houden. En nu voor een uitleg van de server en protocol.

Laten we eerst naar de server kijken.

De serversubroutine fungeert als een dataserver die bovenop het TCP-protocol draait en gebruikmaakt van datastructuren uit het protocolpakket.

De routine gebruikt de volgende pakketten: server, protocol, types. In het pakket zelf tcp_server.go bevat datastructuur Opdienen.

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

Het kan de volgende parameters accepteren:

  • Netwerkpoort via welke gegevens worden uitgewisseld
  • Serverconfiguratiebestand in JSON-indeling
  • Vlag voor uitvoering in debug-modus (privé blockchain)

Voortgang:

  • Leest de configuratie uit het JSON-bestand
  • De vlag voor de foutopsporingsmodus wordt gecontroleerd: als deze is ingesteld, wordt de netwerksynchronisatieplanner niet gestart en wordt de blockchain niet geladen
  • Initialiseren van de configuratiedatastructuur en starten van de server

server

  • Voert de lancering van de TCP-server en netwerkinteractie uit in overeenstemming met het protocol.
  • Het heeft een Serve-datastructuur bestaande uit een poortnummer, een buffergrootte en een verwijzing naar de structuur typen.Instellingen
  • De Run-methode start netwerkinteractie (luisteren naar inkomende verbindingen op een bepaalde poort, wanneer een nieuwe verbinding wordt ontvangen, wordt de verwerking ervan overgedragen naar de private handle-methode in een nieuwe thread)
  • В handvat gegevens uit de verbinding worden in een buffer gelezen, omgezet in een stringrepresentatie en doorgegeven protocol.Keuze
  • protocol.Keuze geeft terug resultaat of een fout veroorzaakt. resultaat vervolgens overgebracht naar protocol.Interpreteerdie terugkeert intrpr - voorwerp van type Interpreteer gegevensof veroorzaakt een fout bij het verwerken van het selectieresultaat
  • Vervolgens wordt de overstap uitgevoerd intrpr.Commands[0] die een van de volgende controleert: resultaat, inv, fout en er is een sectie verzuim
  • In de sectie resultaat schakelaar wordt gevonden op waarde intrpr.Commands[1] die de waarden controleert bufferlengte и versie (in elk geval wordt de overeenkomstige functie aangeroepen)

functies GetVersie и BufferLengte staan ​​in het bestand srvlib.go serverpakket

GetVersion(conn net.Conn, version string)

het wordt eenvoudigweg afgedrukt naar de console en stuurt de versie die in de parameter is doorgegeven naar de client:

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

.
Functie

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

laadt een blok, transactie of andere specifieke gegevens als volgt:

  • Drukt naar de console het type gegevens af dat in het protocol is gespecificeerd en dat moet worden geaccepteerd:
    fmt.Println("DataType:", intrpr.Commands[2])
  • Leest de waarde intrpr.Lichaam naar een numerieke variabele buf_len
  • Creëert een buffer nieuwbuf opgegeven maat:
    make([]byte, buf_len)
  • Stuurt een ok antwoord:
    conn.Write([]byte("result:ok"))
  • Vult de buffer van de leesstroom volledig:
    io.ReadFull(conn, newbuf)

    .

  • Drukt de inhoud van de buffer af naar de console
    fmt.Println(string(newbuf))

    en het aantal gelezen bytes

    fmt.Println("Bytes length:", n)
  • Stuurt een ok antwoord:
    conn.Write([]byte("result:ok"))

Methoden uit het serverpakket zijn geconfigureerd om ontvangen gegevens te verwerken met behulp van functies uit het pakket protocol.

Protocol

Een protocol dient als een middel dat gegevens vertegenwoordigt in netwerkuitwisseling.

Keuze(str string) (string, fout) voert de primaire verwerking uit van door de server ontvangen gegevens, ontvangt een stringrepresentatie van de gegevens als invoer en retourneert een voorbereide string Tolk:

  • De invoerreeks wordt gesplitst in hoofd en lichaam met behulp van VerzoekParseN2(str)
  • head wordt in elementen gesplitst en in een opdrachtsegment geplaatst met behulp van ReqParseHead(head)
  • В schakelaar(opdrachten[0]) selecteer het ontvangen commando (cmd, sleutel, adres of de sectie wordt geactiveerd verzuim)
  • Er worden 2 opdrachten gecontroleerd in cmd switch(commands[1]) — lengte и getversie.
  • lengte controleert het gegevenstype commando's[2] en slaat het op data type
  • Controleert dat lichaam bevat een tekenreekswaarde
    len(body) < 1
  • Retourneert de antwoordtekenreeks:
    "result:bufferlength:" + datatype + "/" + body
  • getversie retourneert een tekenreeks
    return "result:version/auto"

Tolk

Bevat de InterpreteData-structuur en voert secundaire verwerking uit van de gegevens waaruit wordt geretourneerd Keuze snaren en objectvorming Interpreteer gegevens.

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

Functie

Interprete(str string) (*InterpreteData, error)

accepteert een string resultaat en creëert en retourneert een verwijzing naar het object Interpreteer gegevens.

Voortgang:

  • Аналогично Keuze hoofd en lichaam worden geëxtraheerd met behulp van VerzoekParseN2(str)
  • hoofd wordt in elementen gesplitst met behulp van ReqParseHead(hoofd)
  • Het object is geïnitialiseerd Interpreteer gegevens en er wordt een verwijzing ernaar geretourneerd:

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

Dit object wordt gebruikt in server.go pakket belangrijkste.

CLIËNT

Het clientpakket bevat de functies TCPConnect и TCPResponseData.

Functie

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

werkt als volgt:

  • Er wordt verbinding gemaakt met de verbinding die is opgegeven in het doorgegeven instellingenobject
    net.Dial("tcp", s.Host + ":" + s.Port)
  • De gegevens die in de dataparameter worden doorgegeven, worden verzonden:
    conn.Write(data)
  • Het antwoord wordt gelezen
    resp, n, _ := TCPResponseData(conn, s.BufSize)

    en afgedrukt op de console

    fmt.Println(string(resp[:n]))
  • Indien overgedragen laadvermogen geeft het dan door
    conn.Write(payload)

    en leest ook het antwoord van de server en drukt het af naar de console

Functie

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

creëert een buffer van de opgegeven grootte, leest daar het antwoord van de server en retourneert deze buffer en het aantal gelezen bytes, evenals een foutobject.

Client-subroutine

Dient voor het verzenden van opdrachten naar knooppuntservers en voor het verkrijgen van korte statistieken en tests.

Kan de volgende parameters accepteren: configuratiebestand in JSON-formaat, gegevens die als tekenreeks naar de server moeten worden verzonden, pad naar het bestand dat naar de payload moet worden verzonden, emulatievlag van knooppuntplanner, type gegevens dat als numerieke waarde wordt overgedragen.

  • De configuratie ophalen
    st := types.ParseConfig(*config)
  • Als de emu-vlag wordt gepasseerd, begint deze planner
  • Als de f-vlag die het pad naar het bestand aangeeft, wordt opgegeven, laden we de gegevens ervan in fdb en de inhoud wordt naar de server verzonden
    client.TCPConnect(st, []byte(CMD_BUFFER_LENGTH + ":" + strconv.Itoa(*t) + "/" + strconv.Itoa(fdblen)), fdb)
  • Als het bestand niet is gespecificeerd, worden de gegevens van de vlag eenvoudigweg verzonden -d:
    client.TCPConnect(st, []byte(*data), nil)

Dit alles is een vereenvoudigde weergave die de structuur van het protocol toont. Tijdens de ontwikkeling wordt de benodigde functionaliteit aan de structuur toegevoegd.

In het tweede deel zal ik het hebben over datastructuren voor blokken en transacties, in 3 over de WebSocket-server voor het verbinden vanuit JavaScript, in 4 zal ik kijken naar de synchronisatieplanner en vervolgens naar een stapelmachine die bytecode verwerkt van in- en uitgangen, cryptografie en pools voor uitgangen.

Bron: www.habr.com

Voeg een reactie