Négy hónapja dolgozom a „Blokkláncon alapuló adatvédelmi és adatkezelési eszközök fejlesztése kormányzati és ipari szektorokban” című projekten.
Most arról szeretnék mesélni, hogyan kezdtem el ezt a projektet, és most részletesen leírom a programkódot.
Ez az első cikk egy cikksorozatban. Itt leírom a szervert és a protokollt. Valójában az olvasó akár saját verziót is írhat ezekről a blokklánc-elemekről.
Tavaly a Digital Breakthrough hackathonon egy olyan ötlettel álltak elő, hogy az ipar és a digitális gazdaság számára hasznos rendszert készítsenek elosztott főkönyvi technológiával, a fejlesztésre az Innovációs Segítségnyújtás Alapítvány is pályázatot írt ki (külön kellene írnom). cikk a támogatásról, azoknak, akik most kezdik a startupokat ), és most rendben.
A fejlesztés Go nyelven történik, és a blokkokat tartalmazó adatbázis a LevelDB.
A fő részek a protokoll, a szerver (amely a TCP-t és a WebSocketet futtatja - az első a blokklánc szinkronizálására, a második a kliensek összekapcsolására, tranzakciók és parancsok küldésére például JavaScriptből.
Mint már említettük, erre a blokkláncra elsősorban a beszállítók és vásárlók közötti, vagy mindkettő egy személyben történő termékcseréjének automatizálása és védelme érdekében van szükség. Ezek az emberek nem sietnek megbízni egymásban. A feladat azonban nem csak egy beépített számológéppel ellátott „ellenőrző könyv” létrehozása, hanem egy olyan rendszer, amely automatizálja a legtöbb rutinfeladatot, amely a termék életciklusával való munka során felmerül. Az ezért felelős bájtkód, ahogy az a blokkláncoknál megszokott, a tranzakciók be- és kimenetein tárolódik (maguk a tranzakciók blokkokban, a LevelDB blokkjai GOB formátumban vannak előre kódolva). Először beszéljünk a protokollról és a szerverről (más néven csomópont).
A protokoll nem bonyolult, lényege, hogy egy speciális parancssor hatására átvált bizonyos adatok, általában egy blokk vagy tranzakció betöltésének módjára, és szükséges a készletcseréhez is, hogy a csomópont tudja, kiről van szó. csatlakozik, és hogyan van dolguk (a szinkronizálási munkamenethez csatlakoztatott csomópontokat „szomszédnak” is nevezik, mivel az IP-jük ismert, állapotadatai pedig a memóriában vannak tárolva).
A mappákat (könyvtárakat, ahogyan Linux nevezi) a Go programozók felfogásában csomagoknak nevezik, ezért minden fájl elejére, amely ebből a könyvtárból van Go kóddal, azt írják, hogy mappa_név_ahol_ez a fájl található. Ellenkező esetben nem tudja továbbítani a csomagot a fordítónak. Nos, ez nem titok azok számára, akik ismerik ezt a nyelvet. Ezek a csomagok:
- Hálózati kommunikáció (szerver, kliens, protokoll)
- A tárolt és továbbított adatok struktúrái (blokk, tranzakció)
- Adatbázis (blokklánc)
- Konszenzus
- Halmozott virtuális gép (xvm)
- Kiegészítő (kripto, típusok) egyelőre ennyi.
Ez egy oktatási változat, hiányzik belőle a folyamatok közötti interakció és több kísérleti komponens, de a struktúra megfelel annak, amelyen a fejlesztés folyik. Ha van valami javaslatotok kommentben, azt szívesen figyelembe veszem a további fejlesztés során. És most egy magyarázat a szerver és protokoll.
Nézzük először a szervert.
A szerver szubrutin adatszerverként működik, amely a TCP-protokoll tetején fut a protokollcsomagból származó adatstruktúrák használatával.
A rutin a következő csomagokat használja: szerver, protokoll, típusok. Magában a csomagban tcp_server.go adatstruktúrát tartalmaz Szolgál.
type Serve struct {
Port string
BufSize int
ST *types.Settings
}
A következő paramétereket tudja elfogadni:
- Hálózati port, amelyen keresztül adatcsere történik
- A szerver konfigurációs fájlja JSON formátumban
- Megjelölés hibakeresési módban való futtatáshoz (privát blokklánc)
Előrehalad:
- Beolvassa a konfigurációt a JSON-fájlból
- A hibakeresési mód jelzője be van jelölve: ha be van állítva, a hálózati szinkronizálás ütemezője nem indul el és a blokklánc nem töltődik be
- A konfigurációs adatstruktúra inicializálása és a szerver indítása
szerver
- A TCP szerver elindítását és a hálózati interakciót a protokollnak megfelelően végzi.
- Kiszolgálási adatstruktúrával rendelkezik, amely portszámból, pufferméretből és a szerkezetre mutató mutatóból áll típusok.Beállítások
- A Run metódus elindítja a hálózati interakciót (egy adott porton bejövő kapcsolatok figyelése, új kapcsolat fogadásakor a feldolgozása egy új szálban átkerül a privát kezelő metódusba)
- В fogantyú A kapcsolat adatait a rendszer beolvassa egy pufferbe, karakterlánc-reprezentációvá alakítja és továbbítja protokoll.Választás
- protokoll.Választás visszatér eredményez vagy hibát okoz. eredményez majd átkerült a protokoll.Interpreteami visszatér intrpr - típusú objektum InterpreteData, vagy hibát okoz a kiválasztási eredmény feldolgozása során
- Ezután a váltás végrehajtódik intrpr.Commands[0] amely a következők egyikét ellenőrzi: eredmény, bev., hiba és van egy szakasz alapértelmezett
- A szakaszban eredményez kapcsolót érték alapján találjuk meg intrpr.Commands[1] amely ellenőrzi az értékeket pufferhossz и változat (minden esetben a megfelelő függvényt hívjuk meg)
függvények GetVersion и PufferLength vannak a fájlban srvlib.go szerver csomag
GetVersion(conn net.Conn, version string)
egyszerűen kinyomtatja a konzolra, és elküldi a paraméterben átadott verziót a kliensnek:
conn.Write([]byte("result:" + version))
.
Funkció
BufferLength(conn net.Conn, intrpr *protocol.InterpreteData)
blokkot, tranzakciót vagy más konkrét adatot tölt be az alábbiak szerint:
- Kinyomtatja a konzolra a protokollban megadott típusú adatokat, amelyeket el kell fogadni:
fmt.Println("DataType:", intrpr.Commands[2])
- Beolvassa az értéket intrpr.Body numerikus változóhoz buf_len
- Puffert hoz létre newbuf megadott méret:
make([]byte, buf_len)
- Rendben választ küld:
conn.Write([]byte("result:ok"))
- Teljesen kitölti a puffert az olvasási adatfolyamból:
io.ReadFull(conn, newbuf)
.
- Kinyomtatja a puffer tartalmát a konzolra
fmt.Println(string(newbuf))
és az olvasott bájtok száma
fmt.Println("Bytes length:", n)
- Rendben választ küld:
conn.Write([]byte("result:ok"))
A szervercsomag módszerei úgy vannak beállítva, hogy a csomag függvényei segítségével dolgozzák fel a fogadott adatokat protokoll.
Protokoll
A protokoll eszközként szolgál az adatok megjelenítésére a hálózati cserében.
Választás(karakterlánc) (karakterlánc, hiba) elvégzi a szerver által fogadott adatok elsődleges feldolgozását, bemenetként megkapja az adatok sztring reprezentációját, és visszaadja a Tolmács:
- A bemeneti karakterlánc fejre és testre van osztva ReqParseN2(str)
- A fej elemre oszlik, és a ReqParseHead(head) segítségével parancsszeletbe helyezi
- В kapcsoló (parancsok[0]) válassza ki a kapott parancsot (cmd, kulcs, cím vagy a szakasz kioldódik alapértelmezett)
- 2 parancs van bejelölve a cmd-ben kapcsoló(parancsok[1]) — hossza и getversion.
- hossz ellenőrzi az adattípust parancsok[2] és elmenti adattípus
- Azt ellenőrzi test karakterlánc-értéket tartalmaz
len(body) < 1
- A válasz karakterláncát adja vissza:
"result:bufferlength:" + datatype + "/" + body
- getversion karakterláncot ad vissza
return "result:version/auto"
Tolmács
Tartalmazza az InterpreteData struktúrát, és másodlagos feldolgozását végzi a visszaküldött adatokon Választás húrok és tárgyképzés InterpreteData.
type InterpreteData struct {
Head string
Commands []string
Body string
IsErr bool
ErrCode int
ErrMessage string
}
Funkció
Interprete(str string) (*InterpreteData, error)
elfogad egy karakterláncot eredményez és hivatkozást hoz létre és visszaad az objektumra InterpreteData.
Előrehalad:
- hasonlóképpen Választás fejét és testét segítségével vonják ki ReqParseN2(str)
- segítségével a fejet elemekre osztjuk ReqParseHead(fej)
- Az objektum inicializálva van InterpreteData és egy mutatót ad vissza:
res := &InterpreteData{
Head: head,
Commands: commands,
Body: body,
}
return res, nil
Ezt az objektumot használják szerver.go csomag fő.
Vásárló
A kliens csomag tartalmazza a funkciókat TCPConnect и TCPResponseData.
Funkció
TCPConnect(s *types.Settings, data []byte, payload []byte)
a következőképpen működik:
- Létrejön a kapcsolat az átadott beállítások objektumban megadott kapcsolattal
net.Dial("tcp", s.Host + ":" + s.Port)
- Az adatparaméterben átadott adatok átvitele:
conn.Write(data)
- A válasz olvasható
resp, n, _ := TCPResponseData(conn, s.BufSize)
és a konzolra nyomtatva
fmt.Println(string(resp[:n]))
- Ha áthelyezik hasznos teher majd továbbadja
conn.Write(payload)
és beolvassa a szerver választ is, és kinyomtatja a konzolra
Funkció
TCPResponseData(conn net.Conn, bufsiz int) ([]byte, int, error)
létrehoz egy meghatározott méretű puffert, beolvassa a szerver válaszát és visszaadja ezt a puffert és az olvasott bájtok számát, valamint egy hibaobjektumot.
Kliens szubrutin
Parancsok küldésére szolgál a csomóponti kiszolgálóknak, valamint rövid statisztikák és tesztelések lekérésére.
A következő paramétereket tudja elfogadni: konfigurációs fájl JSON formátumban, a szerverre sztringként küldendő adatok, a rakományba küldendő fájl elérési útja, csomópont-ütemező emulációs jelző, numerikus értékként átvitt adatok típusa.
- A konfiguráció lekérése
st := types.ParseConfig(*config)
- Ha az emu zászlót átadjuk, elindul ütemező
- Ha megadjuk a fájl elérési útját jelző f jelzőt, akkor betöltjük az adatait FDB és a tartalom elküldésre kerül a szerverre
client.TCPConnect(st, []byte(CMD_BUFFER_LENGTH + ":" + strconv.Itoa(*t) + "/" + strconv.Itoa(fdblen)), fdb)
- Ha a fájl nincs megadva, akkor a zászlóból származó adatok egyszerűen elküldésre kerülnek -d:
client.TCPConnect(st, []byte(*data), nil)
Mindez a protokoll szerkezetét bemutató leegyszerűsített ábrázolás. A fejlesztés során a szükséges funkcionalitás hozzáadódik a szerkezetéhez.
A második részben a blokkok és tranzakciók adatstruktúráiról fogok beszélni, a 3-ban a WebSocket szerverről a JavaScript-ből való csatlakozáshoz, a 4-ben a szinkronizálás ütemezőt, majd egy veremgépet, amely a bemenetek és kimenetek bájtkódját dolgozza fel, kriptográfia ill. medencék a kimenetekhez.
Forrás: will.com