Հանրային թեստ. լուծում Ethereum-ի գաղտնիության և մասշտաբայնության համար

Բլոկ նորարարական տեխնոլոգիա է, որը խոստանում է բարելավել մարդկային կյանքի բազմաթիվ ոլորտներ: Այն իրական գործընթացներն ու ապրանքները փոխանցում է թվային տարածք, ապահովում է ֆինանսական գործարքների արագությունն ու հուսալիությունը, նվազեցնում դրանց արժեքը, ինչպես նաև թույլ է տալիս ստեղծել ժամանակակից DAPP հավելվածներ՝ օգտագործելով խելացի պայմանագրերը ապակենտրոնացված ցանցերում:

Հաշվի առնելով բլոկչեյնի բազմաթիվ առավելություններն ու բազմազան կիրառությունները՝ կարող է զարմանալի թվալ, որ այս խոստումնալից տեխնոլոգիան դեռևս իր ճանապարհը չի մտել բոլոր ոլորտներում: Խնդիրն այն է, որ ժամանակակից ապակենտրոնացված բլոկչեյնները չունեն մասշտաբայնություն: Ethereum-ը վայրկյանում մշակում է մոտ 20 գործարք, ինչը բավարար չէ այսօրվա դինամիկ բիզնեսի կարիքները բավարարելու համար։ Միևնույն ժամանակ, բլոկչեյն տեխնոլոգիան օգտագործող ընկերությունները տատանվում են հրաժարվել Ethereum-ից՝ հաքերներից և ցանցային խափանումներից նրա պաշտպանվածության բարձր աստիճանի պատճառով:

Ապակենտրոնացում, անվտանգություն և մասշտաբայնություն բլոկչեյնում ապահովելու համար, այդպիսով լուծելով Scalability Trilemma, մշակողների թիմը Հնարավորություն ստեղծել է Plasma Cash-ը` դուստր շղթան, որը բաղկացած է խելացի պայմանագրից և Node.js-ի վրա հիմնված մասնավոր ցանցից, որը պարբերաբար փոխանցում է իր վիճակը արմատային շղթային (Ethereum):

Հանրային թեստ. լուծում Ethereum-ի գաղտնիության և մասշտաբայնության համար

Հիմնական գործընթացները Plasma Cash-ում

1. Օգտագործողը խելացի պայմանագրի գործառույթն անվանում է «դեպոզիտ»՝ դրան փոխանցելով ETH-ի այն գումարը, որը նա ցանկանում է ներդնել Plasma Cash թոքենին: Խելացի պայմանագրային ֆունկցիան ստեղծում է նշան և ստեղծում դրա մասին իրադարձություն:

2. Պլազմային կանխիկացման հանգույցները, որոնք բաժանորդագրվել են խելացի պայմանագրային իրադարձություններին, ստանում են միջոցառում ավանդ ստեղծելու մասին և ավելացնում են գործարք՝ լողավազանում նշան ստեղծելու մասին:

3. Պարբերաբար հատուկ Plasma Cash հանգույցները վերցնում են լողավազանից բոլոր գործարքները (մինչև 1 միլիոն) և դրանցից կազմում բլոկ, հաշվարկում Merkle ծառը և, համապատասխանաբար, հեշը: Այս բլոկը ստուգման համար ուղարկվում է այլ հանգույցներ: Հանգույցները ստուգում են՝ արդյոք Merkle hash-ը վավեր է, և արդյոք գործարքները վավեր են (օրինակ՝ նշանի ուղարկողը դրա տերն է)։ Բլոկը ստուգելուց հետո հանգույցը կանչում է խելացի պայմանագրի «submitBlock» ֆունկցիան, որը պահպանում է բլոկի համարը և Merkle հեշը եզրային շղթայում։ Խելացի պայմանագիրը առաջացնում է իրադարձություն, որը ցույց է տալիս բլոկի հաջող ավելացումը: Գործարքները հանվում են լողավազանից:

4. Բլոկի ներկայացման իրադարձությունը ստացող հանգույցները սկսում են կիրառել բլոկին ավելացված գործարքները:

5. Ինչ-որ պահի նշանի տերը (կամ ոչ սեփականատերը) ցանկանում է հանել այն Plasma Cash-ից: Դա անելու համար նա անվանում է «startExit» ֆունկցիան՝ դրան փոխանցելով տոկենի վերջին 2 գործարքների մասին տեղեկատվություն, որոնք հաստատում են, որ նա է տոկենի սեփականատերը։ Խելացի պայմանագիրը, օգտագործելով Merkle hash-ը, ստուգում է գործարքների առկայությունը բլոկներում և ուղարկում նշանը հանման, որը տեղի կունենա երկու շաբաթից:

6. Եթե ​​նշանի դուրսբերման գործողությունը տեղի է ունեցել խախտումներով (նշանը ծախսվել է դուրսբերման ընթացակարգի սկսվելուց հետո, կամ նշանն արդեն ուրիշինն է եղել մինչև հանումը), ապա նշանի սեփականատերը կարող է հերքել դուրսբերումը երկու շաբաթվա ընթացքում:

Հանրային թեստ. լուծում Ethereum-ի գաղտնիության և մասշտաբայնության համար

Գաղտնիությունը ձեռք է բերվում երկու եղանակով

1. Արմատային շղթան ոչինչ չգիտի այն գործարքների մասին, որոնք ստեղծվում և փոխանցվում են երեխայի շղթայում: Այն մասին, թե ով է ավանդադրել և հանել ETH-ը Plasma Cash-ից, մնում է հրապարակային:

2. Երեխաների շղթան թույլ է տալիս անանուն գործարքներ zk-SNARK-ների միջոցով:

Տեխնոլոգիաների բուրգ

  • NodeJS- ը
  • Redis
  • Եթերիում
  • Հող

Փորձարկում

Plasma Cash-ը մշակելիս մենք փորձարկեցինք համակարգի արագությունը և ստացանք հետևյալ արդյունքները.

  • Լողավազանին ավելացվում է վայրկյանում մինչև 35 գործարք.
  • մինչև 1 գործարքներ կարող են պահվել բլոկում:

Թեստերն իրականացվել են հետևյալ 3 սերվերների վրա.

1. Intel Core i7-6700 Quad-Core Skylake ներառյալ. NVMe SSD – 512 ԳԲ, 64 ԳԲ DDR4 RAM
Բարձրացվել են 3 վավերացնող Plasma Cash հանգույցներ:

2. AMD Ryzen 7 1700X Octa-Core «Summit Ridge» (Zen), SATA SSD – 500 ԳԲ, 64 ԳԲ DDR4 RAM
Ropsten testnet ETH հանգույցը բարձրացվեց:
Բարձրացվել են 3 վավերացնող Plasma Cash հանգույցներ:

3. Intel Core i9-9900K Octa-Core ներառյալ. NVMe SSD – 1 TB, 64 GB DDR4 RAM
Բարձրացվել է 1 Plasma Cash-ի ներկայացման հանգույց:
Բարձրացվել են 3 վավերացնող Plasma Cash հանգույցներ:
Գործարկվել է թեստ՝ գործարքներ ավելացնելու Plasma Cash ցանցին:

Ընդամենը: 10 Plasma Cash հանգույց մասնավոր ցանցում:

Թեստ 1

Մեկ բլոկի համար կա 1 միլիոն գործարքի սահմանափակում: Հետևաբար, 1 միլիոն գործարքներ ընկնում են 2 բլոկի մեջ (քանի որ համակարգին հաջողվում է գործարքների մի մասը վերցնել և ներկայացնել մինչ դրանք ուղարկվում են):


Սկզբնական վիճակ՝ վերջին բլոկ #7; Տվյալների բազայում պահվում են 1 միլիոն գործարքներ և նշաններ:

00:00 — գործարքների ստեղծման սկրիպտի սկիզբ
01:37 - Ստեղծվել է 1 միլիոն գործարք և սկսվել է ուղարկումը դեպի հանգույց
01:46 — ներկայացնել հանգույցը 240 հազար գործարք է վերցրել լողավազանից և ձևավորել թիվ 8 բլոկը: Մենք նաև տեսնում ենք, որ 320 վայրկյանում լողավազանում ավելացվում է 10 հազար գործարք
01:58 — թիվ 8 բլոկը ստորագրվում և ուղարկվում է վավերացման
02:03 — բլոկ #8 վավերացված է, և խելացի պայմանագրի «submitBlock» ֆունկցիան կանչվում է Merkle հեշի և բլոկի համարով։
02:10 — ավարտվեց ցուցադրական սցենարը, որն ուղարկեց 1 միլիոն գործարք 32 վայրկյանում
02:33 - հանգույցները սկսեցին տեղեկատվություն ստանալ, որ #8 բլոկը ավելացվել է արմատային շղթային և սկսեցին կատարել 240 հազար գործարքներ
02:40 - Լողավազանից հեռացվել է 240 հազար գործարք, որոնք արդեն գտնվում են թիվ 8 բլոկում
02:56 — ներկայացնել հանգույցը վերցրեց մնացած 760 հազար գործարքները լողավազանից և սկսեց հաշվարկել Մերկլի հեշը և ստորագրել թիվ 9 բլոկը:
03:20 - բոլոր հանգույցները պարունակում են 1 միլիոն 240 հազար գործարքներ և նշաններ
03:35 — թիվ 9 բլոկը ստորագրվում է և ուղարկվում վավերացման այլ հանգույցներ
03:41 - ցանցի սխալ է տեղի ունեցել
04:40 — թիվ 9 բլոկի վավերացման սպասման ժամանակը սպառվել է
04:54 — ներկայացնել հանգույցը վերցրեց մնացած 760 հազար գործարքները լողավազանից և սկսեց հաշվարկել Մերկլի հեշը և ստորագրել թիվ 9 բլոկը:
05:32 — թիվ 9 բլոկը ստորագրվում է և ուղարկվում վավերացման այլ հանգույցներ
05:53 — թիվ 9 բլոկը վավերացվում է և ուղարկվում արմատային շղթա
06:17 - հանգույցները սկսեցին տեղեկատվություն ստանալ, որ թիվ 9 բլոկը ավելացվել է արմատային շղթային և սկսել է կատարել 760 հազար գործարք:
06:47 — լողավազանը մաքրվել է թիվ 9 բլոկում գտնվող գործարքներից
09:06 - բոլոր հանգույցները պարունակում են 2 միլիոն գործարքներ և նշաններ

Թեստ 2

Մեկ բլոկի համար կա 350 հազար սահմանաչափ: Արդյունքում ունենք 3 բլոկ։


Սկզբնական վիճակ՝ վերջին բլոկ #9; Տվյալների բազայում պահվում են 2 միլիոն գործարքներ և նշաններ

00:00 — գործարքների ստեղծման սցենարն արդեն գործարկվել է
00:44 - Ստեղծվել է 1 միլիոն գործարք և սկսվել է ուղարկումը դեպի հանգույց
00:56 — ներկայացնել հանգույցը 320 հազար գործարք է վերցրել լողավազանից և ձևավորել թիվ 10 բլոկը: Մենք նաև տեսնում ենք, որ 320 վայրկյանում լողավազանում ավելացվում է 10 հազար գործարք
01:12 — թիվ 10 բլոկը ստորագրվում և ուղարկվում է այլ հանգույցներ վավերացման համար
01:18 — ավարտվեց ցուցադրական սցենարը, որն ուղարկեց 1 միլիոն գործարք 34 վայրկյանում
01:20 — թիվ 10 բլոկը վավերացվում է և ուղարկվում արմատային շղթա
01:51 - բոլոր հանգույցները ստացել են տեղեկատվություն արմատային շղթայից, որ թիվ 10 բլոկը ավելացվել է և սկսում է կիրառել 320 հազար գործարքներ
02:01 - լողավազանը մաքրվել է 320 հազար գործարքների համար, որոնք ավելացվել են թիվ 10 բլոկին
02:15 — ներկայացնել հանգույցը վերցրել է 350 հազար գործարք լողավազանից և ձևավորել թիվ 11 բլոկը
02:34 — թիվ 11 բլոկը ստորագրվում և ուղարկվում է այլ հանգույցներ վավերացման համար
02:51 — թիվ 11 բլոկը վավերացվում է և ուղարկվում արմատային շղթա
02:55 — վերջին հանգույցն ավարտեց գործարքները թիվ 10 բլոկում
10:59 — թիվ 9 բլոկի ներկայացմամբ գործարքը շատ երկար տևեց արմատային շղթայում, բայց այն ավարտվեց, և բոլոր հանգույցները տեղեկություն ստացան դրա մասին և սկսեցին կատարել 350 հազար գործարք:
11:05 - լողավազանը մաքրվել է 320 հազար գործարքների համար, որոնք ավելացվել են թիվ 11 բլոկին
12:10 - բոլոր հանգույցները պարունակում են 1 միլիոն 670 հազար գործարքներ և նշաններ
12:17 — ներկայացնել հանգույցը 330 հազար գործարք է վերցրել լողավազանից և ձևավորել թիվ 12 բլոկը
12:32 — թիվ 12 բլոկը ստորագրվում և ուղարկվում է այլ հանգույցներ վավերացման համար
12:39 — թիվ 12 բլոկը վավերացվում է և ուղարկվում արմատային շղթա
13:44 - բոլոր հանգույցները ստացել են տեղեկատվություն արմատային շղթայից, որ թիվ 12 բլոկը ավելացվել է և սկսում է կիրառել 330 հազար գործարք:
14:50 - բոլոր հանգույցները պարունակում են 2 միլիոն գործարքներ և նշաններ

Թեստ 3

Առաջին և երկրորդ սերվերներում մեկ վավերացնող հանգույցը փոխարինվել է ներկայացնող հանգույցով:


Սկզբնական վիճակ՝ վերջին բլոկ #84; Տվյալների բազայում պահված 0 գործարք և նշան

00:00 — Գործարկվել է 3 սցենար, որոնք ստեղծում և ուղարկում են 1 միլիոն գործարք
01:38 — Ստեղծվել է 1 միլիոն գործարք և սկսվել է ուղարկումը թիվ 3 հանգույցին
01:50 — ներկայացնել թիվ 3 հանգույցը լողավազանից վերցրել է 330 հազար գործարք և ձևավորել թիվ 85 բլոկը (f21): Մենք նաև տեսնում ենք, որ 350 վայրկյանում լողավազանում ավելացվում է 10 հազար գործարք
01:53 — Ստեղծվել է 1 միլիոն գործարք և սկսվել է ուղարկումը թիվ 1 հանգույցին
01:50 — ներկայացնել թիվ 3 հանգույցը լողավազանից վերցրել է 330 հազար գործարք և ձևավորել թիվ 85 բլոկը (f21): Մենք նաև տեսնում ենք, որ 350 վայրկյանում լողավազանում ավելացվում է 10 հազար գործարք
02:01 — ներկայացնել թիվ 1 հանգույցը լողավազանից վերցրել է 250 հազար գործարք և ձևավորել թիվ 85 բլոկը (65e)
02:06 — բլոկ #85 (f21) ստորագրվում և ուղարկվում է այլ հանգույցներ վավերացման համար
02:08 — ավարտվեց #3 սերվերի ցուցադրական սկրիպտը, որն ուղարկեց 1 միլիոն գործարք 30 վայրկյանում
02:14 — բլոկ #85 (f21) վավերացվում է և ուղարկվում արմատային շղթա
02:19 — բլոկ #85 (65e) ստորագրվում և ուղարկվում է այլ հանգույցներ վավերացման համար
02:22 — Ստեղծվել է 1 միլիոն գործարք և սկսվել է ուղարկումը թիվ 2 հանգույցին
02:27 — բլոկ #85 (65e) վավերացված և ուղարկված արմատային շղթային
02:29 — ներկայացնել թիվ 2 հանգույցը լողավազանից վերցրել է 111855 գործարք և ձևավորել թիվ 85 (256) բլոկը:
02:36 — բլոկ #85 (256) ստորագրվում և ուղարկվում է այլ հանգույցներ վավերացման համար
02:36 — ավարտվեց #1 սերվերի ցուցադրական սկրիպտը, որն ուղարկեց 1 միլիոն գործարք 42.5 վայրկյանում
02:38 — բլոկ #85 (256) վավերացվում է և ուղարկվում արմատային շղթա
03:08 — ավարտվեց սերվերի #2 սկրիպտը, որն ուղարկեց 1 միլիոն գործարք 47 վայրկյանում
03:38 - բոլոր հանգույցները ստացել են տեղեկատվություն արմատային շղթայից, որոնք ավելացվել են #85 (f21), #86(65e), #87(256) բլոկները և սկսել են կիրառել 330k, 250k, 111855 գործարքներ:
03:49 - լողավազանը մաքրվել է 330k, 250k, 111855 գործարքներով, որոնք ավելացվել են #85 (f21), #86(65e), #87(256) բլոկներին:
03:59 — ներկայացնել թիվ 1 հանգույցը վերցրել է 888145 գործարք լողավազանից և ձևավորել #88 (214) բլոկը, ներկայացնել թիվ 2 հանգույցը վերցրել է 750 հազար գործարք լողավազանից և ձևավորել թիվ 88 բլոկն (50ա), ներկայացնել թիվ 3 հանգույցը վերցրել է 670 հազար գործարք: լողավազան և ձևերի բլոկ #88 (d3b)
04:44 — բլոկ #88 (d3b) ստորագրվում և ուղարկվում է այլ հանգույցներ վավերացման համար
04:58 — բլոկ #88 (214) ստորագրվում և ուղարկվում է այլ հանգույցներ վավերացման համար
05:11 — բլոկ #88 (50a) ստորագրվում և ուղարկվում է այլ հանգույցներ վավերացման համար
05:11 — բլոկ #85 (d3b) վավերացվում է և ուղարկվում արմատային շղթա
05:36 — բլոկ #85 (214) վավերացվում է և ուղարկվում արմատային շղթա
05:43 - բոլոր հանգույցները ստացել են տեղեկատվություն արմատային շղթայից, որոնք ավելացվել են #88 (d3b), #89(214) բլոկները և սկսում են կիրառել 670k, 750k գործարքներ:
06:50 — կապի խափանման պատճառով թիվ 85 բլոկը (50ա) չի վավերացվել
06:55 — ներկայացնել թիվ 2 հանգույցը լողավազանից վերցրել է 888145 գործարք և ձևավորել թիվ 90 բլոկը (50ա)
08:14 — բլոկ #90 (50a) ստորագրվում և ուղարկվում է այլ հանգույցներ վավերացման համար
09:04 — բլոկ #90 (50a) վավերացվում է և ուղարկվում արմատային շղթա
11:23 - բոլոր հանգույցները ստացել են տեղեկատվություն արմատային շղթայից, որ ավելացվել է #90 (50a) բլոկը և սկսում են կիրառել 888145 գործարքներ: Միևնույն ժամանակ, #3 սերվերն արդեն կիրառել է գործարքներ #88 (d3b), #89(214) բլոկներից:
12:11 - բոլոր լողավազանները դատարկ են
13:41 — #3 սերվերի բոլոր հանգույցները պարունակում են 3 միլիոն գործարքներ և նշաններ
14:35 — #1 սերվերի բոլոր հանգույցները պարունակում են 3 միլիոն գործարքներ և նշաններ
19:24 — #2 սերվերի բոլոր հանգույցները պարունակում են 3 միլիոն գործարքներ և նշաններ

Խոչընդոտներ

Plasma Cash-ի մշակման ընթացքում մենք բախվել ենք հետևյալ խնդիրների հետ, որոնք աստիճանաբար լուծել և լուծում ենք.

1. Համակարգի տարբեր գործառույթների փոխազդեցության մեջ հակամարտություն: Օրինակ՝ լողավազանում գործարքներ ավելացնելու գործառույթն արգելափակեց բլոկների ներկայացման և վավերացման աշխատանքը և հակառակը, ինչը հանգեցրեց արագության նվազմանը:

2. Անմիջապես պարզ չէր, թե ինչպես կարելի է ուղարկել հսկայական քանակությամբ գործարքներ՝ նվազագույնի հասցնելով տվյալների փոխանցման ծախսերը:

3. Պարզ չէր, թե ինչպես և որտեղ պահել տվյալները՝ բարձր արդյունքների հասնելու համար։

4. Անհասկանալի էր, թե ինչպես կարելի է ցանց կազմակերպել հանգույցների միջև, քանի որ 1 միլիոն գործարքներով բլոկի չափը զբաղեցնում է մոտ 100 ՄԲ:

5. Մեկ թելային ռեժիմով աշխատելը խախտում է հանգույցների միջև կապը, երբ տեղի են ունենում երկար հաշվարկներ (օրինակ, Merkle ծառ կառուցելը և դրա հեշը հաշվարկելը):

Ինչպե՞ս վարվեցինք այս ամենի հետ։

Plasma Cash հանգույցի առաջին տարբերակը մի տեսակ կոմբինատ էր, որը կարող էր անել ամեն ինչ միաժամանակ՝ ընդունել գործարքներ, ներկայացնել և վավերացնել բլոկները և տրամադրել API տվյալների մուտք գործելու համար: Քանի որ NodeJS-ը բնիկ միաշղթա է, ծանր Merkle ծառի հաշվարկման ֆունկցիան արգելափակեց ավելացնել գործարքի գործառույթը: Մենք տեսանք այս խնդրի լուծման երկու տարբերակ.

1. Գործարկեք մի քանի NodeJS գործընթացներ, որոնցից յուրաքանչյուրը կատարում է հատուկ գործառույթներ:

2. Օգտագործեք worker_threads-ը և կոդի մի մասի կատարումը տեղափոխեք թելեր:

Արդյունքում մենք օգտագործեցինք երկու տարբերակները միաժամանակ. մենք տրամաբանորեն բաժանեցինք մեկ հանգույցը 3 մասի, որոնք կարող են աշխատել առանձին, բայց միևնույն ժամանակ սինխրոն։

1. Ներկայացման հանգույց, որն ընդունում է գործարքները լողավազանում և ստեղծում բլոկներ:

2. Վավերացնող հանգույց, որը ստուգում է հանգույցների վավերականությունը:

3. API հանգույց - ապահովում է API տվյալների մուտք գործելու համար:

Այս դեպքում դուք կարող եք միանալ յուրաքանչյուր հանգույցին unix վարդակի միջոցով՝ օգտագործելով cli:

Մենք ծանր գործողությունները, օրինակ՝ Մերկլի ծառի հաշվարկը, տեղափոխեցինք առանձին թելի մեջ:

Այսպիսով, մենք հասանք բոլոր Plasma Cash գործառույթների նորմալ աշխատանքին միաժամանակ և առանց խափանումների:

Երբ համակարգը գործարկվեց, մենք սկսեցինք փորձարկել արագությունը և, ցավոք, ստացանք անբավարար արդյունքներ՝ 5 գործարք վայրկյանում և մինչև 000 գործարք մեկ բլոկում: Ես պետք է պարզեի, թե ինչն է սխալ իրականացվել։

Սկզբից մենք սկսեցինք փորձարկել Plasma Cash-ի հետ կապի մեխանիզմը՝ պարզելու համակարգի առավելագույն հնարավորությունները: Մենք ավելի վաղ գրել էինք, որ Plasma Cash հանգույցը ապահովում է unix socket ինտերֆեյս: Սկզբում այն ​​տեքստային էր: json օբյեկտներն ուղարկվել են «JSON.parse()»-ի և «JSON.stringify()»-ի միջոցով:

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

Մենք չափեցինք նման առարկաների փոխանցման արագությունը և գտանք ~ 130k վայրկյանում։ Մենք փորձեցինք փոխարինել json-ի հետ աշխատելու ստանդարտ գործառույթները, բայց կատարումը չբարելավվեց: V8 շարժիչը պետք է լավ օպտիմիզացված լինի այդ գործողությունների համար:

Մենք դասերի միջոցով աշխատել ենք գործարքների, նշանների և բլոկների հետ: Նման դասեր ստեղծելիս կատարողականը նվազել է 2 անգամ, ինչը ցույց է տալիս, որ OOP-ը մեզ հարմար չէ։ Ես ստիպված էի ամեն ինչ վերաշարադրել զուտ ֆունկցիոնալ մոտեցմամբ:

Ձայնագրում տվյալների բազայում

Սկզբում Redis-ը ընտրվել է տվյալների պահպանման համար՝ որպես ամենաարդյունավետ լուծումներից մեկը, որը բավարարում է մեր պահանջները՝ բանալի-արժեքի պահեստավորում, աշխատանք հեշ աղյուսակների, հավաքածուների հետ: Մենք գործարկեցինք redis-benchmark-ը և ստացանք ~80 հազար գործողություններ վայրկյանում 1 խողովակաշարային ռեժիմում:

Բարձր կատարողականության համար մենք ավելի լավ լարեցինք Redis-ը.

  • Ստեղծվել է unix վարդակից միացում:
  • Մենք անջատեցինք վիճակի պահպանումը սկավառակի վրա (հուսալիության համար կարող եք ստեղծել կրկնօրինակ և պահել սկավառակի վրա առանձին Redis-ում):

Redis-ում լողավազանը հեշ աղյուսակ է, քանի որ մենք պետք է կարողանանք առբերել բոլոր գործարքները մեկ հարցումով և ջնջել գործարքները մեկ առ մեկ: Մենք փորձեցինք օգտագործել սովորական ցուցակը, բայց այն ավելի դանդաղ է, երբ բեռնաթափում է ամբողջ ցուցակը:

Ստանդարտ NodeJS-ն օգտագործելիս Redis գրադարանները հասել են վայրկյանում 18 հազար գործարքների կատարման: Արագությունն իջել է 9 անգամ։

Քանի որ հենանիշը ցույց տվեց մեզ, որ հնարավորություններն ակնհայտորեն 5 անգամ ավելի մեծ են, մենք սկսեցինք օպտիմալացնել: Մենք գրադարանը փոխեցինք ioredis-ի և ստացանք վայրկյանում 25k կատարում: Մենք մեկ առ մեկ ավելացրել ենք գործարքները՝ օգտագործելով «hset» հրամանը: Այսպիսով, մենք բազմաթիվ հարցումներ էինք առաջացնում Redis-ում: Գաղափարն առաջացավ գործարքները միավորել խմբաքանակների մեջ և ուղարկել դրանք մեկ հրամանով՝ «hmset»: Արդյունքը վայրկյանում 32 հազար է:

Մի քանի պատճառներով, որոնք մենք կնկարագրենք ստորև, մենք աշխատում ենք տվյալների հետ՝ օգտագործելով «Buffer»-ը և, ինչպես պարզվում է, եթե դրանք փոխակերպեք տեքստի («buffer.toString('hex')»), նախքան գրելը, կարող եք լրացուցիչ ստանալ: կատարումը։ Այսպիսով, արագությունը հասցվեց վայրկյանում 35 հազարի: Այս պահին մենք որոշեցինք կասեցնել հետագա օպտիմալացումը։

Մենք ստիպված էինք անցնել երկուական արձանագրության, քանի որ.

1. Համակարգը հաճախ հաշվարկում է հեշեր, ստորագրություններ և այլն, և դրա համար անհրաժեշտ են տվյալներ «Բուֆերում»:

2. Ծառայությունների միջև ուղարկվելիս երկուական տվյալները ավելի քիչ են կշռում, քան տեքստը: Օրինակ, 1 միլիոն գործարքներով բլոկ ուղարկելիս տեքստի տվյալները կարող են զբաղեցնել ավելի քան 300 մեգաբայթ:

3. Տվյալների անընդհատ փոխակերպումն ազդում է կատարողականի վրա:

Հետևաբար, մենք հիմք ընդունեցինք տվյալների պահպանման և փոխանցման մեր սեփական երկուական արձանագրությունը, որը մշակվել է հրաշալի «երկուական տվյալների» գրադարանի հիման վրա:

Արդյունքում մենք ստացանք հետևյալ տվյալների կառուցվածքները.

- Գործարք

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

- Նշան

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

- Արգելափակել

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

Սովորական «BD.encode(block, Protocol).slice();» և «BD.decode(buffer, Protocol)» հրամաններով մենք տվյալները փոխակերպում ենք «Buffer»-ի՝ Redis-ում պահելու կամ մեկ այլ հանգույց փոխանցելու և առբերելու համար: տվյալների վերադարձը:

Մենք ունենք նաև 2 երկուական արձանագրություն՝ ծառայությունների միջև տվյալների փոխանցման համար.

— Արձանագրություն պլազմային հանգույցի հետ փոխազդեցության համար՝ 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)
  }
  ```

Որտեղ:

  • «տեսակ». — կատարվող գործողությունը, օրինակ, 1 — sendTransaction, 2 — getTransaction;
  • «բեռնատար»: — տվյալներ, որոնք պետք է փոխանցվեն համապատասխան գործառույթին.
  • «messageId»: — հաղորդագրության id, որպեսզի պատասխանը հնարավոր լինի նույնականացնել:

- Հանգույցների միջև փոխազդեցության արձանագրություն

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

Որտեղ:

  • «կոդ». — հաղորդագրության կոդը, օրինակ 6 — PREPARE_NEW_BLOCK, 7 — BLOCK_VALID, 8 — BLOCK_COMMIT;
  • «versionProtocol». — արձանագրության տարբերակ, քանի որ տարբեր տարբերակներով հանգույցները կարող են բարձրացվել ցանցում և կարող են տարբեր կերպ աշխատել.
  • «հաջորդություն». - հաղորդագրության նույնացուցիչ;
  • «countChunk»: и «chunk Number»: անհրաժեշտ է մեծ հաղորդագրությունները բաժանելու համար.
  • «երկարություն». и «բեռնատար»: երկարությունը և ինքնին տվյալները:

Քանի որ մենք նախապես մուտքագրել ենք տվյալները, վերջնական համակարգը շատ ավելի արագ է, քան Ethereum-ի «rlp» գրադարանը: Ցավոք, մենք դեռ չենք կարողացել հրաժարվել դրանից, քանի որ անհրաժեշտ է վերջնական տեսքի բերել խելացի պայմանագիրը, որը մենք նախատեսում ենք անել ապագայում։

Եթե ​​մեզ հաջողվեր հասնել արագության 35 000 գործարքները վայրկյանում, մենք նույնպես պետք է դրանք մշակենք օպտիմալ ժամանակում: Քանի որ բլոկի ձևավորման մոտավոր ժամանակը տևում է 30 վայրկյան, մենք պետք է ներառենք բլոկի մեջ 1 000 000 գործարքներ, ինչը նշանակում է ուղարկել ավելին 100 ՄԲ տվյալներ:

Սկզբում մենք օգտագործում էինք «ethereumjs-devp2p» գրադարանը՝ հանգույցների միջև հաղորդակցվելու համար, բայց այն չէր կարող այդքան շատ տվյալներ կառավարել: Արդյունքում մենք օգտագործեցինք «ws» գրադարանը և կազմաձևեցինք երկուական տվյալների ուղարկումը websocket-ի միջոցով: Իհարկե, մենք խնդիրներ ենք ունեցել նաև մեծ տվյալների փաթեթներ ուղարկելիս, բայց մենք դրանք բաժանել ենք կտորների և այժմ այդ խնդիրները վերացել են:

Նաև Merkle ծառի ձևավորում և հաշի հաշվարկ 1 000 000 գործարքները պահանջում են մոտ 10 շարունակական հաշվարկի վայրկյաններ: Այս ընթացքում բոլոր հանգույցների հետ կապը կարողանում է խզվել։ Որոշվեց այս հաշվարկը տեղափոխել առանձին թեմա։

Եզրակացություններ:

Իրականում, մեր բացահայտումները նորություն չեն, բայց ինչ-ինչ պատճառներով շատ փորձագետներ մոռանում են դրանց մասին՝ մշակելիս:

  • Օբյեկտ-կողմնորոշված ​​ծրագրավորման փոխարեն ֆունկցիոնալ ծրագրավորման օգտագործումը բարելավում է արտադրողականությունը:
  • Մոնոլիտը ավելի վատ է, քան սպասարկման ճարտարապետությունը արդյունավետ NodeJS համակարգի համար:
  • Ծանր հաշվարկների համար «worker_threads»-ի օգտագործումը բարելավում է համակարգի պատասխանունակությունը, հատկապես երբ գործ ունենք i/o գործառնությունների հետ:
  • unix վարդակն ավելի կայուն է և ավելի արագ, քան http հարցումները:
  • Եթե ​​Ձեզ անհրաժեշտ է արագորեն մեծ տվյալներ փոխանցել ցանցով, ապա ավելի լավ է օգտագործել վեբսոկետներ և ուղարկել երկուական տվյալներ՝ բաժանված կտորների, որոնք կարող են փոխանցվել, եթե դրանք չժամանեն, այնուհետև միավորվեն մեկ հաղորդագրության մեջ:

Հրավիրում ենք այցելել GitHub նախագիծ: https://github.com/opporty-com/Plasma-Cash/tree/new-version

Հոդվածը համահեղինակվել է Ալեքսանդր Նաշիվան, ավագ ծրագրավորող Clever Solution Inc.

Source: www.habr.com

Добавить комментарий