Pampublikong Pagsusulit: Isang Solusyon para sa Privacy at Scalability sa Ethereum

Blockchain ay isang makabagong teknolohiya na nangangako na mapabuti ang maraming bahagi ng buhay ng tao. Naglilipat ito ng mga tunay na proseso at produkto sa digital space, tinitiyak ang bilis at pagiging maaasahan ng mga transaksyon sa pananalapi, binabawasan ang kanilang gastos, at pinapayagan din kang lumikha ng mga modernong aplikasyon ng DAPP gamit ang mga matalinong kontrata sa mga desentralisadong network.

Dahil sa maraming benepisyo at magkakaibang mga aplikasyon ng blockchain, maaaring mukhang nakakagulat na ang maaasahang teknolohiyang ito ay hindi pa nakakapasok sa bawat industriya. Ang problema ay ang mga modernong desentralisadong blockchain ay kulang sa scalability. Ang Ethereum ay nagpoproseso ng humigit-kumulang 20 mga transaksyon sa bawat segundo, na hindi sapat upang matugunan ang mga pangangailangan ng mga dynamic na negosyo ngayon. Kasabay nito, ang mga kumpanyang gumagamit ng teknolohiya ng blockchain ay nag-aalangan na iwanan ang Ethereum dahil sa mataas na antas ng proteksyon nito mula sa pag-hack at mga pagkabigo sa network.

Upang matiyak ang desentralisasyon, seguridad at scalability sa blockchain, sa gayon ay malulutas ang Scalability Trilemma, ang development team Opporty lumikha ng Plasma Cash, isang subsidiary chain na binubuo ng isang matalinong kontrata at isang pribadong network batay sa Node.js, na pana-panahong naglilipat ng estado nito sa root chain (Ethereum).

Pampublikong Pagsusulit: Isang Solusyon para sa Privacy at Scalability sa Ethereum

Mga pangunahing proseso sa Plasma Cash

1. Tinatawag ng user ang function ng smart contract na `deposit`, na ipinapasa dito ang halaga ng ETH na gusto niyang i-deposito sa token ng Plasma Cash. Gumagawa ng token ang smart contract function at bumubuo ng kaganapan tungkol dito.

2. Ang mga Plasma Cash node na naka-subscribe sa mga smart contract event ay makakatanggap ng kaganapan tungkol sa paggawa ng deposito at pagdaragdag ng transaksyon tungkol sa paggawa ng token sa pool.

3. Pana-panahon, ang mga espesyal na Plasma Cash node ay kumukuha ng lahat ng mga transaksyon mula sa pool (hanggang sa 1 milyon) at bumubuo ng isang bloke mula sa kanila, kalkulahin ang Merkle tree at, nang naaayon, ang hash. Ang block na ito ay ipinadala sa iba pang mga node para sa pag-verify. Tinitingnan ng mga node kung wasto ang Merkle hash at kung wasto ang mga transaksyon (halimbawa, kung ang nagpadala ng token ay ang may-ari nito). Pagkatapos i-verify ang block, tatawagin ng node ang function na `submitBlock` ng smart contract, na nagse-save ng block number at Merkle hash sa edge chain. Ang matalinong kontrata ay bumubuo ng isang kaganapan na nagpapahiwatig ng matagumpay na pagdaragdag ng isang bloke. Inalis ang mga transaksyon sa pool.

4. Ang mga node na tumatanggap ng kaganapan sa pagsusumite ng block ay nagsisimulang ilapat ang mga transaksyong idinagdag sa block.

5. Sa isang punto, gusto ng may-ari (o hindi may-ari) ng token na bawiin ito mula sa Plasma Cash. Upang gawin ito, tinawag niya ang function na `startExit`, na nagpapasa dito ng impormasyon tungkol sa huling 2 transaksyon sa token, na nagpapatunay na siya ang may-ari ng token. Ang matalinong kontrata, gamit ang Merkle hash, ay nagsusuri ng pagkakaroon ng mga transaksyon sa mga bloke at nagpapadala ng token para sa pag-withdraw, na magaganap sa loob ng dalawang linggo.

6. Kung ang pagpapatakbo ng pag-withdraw ng token ay nangyari na may mga paglabag (ginastos ang token pagkatapos magsimula ang pamamaraan ng pag-withdraw o ang token ay sa ibang tao na bago ang pag-withdraw), maaaring pabulaanan ng may-ari ng token ang pag-withdraw sa loob ng dalawang linggo.

Pampublikong Pagsusulit: Isang Solusyon para sa Privacy at Scalability sa Ethereum

Ang privacy ay nakakamit sa dalawang paraan

1. Walang alam ang root chain tungkol sa mga transaksyong nabuo at ipinapasa sa loob ng child chain. Ang impormasyon tungkol sa kung sino ang nagdeposito at nag-withdraw ng ETH mula sa Plasma Cash ay nananatiling pampubliko.

2. Pinapayagan ng child chain ang mga hindi kilalang transaksyon gamit ang zk-SNARKs.

Salansan ng teknolohiya

  • NodeJS
  • Redis
  • Etherium
  • Malambing

Pagsubok

Habang binubuo ang Plasma Cash, sinubukan namin ang bilis ng system at nakuha ang mga sumusunod na resulta:

  • hanggang sa 35 mga transaksyon sa bawat segundo ay idinagdag sa pool;
  • hanggang 1 mga transaksyon ang maaaring maimbak sa isang bloke.

Ang mga pagsubok ay isinagawa sa sumusunod na 3 server:

1. Intel Core i7-6700 Quad-Core Skylake kasama. NVMe SSD – 512 GB, 64 GB DDR4 RAM
Nakataas ang 3 nagpapatunay na Plasma Cash node.

2. AMD Ryzen 7 1700X Octa-Core “Summit Ridge” (Zen), SATA SSD – 500 GB, 64 GB DDR4 RAM
Ang Ropsten testnet ETH node ay itinaas.
Nakataas ang 3 nagpapatunay na Plasma Cash node.

3. Intel Core i9-9900K Octa-Core kasama. NVMe SSD – 1 TB, 64 GB DDR4 RAM
1 Plasma Cash submission node ay itinaas.
Nakataas ang 3 nagpapatunay na Plasma Cash node.
Isang pagsubok ang inilunsad upang magdagdag ng mga transaksyon sa network ng Plasma Cash.

Kabuuan: 10 Plasma Cash node sa isang pribadong network.

Pagsubok 1

May limitasyon na 1 milyong transaksyon bawat bloke. Samakatuwid, ang 1 milyong mga transaksyon ay nahuhulog sa 2 mga bloke (dahil ang sistema ay namamahala upang makilahok sa mga transaksyon at isumite habang sila ay ipinadala).


Paunang estado: huling bloke #7; 1 milyong mga transaksyon at mga token ay naka-imbak sa database.

00:00 — simula ng script ng pagbuo ng transaksyon
01:37 - 1 milyong transaksyon ang nalikha at nagsimula ang pagpapadala sa node
01:46 — ang submit node ay kumuha ng 240k na transaksyon mula sa pool at mga form block #8. Nakikita rin namin na 320k na transaksyon ang idinaragdag sa pool sa loob ng 10 segundo
01:58 — ang block #8 ay nilagdaan at ipinadala para sa pagpapatunay
02:03 — ang block #8 ay napatunayan at ang `submitBlock` function ng smart contract ay tinatawag na may Merkle hash at block number
02:10 — natapos nang gumana ang demo script, na nagpadala ng 1 milyong transaksyon sa loob ng 32 segundo
02:33 - nagsimulang makatanggap ang mga node ng impormasyon na idinagdag ang block #8 sa root chain, at nagsimulang magsagawa ng 240k na transaksyon
02:40 - 240k na transaksyon ang inalis sa pool, na nasa block #8 na
02:56 — kinuha ng submit node ang natitirang 760k na transaksyon mula sa pool at sinimulang kalkulahin ang Merkle hash at signing block #9
03:20 - lahat ng node ay naglalaman ng 1 milyong 240k na transaksyon at token
03:35 — ang block #9 ay nilagdaan at ipinadala para sa pagpapatunay sa ibang mga node
03:41 - naganap ang error sa network
04:40 — nag-time out ang paghihintay para sa pagpapatunay ng block #9
04:54 — kinuha ng submit node ang natitirang 760k na transaksyon mula sa pool at sinimulang kalkulahin ang Merkle hash at signing block #9
05:32 — ang block #9 ay nilagdaan at ipinadala para sa pagpapatunay sa ibang mga node
05:53 — ang block #9 ay napatunayan at ipinadala sa root chain
06:17 - nagsimulang makatanggap ang mga node ng impormasyon na idinagdag ang block #9 sa root chain at nagsimulang magsagawa ng 760k na transaksyon
06:47 — inalis ng pool ang mga transaksyon na nasa block #9
09:06 - lahat ng node ay naglalaman ng 2 milyong transaksyon at token

Pagsubok 2

May limitasyon na 350k bawat bloke. Bilang resulta, mayroon kaming 3 bloke.


Paunang estado: huling bloke #9; 2 milyong mga transaksyon at mga token ay naka-imbak sa database

00:00 — nailunsad na ang script ng pagbuo ng transaksyon
00:44 - 1 milyong transaksyon ang nalikha at nagsimula ang pagpapadala sa node
00:56 — ang submit node ay kumuha ng 320k na transaksyon mula sa pool at mga form block #10. Nakikita rin namin na 320k na transaksyon ang idinaragdag sa pool sa loob ng 10 segundo
01:12 — ang block #10 ay nilagdaan at ipinadala sa iba pang mga node para sa pagpapatunay
01:18 — natapos nang gumana ang demo script, na nagpadala ng 1 milyong transaksyon sa loob ng 34 segundo
01:20 — ang block #10 ay napatunayan at ipinadala sa root chain
01:51 - lahat ng node ay nakatanggap ng impormasyon mula sa root chain na nagdagdag ng block #10 at nagsimulang maglapat ng 320k na transaksyon
02:01 - na-clear na ang pool para sa 320k na transaksyon na idinagdag sa block #10
02:15 — ang submit node ay kumuha ng 350k na transaksyon mula sa pool at mga form block #11
02:34 — ang block #11 ay nilagdaan at ipinadala sa iba pang mga node para sa pagpapatunay
02:51 — ang block #11 ay napatunayan at ipinadala sa root chain
02:55 — ang huling node ay nakumpleto ang mga transaksyon mula sa block #10
10:59 — ang transaksyon sa pagsusumite ng block #9 ay tumagal ng napakatagal sa root chain, ngunit ito ay nakumpleto at ang lahat ng mga node ay nakatanggap ng impormasyon tungkol dito at nagsimulang magsagawa ng 350k na mga transaksyon
11:05 - na-clear na ang pool para sa 320k na transaksyon na idinagdag sa block #11
12:10 - lahat ng node ay naglalaman ng 1 milyong 670k na transaksyon at token
12:17 — ang submit node ay kumuha ng 330k na transaksyon mula sa pool at mga form block #12
12:32 — ang block #12 ay nilagdaan at ipinadala sa iba pang mga node para sa pagpapatunay
12:39 — ang block #12 ay napatunayan at ipinadala sa root chain
13:44 - lahat ng node ay nakatanggap ng impormasyon mula sa root chain na nagdagdag ng block #12 at nagsimulang maglapat ng 330k na transaksyon
14:50 - lahat ng node ay naglalaman ng 2 milyong transaksyon at token

Pagsubok 3

Sa una at pangalawang server, ang isang nagpapatunay na node ay pinalitan ng isang pagsusumite ng node.


Paunang estado: huling bloke #84; 0 mga transaksyon at mga token na na-save sa database

00:00 — 3 script ang inilunsad na bumubuo at nagpapadala ng 1 milyong transaksyon bawat isa
01:38 — 1 milyong transaksyon ang ginawa at nagsimula ang pagpapadala para isumite ang node #3
01:50 — ang pagsusumite ng node #3 ay kumuha ng 330k na transaksyon mula sa pool at mga form block #85 (f21). Nakikita rin namin na 350k na transaksyon ang naidagdag sa pool sa loob ng 10 segundo
01:53 — 1 milyong transaksyon ang ginawa at nagsimula ang pagpapadala para isumite ang node #1
01:50 — ang pagsusumite ng node #3 ay kumuha ng 330k na transaksyon mula sa pool at mga form block #85 (f21). Nakikita rin namin na 350k na transaksyon ang naidagdag sa pool sa loob ng 10 segundo
02:01 — isumite ang node #1 ay kumuha ng 250k na transaksyon mula sa pool at mga form block #85 (65e)
02:06 — ang block #85 (f21) ay nilagdaan at ipinadala sa iba pang mga node para sa pagpapatunay
02:08 — demo script ng server #3, na nagpadala ng 1 milyong transaksyon sa loob ng 30 segundo, natapos nang gumana
02:14 — ang block #85 (f21) ay napatunayan at ipinadala sa root chain
02:19 — ang block #85 (65e) ay nilagdaan at ipinadala sa iba pang mga node para sa pagpapatunay
02:22 — 1 milyong transaksyon ang ginawa at nagsimula ang pagpapadala para isumite ang node #2
02:27 — na-validate ang block #85 (65e) at ipinadala sa root chain
02:29 — isumite ang node #2 ay kumuha ng 111855 na mga transaksyon mula sa pool at mga form block #85 (256).
02:36 — ang block #85 (256) ay nilagdaan at ipinadala sa iba pang mga node para sa pagpapatunay
02:36 — demo script ng server #1, na nagpadala ng 1 milyong transaksyon sa loob ng 42.5 segundo, natapos nang gumana
02:38 — ang block #85 (256) ay napatunayan at ipinadala sa root chain
03:08 — natapos nang gumana ang script ng server #2, na nagpadala ng 1 milyong transaksyon sa loob ng 47 segundo
03:38 - lahat ng node ay nakatanggap ng impormasyon mula sa root chain na humaharang sa #85 (f21), #86(65e), #87(256) ay idinagdag at nagsimulang maglapat ng 330k, 250k, 111855 na transaksyon
03:49 - na-clear ang pool sa 330k, 250k, 111855 na transaksyon na idinagdag sa mga block #85 (f21), #86(65e), #87(256)
03:59 — ang pagsusumite ng node #1 ay kumuha ng 888145 na transaksyon mula sa pool at ang mga form block #88 (214), ang pagsusumite ng node #2 ay kumuha ng 750k na mga transaksyon mula sa pool at ang mga form sa block #88 (50a), ang pagsusumite ng node #3 ay kumuha ng 670k na mga transaksyon mula sa ang pool at mga form block #88 (d3b)
04:44 — ang block #88 (d3b) ay nilagdaan at ipinadala sa iba pang mga node para sa pagpapatunay
04:58 — ang block #88 (214) ay nilagdaan at ipinadala sa iba pang mga node para sa pagpapatunay
05:11 — ang block #88 (50a) ay nilagdaan at ipinadala sa iba pang mga node para sa pagpapatunay
05:11 — ang block #85 (d3b) ay napatunayan at ipinadala sa root chain
05:36 — ang block #85 (214) ay napatunayan at ipinadala sa root chain
05:43 - lahat ng node ay nakatanggap ng impormasyon mula sa root chain na humaharang sa #88 (d3b), #89(214) ay naidagdag at nagsisimula nang maglapat ng 670k, 750k na transaksyon
06:50 — dahil sa pagkabigo sa komunikasyon, hindi na-validate ang block #85 (50a).
06:55 — isumite ang node #2 ay kumuha ng 888145 na transaksyon mula sa pool at mga form block #90 (50a)
08:14 — ang block #90 (50a) ay nilagdaan at ipinadala sa iba pang mga node para sa pagpapatunay
09:04 — ang block #90 (50a) ay napatunayan at ipinadala sa root chain
11:23 - lahat ng node ay nakatanggap ng impormasyon mula sa root chain na nagdagdag ng block #90 (50a), at nagsimulang mag-apply ng 888145 na mga transaksyon. Kasabay nito, naglapat na ang server #3 ng mga transaksyon mula sa block #88 (d3b), #89(214)
12:11 - lahat ng pool ay walang laman
13:41 — lahat ng node ng server #3 ay naglalaman ng 3 milyong transaksyon at token
14:35 — lahat ng node ng server #1 ay naglalaman ng 3 milyong transaksyon at token
19:24 — lahat ng node ng server #2 ay naglalaman ng 3 milyong transaksyon at token

Mga balakid

Sa panahon ng pagbuo ng Plasma Cash, nakatagpo kami ng mga sumusunod na problema, na unti-unti naming nalutas at nilulutas:

1. Salungatan sa pakikipag-ugnayan ng iba't ibang mga function ng system. Halimbawa, ang pag-andar ng pagdaragdag ng mga transaksyon sa pool ay humarang sa gawain ng pagsusumite at pagpapatunay ng mga bloke, at kabaliktaran, na humantong sa pagbaba ng bilis.

2. Hindi agad malinaw kung paano magpadala ng malaking bilang ng mga transaksyon habang pinapaliit ang mga gastos sa paglilipat ng data.

3. Hindi malinaw kung paano at saan mag-iimbak ng data upang makamit ang matataas na resulta.

4. Hindi malinaw kung paano ayusin ang isang network sa pagitan ng mga node, dahil ang laki ng isang bloke na may 1 milyong mga transaksyon ay tumatagal ng halos 100 MB.

5. Ang pagtatrabaho sa single-threaded mode ay sumisira sa koneksyon sa pagitan ng mga node kapag naganap ang mahabang kalkulasyon (halimbawa, pagbuo ng isang Merkle tree at pagkalkula ng hash nito).

Paano natin hinarap ang lahat ng ito?

Ang unang bersyon ng Plasma Cash node ay isang uri ng pagsasama-sama na maaaring gawin ang lahat nang sabay-sabay: tanggapin ang mga transaksyon, isumite at i-validate ang mga bloke, at magbigay ng API para sa pag-access ng data. Dahil ang NodeJS ay native na single-threaded, hinarangan ng mabigat na Merkle tree calculation function ang add transaction function. Nakita namin ang dalawang pagpipilian para sa paglutas ng problemang ito:

1. Ilunsad ang ilang proseso ng NodeJS, bawat isa ay gumaganap ng mga partikular na function.

2. Gumamit ng worker_threads at ilipat ang pagpapatupad ng bahagi ng code sa mga thread.

Bilang resulta, ginamit namin ang parehong mga pagpipilian sa parehong oras: lohikal naming hinati ang isang node sa 3 bahagi na maaaring gumana nang hiwalay, ngunit sa parehong oras nang sabay-sabay

1. Submission node, na tumatanggap ng mga transaksyon sa pool at lumilikha ng mga block.

2. Isang nagpapatunay na node na sumusuri sa bisa ng mga node.

3. API node - nagbibigay ng API para sa pag-access ng data.

Sa kasong ito, maaari kang kumonekta sa bawat node sa pamamagitan ng isang unix socket gamit ang cli.

Inilipat namin ang mabibigat na operasyon, tulad ng pagkalkula ng Merkle tree, sa isang hiwalay na thread.

Kaya, nakamit namin ang normal na operasyon ng lahat ng Plasma Cash function nang sabay-sabay at walang mga pagkabigo.

Sa sandaling gumana ang system, sinimulan naming subukan ang bilis at, sa kasamaang-palad, nakatanggap ng mga hindi kasiya-siyang resulta: 5 mga transaksyon bawat segundo at hanggang 000 mga transaksyon bawat bloke. Kinailangan kong malaman kung ano ang ipinatupad nang hindi tama.

Upang magsimula, sinimulan naming subukan ang mekanismo ng komunikasyon sa Plasma Cash upang malaman ang pinakamataas na kakayahan ng system. Isinulat namin kanina na ang Plasma Cash node ay nagbibigay ng unix socket interface. Sa una ito ay batay sa teksto. Ang mga object ng json ay ipinadala gamit ang `JSON.parse()` at `JSON.stringify()`.

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

Sinukat namin ang bilis ng paglipat ng mga naturang bagay at nakita namin ~ 130k bawat segundo. Sinubukan naming palitan ang mga karaniwang function para sa pagtatrabaho sa json, ngunit hindi bumuti ang pagganap. Ang V8 engine ay dapat na mahusay na na-optimize para sa mga operasyong ito.

Nagtatrabaho kami sa mga transaksyon, token, at block sa pamamagitan ng mga klase. Kapag lumilikha ng gayong mga klase, bumaba ang pagganap ng 2 beses, na nagpapahiwatig na ang OOP ay hindi angkop para sa amin. Kinailangan kong muling isulat ang lahat sa isang purong functional na diskarte.

Pagre-record sa database

Sa una, napili ang Redis para sa pag-iimbak ng data bilang isa sa mga pinakaproduktibong solusyon na nakakatugon sa aming mga kinakailangan: imbakan ng key-value, nagtatrabaho sa mga hash table, mga set. Naglunsad kami ng redis-benchmark at nakakuha ng ~80k na operasyon bawat segundo sa 1 pipelining mode.

Para sa mataas na pagganap, inayos namin ang Redis:

  • Ang koneksyon ng unix socket ay naitatag.
  • Hindi namin pinagana ang pag-save ng estado sa disk (para sa pagiging maaasahan, maaari kang mag-set up ng replika at i-save sa disk sa isang hiwalay na Redis).

Sa Redis, ang pool ay isang hash table dahil kailangan nating makuha ang lahat ng transaksyon sa isang query at tanggalin ang mga transaksyon nang paisa-isa. Sinubukan naming gumamit ng isang regular na listahan, ngunit ito ay mas mabagal kapag ibinababa ang buong listahan.

Kapag gumagamit ng karaniwang NodeJS, ang mga aklatan ng Redis ay nakamit ang pagganap ng 18k na mga transaksyon sa bawat segundo. Ang bilis ay bumaba ng 9 na beses.

Dahil ipinakita sa amin ng benchmark na malinaw na 5 beses na mas malaki ang mga posibilidad, nagsimula kaming mag-optimize. Binago namin ang library sa ioredis at nakakuha kami ng performance na 25k bawat segundo. Nagdagdag kami ng mga transaksyon nang paisa-isa gamit ang command na `hset`. Kaya kami ay bumubuo ng maraming mga query sa Redis. Bumangon ang ideya na pagsamahin ang mga transaksyon sa mga batch at ipadala ang mga ito gamit ang isang command na `hmset`. Ang resulta ay 32k bawat segundo.

Para sa ilang mga kadahilanan, na ilalarawan namin sa ibaba, nagtatrabaho kami sa data gamit ang `Buffer` at, sa lalabas, kung iko-convert mo ito sa text (`buffer.toString('hex')`) bago sumulat, maaari kang makakuha ng karagdagang pagganap. Kaya, ang bilis ay nadagdagan sa 35k bawat segundo. Sa ngayon, nagpasya kaming suspendihin ang karagdagang pag-optimize.

Kinailangan naming lumipat sa isang binary protocol dahil:

1. Madalas na kinakalkula ng system ang mga hash, pirma, atbp., at para dito kailangan nito ng data sa `Buffer.

2. Kapag ipinadala sa pagitan ng mga serbisyo, ang binary data ay mas mababa sa text. Halimbawa, kapag nagpapadala ng block na may 1 milyong transaksyon, ang data sa text ay maaaring tumagal ng higit sa 300 megabytes.

3. Ang patuloy na pagbabago ng data ay nakakaapekto sa pagganap.

Samakatuwid, ginawa namin bilang batayan ang aming sariling binary protocol para sa pag-iimbak at pagpapadala ng data, na binuo batay sa kahanga-hangang `binary-data` library.

Bilang resulta, nakuha namin ang mga sumusunod na istruktura ng data:

—Transaksyon

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

—Harangin

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

Gamit ang karaniwang mga command na `BD.encode(block, Protocol).slice();` at `BD.decode(buffer, Protocol)` kino-convert namin ang data sa `Buffer` para sa pag-save sa Redis o pagpapasa sa isa pang node at pagkuha ng ibalik ang data.

Mayroon din kaming 2 binary protocol para sa paglilipat ng data sa pagitan ng mga serbisyo:

— Protocol para sa pakikipag-ugnayan sa Plasma Node sa pamamagitan ng unix socket

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

kung saan:

  • `uri` — ang aksyon na gagawin, halimbawa, 1 — sendTransaction, 2 — getTransaction;
  • `payload` — data na kailangang maipasa sa naaangkop na function;
  • `messageId` — message id para matukoy ang tugon.

— Protocol para sa pakikipag-ugnayan sa pagitan ng mga node

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

kung saan:

  • `code` — code ng mensahe, halimbawa 6 — PREPARE_NEW_BLOCK, 7 — BLOCK_VALID, 8 — BLOCK_COMMIT;
  • `versionProtocol` — bersyon ng protocol, dahil ang mga node na may iba't ibang bersyon ay maaaring itaas sa network at maaari silang gumana nang iba;
  • `seq` — tagatukoy ng mensahe;
  • `countChunk` и `chunkNumber` kinakailangan para sa paghahati ng malalaking mensahe;
  • `haba` и `payload` haba at ang data mismo.

Dahil na-pre-type namin ang data, ang huling sistema ay mas mabilis kaysa sa library ng `rlp` ng Ethereum. Sa kasamaang palad, hindi pa namin ito nagawang tanggihan, dahil kinakailangan na tapusin ang matalinong kontrata, na plano naming gawin sa hinaharap.

Kung nagawa naming maabot ang bilis 35 000 mga transaksyon sa bawat segundo, kailangan din nating iproseso ang mga ito sa pinakamainam na oras. Dahil ang tinatayang oras ng pagbuo ng bloke ay tumatagal ng 30 segundo, kailangan nating isama sa block +1 000 000 mga transaksyon, na nangangahulugan ng pagpapadala ng higit pa 100 MB ng data.

Noong una, ginamit namin ang library ng `ethereumjs-devp2p` para makipag-ugnayan sa pagitan ng mga node, ngunit hindi nito kayang pangasiwaan ang napakaraming data. Bilang resulta, ginamit namin ang library ng `ws` at na-configure ang pagpapadala ng binary data sa pamamagitan ng websocket. Siyempre, nakaranas din kami ng mga problema sa pagpapadala ng malalaking data packet, ngunit hinati namin ang mga ito sa mga tipak at ngayon ay wala na ang mga problemang ito.

Bumubuo din ng isang Merkle tree at kinakalkula ang hash +1 000 000 ang mga transaksyon ay nangangailangan ng tungkol sa 10 segundo ng tuluy-tuloy na pagkalkula. Sa panahong ito, ang koneksyon sa lahat ng mga node ay namamahala upang masira. Napagpasyahan na ilipat ang pagkalkula na ito sa isang hiwalay na thread.

Konklusyon:

Sa katunayan, ang aming mga natuklasan ay hindi bago, ngunit sa ilang kadahilanan maraming mga eksperto ang nakakalimutan ang tungkol sa mga ito kapag umuunlad.

  • Ang paggamit ng Functional Programming sa halip na Object-Oriented Programming ay nagpapabuti sa pagiging produktibo.
  • Ang monolith ay mas masahol pa kaysa sa isang arkitektura ng serbisyo para sa isang produktibong sistema ng NodeJS.
  • Ang paggamit ng `worker_threads` para sa mabigat na pag-compute ay nagpapabuti sa pagtugon ng system, lalo na kapag nakikitungo sa mga pagpapatakbo ng i/o.
  • Ang unix socket ay mas matatag at mas mabilis kaysa sa mga kahilingan sa http.
  • Kung kailangan mong mabilis na maglipat ng malalaking data sa network, mas mainam na gumamit ng mga websocket at magpadala ng binary data, na nahahati sa mga tipak, na maaaring ipasa kung hindi sila dumating, at pagkatapos ay pinagsama sa isang mensahe.

Inaanyayahan ka naming bisitahin GitHub proyekto: https://github.com/opporty-com/Plasma-Cash/tree/new-version

Ang artikulo ay isinulat ni Alexander Nashivan, senior developer Ang Clever Solution Inc.

Pinagmulan: www.habr.com

Magdagdag ng komento