Blockchain-ontwikkeling vir industrie met behulp van Go. Deel 1

Ek werk nou al vier maande aan 'n projek genaamd "Ontwikkeling van databeskerming en bestuursinstrumente in regerings- en nywerheidsektore gebaseer op blokketting."
Nou wil ek jou graag vertel hoe ek hierdie projek begin het, en nou sal ek die programkode in detail beskryf.

Blockchain-ontwikkeling vir industrie met behulp van Go. Deel 1

Hierdie is die eerste artikel in 'n reeks artikels. Hier beskryf ek die bediener en protokol. Trouens, die leser kan selfs sy eie weergawes van hierdie blokketting-elemente skryf.

En hier is die tweede deel — oor blokketting- en transaksiedatastrukture, sowel as oor die pakket wat interaksie met die databasis implementeer.

Verlede jaar, by die Digital Breakthrough hackathon, het hulle met 'n idee vorendag gekom om 'n nuttige stelsel vir die industrie en die digitale ekonomie te maak deur gebruik te maak van verspreide grootboek tegnologie; 'n toekenning is ook uitgereik vir die ontwikkeling deur die Innovation Assistance Fund (ek moet 'n aparte skryf artikel oor die toelae, vir diegene wat net begin begin), en nou in orde.

Ontwikkeling vind plaas in die Go-taal, en die databasis waarin die blokke gestoor word, is LevelDB.
Die hoofonderdele is die protokol, die bediener (wat TCP en WebSocket laat loop - die eerste vir die sinchronisering van die blokketting, die tweede vir die koppeling van kliënte, die stuur van transaksies en opdragte vanaf JavaScript, byvoorbeeld.

Soos genoem, is hierdie blokketting hoofsaaklik nodig om die uitruil van produkte tussen verskaffers en kliënte, of albei in een persoon, te outomatiseer en te beskerm. Hierdie mense is nie haastig om mekaar te vertrou nie. Maar die taak is nie net om 'n "tjekboek" met 'n ingeboude sakrekenaar te skep nie, maar 'n stelsel wat die meeste van die roetinetake outomatiseer wat ontstaan ​​wanneer daar met die produklewensiklus gewerk word. Die greepkode wat vir hierdie saak verantwoordelik is, soos gebruiklik met blokkettings, word in die insette en uitsette van transaksies gestoor (die transaksies self word in blokke gestoor, die blokke in LevelDB is vooraf geënkodeer in die GOB-formaat). Kom ons praat eers oor die protokol en die bediener (ook bekend as node).

Die protokol is nie ingewikkeld nie, sy hele punt is om oor te skakel na die modus van die laai van sommige data, gewoonlik 'n blok of transaksie, in reaksie op 'n spesiale opdragreël, en dit is ook nodig vir die uitruil van voorraad, sodat die nodus weet wie dit aan gekoppel is en hoe hulle sake het om te doen (die nodusse wat vir die sinchronisasiesessie gekoppel is, word ook "naburige" genoem omdat hul IP bekend is en hul toestanddata in die geheue gestoor word).

Dopgehou (gidse soos Linux dit noem) in die begrip van Go-programmeerders word pakkette genoem, so aan die begin van elke lêer met Go-kode van hierdie gids skryf hulle pakket folder_name_where_this_file is geleë. Andersins sal u nie die pakket na die samesteller kan voer nie. Wel, dit is geen geheim vir diegene wat hierdie taal ken nie. Dit is die pakkette:

  • Netwerkkommunikasie (bediener, kliënt, protokol)
  • Strukture van gestoor en oorgedra data (blok, transaksie)
  • Databasis (blokketting)
  • Konsensus
  • Gestapelde virtuele masjien (xvm)
  • Hulp (crypto, tipes) dit is alles vir nou.

Hier is die skakel na github

Dit is 'n opvoedkundige weergawe, dit het nie interproses-interaksie en verskeie eksperimentele komponente nie, maar die struktuur stem ooreen met die een waarop ontwikkeling uitgevoer word. As jy iets het om voor te stel in die kommentaar, sal ek dit graag in ag neem in verdere ontwikkeling. En nou vir 'n verduideliking van die bediener en protokol.

Kom ons kyk eers na die bediener.

Die bedienersubroetine dien as 'n databediener wat bo-op die TCP-protokol loop deur datastrukture van die protokolpakket te gebruik.

Die roetine gebruik die volgende pakkette: bediener, protokol, tipes. In die pakkie self tcp_server.go datastruktuur bevat Bedien.

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

Dit kan die volgende parameters aanvaar:

  • Netwerkpoort waardeur data uitgeruil sal word
  • Bedienerkonfigurasielêer in JSON-formaat
  • Vlag om in ontfoutingsmodus te hardloop (privaat blokketting)

Vordering:

  • Lees konfigurasie vanaf JSON-lêer
  • Die ontfoutmodusvlag word nagegaan: as dit gestel is, word die netwerksinchronisasieskeduleerder nie geloods nie en die blokketting is nie gelaai nie
  • Inisialiseer die konfigurasiedatastruktuur en begin die bediener

bediener

  • Voer die bekendstelling van die TCP-bediener en netwerkinteraksie uit in ooreenstemming met die protokol.
  • Dit het 'n Bedien-datastruktuur wat bestaan ​​uit 'n poortnommer, 'n buffergrootte en 'n wyser na die struktuur tipes.Instellings
  • Die Run-metode begin netwerkinteraksie (luister vir inkomende verbindings op 'n gegewe poort, wanneer 'n nuwe verbinding ontvang word, word die verwerking daarvan oorgedra na die private hanteermetode in 'n nuwe draad)
  • В hanteer data van die verbinding word in 'n buffer gelees, na 'n stringvoorstelling omgeskakel en na protokol.Keuse
  • protokol.Keuse keer terug lei of 'n fout veroorsaak. lei dan oorgeplaas na protokol.Interpreteerwat terugkeer intrpr - voorwerp van tipe Interpreteer Data, of veroorsaak 'n fout in die verwerking van die seleksie resultaat
  • Dan word die skakelaar uitgevoer intrpr.Commands[0] wat een van: resultaat, inv, fout en daar is 'n afdeling verstek
  • In die afdeling lei skakelaar word volgens waarde gevind intrpr.Commands[1] wat die waardes kontroleer bufferlengte и weergawe (in elke geval word die ooreenstemmende funksie genoem)

Funksies GetVersion и Bufferlengte is in die lêer srvlib.go bediener pakket

GetVersion(conn net.Conn, version string)

dit druk eenvoudig na die konsole en stuur die weergawe wat in die parameter geslaag is na die kliënt:

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

.
Funksie

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

laai 'n blok, transaksie of ander spesifieke data soos volg:

  • Druk na die konsole die tipe data gespesifiseer in die protokol wat aanvaar moet word:
    fmt.Println("DataType:", intrpr.Commands[2])
  • Lees die waarde intrpr.Liggaam na 'n numeriese veranderlike buff_len
  • Skep 'n buffer newbuf gespesifiseerde grootte:
    make([]byte, buf_len)
  • Stuur 'n goeie antwoord:
    conn.Write([]byte("result:ok"))
  • Vul die buffer volledig uit die leesstroom:
    io.ReadFull(conn, newbuf)

    .

  • Druk die inhoud van die buffer na die konsole
    fmt.Println(string(newbuf))

    en die aantal grepe gelees

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

Metodes van die bedienerpakket is gekonfigureer om ontvangde data te verwerk met behulp van funksies van die pakket protokol.

Protokol

'n Protokol dien as 'n middel wat data in netwerkuitruiling verteenwoordig.

Keuse(str string) (string, fout) voer primêre verwerking uit van data wat deur die bediener ontvang is, ontvang 'n stringvoorstelling van die data as invoer en gee 'n string terug wat voorberei is vir Tolk:

  • Die invoerstring word in kop en liggaam verdeel met behulp van ReqParseN2(str)
  • kop word in elemente verdeel en in 'n opdragskyfie geplaas met behulp van ReqParseHead(kop)
  • В skakel (opdragte[0]) kies die ontvangde opdrag (cmd, sleutel, adres of die afdeling word geaktiveer verstek)
  • 2 opdragte is gemerk in cmd skakelaar (opdragte[1]) — lengte и getversion.
  • lengte kontroleer die datatipe in opdragte[2] en stoor dit in data type
  • Kontroleer dit liggaam bevat 'n stringwaarde
    len(body) < 1
  • Wys die antwoordstring:
    "result:bufferlength:" + datatype + "/" + body
  • getversion gee 'n string terug
    return "result:version/auto"

Tolk

Bevat die InterpreteData-struktuur en voer sekondêre verwerking uit van die data wat teruggestuur word Keuse snare en voorwerpvorming Interpreteer Data.

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

Funksie

Interprete(str string) (*InterpreteData, error)

aanvaar 'n string lei en skep en gee 'n verwysing na die voorwerp terug Interpreteer Data.

Vordering:

  • Net so Keuse kop en liggaam word onttrek met behulp van ReqParseN2(str)
  • kop word in elemente verdeel met behulp van ReqParseHead(kop)
  • Die voorwerp word geïnisialiseer Interpreteer Data en 'n wyser daarna word teruggestuur:

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

Hierdie voorwerp word gebruik in bediener.gaan hoofpakket.

Kliënt

Die kliëntpakket bevat die funksies TCPConnect и TCPRsponseData.

Funksie

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

werk so:

  • 'n Verbinding word gemaak met die verbinding wat in die geslaagde instellingsvoorwerp gespesifiseer is
    net.Dial("tcp", s.Host + ":" + s.Port)
  • Die data wat in die dataparameter deurgegee word, word versend:
    conn.Write(data)
  • Die antwoord word gelees
    resp, n, _ := TCPResponseData(conn, s.BufSize)

    en op die konsole gedruk

    fmt.Println(string(resp[:n]))
  • Indien oorgedra loonvrag gee dit dan deur
    conn.Write(payload)

    en lees ook die bedienerantwoord, druk dit na die konsole

Funksie

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

skep 'n buffer van die gespesifiseerde grootte, lees die bedienerrespons daar en gee hierdie buffer en die aantal geleesde grepe terug, sowel as 'n foutvoorwerp.

Kliënt subroetine

Dien om opdragte na nodusbedieners te stuur, asook om kort statistieke en toetse te bekom.

Kan die volgende parameters aanvaar: konfigurasielêer in JSON-formaat, data wat as 'n string na die bediener gestuur moet word, pad na die lêer wat na loonvrag gestuur moet word, nodeskeduleerder-emulasievlag, tipe data wat as 'n numeriese waarde oorgedra word.

  • Kry die konfigurasie
    st := types.ParseConfig(*config)
  • As die emoe-vlag geslaag word, begin dit versorger
  • As die f-vlag wat die pad na die lêer aandui verskaf word, laai ons die data daarvan in fdb en die inhoud word na die bediener gestuur
    client.TCPConnect(st, []byte(CMD_BUFFER_LENGTH + ":" + strconv.Itoa(*t) + "/" + strconv.Itoa(fdblen)), fdb)
  • As die lêer nie gespesifiseer is nie, word die data van die vlag eenvoudig gestuur -d:
    client.TCPConnect(st, []byte(*data), nil)

Dit alles is 'n vereenvoudigde voorstelling wat die struktuur van die protokol toon. Tydens ontwikkeling word die nodige funksionaliteit by die struktuur daarvan gevoeg.

In die tweede deel sal ek praat oor datastrukture vir blokke en transaksies, in 3 oor die WebSocket-bediener om vanaf JavaScript te koppel, in 4 gaan ek kyk na die sinchronisasieskeduleerder, dan 'n stapelmasjien wat greepkode verwerk vanaf insette en uitsette, kriptografie en poele vir uitsette.

Bron: will.com

Voeg 'n opmerking