Blockchain-utveckling för industri med hjälp av Go. Del 1

Sedan fyra månader tillbaka har jag arbetat med ett projekt som heter "Utveckling av dataskydds- och hanteringsverktyg i statliga och industriella sektorer baserade på blockchain."
Nu skulle jag vilja berätta om hur jag startade det här projektet, och nu ska jag beskriva programkoden i detalj.

Blockchain-utveckling för industri med hjälp av Go. Del 1

Detta är den första artikeln i en serie artiklar. Här beskriver jag servern och protokollet. Faktum är att läsaren till och med kan skriva sina egna versioner av dessa blockchain-element.

Och här är den andra delen — om blockchain och transaktionsdatastrukturer, samt om paketet som implementerar interaktion med databasen.

Förra året, på Digital Breakthrough hackathon, kom de med en idé om att göra ett användbart system för industrin och den digitala ekonomin med hjälp av distribuerad reskontrateknologi; ett bidrag gavs också ut för utvecklingen av Innovationsstödsfonden (jag borde skriva en separat artikel om bidraget, för de som precis har börjat starta upp), och nu i ordning.

Utvecklingen sker i Go-språket och databasen som blocken lagras i är LevelDB.
Huvuddelarna är protokollet, servern (som kör TCP och WebSocket - den första för att synkronisera blockkedjan, den andra för att ansluta klienter, skicka transaktioner och kommandon från till exempel JavaScript.

Som nämndes behövs denna blockkedja främst för att automatisera och skydda utbytet av produkter mellan leverantörer och kunder, eller båda i en person. Dessa människor har ingen brådska att lita på varandra. Men uppgiften är inte bara att skapa ett "checkhäfte" med en inbyggd kalkylator, utan ett system som automatiserar de flesta rutinuppgifter som uppstår när man arbetar med produktens livscykel. Bytekoden som är ansvarig för denna fråga, som är vanligt med blockkedjor, lagras i in- och utgångar för transaktioner (transaktionerna i sig lagras i block, blocken i LevelDB är förkodade i GOB-formatet). Låt oss först prata om protokollet och servern (alias nod).

Protokollet är inte komplicerat, hela poängen är att byta till läget för att ladda vissa data, vanligtvis ett block eller transaktion, som svar på en speciell kommandorad, och det behövs också för att utbyta inventering, så att noden vet vem den är ansluten till och hur de har affärer att göra (noderna som är anslutna för synkroniseringssessionen kallas också för "granne" eftersom deras IP är känd och deras tillståndsdata lagras i minnet).

Mappar (kataloger som Linux kallar dem) i förståelsen för Go-programmerare kallas paket, så i början av varje fil med Go-kod från denna katalog skriver de paketet folder_name_where_this_file finns. Annars kommer du inte att kunna mata paketet till kompilatorn. Tja, detta är ingen hemlighet för dem som kan detta språk. Det här är paketen:

  • Nätverkskommunikation (server, klient, protokoll)
  • Strukturer av lagrade och överförda data (block, transaktion)
  • Databas (blockchain)
  • Konsensus
  • Staplad virtuell maskin (xvm)
  • Extra (krypto, typer) det är allt för nu.

Här är länken till github

Detta är en pedagogisk version, den saknar inter-processinteraktion och flera experimentella komponenter, men strukturen motsvarar den som utvecklingen pågår. Om du har något att föreslå i kommentarerna tar jag gärna hänsyn till det i vidareutvecklingen. Och nu för en förklaring av servern och protokoll.

Låt oss först titta på servern.

Serversubrutinen fungerar som en dataserver som körs ovanpå TCP-protokollet med hjälp av datastrukturer från protokollpaketet.

Rutinen använder följande paket: server, protokoll, typer. I själva paketet tcp_server.go innehåller datastruktur Tjäna.

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

Den kan acceptera följande parametrar:

  • Nätverksport genom vilken data kommer att utbytas
  • Serverkonfigurationsfil i JSON-format
  • Flagga för att köras i felsökningsläge (privat blockchain)

Framsteg:

  • Läser konfiguration från JSON-fil
  • Felsökningslägesflaggan är kontrollerad: om den är inställd, startas inte nätverkssynkroniseringsschemaläggaren och blockkedjan laddas inte
  • Initiera konfigurationsdatastrukturen och starta servern

server

  • Utför lanseringen av TCP-servern och nätverksinteraktion i enlighet med protokollet.
  • Den har en Serve-datastruktur som består av ett portnummer, en buffertstorlek och en pekare till strukturen typer.Inställningar
  • Kör-metoden startar nätverksinteraktion (lyssnar efter inkommande anslutningar på en given port, när en ny anslutning tas emot överförs dess bearbetning till den privata hanteringsmetoden i en ny tråd)
  • В hantera data från anslutningen läses in i en buffert, konverteras till en strängrepresentation och skickas till protokoll.Val
  • protokoll.Val returnerar resultera eller orsakar ett fel. resultera sedan överförs till protokoll. Tolkasom återkommer intrpr - objekt av typ Tolka Data, eller orsakar ett fel vid bearbetning av urvalsresultatet
  • Sedan utförs växlingen intrpr.Commands[0] som kontrollerar en av: resultat, inv, fel och det finns ett avsnitt standard
  • I avsnittet resultera switch hittas efter värde intrpr.Commands[1] som kontrollerar värdena buffertlängd и version (i varje fall kallas motsvarande funktion)

funktioner GetVersion и Bufferlängd finns i filen srvlib.go serverpaket

GetVersion(conn net.Conn, version string)

den skriver helt enkelt ut till konsolen och skickar versionen som skickas i parametern till klienten:

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

.
Funktion

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

laddar ett block, transaktion eller annan specifik data enligt följande:

  • Skriver ut till konsolen den typ av data som anges i protokollet som måste accepteras:
    fmt.Println("DataType:", intrpr.Commands[2])
  • Läser värdet intrpr.Body till en numerisk variabel buff_len
  • Skapar en buffert newbuf specificerad storlek:
    make([]byte, buf_len)
  • Skickar ett ok svar:
    conn.Write([]byte("result:ok"))
  • Fyller bufferten från läsströmmen helt:
    io.ReadFull(conn, newbuf)

    .

  • Skriver ut innehållet i bufferten till konsolen
    fmt.Println(string(newbuf))

    och antalet lästa byte

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

Metoder från serverpaketet är konfigurerade att bearbeta mottagen data med hjälp av funktioner från paketet protokoll.

Protokoll

Ett protokoll fungerar som ett medel som representerar data i nätverksutbyte.

Choice(str sträng) (sträng, fel) utför primär bearbetning av data som tas emot av servern, tar emot en strängrepresentation av datan som indata och returnerar en sträng förberedd för Tolk:

  • Inmatningssträngen delas upp i huvud och kropp med hjälp av ReqParseN2(str)
  • huvudet delas upp i element och placeras i ett kommandosegment med hjälp av ReqParseHead(head)
  • В switch(kommandon[0]) välj det mottagna kommandot (cmd, nyckel, adress eller så utlöses avsnittet standard)
  • 2 kommandon är kontrollerade i cmd switch(kommandon[1]) — längd и getversion.
  • längd kontrollerar datatypen kommandon[2] och sparar in den data typ
  • Kollar det kropp innehåller ett strängvärde
    len(body) < 1
  • Returnerar svarssträngen:
    "result:bufferlength:" + datatype + "/" + body
  • getversion returnerar en sträng
    return "result:version/auto"

Tolk

Innehåller InterpreteData-strukturen och utför sekundär bearbetning av data som returneras från Val strängar och objektbildning Tolka Data.

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

Funktion

Interprete(str string) (*InterpreteData, error)

accepterar en sträng resultera och skapar och returnerar en referens till objektet Tolka Data.

Framsteg:

  • på liknande sätt Val huvud och kropp extraheras med hjälp av ReqParseN2(str)
  • huvudet delas upp i element med hjälp av ReqParseHead(huvud)
  • Objektet initieras Tolka Data och en pekare till den returneras:

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

Detta objekt används i server.go huvudpaketet.

Klient

Klientpaketet innehåller funktionerna TCPConnect и TCPResponseData.

Funktion

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

fungerar så här:

  • En anslutning görs till den anslutning som anges i det godkända inställningsobjektet
    net.Dial("tcp", s.Host + ":" + s.Port)
  • Data som skickas i dataparametern överförs:
    conn.Write(data)
  • Svaret är läst
    resp, n, _ := TCPResponseData(conn, s.BufSize)

    och tryckt på konsolen

    fmt.Println(string(resp[:n]))
  • Om den överförs nyttolast skickar det sedan vidare
    conn.Write(payload)

    och läser även serversvaret och skriver ut det till konsolen

Funktion

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

skapar en buffert av angiven storlek, läser serversvaret där och returnerar denna buffert och antalet lästa byte, samt ett felobjekt.

Klientsubrutin

Fungerar för att skicka kommandon till nodservrar, samt få kort statistik och testning.

Kan acceptera följande parametrar: konfigurationsfil i JSON-format, data som ska skickas till servern som en sträng, sökväg till filen som ska skickas till nyttolasten, emuleringsflagga för nodschemaläggning, typ av data som överförs som ett numeriskt värde.

  • Hämta konfigurationen
    st := types.ParseConfig(*config)
  • Om emu-flaggan passeras, startar den sheduler
  • Om f-flaggan som anger sökvägen till filen tillhandahålls, laddar vi in ​​dess data fdb och innehållet skickas till servern
    client.TCPConnect(st, []byte(CMD_BUFFER_LENGTH + ":" + strconv.Itoa(*t) + "/" + strconv.Itoa(fdblen)), fdb)
  • Om filen inte är specificerad skickas helt enkelt data från flaggan -d:
    client.TCPConnect(st, []byte(*data), nil)

Allt detta är en förenklad representation som visar strukturen för protokollet. Under utvecklingen läggs den nödvändiga funktionaliteten till dess struktur.

I den andra delen kommer jag att prata om datastrukturer för block och transaktioner, i 3 om WebSocket-servern för anslutning från JavaScript, i 4 ska jag titta på synkroniseringsschemaläggaren, sedan en stackmaskin som bearbetar bytekod från ingångar och utgångar, kryptografi och pooler för utgångar.

Källa: will.com

Lägg en kommentar