Test i hapur: zgjidhja për privatësinë dhe shkallëzueshmërinë në Ethereum

Blockchain — Teknologji inovative qĂ« premton tĂ« pĂ«rmirĂ«sojĂ« shumĂ« fusha tĂ« jetĂ«s njerĂ«zore. Ajo transferon proceset dhe produktet reale nĂ« hapĂ«sirĂ«n digjitale, garanton shpejtĂ«si dhe besueshmĂ«ri tĂ« operacioneve financiare, ul kostot e tyre, si dhe mundĂ«son krijimin e aplikacioneve moderne DAPP duke pĂ«rdorur kontrata inteligjente nĂ« rrjete tĂ« decentralizuara.

Duke marrë parasysh shumë përfitimet dhe aplikimet e ndryshme të blockchain, mund të duket e çuditshme që kjo teknologji premtuese ende nuk ka depërtuar në të gjitha industrinë. Problemi është se blockchain-et e decentralizuara moderne nuk kanë shkallëzim të mjaftueshëm. Ethereum përpunon rreth 20 transaksione në sekondë, që nuk është e mjaftueshme për të përmbushur nevojat e biznesit dinamik modern. Ndërkohë, kompanitë që përdorin teknologjinë blockchain nuk po guxojnë të braktisin Ethereum për shkak të nivelit të tij të lartë të sigurisë kundër sulmeve dhe dështimeve të rrjetit.

PĂ«r tĂ« siguruar decentralizimin, sigurinĂ« dhe shkallĂ«zimin nĂ« blockchain, duke zgjidhur kĂ«shtu TrilemmĂ«n e ShkallĂ«zimit, ekipi i zhvilluesve Opporty krijoi Plasma Cash — njĂ« zinxhir dytĂ«sor, i pĂ«rbĂ«rĂ« nga njĂ« kontratĂ« inteligjente dhe njĂ« rrjet privat bazuar nĂ« Node.js, qĂ« pĂ«rcjell periodikisht gjendjen e tij nĂ« zinxhirin themelor (Ethereum).

Test i hapur: zgjidhja për privatësinë dhe shkallëzueshmërinë në Ethereum

Proceset kyçe në Plasma Cash

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

2. Nodet e Plasma Cash, të nënshkruara për ngjarjet e kontratës inteligjente, marrin ngjarjen e krijimit të depozitës dhe shtojnë në pulin e transaksioneve të krijimit të tokenit.

3. Periodikisht, nodet speciale të Plasma Cash marrin të gjitha transaksionet nga puli (deri në 1 milion) dhe formojnë një bllok prej tyre, llogaritin pemën Merkle dhe, përkatësisht, hash-in. Ky bllok dërgohet nodëve të tjerë 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ë zotëruesi i tij). Pas verifikimit të bllokut, nodi thërret funksionin `submitBlock` të kontratës inteligjente, e cila ruan numrin dhe hash-in Merkle të bllokut në zinxhirin themelor. Kontrata inteligjente gjeneron një ngjarje mbi shtimin e suksesit të bllokut. Transaksionet fshihen nga puli.

4. Nodet që kanë marrë ngjarjen e dorëzimit të bllokut fillojnë të aplikojnë transaksionet që janë shtuar në bllok.

5. Në një moment, pronari (ose jo pronari) i simbolit dëshiron ta tërheqë atë nga Plasma Cash. Për këtë, ai thërret funksionin `startExit`, duke kaluar në të informacionin mbi dy transaksionet e fundit për simbolin, 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 përpara tërheqjes ishte tashmë i huaj), pronari i simbolit mund të kundërshtojë tërheqjen brenda dy javësh.

Test i hapur: zgjidhja për privatësinë dhe shkallëzueshmërinë në Ethereum

Privatësia arrihet në dy mënyra

1. Pika rrënjësore nuk di asgjë për transaksionet që formohen dhe dërgohen brenda zinxhirit të përbërë. Informacioni publik mbetet për atë kush ka depozituar dhe tërhequr ETH në/dalë nga Plasma Cash.

2. Zinxhiri i fëmijës lejon organizimin e transaksioneve të anëtarësuara, duke përdorur zk-SNARKs.

Stoku teknologjik

  • NodeJS
  • Redis
  • Etherium
  • Soild

Testimi

Duke zhvilluar Plasma Cash, ne testuam shpejtësinë e sistemit dhe morëm rezultatet e mëposhtme:

  • deri nĂ« 35,000 transaksione nĂ« sekundĂ« shtohen nĂ« pool;
  • deri nĂ« 1,000,000 transaksione mund tĂ« ruhen nĂ« bllok.

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

1. Intel Core i7-6700 Quad-Core Skylake me NVMe SSD - 512 GB, 64 GB DDR4 RAM
U ngritën 3 nodet valide 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ë ETH në testnet Ropsten.
U ngritën 3 nodet valide të Plasma Cash.

3. Intel Core i9-9900K Octa-Core me NVMe SSD - 1 TB, 64 GB DDR4 RAM
U ngrit një nodë për dorëzimin e Plasma Cash.
U ngritën 3 nodet valide të Plasma Cash.
U lançua një test për shtimin e transaksioneve në rrjetin Plasma Cash.

Pra, në përfundim: 10 nodet e Plasma Cash në një rrjet privat.

Testi 1

Ka një kufi prej 1 milion transaksionesh në bllok. Prandaj, 1 milion transaksione kalojnë në 2 blloqe (pasi sistemi arrin të marrë një pjesë të transaksioneve dhe të dorëzojë ndonjëherë para se ato të dërgohen).

Luaj videon

Shteti fillestar: blloku i fundit #7; 1 milion transaksionesh dhe simbole janë ruajtur në bazë.

00:00 - nisja e skriptit për gjenerimin e transaksioneve
01:37 - u krijua 1 milion transaksione dhe filloi dërgimi në nodë
01:46 - nodë e dorëzimit mori nga pool 240k transaksione dhe formon bllokun #8. Po ashtu, shohim se në pool shtohen 320k transaksione për 10 sek.
01:58 - blloku #8 është nënshkruar dhe dërguar për validim
02:03 — blloku #8 Ă«shtĂ« verifikuar dhe Ă«shtĂ« thirrur funksioni `submitBlock` i kontratĂ«s smart me hash Merkle dhe numrin e bllokut
02:10 — pĂ«rfundoi skenari demo, i cili dĂ«rgoi 1 milion transaksione pĂ«r 32 sekonda
02:33 — nodet filluan tĂ« marrin informacion nĂ« lidhje me bllokun #8 qĂ« Ă«shtĂ« shtuar nĂ« zinxhirin rrĂ«nor dhe filluan tĂ« ekzekutojnĂ« 240k transaksione
02:40 — nga puli u hoqĂ«n 240k transaksione, tĂ« cilat tashmĂ« ishin nĂ« bllokun #8
02:56 — nodi i dorĂ«zimit mori nga puli 760k transaksione tĂ« mbetura dhe filloi tĂ« llogarisĂ« hash Merkle dhe tĂ« nĂ«nshkruajĂ« bllokun #9
03:20 — tĂ« gjitha nodet pĂ«rmbajnĂ« 1 milion 240k transaksione dhe tokene
03:35 — blloku #9 Ă«shtĂ« nĂ«nshkruar dhe dĂ«rgohet pĂ«r verifikim nĂ« nodet e tjera
03:41 — ndodhi njĂ« gabim rrjeti
04:40 — pĂ«r shkak tĂ« skadimit, u ndal pritja pĂ«r verifikimin e bllokut #9
04:54 — nodi i dorĂ«zimit mori nga puli 760k transaksione tĂ« mbetura dhe filloi tĂ« llogarisĂ« hash Merkle dhe tĂ« nĂ«nshkruajĂ« bllokun #9
05:32 — blloku #9 Ă«shtĂ« nĂ«nshkruar dhe dĂ«rgohet pĂ«r verifikim nĂ« nodet e tjera
05:53 — blloku #9 Ă«shtĂ« verifikuar dhe dĂ«rguar nĂ« zinxhirin rrĂ«nor
06:17 — nodet filluan tĂ« marrin informacion nĂ« lidhje me bllokun #9 qĂ« Ă«shtĂ« shtuar nĂ« zinxhirin rrĂ«nor dhe filluan tĂ« ekzekutojnĂ« 760k transaksione
06:47 — puli Ă«shtĂ« pastruar nga transaksionet, tĂ« cilat janĂ« nĂ« bllokun #9
09:06 — tĂ« gjitha nodet pĂ«rmbajnĂ« 2 milion transaksione dhe tokene

Test 2

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

Luaj videon

Stadi i origjinës: blloku i fundit #9; në bazë janë ruajtur 2 milion transaksione dhe tokene

00:00 — skenari i gjenerimit tĂ« transaksioneve Ă«shtĂ« tashmĂ« nĂ« funksionim
00:44 — janĂ« krijuar 1 milion transaksione dhe Ă«shtĂ« nisur dĂ«rgimi nĂ« nod
00:56 — nodi i dorĂ«zimit mori nga puli 320k transaksione dhe po formon bllokun #10. Gjithashtu shohim se nĂ« pul Ă«shtĂ« shtuar 320k transaksione pĂ«r 10 sekonda
01:12 — blloku #10 Ă«shtĂ« nĂ«nshkruar dhe dĂ«rgohet nĂ« nodet e tjera pĂ«r verifikim
01:18 — skenari demo pĂ«rfundoi, i cili dĂ«rgoi 1 milion transaksione pĂ«r 34 sekonda
01:20 — blloku #10 Ă«shtĂ« verifikuar dhe dĂ«rguar nĂ« zinxhirin rrĂ«nor
01:51 — tĂ« gjitha nodet morĂ«n nga zinxhiri rrĂ«nor informacionin nĂ« lidhje me bllokun #10 qĂ« Ă«shtĂ« shtuar dhe filluan tĂ« aplikojnĂ« 320k transaksione
02:01 — puli Ă«shtĂ« pastruar me 320k transaksione, tĂ« cilat janĂ« shtuar nĂ« bllokun #10
02:15 — nodi i dorĂ«zimit mori nga puli 350k transaksione dhe po formon bllokun #11
02:34 — blloku #11 Ă«shtĂ« nĂ«nshkruar dhe dĂ«rgohet nĂ« nodet e tjera pĂ«r verifikim
02:51 — blloku #11 Ă«shtĂ« verifikuar dhe dĂ«rguar nĂ« zinxhirin rrĂ«nor
02:55 — nodi i fundit ekzekutoi transaksionet nga blloku #10
10:59 — njĂ« transaksion me dĂ«rgimin e bllokut #9 u realizua shumĂ« ngadalĂ« nĂ« zinxhirin kryesor, por u pĂ«rfundua dhe tĂ« gjitha nodet morĂ«n informacion mbi kĂ«tĂ« dhe filluan tĂ« ekzekutojnĂ« 350k transaksione
11:05 — pool-u u pastrua me 320k transaksione, tĂ« cilat u shtuan nĂ« bllokun #11
12:10 — tĂ« gjitha nodet pĂ«rmbajnĂ« 1 milion 670k transaksione dhe tokens
12:17 — nodi i dĂ«rgimit mori 330k transaksione nga pool dhe po formon bllokun #12
12:32 — blloku #12 u nĂ«nshkrua dhe u dĂ«rgua tek nodet e tjera pĂ«r validim
12:39 — blloku #12 u validua dhe u dĂ«rgua nĂ« zinxhirin kryesor
13:44 — tĂ« gjitha nodet morĂ«n informacion nga zinxhiri kryesor se blloku #12 u shtua dhe fillojnĂ« tĂ« aplikojnĂ« 330k transaksione
14:50 — tĂ« gjitha nodet pĂ«rmbajnĂ« 2 milion transaksione dhe tokens

Test 3

Në serverat e parë dhe të dytë, një nod validues u zëvendësua me një nod dërgimi.

Luaj videon

Gjatësia fillestare: blloku i fundit #84; në bazë ruajti 0 transaksione dhe tokens

00:00 — U nisĂ«n 3 skripta, tĂ« cilĂ«t gjenerojnĂ« dhe dĂ«rgojnĂ« nga 1 milion transaksione
01:38 — u krijua 1 milion transaksione dhe filloi dĂ«rgimi nĂ« nodin dĂ«rgues #3
01:50 — nodi dĂ«rgues #3 mori 330k transaksione nga pool dhe po formon bllokun #85 (f21). Po ashtu, shohim se nĂ« pool shtohen 350k transaksione brenda 10 sekondave
01:53 — u krijua 1 milion transaksione dhe filloi dĂ«rgimi nĂ« nodin dĂ«rgues #1
01:50 — nodi dĂ«rgues #3 mori 330k transaksione nga pool dhe po formon bllokun #85 (f21). Po ashtu, shohim se nĂ« pool shtohen 350k transaksione brenda 10 sekondave
02:01 — nodi dĂ«rgues #1 mori 250k transaksione nga pool dhe po formon bllokun #85 (65e)
02:06 — blloku #85 (f21) u nĂ«nshkrua dhe u dĂ«rgua tek nodet e tjera pĂ«r validim
02:08 — demostrapti i serverit #3 pĂ«rfundoi punĂ«n, i cili dĂ«rgoi 1 milion transaksione brenda 30 sekondash
02:14 — blloku #85 (f21) u validua dhe u dĂ«rgua nĂ« zinxhirin kryesor
02:19 — blloku #85 (65e) u nĂ«nshkrua dhe u dĂ«rgua tek nodet e tjera pĂ«r validim
02:22 — u krijua 1 milion transaksione dhe filloi dĂ«rgimi nĂ« nodin dĂ«rgues #2
02:27 — blloku #85 (65e) u validua dhe u dĂ«rgua nĂ« zinxhirin kryesor
02:29 — nodi dĂ«rgues #2 mori 111855 transaksione nga pool dhe po formon bllokun #85 (256).
02:36 — blloku #85 (256) u nĂ«nshkrua dhe u dĂ«rgua tek nodet e tjera pĂ«r validim
02:36 — demostrapti i serverit #1 pĂ«rfundoi punĂ«n, i cili dĂ«rgoi 1 milion transaksione brenda 42.5 sekondash
02:38 — blloku #85 (256) u validua dhe u dĂ«rgua nĂ« zinxhirin kryesor
03:08 — demostrapti i serverit #2 pĂ«rfundoi punĂ«n, i cili dĂ«rgoi 1 milion transaksione brenda 47 sekondash
03:38 — tĂ« gjitha nodet morĂ«n informacion nga zinxhiri kryesor pĂ«r tĂ« treguar se blloqet #85 (f21), #86 (65e), #87 (256) u shtuan dhe fillojnĂ« tĂ« aplikojnĂ« 330k, 250k, 111855 transaksione
03:49 — pooli u pastruar 330k, 250k, 111855 transaksione, tĂ« cilat u shtuan nĂ« blloqet #85 (f21), #86(65e), #87(256)
03:59 — submissi i nodĂ«s #1 mori nga pool 888145 transaksione dhe po formon bllokun #88 (214), submissi i nodĂ«s #2 mori nga pool 750k transaksione dhe po formon bllokun #88 (50a), submissi i nodĂ«s #3 mori nga pool 670k transaksione dhe po formon bllokun #88 (d3b)
04:44 — blloku #88 (d3b) Ă«shtĂ« nĂ«nshkruar dhe dĂ«rgohet nĂ« nodat e tjera pĂ«r validim
04:58 — blloku #88 (214) Ă«shtĂ« nĂ«nshkruar dhe dĂ«rgohet nĂ« nodat e tjera pĂ«r validim
05:11 — blloku #88 (50a) Ă«shtĂ« nĂ«nshkruar dhe dĂ«rgohet nĂ« nodat e tjera pĂ«r validim
05:11 — blloku #85 (d3b) Ă«shtĂ« validuar dhe dĂ«rguar nĂ« zinxhirin rrĂ«njor
05:36 — blloku #85 (214) Ă«shtĂ« validuar dhe dĂ«rguar nĂ« zinxhirin rrĂ«njor
05:43 — tĂ« gjitha nodat morĂ«n nga zinxhiri rrĂ«njor informacionin se blloqet #88 (d3b), #89(214) janĂ« shtuar 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 — submissi i nodĂ«s #2 mori nga pool 888145 transaksione dhe po formon bllokun #90 (50a)
08:14 — blloku #90 (50a) Ă«shtĂ« nĂ«nshkruar dhe dĂ«rgohet nĂ« nodat e tjera pĂ«r validim
09:04 — blloku #90 (50a) Ă«shtĂ« validuar dhe dĂ«rguar nĂ« zinxhirin rrĂ«njor
11:23 — tĂ« gjitha nodat morĂ«n nga zinxhiri rrĂ«njor informacionin se blloku #90 (50a) Ă«shtĂ« shtuar dhe fillojnĂ« tĂ« aplikojnĂ« 888145 transaksione. NdĂ«rkohĂ«, serveri #3 ka aplikuar prej kohĂ«sh transaksionet nga blloqet #88 (d3b), #89(214)
12:11 — tĂ« gjitha pool-et janĂ« bosh
13:41 — tĂ« gjitha nodat e serverit #3 pĂ«rmbajnĂ« 3 milion transaksione dhe token-e
14:35 — tĂ« gjitha nodat e serverit #1 pĂ«rmbajnĂ« 3 milion transaksione dhe token-e
19:24 — tĂ« gjitha nodat e serverit #2 pĂ«rmbajnĂ« 3 milion transaksione dhe token-e

Pengesat

Gjatë zhvillimit të Plasma Cash ne u përballëm me këto probleme, 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 bllokoi punën e submissi dhe validimit të blloqeve, dhe anasjelltas, çka çoi në uljen e shpejtësisë.

2. Nuk ishte e qartë se si të dërgonim një numër të madh transaksionesh dhe në të njëjtën kohë të minimizonim shpenzimet për transferimin e të dhënave.

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

4. Nuk ishte e qartë se si të organizonim rrjetin mes nodave, pasi madhësia e bllokut me 1 milion transaksione merr rreth 100 Mb.

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

Si e përballëm gjithë këtë?

Versioni i parë i nodës Plasma Cash ishte një lloj kombajni që mund të bënte gjithçka njëherësh: merrte transaksione, dorëzonte dhe validonte blloqe, ofronte një API për akses në të dhëna. Pasi NodeJS është fillimisht një proces njëra, funksioni i rëndë i llogaritjes së pemës Merkle bllokonte funksionin e shtimit të transaksionit. Ne shihnim dy zgjidhje për këtë problem:

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

2. Të përdorim worker_threads dhe ta çojmë ekzekutimin e disa kodit në procese të ndara.

Në fund, ne përdorëm të dyja zgjidhjet njëkohësisht: e ndamë logjikisht një nodë në 3 pjesë, të cilat mund të funksionojnë veçmas, por njëkohësisht.

1. Noda e dorëzimit, e cila merr transaksione në pool dhe merret me krijimin e blloqeve.

2. Noda e validimit, e cila kontrollon vlefshmërinë e nodave.

3. Noda API - ofron API për akses në të dhëna.

Në të njëjtën kohë, çdo nodë mund të lidhet përmes unix socket përmes CLI.

Operacionet e rënda, të tilla si llogaritja e pemës Merkle, i kemi çuar në një rrjedhë të veçantë.

Në këtë mënyrë, arritëm të siguronim funksionimin normal të të gjitha funksioneve të Plasma Cash njëkohësisht dhe pa prishje.

Pasi sistemi filloi të funksiononte, filluam të testonim shpejtësinë dhe, fatkeqësisht, morëm rezultate të pakënaqshme: 5,000 transaksione në sekondë dhe deri në 50,000 transaksione në bllok. Na duhej të zbulojmë se çfarë ishte implementuar gabimisht.

Fillimisht, nisëm testimin e mekanizmit të komunikimit me Plasma Cash për të zbuluar kapacitetin maksimal të sistemit. Më parë, kishim shkruar se nodi Plasma Cash ofron një ndërfaqe unix socket. Në fillim, ajo ishte tekstuale. Objektet json dërgoheshin duke përdorur `JSON.parse()` dhe `JSON.stringify()`.

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

Ne matëm shpejtësinë e dërgimit të këtyre objekteve dhe morëm ~ 130k në sekondë. Provuan të zëvendësojnë funksionet standarde për punës me json, por performanca nuk u rrit. Duhet të jetë që motori V8 është optimizuar mirë për këto operacione.

Puna me transaksionet, tokenët, bloket është realizuar përmes klasave. Kur krijuam këto klasa, performanca ra për 2 herë, që tregon se: OOP nuk na përshtatet. Duhet të rikodojmë gjithçka në një qasje në mënyrë funksionale.

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

Fillimisht, për ruajtjen e të dhënave u zgjodh Redis si një nga zgjidhjet më të performuara që plotësonte kërkesat tona: depozitë key-value, punë me tabela hash, grupe. Lanë redis-benchmark dhe morën ~80k operacione në sekondë në modalitetin 1 pipelining.

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

  • VendosĂ«m lidhjen me soket unix.
  • Çaktivizuam ruajtjen e gjendjes nĂ« disk (pĂ«r besueshmĂ«ri, mund tĂ« konfigurohet njĂ« replikĂ« dhe tĂ« bĂ«het ruajtja nĂ« disk nĂ« njĂ« Redis tĂ« veçantĂ«).

Në Redis, pool është një tabelë hash, pasi na nevojitet mundësia për të marrë të gjitha transaksionet me një kërkesë dhe për të fshirë transaksionet një nga një. Provuan të përdorin një listë të zakonshme, por ajo punon më ngadalë gjatë shkarkimit të gjithë listës.

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

Sepse benchmarku na tregoi njĂ« kapacitet kolonialisht 5 herĂ« mĂ« shumĂ«, filluam tĂ« optimizojmĂ«. E zĂ«vendĂ«suam bibliotekĂ«n me ioredisk dhe morĂ«m njĂ« performancĂ« prej 25k nĂ« sekondĂ«. Transaksionet i shtonim njĂ« nga njĂ«, duke pĂ«rdorur komandĂ«n `hset`. KĂ«shtu, gjeneronim shumĂ« kĂ«rkesa nĂ« Redis. LindĂ«n idenĂ« pĂ«r tĂ« bashkuar transaksionet nĂ« grupe dhe pĂ«r t'i dĂ«rguar ata 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ç doli, nĂ«se e kthejmĂ« atĂ« nĂ« tekst (`buffer.toString(‘hex’)`) para shkruarjes, mund tĂ« fitojmĂ« njĂ« performancĂ« shtesĂ«. KĂ«shtu, shpejtĂ«sia arriti 35k nĂ« sekondĂ«. Deri mĂ« tani, vendosĂ«m tĂ« ndalojmĂ« optimizimin e mĂ«tejshĂ«m.

Na duhej të kalonim në protokollin binar pasi:

1. Sistemi shpesh llogarit hashet, nënshkrimet etj., dhe për këtë i nevojiten të dhënat në `Buffer.

2. Kur transferimi midis shërbimeve, të dhënat binar kanë një peshë më të vogël se sa tekstet. 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. Konvertimi i vazhdueshëm i të dhënave ndikon në performancë.

Prandaj, ne kemi marrë si bazë protokollin tonë binar për ruajtjen dhe transferimin e të dhënave, të zhvilluar mbi bazën e bibliotekës së shkëlqyer `binary-data`.

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

— Transaction

  ```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,
  }
  ```

— Token

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

— Block

  ```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 e kthejmë të dhënat në `Buffer` për ruajtje në Redis ose dërgim në nod tjetër dhe nxjerrjen e të dhënave përsëri.

Po ashtu kemi dy protokolle binare 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 nodave

  ```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Ă« ketĂ« nod tĂ« lĂ«shuara me versione tĂ« ndryshme dhe ato mund tĂ« funksionojnĂ« ndryshe;
  • `seq` — identifikuesi i mesazhit;
  • `countChunk` dhe `chunkNumber` nevojiten pĂ«r ndarjen e mesazheve tĂ« mĂ«dha;
  • `length` dhe `payload` gjatĂ«sia dhe vetĂ« tĂ« dhĂ«nat.

Pasi we parapem përpara të dhënat, sistemi përfundimtar punon shumë më shpejt se biblioteka `rlp` e Ethereum. Fatkeqësisht, ne nuk kemi arritur ende të heqim dorë nga ajo, pasi është e nevojshme të përfundojmë kontratën e mençur, gjë që ne planifikojmë ta bëjmë në të ardhmen.

Nëse arritëm të arrijmë shpejtësinë 35 000 e transaksioneve për sekondë, na nevojitet gjithashtu t'i përpunojmë ato brenda kohës optimale. Pasi koha e përafërt e formimit të bllokut merr 30 sekonda, na nevojitet të përfshijmë në bllok 1 000 000 transaksione, që do të thotë dërgim të më shumë 100 mb të dhënash.

Fillimisht kemi përdorur bibliotekën `ethereumjs-devp2p` për komunikimin e node-ve, por ajo nuk ishte në gjendje të përballonte një kaq shumë të dhëna. Si rezultat, kemi përdorur bibliotekën `ws` dhe kemi konfiguruar dërgimin e të dhënave të binarizuara përmes websocket. Sigurisht, ne gjithashtu u përballëm me probleme gjatë dërgimit të paketeve të mëdha të të dhënave, por i ndamë ato në pjesë dhe tani këto probleme nuk ekzistojnë më.

Gjithashtu, formimi i pemës së Merkle dhe llogaritja e hash-it 1 000 000 të transaksioneve kërkon rreth 10 sekonda llogaritjeje të vazhdueshme. Gjatë kësaj kohe, lidhja me të gjitha node-t priret të përfundojë. U vendos të transferohet kjo llogaritje në një thread të veçantë.

Përfundimet:

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

  • PĂ«rdorimi i Programimit Funksional nĂ« vend tĂ« Programimit Objektor rrit produktivitetin.
  • Monoliti Ă«shtĂ« mĂ« i keq se arkitektura e shĂ«rbimeve pĂ«r njĂ« sistem produktiv nĂ« NodeJS.
  • PĂ«rdorimi i `worker_threads` pĂ«r llogaritje tĂ« rĂ«nda pĂ«rmirĂ«son reagimin e sistemit, veçanĂ«risht gjatĂ« operacioneve i/o.
  • socket unix Ă«shtĂ« mĂ« i stabilizuar dhe mĂ« i shpejtĂ« se kĂ«rkesat http.
  • NĂ«se nevojitet tĂ« dĂ«rgoni shpejt tĂ« dhĂ«na tĂ« mĂ«dha pĂ«rmes rrjetit, Ă«shtĂ« mĂ« mirĂ« tĂ« pĂ«rdorni websocket dhe tĂ« dĂ«rgoni tĂ« dhĂ«na binarĂ« tĂ« ndara nĂ« pjesĂ«, tĂ« cilat mund tĂ« dĂ«rgohen pĂ«rsĂ«ri nĂ«se nuk arrijnĂ«, dhe pĂ«r mĂ« tepĂ«r tĂ« bashkohen nĂ« njĂ« mesazh tĂ« vetĂ«m.

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 Nashivanin, zhvilluesi i lartë Clever Solution Inc.

Burimi: habr.com

Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS đŸ”„ Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS | ProHoster