Pag-unlad ng Blockchain para sa industriya gamit ang Go. Bahagi 1

Apat na buwan na akong nagtatrabaho sa isang proyekto na tinatawag na "Pagbuo ng proteksyon ng data at mga tool sa pamamahala sa mga sektor ng gobyerno at industriya batay sa blockchain."
Ngayon nais kong sabihin sa iyo ang tungkol sa kung paano ko sinimulan ang proyektong ito, at ngayon ay ilalarawan ko nang detalyado ang code ng programa.

Pag-unlad ng Blockchain para sa industriya gamit ang Go. Bahagi 1

Ito ang unang artikulo sa isang serye ng mga artikulo. Dito ko inilalarawan ang server at protocol. Sa katunayan, ang mambabasa ay maaaring magsulat ng kanyang sariling mga bersyon ng mga elemento ng blockchain na ito.

At narito ang ikalawang bahagi β€” tungkol sa blockchain at mga istruktura ng data ng transaksyon, pati na rin ang tungkol sa package na nagpapatupad ng pakikipag-ugnayan sa database.

Noong nakaraang taon, sa Digital Breakthrough hackathon, nakaisip sila ng ideya na gumawa ng isang kapaki-pakinabang na sistema para sa industriya at digital na ekonomiya gamit ang distributed ledger technology; isang grant ay inisyu din para sa pagpapaunlad ng Innovation Assistance Fund (dapat akong magsulat ng isang hiwalay na artikulo tungkol sa grant, para sa mga nagsisimula pa lamang sa mga startup ), at ngayon ay nasa order.

Ang pag-unlad ay nagaganap sa wikang Go, at ang database kung saan naka-imbak ang mga bloke ay LevelDB.
Ang mga pangunahing bahagi ay ang protocol, ang server (na nagpapatakbo ng TCP at WebSocket - ang una para sa pag-synchronize ng blockchain, ang pangalawa para sa pagkonekta ng mga kliyente, pagpapadala ng mga transaksyon at mga utos mula sa JavaScript, halimbawa.

Tulad ng nabanggit, ang blockchain na ito ay kinakailangan pangunahin upang i-automate at protektahan ang pagpapalitan ng mga produkto sa pagitan ng mga supplier at customer, o pareho sa isang tao. Ang mga taong ito ay hindi nagmamadaling magtiwala sa isa't isa. Ngunit ang gawain ay hindi lamang lumikha ng isang "checkbook" na may built-in na calculator, ngunit isang sistema na nag-automate ng karamihan sa mga nakagawiang gawain na lumitaw kapag nagtatrabaho sa ikot ng buhay ng produkto. Ang bytecode na responsable para sa bagay na ito, tulad ng nakaugalian sa mga blockchain, ay naka-imbak sa mga input at output ng mga transaksyon (ang mga transaksyon mismo ay naka-imbak sa mga bloke, ang mga bloke sa LevelDB ay paunang naka-encode sa GOB na format). Una, pag-usapan natin ang protocol at ang server (aka node).

Ang protocol ay hindi kumplikado, ang buong punto nito ay lumipat sa mode ng pag-load ng ilang data, karaniwang isang bloke o transaksyon, bilang tugon sa isang espesyal na linya ng command, at kailangan din ito para sa pagpapalitan ng imbentaryo, upang malaman ng node kung sino ito. ay konektado sa at kung paano sila may negosyong gagawin (ang mga node na konektado para sa session ng pag-synchronize ay tinatawag ding "kapitbahay" dahil kilala ang kanilang IP at ang kanilang data ng estado ay naka-imbak sa memorya).

Ang mga folder (mga direktoryo ayon sa tawag sa kanila ng Linux) sa pag-unawa sa mga programmer ng Go ay tinatawag na mga pakete, kaya sa simula ng bawat file na may Go code mula sa direktoryong ito ay nagsusulat sila ng package folder_name_where_this_file ay matatagpuan. Kung hindi, hindi mo magagawang i-feed ang package sa compiler. Well, hindi ito lihim para sa mga nakakaalam ng wikang ito. Ito ang mga package:

  • Komunikasyon sa network (server, client, protocol)
  • Mga istruktura ng nakaimbak at ipinadalang data (block, transaksyon)
  • Database (blockchain)
  • Pinagkasunduan
  • Naka-stack na virtual machine (xvm)
  • Auxiliary (crypto, mga uri) iyon lang sa ngayon.

Narito ang link sa github

Ito ay isang pang-edukasyon na bersyon, wala itong inter-process na interaksyon at ilang mga eksperimentong bahagi, ngunit ang istraktura ay tumutugma sa isa kung saan ang pag-unlad ay isinasagawa. Kung mayroon kang anumang iminumungkahi sa mga komento, ikalulugod kong isaalang-alang ito sa karagdagang pag-unlad. At ngayon para sa isang paliwanag ng server at protokol.

Tingnan muna natin ang server.

Ang subroutine ng server ay gumaganap bilang isang data server na tumatakbo sa itaas ng TCP protocol gamit ang mga istruktura ng data mula sa protocol package.

Ginagamit ng routine ang mga sumusunod na pakete: server, protokol, Mga uri. Sa package mismo tcp_server.go naglalaman ng istraktura ng data Maglingkod.

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

Maaari nitong tanggapin ang mga sumusunod na parameter:

  • Network port kung saan mapapalitan ang data
  • File ng configuration ng server sa format na JSON
  • I-flag para sa pagtakbo sa debug mode (pribadong blockchain)

Pag-unlad:

  • Binabasa ang configuration mula sa JSON file
  • Ang flag ng debug mode ay nasuri: kung ito ay nakatakda, ang network synchronization scheduler ay hindi inilunsad at ang blockchain ay hindi na-load
  • Pagsisimula ng istraktura ng data ng pagsasaayos at pagsisimula ng server

server

  • Isinasagawa ang paglulunsad ng TCP server at pakikipag-ugnayan sa network alinsunod sa protocol.
  • Mayroon itong istraktura ng data ng Serve na binubuo ng isang numero ng port, laki ng buffer at isang pointer sa istraktura mga uri.Mga Setting
  • Ang paraan ng Run ay nagsisimula sa pakikipag-ugnayan sa network (pakikinig para sa mga papasok na koneksyon sa isang naibigay na port, kapag ang isang bagong koneksyon ay natanggap, ang pagproseso nito ay ililipat sa pribadong paraan ng paghawak sa isang bagong thread)
  • Π’ pangasiwaan ang data mula sa koneksyon ay binabasa sa isang buffer, na-convert sa isang representasyon ng string at ipinapasa sa protocol.Pagpipilian
  • protocol.Pagpipilian nagbabalik resulta o nagdudulot ng error. resulta pagkatapos ay inilipat sa protocol.I-interpretna nagbabalik intrpr - bagay ng uri InterpreteData, o nagdudulot ng error sa pagproseso ng resulta ng pagpili
  • Pagkatapos ang switch ay pinaandar intrpr.Commands[0] na sumusuri sa isa sa: resulta, inv, error at may section default
  • Sa seksyon resulta switch ay matatagpuan sa pamamagitan ng halaga intrpr.Commands[1] na sumusuri sa mga halaga bufferlength ΠΈ bersyon (sa bawat kaso ang kaukulang function ay tinatawag)

Pag-andar GetVersion ΠΈ BufferLength ay nasa file srvlib.go pakete ng server

GetVersion(conn net.Conn, version string)

nagpi-print lang ito sa console at ipinapadala ang bersyon na ipinasa sa parameter sa kliyente:

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

.
Tungkulin

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

naglo-load ng block, transaksyon, o iba pang partikular na data gaya ng sumusunod:

  • Ini-print sa console ang uri ng data na tinukoy sa protocol na kailangang tanggapin:
    fmt.Println("DataType:", intrpr.Commands[2])
  • Binabasa ang halaga intrpr.Katawan sa isang numeric variable buf_len
  • Lumilikha ng buffer newbuf tinukoy na laki:
    make([]byte, buf_len)
  • Nagpapadala ng ok na tugon:
    conn.Write([]byte("result:ok"))
  • Ganap na pinunan ang buffer mula sa read stream:
    io.ReadFull(conn, newbuf)

    .

  • Ini-print ang mga nilalaman ng buffer sa console
    fmt.Println(string(newbuf))

    at ang bilang ng mga byte na nabasa

    fmt.Println("Bytes length:", n)
  • Nagpapadala ng ok na tugon:
    conn.Write([]byte("result:ok"))

Ang mga pamamaraan mula sa package ng server ay na-configure upang iproseso ang natanggap na data gamit ang mga function mula sa package protokol.

Protokol

Ang isang protocol ay nagsisilbing isang paraan na kumakatawan sa data sa pagpapalitan ng network.

Choice(str string) (string, error) nagsasagawa ng pangunahing pagproseso ng data na natanggap ng server, tumatanggap ng string na representasyon ng data bilang input at nagbabalik ng string na inihanda para sa Interpreter:

  • Ang input string ay nahahati sa ulo at katawan gamit ReqParseN2(str)
  • ang ulo ay nahati sa mga elemento at inilagay sa isang command slice gamit ang ReqParseHead(head)
  • Π’ switch(mga utos[0]) piliin ang natanggap na utos (cmd, susi, address o ang seksyon ay na-trigger default)
  • 2 command ay naka-check sa cmd switch(commands[1]) β€” haba ΠΈ getversion.
  • haba sinusuri ang uri ng data sa mga utos[2] at i-save ito sa uri ng datos
  • Sinusuri iyon katawan naglalaman ng halaga ng string
    len(body) < 1
  • Ibinabalik ang string ng tugon:
    "result:bufferlength:" + datatype + "/" + body
  • getversion nagbabalik ng string
    return "result:version/auto"

Interpreter

Naglalaman ng istruktura ng InterpreteData at nagsasagawa ng pangalawang pagproseso ng data na ibinalik mula sa pagpili mga string at pagbuo ng bagay InterpreteData.

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

Tungkulin

Interprete(str string) (*InterpreteData, error)

tumatanggap ng string resulta at lumilikha at nagbabalik ng isang sanggunian sa bagay InterpreteData.

Pag-unlad:

  • Katulad nito pagpili ulo at katawan ay kinukuha gamit ReqParseN2(str)
  • ulo ay nahahati sa mga elemento gamit ReqParseHead(ulo)
  • Pinasimulan ang bagay InterpreteData at ibinalik ang isang pointer dito:

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

Ang bagay na ito ay ginagamit sa server.go pangunahing pakete.

Kliente

Ang client package ay naglalaman ng mga function TCPConnect ΠΈ TCPResponseData.

Tungkulin

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

gumagana tulad ng sumusunod:

  • Ang isang koneksyon ay ginawa sa koneksyon na tinukoy sa nakapasa na object ng mga setting
    net.Dial("tcp", s.Host + ":" + s.Port)
  • Ang data na ipinasa sa parameter ng data ay ipinadala:
    conn.Write(data)
  • Binabasa ang sagot
    resp, n, _ := TCPResponseData(conn, s.BufSize)

    at naka-print sa console

    fmt.Println(string(resp[:n]))
  • Kung ililipat kargamento pagkatapos ay ipapasa ito
    conn.Write(payload)

    at binabasa din ang tugon ng server, ini-print ito sa console

Tungkulin

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

lumilikha ng buffer ng tinukoy na laki, binabasa ang tugon ng server doon at ibinalik ang buffer na ito at ang bilang ng mga byte na nabasa, pati na rin ang isang bagay na error.

Subroutine ng kliyente

Nagsisilbi upang magpadala ng mga utos sa mga server ng node, pati na rin makakuha ng maikling istatistika at pagsubok.

Maaaring tanggapin ang mga sumusunod na parameter: configuration file sa JSON format, data na ipapadala sa server bilang string, path sa file na ipapadala sa payload, node scheduler emulation flag, uri ng data na inilipat bilang numeric na halaga.

  • Pagkuha ng configuration
    st := types.ParseConfig(*config)
  • Kung naipasa ang watawat ng emu, magsisimula ito sheduler
  • Kung ang f flag na nagpapahiwatig ng landas sa file ay ibinigay, pagkatapos ay i-load namin ang data nito fdb at ang nilalaman ay ipinadala sa server
    client.TCPConnect(st, []byte(CMD_BUFFER_LENGTH + ":" + strconv.Itoa(*t) + "/" + strconv.Itoa(fdblen)), fdb)
  • Kung ang file ay hindi tinukoy, pagkatapos ay ang data mula sa bandila ay ipinadala lamang -d:
    client.TCPConnect(st, []byte(*data), nil)

Ang lahat ng ito ay isang pinasimple na representasyon na nagpapakita ng istraktura ng protocol. Sa panahon ng pag-unlad, ang kinakailangang pag-andar ay idinagdag sa istraktura nito.

Sa ikalawang bahagi ay magsasalita ako tungkol sa mga istruktura ng data para sa mga bloke at transaksyon, sa 3 tungkol sa WebSocket server para sa pagkonekta mula sa JavaScript, sa 4 titingnan ko ang scheduler ng pag-synchronise, pagkatapos ay isang stack machine na nagpoproseso ng bytecode mula sa mga input at output, cryptography at pool para sa mga output.

Pinagmulan: www.habr.com

Magdagdag ng komento