Test publik: zgjidhje për privatësinë dhe shkallëzueshmërinë në Ethereum

Blockchain — njĂ« teknologji inovative qĂ« premton tĂ« pĂ«rmirĂ«sojĂ« shumĂ« aspekte tĂ« jetĂ«s njerĂ«zore. Ajo transferon procese dhe produkte reale nĂ« hapĂ«sirĂ«n digjitale, siguron shpejtĂ«si dhe besueshmĂ«ri nĂ« operacionet financiare, ul kostot e tyre, si dhe mundĂ«son krijimin e aplikacioneve moderne DAPP duke pĂ«rdorur kontrata inteligjente nĂ« rrjeta tĂ« decentralizuara.

Duke pasur parasysh përfitimet e shumta dhe aplikacionet e ndryshme të blockchain, mund të duket e çuditshme që kjo teknologji premtuese nuk ka depërtuar ende në të gjitha sektoret. Problemi është se blockchain-et e decentralizuar modernë i mangësin shkallëzueshmërisë. Ethereum proceson rreth 20 transaksione në sekondë, që është e pamjaftueshme për të përmbushur nevojat e biznesit dinamik modern. Në të njëjtën kohë, kompanitë që përdorin teknologjinë blockchain nuk guxojnë të dorëzojnë Ethereum për shkak të nivelit të tij të lartë të sigurisë ndaj hakimeve dhe dështimeve të rrjetit.

PĂ«r tĂ« siguruar decentralizimin, sigurinĂ« dhe shkallĂ«zueshmĂ«rinĂ« nĂ« blockchain, duke zgjidhur kĂ«shtu TrilemmĂ«n e ShkallĂ«zueshmĂ«risĂ«, ekipi i zhvilluesve Opporty krijoi Plasma Cash — njĂ« zinxhir derivativ qĂ« pĂ«rbĂ«het nga njĂ« kontratĂ« inteligjente dhe njĂ« rrjet privat tĂ« bazuar nĂ« Node.js, i cili transferon periodikisht gjendjen e tij nĂ« zinxhirin kryesor (Ethereum).

Test publik: zgjidhje për privatësinë dhe shkallëzueshmërinë në Ethereum

Proceset kryesore në Plasma Cash

1. Përdoruesi thërret funksionin e kontratës inteligjente `deposit`, duke i kaluar një shumë në ETH që ai dëshiron të depozitojë në tokenin Plasma Cash. Funksioni i kontratës inteligjente krijon tokenin dhe gjeneron një ngjarje në lidhje me këtë.

2. Nodet e Plasma Cash, që janë shkruar për ngjarjet e kontratës inteligjente, marrin ngjarjen e krijimit të depozitës dhe shtojnë në rezervuar transaksionin e krijimit të tokenit.

3. Periodikisht, nodet speciale të Plasma Cash marrin të gjitha transaksionet nga rezervuari ( deri në 1 milion) dhe formojnë nga ato një bllok, llogarisin pemën Merkle dhe, për rrjedhojë, hash-in. Ky bllok dërgohet te nodet e tjera për verifikim. Nodet kontrollojnë nëse hash-i Merkle është valid, nëse transaksionet janë valide (p.sh., nëse dërguesi i tokenit është pronari i tij). Pas verifikimit të bllokut, nodi thërret funksionin `submitBlock` të kontratës inteligjente, e cila ruan në zinxhirin kryesor numrin dhe hash-in Merkle të bllokut. Kontrata inteligjente gjeneron një ngjarje për shtimin e suksesshëm të bllokut. Transaksionet fshihen nga rezervuari.

4. Nodet që marrin një ngjarje për dërgimin e bllokut fillojnë të aplikojnë transaksionet që u janë shtuar bllokut.

5. Në një moment, pronari (ose një person tjetër) i simbolit dëshiron ta tërheqë atë nga Plasma Cash. Për këtë, ai thërret funksionin `startExit`, duke i kaluar informacionet për dy transaksionet e fundit të simbolit, të cilat konfirmojnë se ai është pronari i simbolit. Kontrata inteligjente, duke përdorur hashin Merkle, verifikon praninë e transaksioneve në blloqe dhe dërgon simbolin për tërheqje, e cila do të ndodhi pas dy javësh.

6. Nëse operacioni i tërheqjes së simbolit ndodhi me shkelje (simboli u shpenzua pas fillimit të procedurës së tërheqjes ose simboli para tërheqjes ishte tashmë i huaj), pronari i simbolit mund ta mohojë tërheqjen brenda dy javësh.

Test publik: zgjidhje për privatësinë dhe shkallëzueshmërinë në Ethereum

Privatësia arrihet në dy mënyra.

1. Zgjidhja rrënjësore nuk di asgjë për transaksionet që formohen dhe dërgohen brenda zgjidhjes dytësore. Informacioni publik mbetet për ata që kanë depozituar dhe tërheqin ETH në/nga Plasma Cash.

2. Zgjidhja dytësore lejon organizimin e transaksioneve anonime duke përdorur zk-SNARKs.

Stoku teknologjik

  • NodeJS
  • Redis
  • Etherium
  • Soild

Testimi

Duke zhvilluar Plasma Cash, ne testuam shpejtësinë e funksionimit të sistemit dhe morëm këto rezultate:

  • deri nĂ« 35,000 transaksione nĂ« sekondĂ« shtohen nĂ« rezervuar;
  • deri nĂ« 1,000,000 transaksione mund tĂ« ruhet nĂ« bllok.

Testet u kryen në 3 serverat e mëposhtëm:

1. Intel Core i7-6700 Quad-Core Skylake pĂ«rfshirĂ« NVMe SSD — 512 GB, 64 GB DDR4 RAM
U ngritën 3 nodet verifikuese të Plasma Cash.

2. AMD Ryzen 7 1700X Octa-Core «Summit Ridge» (Zen), SATA SSD — 500 GB, 64 GB DDR4 RAM
U ngrit një nod Ropsten testnet ETH.
U ngritën 3 nodet verifikuese të Plasma Cash.

3. Intel Core i9-9900K Octa-Core pĂ«rfshirĂ« NVMe SSD — 1 TB, 64 GB DDR4 RAM
U ngrit një nod submit të Plasma Cash.
U ngritën 3 nodet verifikuese të Plasma Cash.
U realizua testi për shtimin e transaksioneve në rrjetin Plasma Cash.

Në përmbledhje: 10 nodet Plasma Cash në rrjetin privat.

Testi 1

Ka një limit prej 1 milion transaksionesh në bllok. Prandaj 1 milion transaksione nisen në 2 blloqe (pasi sistemi arrin të marrë një pjesë të transaksioneve dhe t'i paraqesë ndërsa ato dërgohen).

Luaj videon

Gjendja fillestare: blloku i fundit #7; në bazën e të dhënave janë ruajtur 1 milion transaksione dhe tokena.

00:00 — nisja e skenarit pĂ«r gjenerimin e transaksioneve
01:37 — janĂ« krijuar 1 milion transaksione dhe ka filluar dĂ«rgimi nĂ« nod
01:46 — nodi i submitit mori nga rezervuari 240k transaksione dhe po formon bllokun #8. Gjithashtu shohim se nĂ« rezervuar shtohen 320k transaksione brenda 10 sekondash.
01:58 — blloku #8 Ă«shtĂ« nĂ«nshkruar dhe dĂ«rguar pĂ«r validim
02:03 — blloku #8 Ă«shtĂ« validuar dhe funksioni `submitBlock` i smart kontraktit Ă«shtĂ« thirrur me hash-in Merkle dhe numrin e bllokut
02:10 — skripti demo ka pĂ«rfunduar punĂ«n, i cili dĂ«rgoi 1 milion transaksione nĂ« 32 sekonda
02:33 — nodet filluan tĂ« marrin informacion se blloku #8 Ă«shtĂ« shtuar nĂ« zinxhirin rrĂ«njĂ«sor dhe filluan tĂ« ekzekutojnĂ« 240,000 transaksione
02:40 — nga piscina janĂ« hequr 240,000 transaksione, tĂ« cilat tashmĂ« janĂ« nĂ« bllokun #8
02:56 — nodĂ« e nĂ«nshkrimit mori nga piscina 760,000 transaksione tĂ« mbetura dhe filloi tĂ« llogarisĂ« hash-in Merkle dhe tĂ« nĂ«nshkruajĂ« bllokun #9
03:20 — tĂ« gjitha nodet pĂ«rmbajnĂ« 1 milion 240,000 transaksione dhe token
03:35 — blloku #9 Ă«shtĂ« nĂ«nshkruar dhe dĂ«rgohet pĂ«r validim nĂ« nodet e tjera
03:41 — ndodhi njĂ« gabim nĂ« rrjet
04:40 — pĂ«r shkak tĂ« kohĂ«s sĂ« pritur, pritja pĂ«r validimin e bllokut #9 Ă«shtĂ« ndĂ«rprerĂ«
04:54 — nodĂ« e nĂ«nshkrimit mori nga piscina 760,000 transaksione tĂ« mbetura dhe filloi tĂ« llogarisĂ« hash-in Merkle dhe tĂ« nĂ«nshkruajĂ« bllokun #9
05:32 — blloku #9 Ă«shtĂ« nĂ«nshkruar dhe dĂ«rgohet pĂ«r validim nĂ« nodet e tjera
05:53 — blloku #9 Ă«shtĂ« validuar dhe dĂ«rguar nĂ« zinxhirin rrĂ«njĂ«sor
06:17 — nodet filluan tĂ« marrin informacion se blloku #9 Ă«shtĂ« shtuar nĂ« zinxhirin rrĂ«njĂ«sor dhe filluan tĂ« ekzekutojnĂ« 760,000 transaksione
06:47 — pika u pastruar nga transaksionet, qĂ« ishin nĂ« bllokun #9
09:06 — tĂ« gjithĂ« nodet pĂ«rmbajnĂ« 2 milion transaksione dhe tokena

Test 2

Ka një limit prej 350k për bllok. Si rezultat, kemi 3 blloqe.

Luaj videon

Gjykuar gjendja fillestare: blloku i fundit #9; në bazë janë ruajtur 2 milion transaksione dhe tokena

00:00 — skripti i gjenerimit tĂ« transaksioneve Ă«shtĂ« aktivizuar tashmĂ«
00:44 — janĂ« krijuar 1 milion transaksione dhe ka filluar dĂ«rgimi nĂ« nod
00:56 — nodi i dĂ«rgimit ka marrĂ« nga pika 320k transaksione dhe po formon bllokun #10. Po ashtu shohim se nĂ« pikĂ« shtohen 320k transaksione pĂ«r 10 sekonda
01:12 — blloku #10 Ă«shtĂ« nĂ«nshkruar dhe po dĂ«rgohet te nodet e tjera pĂ«r validim
01:18 — skripti demo pĂ«rfundoi punĂ«n, i cili dĂ«rgoi 1 milion transaksione pĂ«r 34 sekonda
01:20 — blloku #10 Ă«shtĂ« validuar dhe dĂ«rguar nĂ« zinxhirin kryesor
01:51 — tĂ« gjithĂ« nodet morĂ«n nga zinxhiri kryesor informacionin se blloku #10 Ă«shtĂ« shtuar dhe fillojnĂ« tĂ« aplikojnĂ« 320k transaksione
02:01 — pika u pastrua nga 320k transaksione, qĂ« u shtuan nĂ« bllokun #10
02:15 — nodi i dĂ«rgimit ka marrĂ« nga pika 350k transaksione dhe po formon bllokun #11
02:34 — blloku #11 Ă«shtĂ« nĂ«nshkruar dhe po dĂ«rgohet nodet e tjera pĂ«r validim
02:51 — blloku #11 Ă«shtĂ« validuar dhe dĂ«rguar nĂ« zinxhirin kryesor
02:55 — nodi i fundit ka realizuar transaksionet nga bloku #10
10:59 — njĂ« transaksion me dorĂ«zim tĂ« blokut #9 zgjati shumĂ« nĂ« zinxhirin kryesor, por ai u ekzekutua dhe tĂ« gjitha nodet morĂ«n informacionin dhe filluan tĂ« realizonin 350k transaksione
11:05 — pool-i u pastrua me 320k transaksione, tĂ« cilat u shtuan nĂ« blokun #11
12:10 — tĂ« gjitha nodet pĂ«rmbajnĂ« 1 milion 670k transaksione dhe tokene
12:17 — nodi i dorĂ«zimit mori nga pool 330k transaksione dhe po formon blokun #12
12:32 — bloku #12 Ă«shtĂ« nĂ«nshkruar dhe po dĂ«rgohet tek nodet e tjera pĂ«r validim
12:39 — bloku #12 Ă«shtĂ« validuar dhe dĂ«rguar nĂ« zinxhirin kryesor
13:44 — tĂ« gjitha nodet morĂ«n informacion nga zinxhiri kryesor qĂ« bloku #12 Ă«shtĂ« shtuar dhe fillojnĂ« tĂ« aplikojnĂ« 330k transaksione
14:50 — tĂ« gjitha nodet pĂ«rmbajnĂ« 2 milion transaksione dhe tokene

Test 3

Në serverët e parë dhe të dytë, një nod validues u zëvendësua me një nod dorëzimi.

Luaj videon

Stadi i parë: bloku i fundit #84; në bazë janë ruajtur 0 transaksione dhe tokene

00:00 — JanĂ« nisur 3 skripta qĂ« gjenerojnĂ« dhe dĂ«rgojnĂ« nga 1 milion transaksione
01:38 — janĂ« krijuar 1 milion transaksione dhe ka filluar dĂ«rgimi nĂ« nodin e dorĂ«zimit #3
01:50 — nodi i dorĂ«zimit #3 ka marrĂ« nga pool 330k transaksionesh dhe po formon bllokun #85 (f21). Po ashtu, shohim se nĂ« pool po shtohet 350k transaksionesh brenda 10 sekondave.
01:53 — u krijua 1 milion transaksionesh dhe filloi dĂ«rgesa nĂ« nodin e dorĂ«zimit #1.
01:50 — nodi i dorĂ«zimit #3 ka marrĂ« nga pool 330k transaksionesh dhe po formon bllokun #85 (f21). Po ashtu, shohim se nĂ« pool po shtohet 350k transaksionesh brenda 10 sekondave.
02:01 — nodi i dorĂ«zimit #1 ka marrĂ« nga pool 250k transaksionesh dhe po formon bllokun #85 (65e).
02:06 — blloku #85 (f21) Ă«shtĂ« nĂ«nshkruar dhe po dĂ«rgohet nĂ« nodet e tjera pĂ«r verifikim.
02:08 — ka pĂ«rfunduar punĂ«n skripti demo i serverit #3, i cili dĂ«rgoi 1 milion transaksionesh brenda 30 sekondash.
02:14 — blloku #85 (f21) Ă«shtĂ« verifikuar dhe dĂ«rguar nĂ« zinxhirin kryesor.
02:19 — blloku #85 (65e) Ă«shtĂ« nĂ«nshkruar dhe po dĂ«rgohet nĂ« nodet e tjera pĂ«r verifikim.
02:22 — u krijua 1 milion transaksionesh dhe filloi dĂ«rgesa nĂ« nodin e dorĂ«zimit #2.
02:27 — blloku #85 (65e) Ă«shtĂ« verifikuar dhe dĂ«rguar nĂ« zinxhirin kryesor.
02:29 — nodi i dorĂ«zimit #2 ka marrĂ« nga pool 111855 transaksionesh dhe po formon bllokun #85 (256).
02:36 — blloku #85 (256) Ă«shtĂ« nĂ«nshkruar dhe po dĂ«rgohet nĂ« nodet e tjera pĂ«r verifikim.
02:36 — ka pĂ«rfunduar punĂ«n skripti demo i serverit #1, i cili dĂ«rgoi 1 milion transaksionesh brenda 42.5 sekondash.
02:38 — blloku #85 (256) Ă«shtĂ« verifikuar dhe dĂ«rguar nĂ« zinxhirin kryesor.
03:08 — ka pĂ«rfunduar punĂ«n skripti demo i serverit #2, i cili dĂ«rgoi 1 milion transaksionesh brenda 47 sekondash.
03:38 — tĂ« gjitha nodet morĂ«n nga zinxhiri rrĂ«njĂ«sor informacionin se blloqet #85 (f21), #86(65e), #87(256) u shtuan dhe fillojnĂ« tĂ« aplikojnĂ« 330k, 250k, 111855 transaksione
03:49 — pool-i u pastrua me 330k, 250k, 111855 transaksione, tĂ« cilat u shtuan nĂ« blloqet #85 (f21), #86(65e), #87(256)
03:59 — submit nodi #1 mori nga pool-i 888145 transaksione dhe po formon bllokun #88 (214), submit nodi #2 mori nga pool-i 750k transaksione dhe po formon bllokun #88 (50a), submit nodi #3 mori nga pool-i 670k transaksione dhe po formon bllokun #88 (d3b)
04:44 — blloku #88 (d3b) u nĂ«nshkrua dhe dĂ«rgohet nodet e tjera pĂ«r validim
04:58 — blloku #88 (214) u nĂ«nshkrua dhe dĂ«rgohet nodet e tjera pĂ«r validim
05:11 — blloku #88 (50a) u nĂ«nshkrua dhe dĂ«rgohet nodet e tjera pĂ«r validim
05:11 — blloku #85 (d3b) u validua dhe u dĂ«rgua nĂ« zinxhirin rrĂ«njor
05:36 — blloku #85 (214) u validua dhe u dĂ«rgua nĂ« zinxhirin rrĂ«njor
05:43 — tĂ« gjitha nodet morĂ«n nga zinxhiri rrĂ«njĂ«sor informacionin se blloqet #88 (d3b), #89(214) u shtuan dhe fillojnĂ« tĂ« aplikojnĂ« 670k, 750k transaksione
06:50 — pĂ«r shkak tĂ« ndĂ«rprerjes sĂ« lidhjes, blloku #85 (50a) nuk u validua
06:55 — submit nodi #2 mori nga pool 888145 transaksione dhe po formon bllokun #90 (50a)
08:14 — blloku #90 (50a) u nĂ«nshkrua dhe dĂ«rgohet nodet e tjera pĂ«r validim
09:04 — blloku #90 (50a) Ă«shtĂ« validuar dhe dĂ«rguar nĂ« zinxhirin rrĂ«njor
11:23 — tĂ« gjitha nodet morĂ«n nga zinxhirin rrĂ«njor informacionin se blloku #90 (50a) Ă«shtĂ« shtuar dhe fillojnĂ« tĂ« zbatojnĂ« 888145 transaksione. NĂ« kĂ«tĂ« moment, serveri #3 ka zbatuar tashmĂ« transaksionet nga blloqet #88 (d3b), #89 (214)
12:11 — tĂ« gjitha pool-at janĂ« bosh
13:41 — tĂ« gjitha nodet e serverit #3 pĂ«rmbajnĂ« 3 milion transaksione dhe tokena
14:35 — tĂ« gjitha nodet e serverit #1 pĂ«rmbajnĂ« 3 milion transaksione dhe tokena
19:24 — tĂ« gjitha nodet e serverit #2 pĂ«rmbajnĂ« 3 milion transaksione dhe tokena

Pengesat

Gjatë zhvillimit të Plasma Cash, ne u përballëm me problemet e mëposhtme, të cilat i zgjidhim gradualisht:

1. Konflikti i ndërveprimit të funksioneve të ndryshme të sistemit. Për shembull, funksioni i shtimit të transaksioneve në pool bllokonte punën e dërgimit dhe validimit të blloqeve dhe anasjelltas, çka çonte në një rënie të shpejtësisë.

2. Nuk ishte e qartë se si të dërgohej një numër i madh transaksionesh dhe njëkohësisht të minimalizoheshin shpenzimet për transferimin e të dhënave.

3. Nuk ishte e qartë se si dhe ku të ruheshin të dhënat për të arritur rezultate të larta.

4. Nuk ishte e qartë se si të organizohej rrjeti midis nodave, pasi madhësia e bllokut me 1 milion transaksione zë rreth 100 MB.

5. Puna në mënyrë njënjërë shkëput lidhjen mes node-ve kur ndodhin llogaritje të gjatë (p.sh., ndërtimi i pemës Merkle dhe llogaritja e hash-it të saj).

Si e zgjidhëm këtë?

Versioni i parë i nodës Plasma Cash ishte një kombinim që mund të bënte gjithçka njëkohësisht: pranonte transaksionet, dërgonte dhe validonte blloqet, siguronte një API për qasje në të dhëna. Duke qenë se NodeJS është fillimisht një njënjërëshe, funksioni i rëndë i llogaritjes së pemës Merkle bllokonte funksionin e shtimit të transaksionit. Ne shihnim dy opsione për të zgjidhur këtë problem:

1. Të nisnim disa procese NodeJS, secili prej të cilëve kryen funksione të caktuara.

2. Të përdornim worker_threads dhe të nxirrnim ekzekutimin e pjesës së kodit nëThreads.

Më në fund, ne përdorëm të dyja opsionet njëkohësisht: e ndamë logjikisht një nod në 3 pjesë që mund të punojnë veçmas, por njëkohësisht dhe në harmoni.

1. Nodë e dërgimit, që pranon transaksione në pool dhe merret me krijimin e blloqeve.

2. Nodë valide, që kontrollon vlefshmërinë e nodes.

3. Nodë API - ofron API për qasje në të dhëna.

Çdo nodĂ« mund tĂ« lidhet pĂ«rmes unix socket-i duke pĂ«rdorur cli.

Operacionet e mëdha, si llogaritja e pemës Merkle, i kemi nxjerrë në një thread të veçantë.

Kështu arritëm që të funksionojmë të gjitha funksionet e Plasma Cash në të njëjtën kohë dhe pa ndërprerje.

Sa herë që sistemi filloi të funksiononte, filluam të testonim shpejtësinë dhe, për fat të keq, morëm rezultate të pakënaqshme: 5,000 transaksione për sekondë dhe deri në 50,000 transaksione në bllok. Duhej të zbulojmë se çfarë ishte implementuar gabim.

Së pari, filluam të testonim mekanizmin e komunikimit me Plasma Cash për të zbuluar kapacitetin maksimal të sistemit. Më parë kishim shkruar se nodë Plasma Cash ofron një ndërfaqe unix socket. Fillimisht, ajo ishte tekstuale. Objektet json dërgoheshin duke përdorur `JSON.parse()` dhe `JSON.stringify()`.

```json
{
  "action": "sendTransaction",
  "payload":{
    "prevHash": "0x8a88cc4217745fd0b4eb161f6923235da10593be66b841d47da86b9cd95d93e0",
    "prevBlock": 41,
    "tokenId": "57570139642005649136210751546585740989890521125187435281313126554130572876445",
    "newOwner": "0x200eabe5b26e547446ae5821622892291632d4f4",
    "type": "pay",
    "data": "",
    "signature": "0xd1107d0c6df15e01e168e631a386363c72206cb75b233f8f3cf883134854967e1cd9b3306cc5c0ce58f0a7397ae9b2487501b56695fe3a3c90ec0f61c7ea4a721c"
  }
}
```

Ne kemi matur shpejtësinë e transferimit të këtyre objekteve dhe kemi marrë ~ 130,000 në sekondë. Provuam të zëvendësonim funksionet standarde për punën me json, por performance nuk u përmirësua. Duhet të jetë që motori V8 është optimizuar mirë për këto operacione.

Puna me transaksionet, tokenët, blloqet u realizua përmes klasave. Gjatë krijimit të këtyre klasave, performanca ra dyfish, gjë që tregon se OOP nuk na përshtatet. Na duhej të riprogramonim gjithçka në një qasje të pastër funksionale.

Shkruaj në bazën e të dhënave

Fillimisht për ruajtjen e të dhënave u zgjodh Redis si një nga zgjidhjet më të produeshuara, që përmbush kërkesat tona: depo e çelëset-vlerat, punë me tabela hash, grupe. Lansuam redis-benchmark dhe morëm ~80,000 operacione në sekondë në reimin 1 pipelining.

Për performancë të lartë, e kemi konfiguruar Redis më hollësisht:

  • VendosĂ«m lidhjen unix socket.
  • Kemi çaktivizuar ruajtjen e gjendjes nĂ« disk (pĂ«r siguri, mund tĂ« konfigurojmĂ« njĂ« replikĂ« dhe tashmĂ« nĂ« njĂ« Redis tĂ« veçantĂ« tĂ« bĂ«jmĂ« ruajtjen nĂ« disk).

Në Redis, një pool është një tabelë hash, pasi kemi nevojë për mundësinë për të marrë të gjitha transaksionet me një kërkesë dhe për të fshirë transaksionet një nga një. Provuam të përdorim një listë të zakonshme, por ajo funksionon më ngadalë kur del lista e plotë.

Duke përdorur bibliotekën standarde NodeJS për Redis, arritëm një performancë prej 18k transaksionesh në sekondë. Shpejtësia ra 9 herë.

Duke parĂ« qĂ« benchmarku na tregoi njĂ« performancĂ« deri nĂ« 5 herĂ« mĂ« shumĂ«, filluam tĂ« optimizojmĂ«. NdĂ«moruam bibliotekĂ«n nĂ« ioredis dhe arritĂ«m 25k nĂ« sekondĂ«. Transaksionet i shtonim njĂ« nga njĂ«, duke pĂ«rdorur komandĂ«n `hset`. KĂ«shtu, krijonim shumĂ« kĂ«rkesa nĂ« Redis. ErdhĂ«n nĂ« mendje njĂ« ide pĂ«r tĂ« bashkuar transaksionet nĂ« grupe dhe pĂ«r t'i dĂ«rguar ato me njĂ« komandĂ« `hmset`. Rezultati — 32k nĂ« sekondĂ«.

PĂ«r disa arsye, tĂ« cilat do t'i pĂ«rshkruajmĂ« mĂ« poshtĂ«, ne punojmĂ« me tĂ« dhĂ«nat duke pĂ«rdorur `Buffer` dhe, siç duket, nĂ«se e konvertojmĂ« nĂ« tekst (`buffer.toString(‘hex’)`) para se ta shkruajmĂ«, mund tĂ« arrijmĂ« njĂ« performancĂ« shtesĂ«. NĂ« kĂ«tĂ« mĂ«nyrĂ«, arritĂ«m tĂ« rrisim shpejtĂ«sinĂ« deri nĂ« 35k nĂ« sekondĂ«. NĂ« kĂ«tĂ« moment, vendosĂ«m tĂ« ndalojmĂ« optimizimin e mĂ«tejshĂ«m.

Na duhej qëndruam te protokolli binar pasi:

1. Sistemi shpesh llogarit hash-e, nënshkrime dhe të ngjashme, dhe për këtë i nevojiten të dhënat në `Buffer.

2. Kur dërgohen midis shërbimeve, të dhënat binare peshojnë më pak se tekstualet. Për shembull, kur dërgohet një bllok me 1 milion transaksione, të dhënat në tekst mund të zënë më shumë se 300 megabajt.

3. Transformimi konstant i të dhënave ndikon në performancë.

Prandaj, ne morëm si bazë një protokoll të ndryshëm binar për ruajtjen dhe transmetimin e të dhënave, të zhvilluar mbi bibliotekën e shkëlqyer `binary-data.

Si rezultat, kemi krijuar strukturat e mëposhtme të të dhënave:

— Transaksioni

  ```json
  {
    prevHash: BD.types.buffer(20),
    prevBlock: BD.types.uint24le,
    tokenId: BD.types.string(null),
    type: BD.types.uint8,
    newOwner: BD.types.buffer(20),
    dataLength: BD.types.uint24le,
    data: BD.types.buffer(({current}) => current.dataLength),
    signature: BD.types.buffer(65),
    hash: BD.types.buffer(32),
    blockNumber: BD.types.uint24le,
    timestamp: BD.types.uint48le,
  }
  ```

— Tokeni

  ```json
  {
    id: BD.types.string(null),
    owner: BD.types.buffer(20),
    block: BD.types.uint24le,
    amount: BD.types.string(null),
  }
  ```

— Blloku

  ```json
  {
    number: BD.types.uint24le,
    merkleRootHash: BD.types.buffer(32),
    signature: BD.types.buffer(65),
    countTx: BD.types.uint24le,
    transactions: BD.types.array(Transaction.Protocol, ({current}) => current.countTx),
    timestamp: BD.types.uint48le,
  }
  ```

Me komandat e zakonshme `BD.encode(block, Protocol).slice();` dhe `BD.decode(buffer, Protocol)` ne transformojmë të dhënat në `Buffer` për t'i ruajtur në Redis ose për t'i dërguar në nodin tjetër dhe për t'i tërhequr të dhënat përsëri.

Gjithashtu, ne kemi 2 protokolle binar për transferimin e të dhënave midis shërbimeve:

— Protokolli pĂ«r ndĂ«rveprimin me Plasma Node pĂ«rmes socket unix

  ```json
  {
    type: BD.types.uint8,
    messageId: BD.types.uint24le,
    error: BD.types.uint8,
    length: BD.types.uint24le,
    payload: BD.types.buffer(({node}) => node.length)
  }
  ```

ku:

  • `type` — veprimi qĂ« duhet tĂ« kryhet, pĂ«r shembull, 1 — sendTransaction, 2 — getTransaction;
  • `payload` — tĂ« dhĂ«nat qĂ« duhet tĂ« dĂ«rgohen nĂ« funksionin pĂ«rkatĂ«s;
  • `messageId` — ID e mesazhit, pĂ«r tĂ« identifikuar pĂ«rgjigjen.

— Protokolli i ndĂ«rveprimit midis nodĂ«ve

  ```json
  {
    code: BD.types.uint8,
    versionProtocol: BD.types.uint24le,
    seq: BD.types.uint8,
    countChunk: BD.types.uint24le,
    chunkNumber: BD.types.uint24le,
    length: BD.types.uint24le,
    payload: BD.types.buffer(({node}) => node.length)
  }
  ```

ku:

  • `code` — kodi i mesazhit, pĂ«r shembull 6 — PREPARE_NEW_BLOCK, 7 — BLOCK_VALID, 8 — BLOCK_COMMIT;
  • `versionProtocol` — versioni i protokollit, pasi nĂ« rrjet mund tĂ« jenĂ« aktivizuar nodĂ« me versione tĂ« ndryshme dhe ato mund tĂ« punojnĂ« ndryshe;
  • `seq` — identifikuesi i mesazhit;
  • `countChunk` dhe `chunkNumber` nevojiten pĂ«r ndarjen e mesazheve tĂ« mĂ«dha;
  • `length` dhe `payload` gjatĂ«sia dhe tĂ« dhĂ«nat vetĂ«.

Duke qenë se ne i kemi tipizuar të dhënat paraprakisht, sistemi përfundimtar funksionon më shpejt se biblioteka `rlp` e Ethereumit. Fatkeqësisht, nuk kemi arritur ende të heqim dorë prej saj, pasi është e nevojshme të përmirësojmë kontratën e smart, që ne planifikojmë ta bëjmë në të ardhmen.

Nëse kemi arritur të përfundojmë shpejtësinë 35 000 transaksioneve për sekondë, gjithashtu na nevojitet t'i përpunojmë ato në një kohë optimale. Duke qenë se koha e përafërt për formimin e bllokut është 30 sekonda, është e nevojshme të përfshijmë në bllok 1 000 000 transaksione, që do të thotë dërgimi i më shumë 100 mb të dhënash.

Fillimisht, ne përdorëm bibliotekën `ethereumjs-devp2p` për komunikimin e nodëve, por ajo nuk përballonte një numër të tillë të madh të dhënash. Si rezultat, ne përdorëm bibliotekën `ws` dhe konfiguravëm dërgimin e të dhënave binar përmes websocket. Sigurisht, ne gjithashtu u përballëm me probleme gjatë dërgimit të pakove të mëdha të të dhënave, por ne i ndamë ato në seksione dhe tani këto probleme nuk ekzistojnë më.

Po ashtu, formimi i pemës Merkle dhe llogaritja e hash-it 1 000 000 të transaksioneve kërkon rreth 10 sekonda të llogaritjes së pandërprerë. Gjatë kësaj kohe, e gjithë lidhja me nodet ndërpritet. U vendos të transferohet kjo llogaritje në një thread të veçantë.

Konkluzione:

Në të vërtetë, përfundimet tona nuk janë të reja, por për një arsye të çuditshme shumë specialistë i harrojnë ato gjatë zhvillimit.

  • PĂ«rdorimi i Programimit Funksional nĂ« vend tĂ« Programimit tĂ« Orientuar ndaj Objektit rrit efikasitetin.
  • Monoliti Ă«shtĂ« mĂ« i keq se arkitektura e shĂ«rbimeve pĂ«r njĂ« sistem tĂ« fuqishĂ«m nĂ« NodeJS.
  • PĂ«rdorimi i `worker_threads` pĂ«r kalkulime tĂ« rĂ«nda pĂ«rmirĂ«son reagimin e sistemit, veçanĂ«risht gjatĂ« operacioneve i/o.
  • unix socket Ă«shtĂ« mĂ« stabil dhe mĂ« i shpejtĂ« se kĂ«rkesat http.
  • NĂ«se duhet tĂ« transferoni shpejt tĂ« dhĂ«na tĂ« mĂ«dha pĂ«rmes rrjetit, Ă«shtĂ« mĂ« mirĂ« tĂ« pĂ«rdorni websocket dhe tĂ« dĂ«rgoni tĂ« dhĂ«na binare tĂ« ndara nĂ« chanks, tĂ« cilat mund tĂ« dĂ«rgohen nĂ«se nuk arrijnĂ« dhe pastaj tĂ« bashkohen nĂ« njĂ« mesazh.

Ju ftojmë të vizitoni GitHub projekti: https://github.com/opporty-com/Plasma-Cash/tree/new-version

Artikulli është shkruar në bashkëautori me Aleksandër Naishivan, zhvillues të lartë Clever Solution Inc.

Burimi: habr.com

Bli njĂ« hosting tĂ« besueshĂ«m pĂ«r faqet me mbrojtje DDoS, VPS VDS serverĂ« đŸ”„ Bli njĂ« hosting tĂ« besueshĂ«m pĂ«r faqet me mbrojtje DDoS, VPS VDS serverĂ« | ProHoster