Desenvolupament de blockchain per a la indústria amb Go. Part 1

Des de fa quatre mesos treballo en un projecte anomenat "Desenvolupament d'eines de gestió i protecció de dades en sectors governamentals i industrials basades en blockchain".
Ara m'agradaria explicar-vos com vaig començar aquest projecte i ara descriuré el codi del programa amb detall.

Desenvolupament de blockchain per a la indústria amb Go. Part 1

Aquest és el primer article d'una sèrie d'articles. Aquí descric el servidor i el protocol. De fet, el lector fins i tot pot escriure les seves pròpies versions d'aquests elements blockchain.

I aquí teniu la segona part — sobre blockchain i estructures de dades de transaccions, així com sobre el paquet que implementa la interacció amb la base de dades.

L'any passat, a la hackathon Digital Breakthrough, se'ls va ocórrer la idea de crear un sistema útil per a la indústria i l'economia digital mitjançant la tecnologia de registre distribuït; també es va emetre una subvenció per al desenvolupament per part de la Innovation Assistance Foundation (hauria d'escriure un document separat). article sobre la beca, per als que tot just comencen a fer startups ), i ara en ordre.

El desenvolupament té lloc en l'idioma Go i la base de dades on s'emmagatzemen els blocs és LevelDB.
Les parts principals són el protocol, el servidor (que executa TCP i WebSocket: el primer per sincronitzar la cadena de blocs, el segon per connectar clients, enviar transaccions i ordres des de JavaScript, per exemple.

Com es va esmentar, aquesta cadena de blocs és necessària principalment per automatitzar i protegir l'intercanvi de productes entre proveïdors i clients, o ambdós en una sola persona. Aquesta gent no té pressa per confiar mútuament. Però la tasca no és només crear un "quadre de xecs" amb una calculadora integrada, sinó un sistema que automatitzi la majoria de les tasques rutinàries que sorgeixen quan es treballa amb el cicle de vida del producte. El bytecode responsable d'aquest tema, com és habitual amb les cadenes de blocs, s'emmagatzema a les entrades i sortides de les transaccions (les transaccions en si s'emmagatzemen en blocs, els blocs de LevelDB estan precodificats en format GOB). Primer, parlem del protocol i del servidor (també conegut com node).

El protocol no és complicat, la seva finalitat és canviar al mode de càrrega d'algunes dades, normalment un bloc o transacció, en resposta a una línia d'ordres especial, i també es necessita per intercanviar inventari, de manera que el node sàpiga qui és. està connectat i com han de fer negocis (els nodes connectats per a la sessió de sincronització també s'anomenen "veïns" perquè es coneix la seva IP i les dades del seu estat s'emmagatzemen a la memòria).

Les carpetes (directoris com els anomena Linux) en la comprensió dels programadors Go s'anomenen paquets, de manera que al principi de cada fitxer amb codi Go d'aquest directori escriuen el paquet nom_carpeta_on es troba aquest_arxiu. En cas contrari, no podreu alimentar el paquet al compilador. Bé, això no és cap secret per a aquells que coneixen aquesta llengua. Aquests són els paquets:

  • Comunicació de xarxa (servidor, client, protocol)
  • Estructures de dades emmagatzemades i transmeses (bloc, transacció)
  • Base de dades (blockchain)
  • Consens
  • Màquina virtual apilada (xvm)
  • Auxiliar (criptografia, tipus) això és tot per ara.

Aquí teniu l'enllaç a github

Es tracta d'una versió educativa, manca d'interacció entre processos i de diversos components experimentals, però l'estructura correspon a la que s'està desenvolupant. Si teniu alguna cosa a suggerir als comentaris, estaré encantada de tenir-ho en compte en el desenvolupament posterior. I ara per una explicació del servidor i protocol.

Vegem primer el servidor.

La subrutina del servidor actua com un servidor de dades que s'executa sobre el protocol TCP mitjançant estructures de dades del paquet de protocols.

La rutina utilitza els paquets següents: servidor, protocol, tipus. En el mateix paquet tcp_server.go conté l'estructura de dades servir.

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

Pot acceptar els següents paràmetres:

  • Port de xarxa a través del qual s'intercanviaran dades
  • Fitxer de configuració del servidor en format JSON
  • Marca per executar-se en mode de depuració (chain de blocs privat)

Progrés, progressar:

  • Llegeix la configuració del fitxer JSON
  • La marca del mode de depuració està marcada: si està establerta, el planificador de sincronització de xarxa no s'inicia i la cadena de blocs no es carrega
  • Inicialització de l'estructura de dades de configuració i inici del servidor

Server

  • Realitza el llançament del servidor TCP i la interacció de xarxa d'acord amb el protocol.
  • Té una estructura de dades Serve que consta d'un número de port, una mida de memòria intermèdia i un punter a l'estructura tipus.Configuració
  • El mètode Run inicia la interacció de la xarxa (escoltant connexions entrants en un port determinat, quan es rep una connexió nova, el seu processament es transfereix al mètode de control privat en un fil nou)
  • В gestionar les dades de la connexió es llegeixen en un buffer, es converteixen en una representació de cadena i es passen a protocol.Elecció
  • protocol.Elecció torna resultat o provoca un error. resultat després transferit a protocol.Interpretarque torna intrpr - objecte de tipus Interpreta les dades, o provoca un error en processar el resultat de la selecció
  • Llavors s'executa l'interruptor intrpr.Comands[0] que comprova un de: resultat, inv, error i hi ha una secció defecte
  • A la secció resultat l'interruptor es troba pel valor intrpr.Comands[1] que verifica els valors longitud del buffer и versió (en cada cas s'anomena la funció corresponent)

Funcions GetVersion и Longitud del buffer estan a l'arxiu srvlib.go paquet de servidor

GetVersion(conn net.Conn, version string)

simplement imprimeix a la consola i envia la versió passada al paràmetre al client:

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

.
Funció

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

carrega un bloc, una transacció o altres dades específiques de la manera següent:

  • Imprimeix a la consola el tipus de dades especificades al protocol que cal acceptar:
    fmt.Println("DataType:", intrpr.Commands[2])
  • Llegeix el valor intrpr.Cos a una variable numèrica buf_len
  • Crea un buffer newbuf mida especificada:
    make([]byte, buf_len)
  • Envia una resposta correcta:
    conn.Write([]byte("result:ok"))
  • Omple completament la memòria intermèdia del flux de lectura:
    io.ReadFull(conn, newbuf)

    .

  • Imprimeix el contingut de la memòria intermèdia a la consola
    fmt.Println(string(newbuf))

    i el nombre de bytes llegits

    fmt.Println("Bytes length:", n)
  • Envia una resposta correcta:
    conn.Write([]byte("result:ok"))

Els mètodes del paquet del servidor es configuren per processar les dades rebudes mitjançant les funcions del paquet protocol.

Protocol

Un protocol serveix com a mitjà que representa dades en l'intercanvi de xarxa.

Opció (cadena de cadena) (cadena, error) realitza el processament primari de les dades rebudes pel servidor, rep una cadena de representació de les dades com a entrada i retorna una cadena preparada per Intèrpret:

  • La cadena d'entrada es divideix en cap i cos utilitzant ReqParseN2(str)
  • el cap es divideix en elements i es col·loca en una secció d'ordres mitjançant ReqParseHead(head)
  • В commuta (ordres[0]) seleccioneu l'ordre rebuda (cmd, clau, adreça o s'activa la secció defecte)
  • 2 ordres es marquen a cmd switch(ordres[1]) — longitud и getversion.
  • longitud comprova el tipus de dades ordres[2] i el guarda tipus de dades
  • Comprova això body conté un valor de cadena
    len(body) < 1
  • Retorna la cadena de resposta:
    "result:bufferlength:" + datatype + "/" + body
  • getversion retorna una cadena
    return "result:version/auto"

Intèrpret

Conté l'estructura InterpreteData i realitza un processament secundari de les dades retornades Elecció cordes i formació d'objectes Interpreta les dades.

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

Funció

Interprete(str string) (*InterpreteData, error)

accepta una cadena resultat i crea i retorna una referència a l'objecte Interpreta les dades.

Progrés, progressar:

  • De la mateixa manera Elecció s'extreuen el cap i el cos utilitzant ReqParseN2(str)
  • el cap es divideix en elements utilitzant ReqParseHead(cap)
  • L'objecte està inicialitzat Interpreta les dades i es retorna un punter:

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

Aquest objecte s'utilitza a server.go paquet principal.

client

El paquet client conté les funcions TCPConnect и Dades de resposta de TCP.

Funció

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

funciona així:

  • S'estableix una connexió a la connexió especificada a l'objecte de configuració passat
    net.Dial("tcp", s.Host + ":" + s.Port)
  • Les dades passades al paràmetre de dades es transmeten:
    conn.Write(data)
  • La resposta es llegeix
    resp, n, _ := TCPResponseData(conn, s.BufSize)

    i imprès a la consola

    fmt.Println(string(resp[:n]))
  • Si es transfereix càrrega útil després ho passa
    conn.Write(payload)

    i també llegeix la resposta del servidor, imprimint-la a la consola

Funció

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

crea un buffer de la mida especificada, hi llegeix la resposta del servidor i retorna aquest buffer i el nombre de bytes llegits, així com un objecte d'error.

Subrutina del client

Serveix per enviar ordres als servidors de nodes, així com per obtenir estadístiques i proves breus.

Pot acceptar els següents paràmetres: fitxer de configuració en format JSON, dades que s'enviaran al servidor com a cadena, camí del fitxer que s'enviarà a la càrrega útil, bandera d'emulació del planificador de nodes, tipus de dades transferides com a valor numèric.

  • Obtenció de la configuració
    st := types.ParseConfig(*config)
  • Si es passa la bandera de l'emú, comença planificador
  • Si es proporciona el senyalador f que indica la ruta al fitxer, carreguem les seves dades fdb i el contingut s'envia al servidor
    client.TCPConnect(st, []byte(CMD_BUFFER_LENGTH + ":" + strconv.Itoa(*t) + "/" + strconv.Itoa(fdblen)), fdb)
  • Si no s'especifica el fitxer, simplement s'envien les dades del senyalador -d:
    client.TCPConnect(st, []byte(*data), nil)

Tot això és una representació simplificada que mostra l'estructura del protocol. Durant el desenvolupament, s'afegeix la funcionalitat necessària a la seva estructura.

A la segona part parlaré de les estructures de dades per a blocs i transaccions, a la 3 del servidor WebSocket per connectar-se des de JavaScript, a la 4 miraré el planificador de sincronització, després una màquina de pila que processa bytecode d'entrades i sortides, criptografia i pools per a sortides.

Font: www.habr.com

Afegeix comentari