Publika Testo: Solvo por Privateco kaj Skalebleco sur Ethereum

Blokĉeno estas noviga teknologio, kiu promesas plibonigi multajn areojn de homa vivo. Ĝi transdonas realajn procezojn kaj produktojn en la ciferecan spacon, certigas rapidecon kaj fidindecon de financaj transakcioj, reduktas ilian koston, kaj ankaŭ permesas krei modernajn DAPP-aplikojn uzante inteligentajn kontraktojn en malcentralizitaj retoj.

Konsiderante la multajn avantaĝojn kaj diversajn aplikojn de blokĉeno, povas ŝajni surprize, ke ĉi tiu promesplena teknologio ankoraŭ ne eniris ĉiun industrion. La problemo estas, ke modernaj malcentralizitaj blokĉenoj malhavas de skalebleco. Ethereum procesas ĉirkaŭ 20-transakciojn por sekundo, kio ne sufiĉas por plenumi la bezonojn de la hodiaŭaj dinamikaj entreprenoj. Samtempe, kompanioj uzantaj blokĉenteknologion hezitas forlasi Ethereum pro ĝia alta grado de protekto kontraŭ hakado kaj retaj fiaskoj.

Por certigi malcentralizon, sekurecon kaj skaleblon en la blokĉeno, tiel solvante la Scalability Trilemon, la evolua teamo Ŝanco kreis Plasma Cash, filan ĉenon konsistantan el inteligenta kontrakto kaj privata reto bazita sur Node.js, kiu periode transdonas sian staton al la radika ĉeno (Ethereum).

Publika Testo: Solvo por Privateco kaj Skalebleco sur Ethereum

Ŝlosilprocezoj en Plasma Cash

1. La uzanto nomas la inteligentan kontraktan funkcion `deponejo`, pasante al ĝi la kvanton de ETH, kiun li volas deponi en la Plasma Cash-ĵetonon. La inteligenta kontrakto-funkcio kreas ĵetonon kaj generas eventon pri ĝi.

2. Plasma Cash-nodoj abonitaj al inteligentaj kontraktaj eventoj ricevas eventon pri kreado de deponejo kaj aldonas transakcion pri kreado de ĵetono al la naĝejo.

3. Periode, specialaj Plasma Cash-nodoj prenas ĉiujn transakciojn de la naĝejo (ĝis 1 miliono) kaj formas blokon de ili, kalkulas la Merkle-arbon kaj, laŭe, la haŝiŝon. Ĉi tiu bloko estas sendita al aliaj nodoj por konfirmo. La nodoj kontrolas ĉu la Merkle hash validas kaj ĉu la transakcioj validas (ekzemple, ĉu la sendinto de la ĵetono estas ĝia posedanto). Post kontroli la blokon, la nodo nomas la funkcion "submitBlock" de la inteligenta kontrakto, kiu konservas la blokan numeron kaj Merkle-haŝiŝon al la randĉeno. La inteligenta kontrakto generas eventon indikantan la sukcesan aldonon de bloko. Transakcioj estas forigitaj de la naĝejo.

4. Nodoj, kiuj ricevas la blokan submetiĝon, komencas apliki la transakciojn, kiuj estis aldonitaj al la bloko.

5. En iu momento, la posedanto (aŭ ne-posedanto) de la ĵetono volas retiri ĝin de Plasma Cash. Por fari tion, li vokas la funkcion `startExit`, pasante al ĝi informojn pri la lastaj 2 transakcioj sur la ĵetono, kiuj konfirmas, ke li estas la posedanto de la ĵetono. La inteligenta kontrakto, uzante la Merkle-haŝiŝon, kontrolas la ĉeeston de transakcioj en la blokoj kaj sendas la ĵetonon por retiriĝo, kiu okazos en du semajnoj.

6. Se la ĵetono-retiro-operacio okazis kun malobservoj (la ĵetono estis elspezita post kiam la retiriĝa proceduro komenciĝis aŭ la ĵetono jam estis de iu alia antaŭ la retiro), la posedanto de la ĵetono povas refuti la retiron ene de du semajnoj.

Publika Testo: Solvo por Privateco kaj Skalebleco sur Ethereum

Privateco estas atingita en du manieroj

1. La radika ĉeno scias nenion pri la transakcioj kiuj estas generitaj kaj plusenditaj ene de la infana ĉeno. Informoj pri kiu deponis kaj retiris ETH de Plasma Cash restas publikaj.

2. La infanĉeno permesas anonimajn transakciojn uzante zk-SNARKs.

Teknologia stako

  • NodeJS
  • Redis
  • Eterio
  • Sild

Testado

Disvolvante Plasma Cash, ni testis la rapidecon de la sistemo kaj akiris la sekvajn rezultojn:

  • ĝis 35 transakcioj je sekundo estas aldonitaj al la naĝejo;
  • ĝis 1 transakcioj povas esti stokitaj en bloko.

Testoj estis faritaj sur la sekvaj 3 serviloj:

1. Intel Core i7-6700 Quad-Core Skylake inkl. NVMe SSD - 512 GB, 64 GB DDR4 RAM
3 validigantaj Plasma Cash-nodoj estis levitaj.

2. AMD Ryzen 7 1700X Octa-Core "Summit Ridge" (Zen), SATA SSD - 500 GB, 64 GB DDR4 RAM
La Ropsten testnet ETH-nodo estis levita.
3 validigantaj Plasma Cash-nodoj estis levitaj.

3. Intel Core i9-9900K Octa-Core inkl. NVMe SSD - 1 TB, 64 GB DDR4 RAM
1 Plasma Cash-submetadnodo estis levita.
3 validigantaj Plasma Cash-nodoj estis levitaj.
Testo estis lanĉita por aldoni transakciojn al la Plasma Cash-reto.

Sumo: 10 Plasma Cash-nodoj en privata reto.

Testo 1

Estas limo de 1 miliono da transakcioj per bloko. Tial, 1 miliono da transakcioj falas en 2 blokojn (ĉar la sistemo sukcesas partopreni la transakciojn kaj submeti dum ili estas senditaj).


Komenca stato: lasta bloko #7; 1 miliono da transakcioj kaj ĵetonoj estas stokitaj en la datumbazo.

00:00 — komenco de transakcia genera skripto
01:37 - 1 miliono da transakcioj estis kreitaj kaj sendado al la nodo komenciĝis
01:46 — submeti nodon prenis 240k transakciojn de la naĝejo kaj formo-bloko #8. Ni ankaŭ vidas, ke 320k transakcioj estas aldonitaj al la naĝejo en 10 sekundoj
01:58 — bloko #8 estas subskribita kaj sendita por validigo
02:03 — bloko #8 estas validigita kaj la funkcio `submitBlock` de la inteligenta kontrakto estas vokita kun la Merkle hash kaj bloknumero
02:10 — demo-skripto finiĝis funkcii, kiu sendis 1 milionon da transakcioj en 32 sekundoj
02:33 - nodoj komencis ricevi informojn, ke bloko n-ro 8 estis aldonita al la radika ĉeno, kaj komencis fari 240k transakciojn
02:40 - 240k transakcioj estis forigitaj de la naĝejo, kiuj jam estas en bloko n-ro 8
02:56 — submeta nodo prenis la ceterajn 760k transakciojn el la naĝejo kaj komencis kalkuli la Merkle-haŝiŝon kaj subskriban blokon #9
03:20 - ĉiuj nodoj enhavas 1 milionon da 240k transakcioj kaj ĵetonoj
03:35 — bloko #9 estas subskribita kaj sendita por validigo al aliaj nodoj
03:41 - reta eraro okazis
04:40 — atendado pri validumado de bloko #9 eksvalidiĝis
04:54 — submeta nodo prenis la ceterajn 760k transakciojn el la naĝejo kaj komencis kalkuli la Merkle-haŝiŝon kaj subskriban blokon #9
05:32 — bloko #9 estas subskribita kaj sendita por validigo al aliaj nodoj
05:53 — bloko #9 estas validigita kaj sendita al la radika ĉeno
06:17 - nodoj komencis ricevi informojn, ke bloko n-ro 9 estis aldonita al la radika ĉeno kaj komencis fari 760k transakciojn
06:47 - la naĝejo forigis transakciojn kiuj estas en bloko #9
09:06 - ĉiuj nodoj enhavas 2 milionojn da transakcioj kaj ĵetonoj

Testo 2

Estas limo de 350k por bloko. Kiel rezulto, ni havas 3 blokojn.


Komenca stato: lasta bloko #9; 2 milionoj da transakcioj kaj ĵetonoj estas stokitaj en la datumbazo

00:00 - transakcia genera skripto jam estis lanĉita
00:44 - 1 miliono da transakcioj estis kreitaj kaj sendado al la nodo komenciĝis
00:56 — submeti nodon prenis 320k transakciojn de la naĝejo kaj formo-bloko #10. Ni ankaŭ vidas, ke 320k transakcioj estas aldonitaj al la naĝejo en 10 sekundoj
01:12 — bloko #10 estas subskribita kaj sendita al aliaj nodoj por validigo
01:18 — demo-skripto finiĝis funkcii, kiu sendis 1 milionon da transakcioj en 34 sekundoj
01:20 — bloko #10 estas validigita kaj sendita al la radika ĉeno
01:51 - ĉiuj nodoj ricevis informojn de la radika ĉeno, ke bloko n-ro 10 estis aldonita kaj komencas apliki 320k transakciojn
02:01 - la naĝejo malbaris por 320k transakcioj kiuj estis aldonitaj al bloko #10
02:15 — submeta nodo prenis 350k transakciojn el la naĝejo kaj formo-bloko #11
02:34 — bloko #11 estas subskribita kaj sendita al aliaj nodoj por validigo
02:51 — bloko #11 estas validigita kaj sendita al la radika ĉeno
02:55 — la lasta nodo kompletigis transakciojn de bloko #10
10:59 — la transakcio kun la submetado de bloko #9 daŭris tre longan tempon en la radika ĉeno, sed ĝi estis kompletigita kaj ĉiuj nodoj ricevis informojn pri ĝi kaj komencis fari 350k transakciojn.
11:05 - la naĝejo malbaris por 320k transakcioj kiuj estis aldonitaj al bloko #11
12:10 - ĉiuj nodoj enhavas 1 milionon da 670k transakcioj kaj ĵetonoj
12:17 — submeta nodo prenis 330k transakciojn de la naĝejo kaj formo-bloko #12
12:32 — bloko #12 estas subskribita kaj sendita al aliaj nodoj por validigo
12:39 — bloko #12 estas validigita kaj sendita al la radika ĉeno
13:44 - ĉiuj nodoj ricevis informojn de la radika ĉeno, ke bloko n-ro 12 estis aldonita kaj komencas apliki 330k transakciojn
14:50 - ĉiuj nodoj enhavas 2 milionojn da transakcioj kaj ĵetonoj

Testo 3

En la unua kaj dua serviloj, unu validiga nodo estis anstataŭigita per alsenda nodo.


Komenca stato: lasta bloko #84; 0 transakcioj kaj ĵetonoj konservitaj en la datumbazo

00:00 — 3 skriptoj estis lanĉitaj, kiuj generas kaj sendas 1 milionon da transakcioj ĉiu
01:38 — 1 miliono da transakcioj estis kreitaj kaj sendado por sendi nodon #3 komenciĝis
01:50 — submeti nodo #3 prenis 330k transakciojn de la naĝejo kaj formoj bloko #85 (f21). Ni ankaŭ vidas, ke 350k transakcioj estas aldonitaj al la naĝejo en 10 sekundoj
01:53 — 1 miliono da transakcioj estis kreitaj kaj sendado por sendi nodon #1 komenciĝis
01:50 — submeti nodo #3 prenis 330k transakciojn de la naĝejo kaj formoj bloko #85 (f21). Ni ankaŭ vidas, ke 350k transakcioj estas aldonitaj al la naĝejo en 10 sekundoj
02:01 — submeti nodo n-ro 1 prenis 250k transakciojn de la naĝejo kaj formbloko n-ro 85 (65e)
02:06 — bloko #85 (f21) estas subskribita kaj sendita al aliaj nodoj por validigo
02:08 — demo-skripto de servilo n-ro 3, kiu sendis 1 milionon da transakcioj en 30 sekundoj, finiĝis
02:14 — bloko #85 (f21) estas validigita kaj sendita al la radika ĉeno
02:19 — bloko #85 (65e) estas subskribita kaj sendita al aliaj nodoj por validigo
02:22 — 1 miliono da transakcioj estis kreitaj kaj sendado por sendi nodon #2 komenciĝis
02:27 — bloko #85 (65e) validigita kaj sendita al la radika ĉeno
02:29 — submeti nodo #2 prenis 111855 transakciojn de la naĝejo kaj formoj bloko #85 (256).
02:36 — bloko #85 (256) estas subskribita kaj sendita al aliaj nodoj por validigo
02:36 — demo-skripto de servilo n-ro 1, kiu sendis 1 milionon da transakcioj en 42.5 sekundoj, finiĝis
02:38 — bloko #85 (256) estas validigita kaj sendita al la radika ĉeno
03:08 — servilo #2-skripto finiĝis funkcii, kiu sendis 1 milionon da transakcioj en 47 sekundoj
03:38 - ĉiuj nodoj ricevis informojn de la radika ĉeno, kiu blokoj #85 (f21), #86(65e), #87(256) estis aldonitaj kaj komencis apliki 330k, 250k, 111855 transakciojn
03:49 - la naĝejo estis malbarita je 330k, 250k, 111855 transakcioj kiuj estis aldonitaj al blokoj #85 (f21), #86 (65e), #87 (256)
03:59 — submeti nodo n-ro 1 prenis 888145 transakciojn de la naĝejo kaj formo-bloko n-ro 88 (214), submeti nodo n-ro 2 prenis 750k transakciojn de la naĝejo kaj formoj-blokon n-ro 88 (50a), submeti nodo n-ro 3 prenis 670k transakciojn de la naĝejo kaj formas blokon numero 88 (d3b)
04:44 — bloko #88 (d3b) estas subskribita kaj sendita al aliaj nodoj por validumado
04:58 — bloko #88 (214) estas subskribita kaj sendita al aliaj nodoj por validigo
05:11 — bloko #88 (50a) estas subskribita kaj sendita al aliaj nodoj por validigo
05:11 — bloko #85 (d3b) estas validigita kaj sendita al la radika ĉeno
05:36 — bloko #85 (214) estas validigita kaj sendita al la radika ĉeno
05:43 - ĉiuj nodoj ricevis informojn de la radika ĉeno, kiu blokas #88 (d3b), #89(214) estis aldonitaj kaj komencas apliki 670k, 750k transakcioj
06:50 — pro misfunkciado de komunikado, bloko #85 (50a) ne estis validigita
06:55 — submeti nodon #2 prenis 888145 transakciojn el la naĝejo kaj formo-bloko #90 (50a)
08:14 — bloko #90 (50a) estas subskribita kaj sendita al aliaj nodoj por validigo
09:04 — bloko #90 (50a) estas validigita kaj sendita al la radika ĉeno
11:23 - ĉiuj nodoj ricevis informojn de la radika ĉeno, ke bloko #90 (50a) estis aldonita, kaj komencas apliki 888145 transakciojn. Samtempe, servilo #3 jam aplikis transakciojn de blokoj #88 (d3b), #89(214)
12:11 - ĉiuj naĝejoj estas malplenaj
13:41 - ĉiuj nodoj de servilo #3 enhavas 3 milionojn da transakcioj kaj ĵetonoj
14:35 - ĉiuj nodoj de servilo #1 enhavas 3 milionojn da transakcioj kaj ĵetonoj
19:24 - ĉiuj nodoj de servilo #2 enhavas 3 milionojn da transakcioj kaj ĵetonoj

Obstakloj

Dum la disvolviĝo de Plasma Cash, ni renkontis la jenajn problemojn, kiujn ni iom post iom solvis kaj solvas:

1. Konflikto en la interago de diversaj sistemaj funkcioj. Ekzemple, la funkcio aldoni transakciojn al la naĝejo blokis la laboron de sendado kaj validigo de blokoj, kaj inverse, kio kaŭzis malpliiĝon de rapideco.

2. Ne estis tuj klare kiel sendi grandegan nombron da transakcioj dum minimumigo de datumtransigaj kostoj.

3. Ne estis klare kiel kaj kie stoki datumojn por atingi altajn rezultojn.

4. Ne estis klare kiel organizi reton inter nodoj, ĉar la grandeco de bloko kun 1 miliono da transakcioj okupas ĉirkaŭ 100 MB.

5. Labori en unu-fadena reĝimo rompas la ligon inter nodoj kiam okazas longaj kalkuloj (ekzemple, konstrui Merkle-arbon kaj kalkulante ĝian haŝiŝon).

Kiel ni traktis ĉion ĉi?

La unua versio de la nodo Plasma Cash estis speco de kombinaĵo, kiu povis fari ĉion samtempe: akcepti transakciojn, sendi kaj validigi blokojn, kaj provizi API por aliri datumojn. Ĉar NodeJS estas denaske unu-fadena, la peza Merkle-arba kalkulfunkcio blokis la aldoni transakcian funkcion. Ni vidis du eblojn por solvi ĉi tiun problemon:

1. Lanĉu plurajn NodeJS-procezojn, ĉiu el kiuj plenumas specifajn funkciojn.

2. Uzu worker_threads kaj movu la ekzekuton de parto de la kodo en fadenojn.

Kiel rezulto, ni uzis ambaŭ opciojn samtempe: ni logike dividis unu nodon en 3 partojn kiuj povas funkcii aparte, sed samtempe sinkrone.

1. Submeta nodo, kiu akceptas transakciojn en la naĝejon kaj kreas blokojn.

2. Konfirmanta nodo kiu kontrolas la validecon de nodoj.

3. API-nodo - provizas API por aliri datumojn.

En ĉi tiu kazo, vi povas konekti al ĉiu nodo per uniksa ingo uzante cli.

Ni movis pezajn operaciojn, kiel kalkuli la Merkle-arbon, en apartan fadenon.

Tiel, ni atingis normalan funkciadon de ĉiuj funkcioj de Plasma Cash samtempe kaj sen misfunkciadoj.

Post kiam la sistemo estis funkcia, ni komencis testi la rapidecon kaj, bedaŭrinde, ricevis malkontentigajn rezultojn: 5 transakcioj por sekundo kaj ĝis 000 transakcioj por bloko. Mi devis eltrovi kio estis efektivigita malĝuste.

Komence, ni komencis testi la mekanismon por komuniki kun Plasma Cash por ekscii la pintan kapablon de la sistemo. Ni skribis pli frue, ke la nodo Plasma Cash disponigas uniksan ingo-interfacon. Komence ĝi estis tekstbazita. json-objektoj estis senditaj uzante `JSON.parse()` kaj `JSON.stringify()`.

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

Ni mezuris la transigan rapidon de tiaj objektoj kaj trovis ~ 130k sekundo. Ni provis anstataŭigi la normajn funkciojn por labori kun json, sed agado ne pliboniĝis. La V8-motoro devas esti bone optimumigita por ĉi tiuj operacioj.

Ni laboris kun transakcioj, ĵetonoj kaj blokoj per klasoj. Kreinte tiajn klasojn, la rendimento malpliiĝis je 2 fojojn, kio indikas, ke OOP ne taŭgas por ni. Mi devis reverki ĉion al pure funkcia aliro.

Registrado en la datumbazo

Komence, Redis estis elektita por datumstokado kiel unu el la plej produktivaj solvoj, kiuj kontentigas niajn postulojn: ŝlosilvalora stokado, laborado kun hashtabloj, aroj. Ni lanĉis redis-benchmark kaj ricevis ~80k operaciojn je sekundo en 1-dukta reĝimo.

Por alta rendimento, ni agordis Redis pli fajne:

  • Uniksa ingo-konekto estis establita.
  • Ni malebligis konservadon de la stato al disko (por fidindeco, vi povas agordi kopion kaj konservi al disko en aparta Redis).

En Redis, naĝejo estas hashtabelo ĉar ni devas povi retrovi ĉiujn transakciojn en unu demando kaj forigi transakciojn unu post alia. Ni provis uzi regulan liston, sed ĝi estas pli malrapida dum malŝarĝo de la tuta listo.

Uzante norman NodeJS, la bibliotekoj Redis atingis rendimenton de 18k transakcioj je sekundo. La rapideco malpliiĝis 9 fojojn.

Ĉar la komparnormo montris al ni, ke la eblecoj estis klare 5 fojojn pli grandaj, ni komencis optimumigi. Ni ŝanĝis la bibliotekon al ioredis kaj ricevis rendimenton de 25k sekundo. Ni aldonis transakciojn unu post alia per la komando `hset`. Do ni generis multajn demandojn en Redis. La ideo ekestis kombini transakciojn en arojn kaj sendi ilin per unu komando `hmset`. La rezulto estas 32k sekundo.

Pro pluraj kialoj, kiujn ni priskribos ĉi-sube, ni laboras kun datumoj uzante `Buffer' kaj, kiel rezultas, se vi konvertas ĝin al teksto (`buffer.toString('hex')`) antaŭ skribi, vi povas akiri pliajn. agado. Tiel, la rapido estis pliigita al 35k je sekundo. Nuntempe, ni decidis ĉesigi plian optimumigon.

Ni devis ŝanĝi al binara protokolo ĉar:

1. La sistemo ofte kalkulas hashojn, subskribojn ktp., kaj por tio ĝi bezonas datumojn en la `Buffer.

2. Se sendite inter servoj, binaraj datumoj pezas malpli ol teksto. Ekzemple, kiam vi sendas blokon kun 1 miliono da transakcioj, la datumoj en la teksto povas preni pli ol 300 megabajtojn.

3. Konstante transformado de datumoj influas rendimenton.

Tial ni prenis kiel bazon nian propran binaran protokolon por konservi kaj transdoni datumojn, evoluigitan surbaze de la mirinda biblioteko `binara-datumoj`.

Kiel rezulto, ni ricevis la sekvajn datumstrukturojn:

—Transakcio

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

— Ĵetono

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

—Bloku

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

Per la kutimaj ordonoj `BD.encode(bloko, Protocol).slice();` kaj `BD.decode(buffer, Protocol)` ni konvertas la datumojn en `Buffer' por konservi en Redis aŭ plusendi al alia nodo kaj retrovi la datumojn. datumoj reen.

Ni ankaŭ havas 2 binarajn protokolojn por transdoni datumojn inter servoj:

— Protokolo por interagado kun Plasma Nodo per uniksa ingo

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

kie:

  • `tipo` — la farota ago, ekzemple, 1 — sendTransaction, 2 — getTransaction;
  • `utila ŝarĝo` — datumoj, kiujn oni devas transdoni al la taŭga funkcio;
  • `messageId` — mesaĝo-identigilo por ke la respondo estu identigita.

— Protokolo por interago inter nodoj

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

kie:

  • `kodo` — mesaĝkodo, ekzemple 6 — PREPARE_NEW_BLOCK, 7 — BLOCK_VALID, 8 — BLOCK_COMMIT;
  • `versionProtokolo` — protokola versio, ĉar nodoj kun malsamaj versioj povas esti levitaj en la reto kaj ili povas funkcii alimaniere;
  • `sekv` — mesaĝo-identigilo;
  • `countChunk` и `pecetoNumero` necesa por dividi grandajn mesaĝojn;
  • `longo` и `utila ŝarĝo` longo kaj la datumoj mem.

Ĉar ni antaŭtajpis la datumojn, la fina sistemo estas multe pli rapida ol la biblioteko `rlp` de Ethereum. Bedaŭrinde, ni ankoraŭ ne povis rifuzi ĝin, ĉar necesas fini la inteligentan kontrakton, kion ni planas fari estonte.

Se ni sukcesus atingi la rapidon 35 000 transakcioj por sekundo, ni ankaŭ devas prilabori ilin en la optimuma tempo. Ĉar la proksimuma blokforma tempo prenas 30 sekundojn, ni devas inkluzivi en la bloko 1 000 000 transakcioj, kio signifas sendi pli 100 MB da datumoj.

Komence, ni uzis la bibliotekon `ethereumjs-devp2p` por komuniki inter nodoj, sed ĝi ne povis pritrakti tiom da datumoj. Kiel rezulto, ni uzis la bibliotekon `ws` kaj agordis sendi binarajn datumojn per websocket. Kompreneble, ni ankaŭ renkontis problemojn dum sendado de grandaj datumpakaĵoj, sed ni dividis ilin en pecojn kaj nun ĉi tiuj problemoj malaperis.

Ankaŭ formante Merkle-arbon kaj kalkulante la haŝiŝon 1 000 000 transakcioj postulas pri 10 sekundoj de kontinua kalkulo. Dum ĉi tiu tempo, la ligo kun ĉiuj nodoj sukcesas rompi. Oni decidis movi ĉi tiun kalkulon al aparta fadeno.

Konkludoj:

Fakte, niaj trovoj ne estas novaj, sed ial multaj fakuloj forgesas pri ili dum evoluado.

  • Uzado de Funkcia Programado anstataŭ Objekt-Orientita Programado plibonigas produktivecon.
  • La monolito estas pli malbona ol serva arkitekturo por produktiva NodeJS-sistemo.
  • Uzado de `worker_threads` por peza komputado plibonigas sisteman respondecon, precipe kiam oni traktas i/o-operaciojn.
  • Unix-socket estas pli stabila kaj pli rapida ol http-petoj.
  • Se vi bezonas rapide translokigi grandajn datumojn tra la reto, estas pli bone uzi retajn sockets kaj sendi binarajn datumojn, dividitajn en pecojn, kiuj povas esti plusenditaj se ili ne alvenos, kaj poste kombinitaj en unu mesaĝon.

Ni invitas vin viziti GitHub projekto: https://github.com/opporty-com/Plasma-Cash/tree/new-version

La artikolo estis kunverkita de Aleksandro Naŝivan, altranga programisto Clever Solution Inc.

fonto: www.habr.com

Aldoni komenton