Zcash Ironwood: 2.700 định lý máy và cái bẫy của sự chắc chắn tuyệt đối
Nguyễn Tuệ
Một buổi tối tuần trước, tôi đọc lại một dòng trong báo cáo kỹ thuật của nhóm nghiên cứu Zcash: "2.739 định lý đã được kiểm tra bằng máy." Tôi dừng lại. Trong mười ba năm theo dõi ngành này, tôi hiếm khi thấy một dự án dám công bố một con số thuộc về "hình thức hóa" nhiều đến vậy. Hầu hết các team ZK chỉ dừng lại ở hai vòng audit rồi viết blog khoe "đã được kiểm tra bởi công ty X nào đó". Zcash lại nói rằng họ đã dùng máy tính để kiểm tra từng bước suy luận toán học, nhằm chứng minh rằng bản nâng cấp Ironwood không tồn tại lỗ hổng đúc tiền giả không thể phát hiện. Nhưng tôi cần nói thẳng ngay từ đầu: 2.700 định lý không có nghĩa là "không có lỗi". Nó có nghĩa là "lỗi đã bị đẩy sang một nơi khác".
Với người chưa theo dõi, cần nhắc lại lịch sử: Zcash là giao thức privacy đầu tiên dùng zk-SNARKs. Mỗi giao dịch ẩn danh được bảo vệ bởi một bằng chứng mật mã rất phức tạp, và thứ duy nhất đứng giữa bạn và một kẻ tấn công có thể đúc vô hạn ZEC là một đoạn code xác minh bằng chứng. Năm 2018, các nhà nghiên cứu công bố BCTV14: một lỗi trong cách chọn tham số của đường cong elliptic khiến ai đó có thể tạo ra các bằng chứng giả. Không một node nào trên mạng lưới nhận ra. Zcash đã vá nó, nhưng vết sẹo đó vẫn nằm trong hồ sơ của ngành như một lời nhắc rằng lòng tin vào một hệ thống tiền tệ không thể dựa vào hy vọng.
Giờ Ironwood mang đến một lời hứa khác hẳn. Họ không còn nói "chúng tôi đã audit kỹ". Họ nói "chúng tôi đã chứng minh bằng máy rằng không có đường đi nào dẫn đến loại lỗ hổng đó trong các thay đổi mới". Đó là một mức tuyên bố nặng ký hơn nhiều.
Trước khi bàn về ý nghĩa, cần hiểu "máy kiểm tra định lý" là gì. Hãy tưởng tượng một lập trình viên viết phần mềm quản lý tiền. Anh ta có thể test 1000 trường hợp, nhưng vẫn có trường hợp thứ 1001 làm sập hệ thống. Kiểm tra hình thức, hay formal verification, không đi theo hướng đó. Nó mã hóa toàn bộ logic của chương trình thành các mệnh đề toán học, rồi dùng một công cụ như Coq hoặc Isabelle để kiểm tra từng bước suy luận. Nếu suy luận không hợp lệ, máy sẽ từ chối. Khi tất cả các định lý được kiểm tra, bạn không còn nghi ngờ về tính đúng đắn của chương trình — trong một phạm vi đã thiết lập.
Đây là những gì code thực sự nói: Zcash đã xây dựng một bản formalization riêng, gọi là "zf", bao quanh bộ kiểm chứng SNARK của họ. Họ mô tả các trạng thái của bộ kiểm chứng, các điều kiện để một bằng chứng bị từ chối, và các giả định về các thành phần mật mã. Sau đó họ chứng minh rằng bộ kiểm chứng không bao giờ chấp nhận một bằng chứng không có witness tương ứng. Nếu bạn đọc kỹ whitepaper, bạn sẽ thấy khái niệm soundness được tách thành một spec nhỏ, và spec đó được chứng minh là đúng. Rất đẹp. Nhưng ở đoạn này tôi bắt đầu cảm thấy khó chịu, vì bạn càng xây cao một tòa tháp bằng chứng, bạn càng phải tin vào nền móng.
Giả định tin cậy họ đang đặt ra: thứ nhất, họ tin Coq hoặc Isabelle không có bug. Thứ hai, họ tin rằng spec họ viết ra — cái được chứng minh — chính là cái mà con người thực sự muốn. Thứ ba, họ tin rằng phần còn lại của node Zcash, ngoài phạm vi formalization, không thể phá vỡ những gì formalization đã đảm bảo. Cả ba giả định đều hợp lý, nhưng không phải là điều hiển nhiên.
Tôi đã fork repo và phát hiện một ranh giới tinh tế. Phần được chứng minh nằm trong "zf" — tập trung vào verifier và mô hình trạng thái consensus. Nó không bao phủ toàn bộ librustzcash, không bao phủ parser của P2P message, không bao phủ Equihash, và không bao phủ toàn bộ node. Đây là lỗ hổng kiến trúc: khái niệm "đã được kiểm tra bởi máy" có thể khiến người mới nghĩ rằng toàn bộ Zcash là một quả bọc thép. Nhưng lịch sử bảo mật của mọi hệ thống lớn đều chỉ ra rằng các lỗi không nằm ở phần nổi tiếng nhất; chúng nằm ở interfaces, ở cách serialization, ở biên giới giữa các module. Một kẻ tấn công thông minh sẽ không cố đánh vào verifier; nó sẽ tìm phần public input được parse trước khi verifier nhìn thấy. Nếu read xong public input mà không kiểm tra độ dài, một vài byte thừa có thể trở thành một cánh cửa.
Nếu chúng ta nhìn vào merkle tree trong Zcash, mọi thứ trở nên rõ ràng hơn. Giao thức xây dựng các commitment thành một cây Merkle. Verifier cần biết gốc cây để xác minh bằng chứng. Nhưng câu hỏi "ai được phép cập nhật cây này", "làm thế nào cây được xây trong block" lại nằm ở phần code không được chứng minh. Nếu có một lỗi ở lớp cập nhật cây, mọi tính đúng đắn của verifier trở nên vô nghĩa. Formality không giải quyết vấn đề "lớp không được formality". Đó là một trong những điều khó nói nhất khi phân tích các hệ thống chứng minh hình thức.
Thực tế, ranh giới giữa code được chứng minh và code không được chứng minh chính là một chuỗi các câu hỏi "nếu". Ví dụ: nếu một kẻ tấn công có thể trigger panic trong Rust khi parsing, một crash trong node có thể dẫn đến mất đồng thuận. Formality giúp ta yên tâm về soundness, nhưng soundness không bao gồm liveness. Bạn có thể có một verifier hoàn toàn đúng, nhưng nếu node liên tục crash, mạng lưới không hoạt động. Và cũng có một lớp nữa: quá khứ trusted setup của Zcash. Dù Zcash đã chuyển sang Sapling và sau đó là Halo 2 với mô hình không tin cậy, cộng đồng vẫn còn những vết sẹo. Một nền tảng bạn cho là đã an toàn về toán học vẫn có thể bị tổn thương bởi một lỗi trong toolchain biên dịch.
Bây giờ đến góc phản trực giác mà tôi luôn muốn mọi người nghe: 2.700 định lý càng ấn tượng, thì nguy cơ do quá tự tin càng lớn. Con người có xu hướng khoán trắng cho "máy kiểm tra" và quên rằng chiến tuyến bảo mật không nằm ở toán học. Lấy BCTV14 làm ví dụ. Lỗi năm 2018 nằm ở việc sử dụng một curve mà trong một số điều kiện hiếm gặp, nhóm các điểm được tạo ra bởi generator không phân bố đủ. Nếu bạn hỏi tôi "formality có chặn được kiểu lỗi đó không?", câu trả lời là có thể có, nếu spec bao phủ đúng thuộc tính phân bố. Nhưng câu trả lời cũng có thể là không, nếu spec chỉ mô tả "verifier reject invalid proof" chứ không mô tả "với mọi input được parse trong miền hợp lệ". Đó là lý do tôi không bao giờ dùng chữ "an toàn tuyệt đối" cho bất kỳ ai. Một hệ thống chứng minh hình thức mạnh đến đâu cũng chỉ mạnh bằng đặc tả của nó.
Zcash, dù vậy, vẫn khiến tôi nể. Tôi từng ngồi với một nhóm nghiên cứu Layer2 nói về chi phí prover trong ZK Rollup. Họ than vãn rằng chi phí chứng minh gas quá phi lý, rằng các operator đang chảy máu tiền. Tôi nhớ mình đã cười và nói: "Ít nhất anh còn có một hệ thống đang chạy. Thử formalize một production codebase rồi xem chi phí nó ra sao." Khối lượng công việc cho 2.700 định lý không phải là vài tuần. Nó là nhiều tháng của những người có trình độ toán học cao. Zcash đã chọn đầu tư vào lớp đắt nhất và ít được nhìn thấy nhất — mức độ chắc chắn mà người dùng cuối không bao giờ cảm nhận được trực tiếp. Trong thị trường đi ngang này, thứ đó sẽ không đưa ZEC lên top nhanh chóng, nhưng nó sẽ tạo ra một thứ khác: một rào cản kỹ thuật rất khó để đối thủ bắt chước.
Nhưng tôi vẫn muốn đặt câu hỏi cuối cùng vào chỗ mà tất cả các bằng chứng toán học đều không chạm tới được: ai sẽ là người dùng Zcash trong môi trường pháp lý hiện tại? Một giao thức privacy vừa được chứng minh là không thể đúc tiền giả vẫn đang đối mặt với một lớp trừu tượng khác — đó là quy định, danh sách đen của sàn giao dịch, và sự biến mất thanh khoản. Sự chắc chắn toán học không cứu được token khỏi một thông báo pháp lý. Nó chỉ làm giảm một rủi ro kỹ thuật cụ thể, trong khi những rủi ro lớn hơn đang ngồi ngay ngoài phạm vi của một bằng chứng hình thức. Câu hỏi tồi tệ nhất bạn có thể hỏi về Ironwood là "nó có an toàn không?". Câu hỏi đúng là: "Phần nào của Zcash vẫn chưa được chứng minh, và ai đang chịu trách nhiệm cho phần đó?". Cho đến khi có lời giải cho câu đó, tôi vẫn sẽ phân loại Zcash là một kỳ quan về mật mã, nhưng cũng là một vết nứt ở lớp con người.