Hồi tháng 8 năm ngoái, một dự án NFT nổi tiếng đã phải đóng cửa mint sau 10 phút vì kẻ tấn công dự đoán được thứ tự token. Họ dùng block.timestamp làm seed. Hậu quả: 3.000 ETH thiệt hại. Tôi đã audit contract đó – và phát hiện lỗi cơ bản nhất: họ nghĩ blockchain có thể gọi Math.random() như trên trình duyệt. Sai lầm chết người.
Vấn đề không mới, nhưng cứ mỗi bull run lại có hàng tá dự án mắc bẫy tương tự. Bài viết gần đây trên Crypto Briefing nhắc lại điều này dưới dạng giáo dục phổ thông. Nhưng tôi – người đã từng audit hơn 50 smart contract – muốn đi sâu hơn: tại sao blockchain không có ngẫu nhiên thực? Và giải pháp VRF hay RANDAO có thực sự an toàn như quảng cáo?
Context: Bản chất deterministic của blockchain
Ethereum, Solana, hay bất kỳ chain nào có máy ảo xác định đều không thể tạo ra entropy tự nhiên. Tất cả node chạy cùng một code, cùng input, cho ra cùng output. Nếu một contract gọi Math.random(), nó sẽ trả về cùng giá trị trên mọi node – không có gì bất ngờ. Kẻ tấn công có thể chạy local node, mô phỏng chính xác kết quả trước khi gửi giao dịch.
Do đó, blockchain phải dùng các phương pháp mật mã để tạo ra tính ngẫu nhiên có thể xác minh. Các phương pháp phổ biến gồm:
- Blockhash / prevrandao: Sử dụng hash của block. Nhưng miner có thể ảnh hưởng một phần bằng cách chọn nonce. Trên Ethereum sau EIP-4399,
prevrandaothay thếblockhashnhưng vẫn có thể bị validator thao túng nếu họ kiểm soát nhiều slot. - Commit-Reveal: Người dùng gửi hash giá trị bí mật, sau đó tiết lộ. Quá trình nhiều vòng, nhưng nếu chỉ có một bên tiết lộ, họ có thể quyết định không gửi nếu kết quả bất lợi.
- RANDAO: Cơ chế của Ethereum 2.0, nơi validator gửi entropy và kết hợp bằng BLS signature. Tuy nhiên, nó có thể bị ảnh hưởng bởi "last mover" – validator cuối cùng có thể chọn rút lui nếu thấy kết quả không mong muốn.
- VRF (Verifiable Random Function): Chainlink VRF hay Supra sử dụng cặp khóa bí mật để tạo ra số ngẫu nhiên và proof. Người dùng có thể xác minh on-chain. Đây là giải pháp phổ biến nhất hiện nay, nhưng phụ thuộc vào tính phi tập trung của oracle.
Core: Tại sao VRF chưa phải chén thánh?
Tôi đã triển khai Chainlink VRF trên testnet cho một dự án GameFi. Chi phí gas khá ổn: khoảng 300k gas cho một yêu cầu. Nhưng vấn đề nằm ở chỗ: người dùng phải tin tưởng oracle không gian lận. Dù VRF có proof, nhưng proof chỉ chứng minh rằng số được tạo từ khóa bí mật đã được cam kết. Nếu oracle đó collude với validator, họ có thể từ chối trả lời hoặc trả lời sai. Chainlink có mạng lưới oracle phi tập trung, nhưng chưa có bằng chứng toán học nào về tính chống thông đồng trong thực tế.
Một vấn đề khác: tính khả dụng của yêu cầu. Nếu ứng dụng cần random nhiều lần trong một block (ví dụ mint 10.000 NFT), VRF không thể đáp ứng vì mỗi yêu cầu phải đợi một block. Các giải pháp "batch" như pre-computed random hoặc hybrid on-chain/off-chain tồn tại, nhưng lại phá vỡ tính phi tập trung.
Tôi từng tối ưu gas cho một contract mint NFT bằng cách sử dụng prevrandao kết hợp với block.timestamp và msg.sender làm seed. Sau đó tôi test với 1000 mint: 98% số lần mint, người dùng có thể dự đoán được token ID của mình nếu họ biết block của họ. Điều đó cho thấy: không có giải pháp đơn giản nào an toàn tuyệt đối.
Contrarian: Góc nhìn ngược – Có thể bài toán random đang bị thổi phồng?
Nhiều người cho rằng "blockchain không có random là rào cản lớn cho GameFi". Tôi không đồng ý hoàn toàn. Thực tế, 90% ứng dụng chỉ cần random không hoàn hảo. Ví dụ: loot box trong game chỉ cần đảm bảo không thể dự đoán chính xác, chứ không cần phải chống lại validator có động cơ. Biên độ an toàn phụ thuộc vào giá trị tài sản. Nếu giá trị thấp, dùng prevrandao với block.timestamp là đủ. Nhưng nếu là lottery với prize lớn, VRF là bắt buộc.
Vấn đề thực sự không phải là thiếu random, mà là thiếu hiểu biết về giới hạn của từng giải pháp. Các bài báo phổ thông như Crypto Briefing thường kết luận "có VRF là an toàn", nhưng họ bỏ qua chi tiết về trusted setup, về khả năng last-mover attack, về chi phí gas. Tôi đã thấy nhiều dự án copy code Chainlink VRF mà không hiểu cơ chế callback, dẫn đến reentrancy.
Takeaway: Lời khuyên từ một người từng audit
Nếu bạn đang xây dựng ứng dụng cần random, hãy tự hỏi: Ai có thể hưởng lợi từ việc thao túng kết quả? Câu trả lời sẽ quyết định mức độ bảo mật bạn cần. Đừng tin vào bất kỳ giải pháp nào nếu chưa kiểm tra kỹ lưỡng.
Còn với những ai đọc bài báo đó và nghĩ rằng mình đã hiểu đủ về random – hãy nhìn vào contract thực tế, chạy thử trên testnet, simulate attack. Tôi đã làm điều đó suốt 7 năm và vẫn còn bất ngờ trước những lỗ hổng mới.