تست عمومی: راه حلی برای حفظ حریم خصوصی و مقیاس پذیری در اتریوم

بلاکچین یک فناوری نوآورانه است که نویدبخش بهبود بسیاری از زمینه های زندگی انسان است. فرآیندها و محصولات واقعی را به فضای دیجیتال منتقل می کند، سرعت و قابلیت اطمینان تراکنش های مالی را تضمین می کند، هزینه آنها را کاهش می دهد و همچنین به شما امکان می دهد برنامه های مدرن DAPP را با استفاده از قراردادهای هوشمند در شبکه های غیرمتمرکز ایجاد کنید.

با توجه به مزایا و کاربردهای متنوع بلاک چین، ممکن است تعجب برانگیز به نظر برسد که این فناوری امیدوارکننده هنوز به هر صنعتی راه پیدا نکرده است. مشکل این است که بلاک چین های غیرمتمرکز مدرن فاقد مقیاس پذیری هستند. اتریوم در هر ثانیه حدود 20 تراکنش را پردازش می کند که برای پاسخگویی به نیازهای کسب و کارهای پویا امروزی کافی نیست. در عین حال، شرکت‌هایی که از فناوری بلاک چین استفاده می‌کنند، به دلیل محافظت بالای اتریوم در برابر هک و شکست شبکه، در ترک اتریوم مردد هستند.

تیم توسعه برای اطمینان از تمرکززدایی، امنیت و مقیاس پذیری در بلاک چین، در نتیجه حل مشکل مقیاس پذیری فرصت Plasma Cash را ایجاد کرد، یک زنجیره فرعی متشکل از یک قرارداد هوشمند و یک شبکه خصوصی مبتنی بر Node.js، که به صورت دوره ای حالت خود را به زنجیره ریشه (اتریوم) منتقل می کند.

تست عمومی: راه حلی برای حفظ حریم خصوصی و مقیاس پذیری در اتریوم

فرآیندهای کلیدی در نقد پلاسما

1. کاربر تابع قرارداد هوشمند را "سپرده" می نامد و مقدار ETH را که می خواهد به توکن Plasma Cash واریز کند به آن منتقل می کند. تابع قرارداد هوشمند یک نشانه ایجاد می کند و یک رویداد در مورد آن ایجاد می کند.

2. گره‌های نقدی پلاسما که در رویدادهای قرارداد هوشمند مشترک شده‌اند، رویدادی درباره ایجاد سپرده دریافت می‌کنند و تراکنشی درباره ایجاد توکن به استخر اضافه می‌کنند.

3. به صورت دوره ای، گره های ویژه Plasma Cash تمام تراکنش ها را از استخر (تا 1 میلیون) می گیرند و یک بلوک از آنها تشکیل می دهند، درخت Merkle و بر این اساس، هش را محاسبه می کنند. این بلوک برای تایید به گره های دیگر ارسال می شود. گره ها بررسی می کنند که آیا هش Merkle معتبر است و آیا تراکنش ها معتبر هستند (به عنوان مثال، آیا فرستنده توکن صاحب آن است یا خیر). پس از تأیید بلوک، گره تابع «submitBlock» قرارداد هوشمند را فراخوانی می‌کند که شماره بلوک و هش Merkle را در زنجیره لبه ذخیره می‌کند. قرارداد هوشمند رویدادی را ایجاد می کند که نشان دهنده اضافه شدن موفقیت آمیز یک بلوک است. تراکنش ها از استخر حذف می شوند.

4. گره هایی که رویداد ارسال بلوک را دریافت می کنند شروع به اعمال تراکنش هایی می کنند که به بلوک اضافه شده اند.

5. در برخی مواقع، مالک (یا غیرمالک) توکن می خواهد آن را از پلاسما کش برداشت کند. برای انجام این کار، تابع «startExit» را فراخوانی می‌کند و اطلاعات 2 تراکنش آخر روی توکن را به آن منتقل می‌کند که تأیید می‌کند که او مالک توکن است. قرارداد هوشمند، با استفاده از هش Merkle، وجود تراکنش‌ها را در بلوک‌ها بررسی می‌کند و توکن را برای برداشت ارسال می‌کند که در عرض دو هفته اتفاق می‌افتد.

6. اگر عملیات برداشت توکن با تخلف صورت گرفته باشد (توکن پس از شروع مراحل برداشت خرج شده است یا قبل از برداشت توکن متعلق به شخص دیگری بوده است)، صاحب توکن می‌تواند ظرف دو هفته برداشت را رد کند.

تست عمومی: راه حلی برای حفظ حریم خصوصی و مقیاس پذیری در اتریوم

حریم خصوصی از دو طریق به دست می آید

1. زنجیره ریشه در مورد تراکنش هایی که در زنجیره فرزند تولید و ارسال می شوند چیزی نمی داند. اطلاعات در مورد افرادی که ETH را از Plasma Cash واریز و برداشت کرده اند عمومی است.

2. زنجیره فرزند امکان تراکنش های ناشناس را با استفاده از zk-SNARK می دهد.

پشته فناوری

  • NodeJS
  • Redis
  • من Etheria
  • خاک

آزمایش

هنگام توسعه Plasma Cash، سرعت سیستم را آزمایش کردیم و نتایج زیر را به دست آوردیم:

  • حداکثر 35 تراکنش در ثانیه به استخر اضافه می شود.
  • حداکثر 1 تراکنش را می توان در یک بلوک ذخیره کرد.

تست ها بر روی 3 سرور زیر انجام شد:

1. Intel Core i7-6700 Quad-Core Skylake incl. NVMe SSD – 512 گیگابایت، 64 گیگابایت رم DDR4
3 گره نقدی پلاسما اعتبار سنجی ایجاد شد.

2. AMD Ryzen 7 1700X Octa-Core "Summit Ridge" (Zen)، SATA SSD – 500 گیگابایت، 64 گیگابایت رم DDR4
گره ETH شبکه آزمایشی Ropsten مطرح شد.
3 گره نقدی پلاسما اعتبار سنجی ایجاد شد.

3. Intel Core i9-9900K Octa-Core incl. NVMe SSD – 1 ترابایت، 64 گیگابایت رم DDR4
1 گره ارسال نقدی پلاسما مطرح شد.
3 گره نقدی پلاسما اعتبار سنجی ایجاد شد.
آزمایشی برای افزودن تراکنش ها به شبکه پلاسما کش راه اندازی شد.

مجموع: 10 گره نقدی پلاسما در یک شبکه خصوصی.

تست 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 به زنجیره ریشه اضافه شده است و شروع به انجام 760k تراکنش کردند.
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 (50a) را تشکیل داد، گره شماره 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 (50a) اعتبارسنجی نشد
06:55 — ارسال گره شماره 2 888145 تراکنش را از استخر گرفته و بلوک شماره 90 را تشکیل می دهد (50a)
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. کار در حالت تک رشته ای ارتباط بین گره ها را زمانی که محاسبات طولانی انجام می شود قطع می کند (به عنوان مثال، ساختن درخت مرکل و محاسبه هش آن).

چگونه با این همه برخورد کردیم؟

اولین نسخه از گره نقدی پلاسما نوعی ترکیب بود که می توانست همه کارها را همزمان انجام دهد: پذیرش تراکنش ها، ارسال و اعتبارسنجی بلوک ها و ارائه یک API برای دسترسی به داده ها. از آنجایی که NodeJS به طور بومی تک رشته ای است، تابع محاسبه درخت مرکل سنگین، تابع تراکنش افزودن را مسدود کرد. ما دو گزینه برای حل این مشکل دیدیم:

1. چندین فرآیند NodeJS را راه اندازی کنید که هر کدام عملکردهای خاصی را انجام می دهند.

2. از worker_threads استفاده کنید و اجرای بخشی از کد را به رشته ها منتقل کنید.

در نتیجه، ما از هر دو گزینه به طور همزمان استفاده کردیم: ما به طور منطقی یک گره را به 3 قسمت تقسیم کردیم که می توانند جداگانه کار کنند، اما در همان زمان به طور همزمان

1. گره ارسال، که تراکنش ها را در استخر می پذیرد و بلوک ها را ایجاد می کند.

2. یک گره اعتبار سنجی که اعتبار گره ها را بررسی می کند.

3. گره API - یک API برای دسترسی به داده ها فراهم می کند.

در این حالت می توانید با استفاده از cli از طریق یک سوکت یونیکس به هر گره متصل شوید.

ما عملیات های سنگین، مانند محاسبه درخت مرکل را به یک رشته جداگانه منتقل کردیم.

بنابراین، ما به عملکرد طبیعی همه عملکردهای نقدی پلاسما به طور همزمان و بدون خرابی دست یافته ایم.

هنگامی که سیستم کار کرد، ما شروع به آزمایش سرعت کردیم و متأسفانه نتایج نامناسبی دریافت کردیم: 5 تراکنش در ثانیه و حداکثر 000 تراکنش در هر بلوک. باید بفهمم چه چیزی اشتباه اجرا شده است.

برای شروع، ما شروع به آزمایش مکانیسم ارتباط با Plasma Cash برای پی بردن به حداکثر توانایی سیستم کردیم. قبلاً نوشتیم که گره نقدی پلاسما یک رابط سوکت یونیکس را فراهم می کند. در ابتدا مبتنی بر متن بود. اشیاء 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 را دقیق تر تنظیم کردیم:

  • یک اتصال سوکت یونیکس برقرار شده است.
  • ذخیره وضعیت در دیسک را غیرفعال کردیم (برای اطمینان، می‌توانید یک کپی تنظیم کنید و در یک Redis جداگانه روی دیسک ذخیره کنید).

در Redis، pool یک جدول هش است زیرا باید بتوانیم تمام تراکنش ها را در یک کوئری بازیابی کنیم و تراکنش ها را یکی یکی حذف کنیم. ما سعی کردیم از یک لیست معمولی استفاده کنیم، اما هنگام تخلیه کل لیست کندتر است.

هنگام استفاده از NodeJS استاندارد، کتابخانه های Redis به عملکرد 18 هزار تراکنش در ثانیه دست یافتند. سرعت 9 برابر کاهش یافت.

از آنجایی که معیار به ما نشان داد که احتمالات به وضوح 5 برابر بیشتر است، ما شروع به بهینه سازی کردیم. ما کتابخانه را به ioredis تغییر دادیم و عملکرد 25k در ثانیه داشتیم. ما تراکنش ها را یکی یکی با استفاده از دستور «hset» اضافه کردیم. بنابراین ما در ردیس پرس و جوهای زیادی ایجاد می کردیم. ایده ترکیب تراکنش ها در دسته ها و ارسال آنها با یک دستور "hmset" بوجود آمد. نتیجه 32 هزار در ثانیه است.

به دلایل متعددی که در زیر توضیح خواهیم داد، ما با داده‌ها با استفاده از «Buffer» کار می‌کنیم و همانطور که مشخص است، اگر قبل از نوشتن آن‌ها را به متن («buffer.toString('hex')») تبدیل کنید، می‌توانید اطلاعات اضافی دریافت کنید. کارایی. بنابراین، سرعت به 35k در ثانیه افزایش یافت. در حال حاضر، ما تصمیم گرفتیم که بهینه سازی بیشتر را متوقف کنیم.

ما مجبور شدیم به یک پروتکل باینری سوئیچ کنیم زیرا:

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 (بافر، پروتکل)» داده ها را برای ذخیره در Redis یا ارسال به گره دیگر و بازیابی آن به «Buffer» تبدیل می کنیم. داده ها

ما همچنین 2 پروتکل باینری برای انتقال داده بین سرویس ها داریم:

- پروتکل تعامل با نود پلاسما از طریق سوکت یونیکس

  ```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.
  • "بارگیری". - داده هایی که باید به تابع مناسب منتقل شوند.
  • "شناسه پیام". - شناسه پیام برای اینکه بتوان پاسخ را شناسایی کرد.

- پروتکل برای تعامل بین گره ها

  ```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؛
  • "پروتکل نسخه". - نسخه پروتکل، زیرا گره هایی با نسخه های مختلف را می توان در شبکه افزایش داد و آنها می توانند متفاوت کار کنند.
  • دنباله - شناسه پیام؛
  • "countChunk". и "شماره تکه". برای تقسیم پیام های بزرگ ضروری است.
  • "طول". и "بارگیری". طول و خود داده

از آنجایی که ما داده ها را از قبل تایپ کردیم، سیستم نهایی بسیار سریعتر از کتابخانه «rlp» اتریوم است. متأسفانه ما هنوز نتوانسته ایم از آن خودداری کنیم، زیرا لازم است قرارداد هوشمند نهایی شود که در آینده قصد داریم انجام دهیم.

اگر توانستیم به سرعت برسیم 35 000 تراکنش ها در ثانیه، ما همچنین باید آنها را در زمان بهینه پردازش کنیم. از آنجایی که زمان تقریبی تشکیل بلوک 30 ثانیه طول می کشد، باید آن را در بلوک قرار دهیم 1 000 000 معاملات، که به معنای ارسال بیشتر است 100 مگابایت داده

در ابتدا، ما از کتابخانه «ethereumjs-devp2p» برای برقراری ارتباط بین گره‌ها استفاده می‌کردیم، اما نمی‌توانست داده‌های زیادی را مدیریت کند. در نتیجه، ما از کتابخانه «ws» استفاده کردیم و ارسال داده های باینری را از طریق وب سوکت پیکربندی کردیم. البته در ارسال بسته های داده بزرگ نیز با مشکلاتی مواجه شدیم، اما آنها را به تکه هایی تقسیم کردیم و اکنون این مشکلات برطرف شده است.

همچنین تشکیل درخت مرکل و محاسبه هش 1 000 000 معاملات نیاز به در مورد 10 ثانیه محاسبه مداوم در طول این مدت، اتصال با تمام گره‌ها قطع می‌شود. تصمیم گرفته شد که این محاسبه به یک موضوع جداگانه منتقل شود.

نتیجه گیری:

در واقع، یافته های ما جدید نیست، اما به دلایلی بسیاری از کارشناسان در هنگام توسعه آنها را فراموش می کنند.

  • استفاده از برنامه نویسی تابعی به جای برنامه نویسی شی گرا بهره وری را بهبود می بخشد.
  • یکپارچگی بدتر از یک معماری سرویس برای یک سیستم NodeJS مولد است.
  • استفاده از «worker_threads» برای محاسبات سنگین، پاسخگویی سیستم را بهبود می‌بخشد، به‌ویژه هنگامی که با عملیات i/o سروکار داریم.
  • سوکت یونیکس پایدارتر و سریعتر از درخواست های http است.
  • اگر نیاز به انتقال سریع داده‌های بزرگ از طریق شبکه دارید، بهتر است از سوکت‌های وب استفاده کنید و داده‌های باینری را که به تکه‌هایی تقسیم می‌شوند، ارسال کنید، که در صورت عدم رسیدن می‌توان آنها را فوروارد کرد و سپس در یک پیام ترکیب کرد.

شما را به بازدید دعوت می کنیم GitHub پروژه: https://github.com/opporty-com/Plasma-Cash/tree/new-version

مقاله توسط مشترک نوشته شده است الکساندر نشیوان، توسعه دهنده ارشد شرکت راه حل هوشمند.

منبع: www.habr.com

اضافه کردن نظر