Thử nghiệm công khai: Giải pháp về quyền riêng tư và khả năng mở rộng trên Ethereum

Chuỗi khối là một công nghệ tiên tiến hứa hẹn sẽ cải thiện nhiều lĩnh vực của đời sống con người. Nó chuyển các quy trình và sản phẩm thực vào không gian kỹ thuật số, đảm bảo tốc độ và độ tin cậy của các giao dịch tài chính, giảm chi phí và cũng cho phép bạn tạo các ứng dụng DAPP hiện đại bằng cách sử dụng hợp đồng thông minh trong mạng phi tập trung.

Với nhiều lợi ích và ứng dụng đa dạng của blockchain, có vẻ đáng ngạc nhiên khi công nghệ đầy hứa hẹn này vẫn chưa được áp dụng vào mọi ngành công nghiệp. Vấn đề là các blockchain phi tập trung hiện đại thiếu khả năng mở rộng. Ethereum xử lý khoảng 20 giao dịch mỗi giây, không đủ để đáp ứng nhu cầu của các doanh nghiệp năng động ngày nay. Đồng thời, các công ty sử dụng công nghệ blockchain đang do dự từ bỏ Ethereum do mức độ bảo vệ cao khỏi bị hack và lỗi mạng.

Để đảm bảo tính phân cấp, bảo mật và khả năng mở rộng trong chuỗi khối, từ đó giải quyết Bộ ba bất khả thi về khả năng mở rộng, nhóm phát triển Có cơ hội đã tạo ra Plasma Cash, một chuỗi con bao gồm hợp đồng thông minh và mạng riêng dựa trên Node.js, định kỳ chuyển trạng thái của nó sang chuỗi gốc (Ethereum).

Thử nghiệm công khai: Giải pháp về quyền riêng tư và khả năng mở rộng trên Ethereum

Các quy trình chính trong Plasma Cash

1. Người dùng gọi chức năng hợp đồng thông minh là "tiền gửi", chuyển vào đó số lượng ETH mà anh ta muốn gửi vào mã thông báo Plasma Cash. Chức năng hợp đồng thông minh tạo mã thông báo và tạo sự kiện về nó.

2. Các nút Plasma Cash đã đăng ký các sự kiện hợp đồng thông minh sẽ nhận được một sự kiện về việc tạo tiền gửi và thêm giao dịch về việc tạo mã thông báo vào nhóm.

3. Theo định kỳ, các nút Plasma Cash đặc biệt sẽ lấy tất cả các giao dịch từ nhóm (lên tới 1 triệu) và tạo thành một khối từ chúng, tính toán cây Merkle và theo đó là hàm băm. Khối này được gửi đến các nút khác để xác minh. Các nút kiểm tra xem hàm băm Merkle có hợp lệ hay không và liệu các giao dịch có hợp lệ hay không (ví dụ: người gửi mã thông báo có phải là chủ sở hữu của nó hay không). Sau khi xác minh khối, nút gọi hàm `submitBlock` của hợp đồng thông minh, hàm này sẽ lưu số khối và hàm băm Merkle vào chuỗi cạnh. Hợp đồng thông minh tạo ra một sự kiện cho biết việc bổ sung thành công một khối. Các giao dịch được xóa khỏi nhóm.

4. Các nút nhận được sự kiện gửi khối bắt đầu áp dụng các giao dịch đã được thêm vào khối.

5. Tại một thời điểm nào đó, chủ sở hữu (hoặc không phải chủ sở hữu) mã thông báo muốn rút nó khỏi Plasma Cash. Để làm điều này, anh ta gọi hàm `startExit`, chuyển vào đó thông tin về 2 giao dịch cuối cùng trên mã thông báo, xác nhận rằng anh ta là chủ sở hữu của mã thông báo. Hợp đồng thông minh, sử dụng hàm băm Merkle, kiểm tra sự hiện diện của các giao dịch trong các khối và gửi mã thông báo để rút tiền, việc này sẽ diễn ra sau hai tuần.

6. Nếu hoạt động rút mã thông báo xảy ra vi phạm (mã thông báo đã được sử dụng sau khi thủ tục rút tiền bắt đầu hoặc mã thông báo đã là của người khác trước khi rút), chủ sở hữu mã thông báo có thể từ chối việc rút tiền trong vòng hai tuần.

Thử nghiệm công khai: Giải pháp về quyền riêng tư và khả năng mở rộng trên Ethereum

Quyền riêng tư đạt được theo hai cách

1. Chuỗi gốc không biết gì về các giao dịch được tạo và chuyển tiếp trong chuỗi con. Thông tin về người gửi và rút ETH từ Plasma Cash vẫn được công khai.

2. Chuỗi con cho phép các giao dịch ẩn danh sử dụng zk-SNARK.

ngăn xếp công nghệ

  • NodeJS
  • Redis
  • Etherium
  • Sild

Kiểm tra

Trong khi phát triển Plasma Cash, chúng tôi đã kiểm tra tốc độ của hệ thống và thu được kết quả như sau:

  • lên tới 35 giao dịch mỗi giây được thêm vào nhóm;
  • lên tới 1 giao dịch có thể được lưu trữ trong một khối.

Các thử nghiệm đã được thực hiện trên 3 máy chủ sau:

1. Bao gồm Skylake lõi tứ Intel Core i7-6700. SSD NVMe – RAM DDR512 64 GB, 4 GB
3 nút Plasma Cash xác thực đã được nâng lên.

2. AMD Ryzen 7 1700X Octa-Core “Summit Ridge” (Zen), SSD SATA – RAM 500 GB, 64 GB DDR4
Nút ETH của mạng thử nghiệm Ropsten đã được nâng lên.
3 nút Plasma Cash xác thực đã được nâng lên.

3. Bao gồm Intel Core i9-9900K Octa-Core. SSD NVMe – RAM 1 TB, 64 GB DDR4
1 nút gửi Tiền mặt Plasma đã được nâng lên.
3 nút Plasma Cash xác thực đã được nâng lên.
Một thử nghiệm đã được triển khai để thêm giao dịch vào mạng Plasma Cash.

Tổng số: 10 nút Plasma Cash trong mạng riêng.

Kiểm tra 1

Có giới hạn 1 triệu giao dịch trên mỗi khối. Do đó, 1 triệu giao dịch được chia thành 2 khối (vì hệ thống quản lý việc tham gia các giao dịch và gửi trong khi chúng đang được gửi).


Trạng thái ban đầu: khối cuối cùng # 7; 1 triệu giao dịch và mã thông báo được lưu trữ trong cơ sở dữ liệu.

00:00 — bắt đầu tập lệnh tạo giao dịch
01:37 - 1 triệu giao dịch đã được tạo và bắt đầu gửi đến nút
01:46 — nút gửi đã thực hiện 240k giao dịch từ nhóm và tạo khối số 8. Chúng tôi cũng thấy rằng các giao dịch 320k được thêm vào nhóm sau 10 giây
01:58 — khối số 8 được ký và gửi để xác thực
02:03 — khối số 8 được xác thực và chức năng `submitBlock` của hợp đồng thông minh được gọi với số khối và hàm băm Merkle
02:10 - tập lệnh demo đã hoàn tất hoạt động, gửi 1 triệu giao dịch trong 32 giây
02:33 - các nút bắt đầu nhận được thông tin rằng khối số 8 đã được thêm vào chuỗi gốc và bắt đầu thực hiện các giao dịch 240k
02:40 - 240k giao dịch đã bị xóa khỏi nhóm vốn đã nằm trong khối số 8
02:56 — nút gửi đã lấy 760k giao dịch còn lại từ nhóm và bắt đầu tính toán hàm băm Merkle và khối ký số 9
03:20 - tất cả các nút chứa 1 triệu giao dịch và mã thông báo 240k
03:35 - khối số 9 được ký và gửi để xác thực đến các nút khác
03:41 - xảy ra lỗi mạng
04:40 — việc chờ xác thực khối số 9 đã hết thời gian chờ
04:54 — nút gửi đã lấy 760k giao dịch còn lại từ nhóm và bắt đầu tính toán hàm băm Merkle và khối ký số 9
05:32 - khối số 9 được ký và gửi để xác thực đến các nút khác
05:53 — khối số 9 được xác thực và gửi đến chuỗi gốc
06:17 - các nút bắt đầu nhận được thông tin rằng khối số 9 đã được thêm vào chuỗi gốc và bắt đầu thực hiện các giao dịch 760k
06:47 — nhóm đã xóa các giao dịch trong khối số 9
09:06 - tất cả các nút chứa 2 triệu giao dịch và mã thông báo

Kiểm tra 2

Có giới hạn 350k mỗi khối. Kết quả là chúng ta có 3 khối.


Trạng thái ban đầu: khối cuối cùng # 9; 2 triệu giao dịch và mã thông báo được lưu trữ trong cơ sở dữ liệu

00:00 — tập lệnh tạo giao dịch đã được khởi chạy
00:44 - 1 triệu giao dịch đã được tạo và bắt đầu gửi đến nút
00:56 — nút gửi đã thực hiện 320k giao dịch từ nhóm và tạo khối số 10. Chúng tôi cũng thấy rằng các giao dịch 320k được thêm vào nhóm sau 10 giây
01:12 — khối số 10 được ký và gửi đến các nút khác để xác thực
01:18 - tập lệnh demo đã hoàn tất hoạt động, gửi 1 triệu giao dịch trong 34 giây
01:20 — khối số 10 được xác thực và gửi đến chuỗi gốc
01:51 - tất cả các nút đã nhận được thông tin từ chuỗi gốc rằng khối số 10 đã được thêm vào và bắt đầu áp dụng các giao dịch 320k
02:01 - nhóm đã xóa các giao dịch 320k được thêm vào khối số 10
02:15 — nút gửi đã thực hiện 350k giao dịch từ nhóm và tạo khối số 11
02:34 - khối số 11 được ký và gửi đến các nút khác để xác thực
02:51 — khối số 11 được xác thực và gửi đến chuỗi gốc
02:55 — nút cuối cùng đã hoàn thành giao dịch từ khối số 10
10:59 — giao dịch gửi khối số 9 mất rất nhiều thời gian trong chuỗi gốc, nhưng nó đã được hoàn thành và tất cả các nút đều nhận được thông tin về nó và bắt đầu thực hiện các giao dịch 350k
11:05 - nhóm đã xóa các giao dịch 320k được thêm vào khối số 11
12:10 - tất cả các nút chứa 1 triệu giao dịch và mã thông báo 670k
12:17 — nút gửi đã thực hiện 330k giao dịch từ nhóm và tạo khối #12
12:32 - khối số 12 được ký và gửi đến các nút khác để xác thực
12:39 — khối số 12 được xác thực và gửi đến chuỗi gốc
13:44 - tất cả các nút đã nhận được thông tin từ chuỗi gốc rằng khối số 12 đã được thêm vào và bắt đầu áp dụng các giao dịch 330k
14:50 - tất cả các nút chứa 2 triệu giao dịch và mã thông báo

Kiểm tra 3

Trong máy chủ thứ nhất và thứ hai, một nút xác thực đã được thay thế bằng nút gửi.


Trạng thái ban đầu: khối cuối cùng #84; 0 giao dịch và mã thông báo được lưu trong cơ sở dữ liệu

00:00 — 3 tập lệnh đã được khởi chạy, mỗi tập lệnh tạo và gửi 1 triệu giao dịch
01:38 — 1 triệu giao dịch đã được tạo và quá trình gửi tới nút số 3 đã bắt đầu
01:50 — nút gửi số 3 đã thực hiện 330 nghìn giao dịch từ nhóm và tạo khối số 85 (f21). Chúng tôi cũng thấy rằng 350k giao dịch được thêm vào nhóm trong 10 giây
01:53 — 1 triệu giao dịch đã được tạo và quá trình gửi tới nút số 1 đã bắt đầu
01:50 — nút gửi số 3 đã thực hiện 330 nghìn giao dịch từ nhóm và tạo khối số 85 (f21). Chúng tôi cũng thấy rằng 350k giao dịch được thêm vào nhóm trong 10 giây
02:01 — gửi nút số 1 đã thực hiện 250k giao dịch từ nhóm và tạo khối số 85 (65e)
02:06 — khối #85 (f21) được ký và gửi đến các nút khác để xác thực
02:08 — tập lệnh demo của máy chủ số 3, đã gửi 1 triệu giao dịch trong 30 giây, đã hoạt động xong
02:14 — khối #85 (f21) được xác thực và gửi đến chuỗi gốc
02:19 — khối #85 (65e) được ký và gửi đến các nút khác để xác thực
02:22 — 1 triệu giao dịch đã được tạo và quá trình gửi tới nút số 2 đã bắt đầu
02:27 — khối #85 (65e) được xác thực và gửi đến chuỗi gốc
02:29 — gửi nút số 2 đã thực hiện 111855 giao dịch từ nhóm và tạo khối số 85 (256).
02:36 — khối #85 (256) được ký và gửi đến các nút khác để xác thực
02:36 — tập lệnh demo của máy chủ số 1, đã gửi 1 triệu giao dịch trong 42.5 giây, đã hoạt động xong
02:38 — khối #85 (256) được xác thực và gửi đến chuỗi gốc
03:08 — tập lệnh máy chủ số 2 đã hoàn tất hoạt động, gửi 1 triệu giao dịch trong 47 giây
03:38 - tất cả các nút nhận được thông tin từ chuỗi gốc các khối #85 (f21), #86(65e), #87(256) đã được thêm vào và bắt đầu áp dụng các giao dịch 330k, 250k, 111855
03:49 - pool đã bị xóa ở mức 330k, 250k, 111855 giao dịch được thêm vào khối #85 (f21), #86(65e), #87(256)
03:59 — gửi nút số 1 đã nhận 888145 giao dịch từ nhóm và khối biểu mẫu số 88 (214), gửi nút số 2 đã nhận 750k giao dịch từ nhóm và khối biểu mẫu số 88 (50a), gửi nút số 3 đã lấy 670k giao dịch từ khối nhóm và biểu mẫu #88 (d3b)
04:44 — khối #88 (d3b) được ký và gửi đến các nút khác để xác thực
04:58 — khối #88 (214) được ký và gửi đến các nút khác để xác thực
05:11 — khối #88 (50a) được ký và gửi đến các nút khác để xác thực
05:11 — khối #85 (d3b) được xác thực và gửi đến chuỗi gốc
05:36 — khối #85 (214) được xác thực và gửi đến chuỗi gốc
05:43 - tất cả các nút nhận được thông tin từ chuỗi gốc có khối #88 (d3b), #89(214) đã được thêm vào và đang bắt đầu áp dụng các giao dịch 670k, 750k
06:50 — do lỗi giao tiếp, khối số 85 (50a) không được xác thực
06:55 — nút gửi số 2 đã thực hiện 888145 giao dịch từ nhóm và tạo khối số 90 (50a)
08:14 — khối #90 (50a) được ký và gửi đến các nút khác để xác thực
09:04 — khối #90 (50a) được xác thực và gửi đến chuỗi gốc
11:23 - tất cả các nút đã nhận được thông tin từ chuỗi gốc rằng khối số 90 (50a) đã được thêm vào và bắt đầu áp dụng 888145 giao dịch. Đồng thời, máy chủ số 3 đã áp dụng các giao dịch từ các khối #88 (d3b), #89(214)
12:11 - tất cả các hồ đều trống
13:41 — tất cả các nút của máy chủ số 3 chứa 3 triệu giao dịch và mã thông báo
14:35 — tất cả các nút của máy chủ số 1 chứa 3 triệu giao dịch và mã thông báo
19:24 — tất cả các nút của máy chủ số 2 chứa 3 triệu giao dịch và mã thông báo

Chướng ngại vật

Trong quá trình phát triển Plasma Cash, chúng tôi đã gặp phải các vấn đề sau mà chúng tôi đã dần dần giải quyết và đang giải quyết:

1. Xung đột trong sự tương tác của các chức năng hệ thống khác nhau. Ví dụ: chức năng thêm giao dịch vào nhóm đã chặn công việc gửi và xác thực các khối và ngược lại, dẫn đến tốc độ giảm.

2. Hiện vẫn chưa rõ làm thế nào để gửi một số lượng lớn giao dịch trong khi giảm thiểu chi phí truyền dữ liệu.

3. Không rõ cách thức và nơi lưu trữ dữ liệu để đạt được kết quả cao.

4. Không rõ cách tổ chức mạng giữa các nút vì kích thước của một khối với 1 triệu giao dịch chiếm khoảng 100 MB.

5. Làm việc ở chế độ đơn luồng sẽ phá vỡ kết nối giữa các nút khi thực hiện các phép tính dài (ví dụ: xây dựng cây Merkle và tính toán hàm băm của nó).

Chúng ta đã giải quyết tất cả chuyện này như thế nào?

Phiên bản đầu tiên của nút Plasma Cash là một loại kết hợp có thể thực hiện mọi thứ cùng một lúc: chấp nhận giao dịch, gửi và xác thực các khối cũng như cung cấp API để truy cập dữ liệu. Vì NodeJS vốn là đơn luồng nên hàm tính toán cây Merkle nặng đã chặn hàm thêm giao dịch. Chúng tôi thấy có hai lựa chọn để giải quyết vấn đề này:

1. Khởi chạy một số quy trình NodeJS, mỗi quy trình thực hiện các chức năng cụ thể.

2. Sử dụng worker_threads và chuyển việc thực thi một phần mã thành các luồng.

Do đó, chúng tôi đã sử dụng cả hai tùy chọn cùng một lúc: chúng tôi chia một nút thành 3 phần một cách hợp lý, có thể hoạt động riêng biệt nhưng đồng thời

1. Nút gửi, chấp nhận các giao dịch vào nhóm và tạo các khối.

2. Một nút xác thực để kiểm tra tính hợp lệ của các nút.

3. Nút API - cung cấp API để truy cập dữ liệu.

Trong trường hợp này, bạn có thể kết nối với từng nút thông qua ổ cắm unix bằng cli.

Chúng tôi đã chuyển các thao tác nặng, chẳng hạn như tính toán cây Merkle, thành một luồng riêng biệt.

Vì vậy, chúng tôi đã đạt được hoạt động bình thường đồng thời của tất cả các chức năng của Plasma Cash và không gặp trục trặc.

Sau khi hệ thống hoạt động, chúng tôi bắt đầu kiểm tra tốc độ và thật không may, đã nhận được kết quả không đạt yêu cầu: 5 giao dịch mỗi giây và lên tới 000 giao dịch mỗi khối. Tôi phải tìm ra những gì đã được thực hiện không chính xác.

Để bắt đầu, chúng tôi bắt đầu thử nghiệm cơ chế giao tiếp với Plasma Cash để tìm ra khả năng đỉnh cao của hệ thống. Chúng tôi đã viết trước đó rằng nút Plasma Cash cung cấp giao diện ổ cắm unix. Ban đầu nó dựa trên văn bản. các đối tượng json đã được gửi bằng `JSON.parse()` và `JSON.stringify()`.

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

Chúng tôi đã đo tốc độ truyền của những vật thể đó và nhận thấy ~ 130k mỗi giây. Chúng tôi đã cố gắng thay thế các chức năng tiêu chuẩn để làm việc với json nhưng hiệu suất không được cải thiện. Động cơ V8 phải được tối ưu hóa tốt cho các hoạt động này.

Chúng tôi đã làm việc với các giao dịch, mã thông báo và khối thông qua các lớp học. Khi tạo các lớp như vậy, hiệu suất giảm 2 lần, điều đó cho thấy OOP không phù hợp với chúng ta. Tôi đã phải viết lại mọi thứ theo cách tiếp cận thuần túy chức năng.

Ghi vào cơ sở dữ liệu

Ban đầu, Redis được chọn để lưu trữ dữ liệu vì một trong những giải pháp hiệu quả nhất đáp ứng yêu cầu của chúng tôi: lưu trữ khóa-giá trị, làm việc với bảng băm, bộ. Chúng tôi đã khởi chạy redis-benchmark và nhận được ~80 nghìn thao tác mỗi giây ở 1 chế độ đường ống.

Để có hiệu suất cao, chúng tôi đã điều chỉnh Redis tinh tế hơn:

  • Một kết nối ổ cắm unix đã được thiết lập.
  • Chúng tôi đã vô hiệu hóa việc lưu trạng thái vào đĩa (để đảm bảo độ tin cậy, bạn có thể thiết lập bản sao và lưu vào đĩa trong Redis riêng).

Trong Redis, nhóm là một bảng băm vì chúng ta cần có khả năng truy xuất tất cả các giao dịch trong một truy vấn và xóa từng giao dịch một. Chúng tôi đã thử sử dụng danh sách thông thường nhưng sẽ chậm hơn khi dỡ toàn bộ danh sách.

Khi sử dụng NodeJS tiêu chuẩn, thư viện Redis đạt được hiệu suất 18k giao dịch mỗi giây. Tốc độ giảm đi 9 lần.

Vì điểm chuẩn cho chúng tôi thấy khả năng rõ ràng là lớn hơn gấp 5 lần nên chúng tôi bắt đầu tối ưu hóa. Chúng tôi đã thay đổi thư viện thành ioredis và đạt hiệu suất 25k mỗi giây. Chúng tôi đã thêm từng giao dịch một bằng lệnh `hset`. Vì vậy, chúng tôi đã tạo ra rất nhiều truy vấn trong Redis. Ý tưởng nảy sinh là kết hợp các giao dịch thành các đợt và gửi chúng bằng một lệnh `hmset`. Kết quả là 32k mỗi giây.

Vì một số lý do mà chúng tôi sẽ mô tả bên dưới, chúng tôi làm việc với dữ liệu bằng cách sử dụng `Buffer` và hóa ra là nếu bạn chuyển đổi nó thành văn bản (`buffer.toString('hex')`) trước khi viết, bạn có thể nhận được thêm hiệu suất. Do đó, tốc độ đã được tăng lên 35k mỗi giây. Hiện tại, chúng tôi quyết định tạm dừng tối ưu hóa thêm.

Chúng tôi phải chuyển sang giao thức nhị phân vì:

1. Hệ thống thường tính toán các giá trị băm, chữ ký, v.v. và để làm được điều này, nó cần dữ liệu trong `Bộ đệm.

2. Khi được gửi giữa các dịch vụ, dữ liệu nhị phân nặng hơn văn bản. Ví dụ: khi gửi một khối có 1 triệu giao dịch, dữ liệu trong văn bản có thể chiếm hơn 300 megabyte.

3. Việc chuyển đổi dữ liệu liên tục ảnh hưởng đến hiệu suất.

Do đó, chúng tôi lấy giao thức nhị phân của riêng mình làm cơ sở để lưu trữ và truyền dữ liệu, được phát triển trên cơ sở thư viện `dữ liệu nhị phân` tuyệt vời.

Kết quả là chúng ta có cấu trúc dữ liệu sau:

-Giao dịch

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

- Mã thông báo

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

-Khối

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

Với các lệnh thông thường `BD.encode(block, Protocol).slice();` và `BD.decode(buffer, Protocol)`, chúng ta chuyển đổi dữ liệu thành `Buffer` để lưu trong Redis hoặc chuyển tiếp đến một nút khác và truy xuất dữ liệu trở lại.

Chúng tôi cũng có 2 giao thức nhị phân để truyền dữ liệu giữa các dịch vụ:

— Giao thức tương tác với Plasma Node thông qua ổ cắm 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)
  }
  ```

Trong đó:

  • `loại` — hành động cần thực hiện, ví dụ: 1 — sendTransaction, 2 — getTransaction;
  • `tải trọng` - dữ liệu cần được chuyển đến chức năng thích hợp;
  • `Id tin nhắn` - id tin nhắn để có thể xác định được phản hồi.

- Giao thức tương tác giữa các nút

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

Trong đó:

  • `mã` — mã tin nhắn, ví dụ 6 — PREPARE_NEW_BLOCK, 7 — BLOCK_VALID, 8 — BLOCK_COMMIT;
  • `phiên bảnGiao thức` — phiên bản giao thức, vì các nút có phiên bản khác nhau có thể được nâng lên trên mạng và chúng có thể hoạt động khác nhau;
  • `sq` - định danh thông điệp;
  • `đếmChunk` и `số đoạn` cần thiết để phân chia các tin nhắn lớn;
  • `chiều dài` и `tải trọng` chiều dài và dữ liệu của chính nó.

Vì chúng tôi đã nhập trước dữ liệu nên hệ thống cuối cùng nhanh hơn nhiều so với thư viện `rlp` của Ethereum. Thật không may, chúng tôi vẫn chưa thể từ chối vì cần phải hoàn tất hợp đồng thông minh mà chúng tôi dự định thực hiện trong tương lai.

Nếu chúng ta đạt được tốc độ 35 000 giao dịch mỗi giây, chúng ta cũng cần xử lý chúng trong thời gian tối ưu. Vì thời gian hình thành khối gần đúng mất 30 giây nên chúng ta cần đưa vào khối 1 000 000 giao dịch, có nghĩa là gửi nhiều hơn 100 MB dữ liệu.

Ban đầu, chúng tôi sử dụng thư viện `ethereumjs-devp2p` để liên lạc giữa các nút, nhưng nó không thể xử lý nhiều dữ liệu như vậy. Do đó, chúng tôi đã sử dụng thư viện `ws` và định cấu hình gửi dữ liệu nhị phân qua websocket. Tất nhiên, chúng tôi cũng gặp phải vấn đề khi gửi các gói dữ liệu lớn, nhưng chúng tôi đã chia chúng thành nhiều phần và giờ đây những vấn đề này đã không còn nữa.

Đồng thời hình thành cây Merkle và tính toán hàm băm 1 000 000 giao dịch yêu cầu khoảng 10 giây tính toán liên tục. Trong thời gian này, kết nối với tất cả các nút bị ngắt. Người ta quyết định chuyển phép tính này sang một chủ đề riêng.

Kết luận:

Trên thực tế, phát hiện của chúng tôi không mới nhưng vì lý do nào đó mà nhiều chuyên gia lại quên mất chúng khi phát triển.

  • Sử dụng Lập trình chức năng thay vì Lập trình hướng đối tượng sẽ cải thiện năng suất.
  • Khối nguyên khối còn tệ hơn kiến ​​trúc dịch vụ cho hệ thống NodeJS hiệu quả.
  • Việc sử dụng `worker_threads` để tính toán nặng sẽ cải thiện khả năng phản hồi của hệ thống, đặc biệt là khi xử lý các hoạt động i/o.
  • Ổ cắm unix ổn định hơn và nhanh hơn các yêu cầu http.
  • Nếu bạn cần truyền nhanh dữ liệu lớn qua mạng, tốt hơn nên sử dụng websockets và gửi dữ liệu nhị phân, được chia thành các phần, có thể chuyển tiếp nếu chúng không đến, sau đó kết hợp thành một tin nhắn.

Chúng tôi mời bạn đến thăm GitHub dự án: https://github.com/opporty-com/Plasma-Cash/tree/new-version

Bài viết được đồng viết bởi Alexander Nashivan, lập trình viên lâu năm Giải pháp thông minh Inc.

Nguồn: www.habr.com

Thêm một lời nhận xét