Mình tự xây một Simple Scalable Solution cho Ethereum - phần 1

Legos LightLegos Light
5 min read
💡
Nếu bạn là một lập trình viên, bạn gõ từ khóa “zk-rollups“ vào Google, bạn sẽ ngay lập tức chết chìm trong mớ thông tin khổng lồ trả về. Ban đầu, mình cũng định viết bài này để nói về zk-Rollups. Tuy nhiên, càng viết mình càng thấy không ổn vì càng cố làm đơn giản càng cảm thấy xa rời với lý thuyết. Mình không thể cho phép mình đặt tựa đề “câu view“ như vậy được… Nhưng xin hẹn chắc chắn các bạn vào một series khác chuyên về zk-Rollups sau.

Góp nhặt từ những thứ mình đọc được…

Blockchain lưu lại tất cả những transaction trên một cuốn sổ cái (public ledger) để chứng minh cho sự minh bạch tuyệt đối của nó. Điều này dẫn đến một vấn đề cực kỳ nan giải, đó là dữ liệu nó chứa sẽ càng ngày càng phình to lên, và theo lý thuyết là sẽ lớn vô hạn. Vì thế, người ta đang tìm rất nhiều phương pháp để mở rộng nó. Một trong những phương pháp mở rộng blockchain được ưa chuộng gần đây là zk-Rollups hay Optimistic Rollup. Điểm hay của các phương pháp này là nó có thể verify cả các hoạt động của virtual machine (execution codes) chứ không chỉ đơn thuần là các giao dịch payment (from, to, amount) thông thường. Trước khi có các công nghệ kể trên, Ethereum đã từng giới thiệu một công nghệ mang tên Plasma, xem như là một phương pháp đơn giản hơn để mở rộng mạng blockchain của mình. Về mặt ý tưởng, Plasma có phần hơi giống với Lightning Network của Bitcoin, có thể hình dung thế này:

  1. Có một nhóm người muốn giao dịch với nhau, nhưng vì một số vấn đề như tốc độ hoặc phí giao dịch, … của mạng blockchain public (Layer 1) như Ethereum, Solana,… họ quyết định tổ chức giao dịch trên một network riêng (Layer 2).

  2. Ai muốn tham gia kênh giao dịch thì phải có một số tiền deposit sẵn trên Layer 1, rồi sau đó, sang Layer 2 claim lại tiền và giao dịch trên đó. Layer 2 sẽ lưu lại tất cả các giao dịch đã xảy ra giữa nhóm người này.

  3. Để bảo vệ tính toàn vẹn dữ liệu, gom tất cả các giao dịch trên Layer 2 rồi bằng một cách nào đó “nén“ cho thật gọn lại, rồi định kỳ submit dữ liệu đã thu gọn lên Layer 1 làm bằng chứng. (*)

  4. Lúc nào một người muốn rút tiền, họ thông báo với Layer 2. Layer 2 sẽ trả về cho họ một bằng chứng (giống kiểu receipt) để mang qua Layer 1 verify. Nếu thành công, tiền trên Layer 1 sẽ được release cho người đó.

Trong series bài viết này, mình sẽ cố gắng tự xây dựng một protocol (cùng với demo) từa tựa như Plasma để cung cấp khái quát giải pháp mở rộng mạng L1.

Các vấn đề phải giải quyết

  1. Các setup ban đầu

Để giải quyết bài toán, mình đặt ra một số setup như sau:

  • Layer 1 là một public blockchain, có hỗ trợ smart contract và các công cụ, thư viện mật mã như: chữ ký số, hash, …

  • Layer 2 có thể là một private blockchain hoặc ít nhất có một hệ thống centralize:

    • Toàn bộ giao dịch có thể công khai và chỉ ở dạng (from, to, amount).

    • Không có vấn đề về mặt lưu trữ và chi phí lưu trữ

    • Có thể che giấu một phần dữ liệu cần thiết

  1. Khởi tạo

Theo đề cập ở trên, mọi giao dịch sẽ được thực hiện trên Layer 2, Layer 1 sẽ chỉ là nơi lưu lại các bằng chứng thu gọn. Như vậy, trên Layer 1 phải có 1 smart contract S để thực hiện:

  • Những người tham gia deposit tiền vào đó. Khi đã deposit tiền vào, cần một cách nào đó để mang qua Layer 2 để claim tiền mới có thể dùng được. (**)

  • Lưu trạng thái ban đầu (số tiền) của từng người.

  • Cập nhật các trạng thái tiếp theo và kèm với bằng chứng.

  • Verify các claim request của người muốn rời khỏi giao dịch (***)

  1. Chứng nhận claim tiền (**) cách nào?

Trong thực tế, Layer 2 sẽ liên tục lắng nghe các event trên smart contract S của Layer 1. Ngay khi user deposit tiền vào S, một event sẽ phát ra và Layer 2 nhận được event sẽ mint tiền cho user này.

  1. Nén (*) thế nào, cập nhật gì lên Layer 1?

Ở bài này, mình dùng cấu trúc dữ liệu Merkle Tree (đã giới thiệu ở một bài viết khác) để tạo ra bằng chứng lưu trên Layer 1 như sau:

  • Sau một khoảng thời gian định kỳ, hoặc sau một số lượng giao dịch nhất định, gom tất cả các giao dịch đã xảy ra để tính Merkle Root r.

  • Gọi function updateState(r) của S trên Layer 1 để lưu lại bằng chứng mới nhất của tất cả các giao dịch trên Layer 2. Như vậy, ta chỉ cần lưu đúng 1 giá trị hash đại diện cho toàn bộ các giao dịch trên Layer 2 => rất tiết kiệm.

  1. Verify (***) thế nào?

Mình dùng Merkle Path để xác thực. Điểm đặc sắc của phương pháp này là nếu có \(n\) giao dịch, thì Merkle path chỉ cần \(\log n\) thành phần hash, nghĩa là, nếu có tổng cộng 1000 giao dịch thì để xác thực 1 giao dịch cũng chỉ cần 10 hash cho Merkle Path.

Giả sử một user có \(k\) giao dịch và muốn verify toàn bộ \(k\) giao dịch này, cũng chỉ cần số lượng Hash \(\log n + k\) hash để xây dựng lại cây merkle.

Như hình trên, giả sử cần xác thực các giao dịch là nút a,c,d,g,m (nút màu hồng), ta chỉ cần merkle path là các màu xanh dương là đủ để xây dựng lại merkle root.

Bài tiếp theo, mình sẽ dựa trên ý tưởng này để xây dựng giao thức và cài đặt

0
Subscribe to my newsletter

Read articles from Legos Light directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Legos Light
Legos Light