Giới thiệu:
Gần đây, Vitalik và một số học giả đã cùng nhau công bố một bài báo mới về cách Tornado Cash thực hiện chương trình chống rửa tiền của mình (về cơ bản là cho phép người rút tiền chứng minh rằng lịch sử gửi tiền của họ thuộc một tập hợp không bao gồm tiền bẩn). Tuy nhiên, bài báo thiếu một giải thích tinh tế về logic kinh doanh và nguyên tắc của Tornado Cash, khiến một số đọc giả chỉ có hiểu biết nông cạn về chủ đề.
Đáng lưu ý là các dự án như Tornado, đại diện cho các dự án về quyền riêng tư, thực sự sử dụng khía cạnh không có kiến thức của thuật toán ZK-SNARK. Trong khi đó, hầu hết các giải pháp đưa ra biểu ngữ ZK, như Rollups, chỉ sử dụng sự ngắn gọn của ZK-SNARK. Thường, mọi người thường lẫn lộn Giải pháp Chứng minh Đúng Đắn với ZK, và Tornado là một ví dụ xuất sắc để làm rõ ứng dụng thực tế của ZK.
Tác giả của bài viết này đã viết một bài về nguyên tắc của Tornado vào năm 2022 cho Viện nghiên cứu Web3Caff. Hôm nay, chúng tôi đã trích dẫn và mở rộng trên một số phần của công việc gốc đó để cung cấp một hiểu biết có hệ thống về Tornado Cash.
Liên kết bài viết gốc:https://research.web3caff.com/zh/archives/2663?ref=157
Tornado Cash sử dụng chứng minh không biết như giao thức trộn của mình. Trong khi phiên bản cũ được ra mắt vào năm 2019, một phiên bản beta của mô hình cập nhật đã được triển khai vào cuối năm 2021. Phiên bản trước của Tornado đạt được mức độ phân quyền tốt với các hợp đồng trên chuỗi mở và không bị kiểm soát bởi đa chữ ký. Hơn nữa, mã nguồn frontend là mã nguồn mở và được sao lưu trên mạng lưới IPFS. Do tính đơn giản của phiên bản cũ của Tornado, bài viết này tập trung vào giải thích nó.
Phương pháp chính của Tornado là kết hợp nhiều hành động gửi và rút tiền lại với nhau. Sau khi gửi Token vào Tornado, người gửi cung cấp một bằng chứng ZK để xác minh giao dịch gửi trước đó của họ và sau đó rút tiền bằng cách sử dụng một địa chỉ mới, từ đó phá vỡ liên kết giữa địa chỉ gửi và rút tiền.
Để nói một cách ngắn gọn hơn, hãy tưởng tượng Tornado như một hộp kính đựng đầy tiền xu (Coins) được gửi bởi nhiều người. Chúng ta có thể thấy ai đã gửi tiền xu, nhưng những đồng tiền này rất đồng nhất. Nếu ai đó không quen thuộc lấy một đồng tiền từ hộp, sẽ rất khó để theo dõi xem ai đã gửi đồng tiền đó ban đầu.
(Nguồn ảnh: rareskills)
Các kịch bản như vậy dường như phổ biến. Khi chúng ta SWAP một số ETH từ một hồ bơi Uniswap, không thể xác định ETH của ai chúng ta đã nhận, với số lượng nhà cung cấp thanh khoản cho Uniswap. Tuy nhiên, sự khác biệt nằm ở quy trình. Với Uniswap, việc đổi Tokens đòi hỏi một Token khác có giá trị tương đương, và quỹ không thể được chuyển “riêng tư”. Ngược lại, một máy trộn chỉ đòi hỏi người rút tiền phải trình bày biên nhận gửi tiền của họ.
Để làm cho các hành động gửi tiền và rút tiền trở nên đồng nhất, hồ bơi Tornado duy trì tính nhất quán trong số tiền gửi và rút. Ví dụ, nếu một hồ bơi có 100 người gửi tiền và 100 người rút tiền, mặc dù các hành động này có thể được nhìn thấy công khai, nhưng dường như không có mối liên hệ giữa chúng. Mọi người gửi tiền và rút tiền cùng một số tiền, làm cho việc theo dõi sự di chuyển của quỹ trở nên khó khăn. Rõ ràng, điều này mang lại lợi ích bẩm sinh cho việc rửa tiền.
Câu hỏi then chốt đặt ra là: khi rút tiền, làm thế nào để chứng minh khoản tiền gửi trước đó? Địa chỉ khởi tạo giao dịch rút tiền không liên kết với bất kỳ địa chỉ nào đã gửi tiền, vì vậy làm thế nào để xác minh quyền rút tiền? Phương pháp trực tiếp nhất sẽ là người rút tiền tiết lộ hồ sơ gửi tiền của họ, nhưng điều đó sẽ tiết lộ danh tính của họ. Đây là nơi mà chứng minh không cần biết đến được đưa vào trò chơi.
Với một Bằng chứng ZK, người rút tiền có thể xác nhận họ có một hồ sơ gửi tiền trong hợp đồng Tornado và rằng khoản gửi tiền này chưa được rút. Điều đẹp của bằng chứng zero-knowledge là chúng bảo vệ sự riêng tư. Công chúng chỉ biết rằng người rút tiền thực sự đã gửi tiền nhưng không thể xác định danh tính cụ thể của họ.
Để chứng minh “Tôi đã gửi tiền vào hồ bơi Tornado” có thể dịch thành “Hồ sơ gửi tiền của tôi có thể được tìm thấy trong hợp đồng Tornado.” Nếu Cn biểu thị một hồ sơ gửi tiền, thì với tập hồ sơ gửi tiền của Tornado là {C1, C2,…C100…}, Bob cần chứng minh rằng anh ấy đã sử dụng khóa riêng của mình để tạo ra một hồ sơ trong tập hồ sơ này mà không tiết lộ chính xác Cn cụ thể đó là gì. Điều này sử dụng các thuộc tính duy nhất của Chứng minh Merkle.
Tất cả các bản ghi gửi của Tornado được tổng hợp vào một Cây Merkle được xây dựng trên chuỗi. Đa số các lá này (khoảng 2^20, hơn 1 triệu) vẫn trống (với giá trị ban đầu). Mỗi khoản gửi mới cập nhật một lá cam kết tương ứng và sau đó là gốc của cây.
Ví dụ, nếu tiền gửi của Bob là giao dịch thứ 10,000 trong lịch sử của Tornado, giá trị liên quan Cn sẽ là lá thứ 10,000 của cây, tức là C10000 = Cn. Hợp đồng sau đó sẽ tự động tính toán Root mới.
(图源:RareSkills)
Chứng minh Merkle một cách súc tích và hiệu quả. Để chứng minh giao dịch TD tồn tại trong Mạng lưới Merkle, chỉ cần cung cấp bằng chứng Merkle phù hợp, vẫn rất nhỏ gọn ngay cả khi Mạng lưới Merkle rất lớn.
Để xác minh rằng một giao dịch, ví dụ H3, thực sự được bao gồm trong Merkle Tree, người ta phải chứng minh rằng việc sử dụng H3 và dữ liệu khác từ Merkle Tree có thể tạo ra Root. Dữ liệu này (bao gồm cả Td) tạo thành Merkle Proof. Khi Bob muốn rút tiền, anh ấy cần xác minh hai điều:
·Cn được xây dựng trên Merkle Tree trên chuỗi bởi Tornado, trong đó anh ta có thể xây dựng một Merkle Proof chứa Cn;
·Cn liên quan đến phiếu gửi của Bob.
Trong mã nguồn frontend của giao diện người dùng của Tornado, nhiều chức năng đã được triển khai trước. Khi một người nộp tiền mở trang web Tornado Cash và nhấp vào nút gửi tiền, mã nguồn frontend đính kèm tạo ra hai số ngẫu nhiên, K và r, cục bộ. Sau đó, nó tính giá trị của Cn=Hash(K, r), truyền Cn (được gọi là cam kết trong sơ đồ dưới đây) vào hợp đồng Tornado để được tích hợp vào cây Merkle của nó. Đơn giản nói, K và r hoạt động như các khóa riêng tư. Chúng quan trọng, và người dùng được khuyến khích lưu trữ chúng một cách an toàn, vì chúng sẽ được yêu cầu lại trong quá trình rút tiền sau này.
Một “encryptedNote” là một tính năng tùy chọn cho phép người dùng mã hóa các chứng chỉ K và r bằng một khóa riêng và lưu trữ chúng trên chuỗi để ngăn không quên.
Đáng chú ý rằng tất cả các hoạt động trên đều diễn ra ngoại chuỗi, có nghĩa là cả hợp đồng Tornado lẫn bất kỳ quan sát ngoài nào đều không nhận biết K và r. Nếu K và r bị tiết lộ, đó tương đương với việc mất khóa riêng tư của ví của mình.
Khi nhận được tiền gửi của người dùng và Cn = Băm (K, r) được tính toán, hợp đồng Tornado đặt Cn ở mức cơ sở của Cây Merkle, biến nó thành một nút lá mới và sau đó cập nhật giá trị của gốc. Tuy nhiên, điều quan trọng là phải hiểu rằng lá của Merkle Tree này không được ghi lại trong trạng thái của hợp đồng mà chỉ được ghi lại dưới dạng tham số sự kiện trong các khối trước đây. Hợp đồng Tornado chỉ ghi lại gốc Merkle. Trong quá trình rút tiền, người dùng có thể chứng minh, thông qua Merkle Proof, rằng hồ sơ tiền gửi tương ứng với gốc Merkle hiện tại, một khái niệm hơi giống với rút tiền cầu nối xuyên chuỗi của khách hàng nhẹ. Thiết kế này cho thấy sự khéo léo của Tornado: để tiết kiệm chi phí khí đốt, cây Merkle đầy đủ không được ghi vào trạng thái của hợp đồng, chỉ có gốc của nó. Lá của cây được ghi lại đơn giản trong các hồ sơ khối lịch sử, một cơ chế tương tự như nguyên tắc tiết kiệm khí của Rollup (mặc dù các chi tiết khác nhau).
Trong quá trình rút tiền, người rút nhập thông tin/ khóa riêng (số ngẫu nhiên K và r được tạo ra trong quá trình gửi tiền) trên trang web giao diện người dùng. Mã nguồn giao diện Tornado Cash sử dụng K và r, Cn=Hash(K, r), và Chứng minh Merkle tương ứng với Cn để tạo ra Chứng minh ZK, xác nhận rằng Cn tương ứng với một bản ghi gửi tiền trên cây Merkle và rằng K và r là thông tin/ khóa hợp lệ cho Cn. Bước này về cơ bản chứng minh sự hiểu biết về khóa của một bản ghi gửi tiền trên cây Merkle. Khi Chứng minh ZK được gửi đến hợp đồng Tornado, tất cả bốn tham số đều được che giấu, đảm bảo rằng bên ngoài, bao gồm cả hợp đồng Tornado chính nó, đều không biết, từ đó bảo vệ quyền riêng tư của người dùng.
Một chi tiết thú vị là thao tác gửi tiền sử dụng hai số ngẫu nhiên, K và r, để tạo ra Cn thay vì chỉ sử dụng một số ngẫu nhiên vì một số ngẫu nhiên đơn lẻ có thể không đủ an toàn và có thể bị tấn công bằng cách thử mật khẩu.
Về ký hiệu “A” trong hình minh họa, nó đại diện cho địa chỉ nhận rút và được cung cấp bởi người rút tiền. Trong khi đó, “nf” là một bộ nhận dạng được thiết lập để ngăn chặn các cuộc tấn công phát lại, giá trị của nó được xác định như nf=Hash(K), trong đó K là một trong hai số ngẫu nhiên (K và r) được sử dụng trong quá trình gửi tiền để tạo ra Cn. Do đó, mỗi Cn có một nf tương ứng, và hai cái này được kết nối một cách duy nhất.
Tại sao cần ngăn chặn cuộc tấn công phát lại? Do các tính năng thiết kế của mixer, khi rút tiền, không rõ tiền gửi nào trong Merkle Tree tương ứng với số tiền đã rút. Khi mối quan hệ giữa người gửi tiền và số tiền đã rút vẫn mơ hồ, người dùng độc hại có thể lợi dụng điều này và liên tục rút tiền từ mixer, làm cạn kiệt nguồn tiền.
Ở đây, nhận dạng nf hoạt động tương tự như bộ đếm giao dịch “nonce” ẩn trong mỗi địa chỉ Ethereum, được thiết lập để ngăn chặn việc giao dịch được lặp lại. Khi yêu cầu rút tiền, người dùng phải gửi một nf. Hệ thống kiểm tra xem nf này đã được sử dụng trước đó chưa: nếu có, yêu cầu rút tiền sẽ bị vô hiệu; nếu không, quá trình rút tiền tiếp tục và nf được ghi lại, đảm bảo việc sử dụng sau này sẽ dẫn đến việc vô hiệu hóa.
Một số người có thể tự hỏi: Liệu có ai có thể tạo ra một nf mà hợp đồng không ghi nhận? Điều đó khó xảy ra. Trong quá trình tạo ZK Proof, việc đảm bảo nf=Hash(K) và số ngẫu nhiên K liên kết với ghi chú gửi tiền Cn là rất quan trọng. Nếu ai đó tạo một nf một cách tùy ý, nó sẽ không khớp với bất kỳ ghi chú nào đã được ghi nhận, từ đó làm cho việc tạo ra một ZK Proof hợp lệ là không thể, dẫn đến quá trình rút tiền bị trì hoãn.
Người khác có thể đặt câu hỏi: Có cách nào để tránh việc sử dụng nf không? Vì những người rút tiền phải nộp một bằng chứng ZK, chứng minh mối quan hệ của họ với một Cn cụ thể, liệu có đủ để kiểm tra xem một Bằng chứng ZK tương ứng đã được ghi trên chuỗi chính chưa? Tuy nhiên, các chi phí liên quan đến cách tiếp cận này là quá cao vì hợp đồng Tornado Cash không lưu trữ một cách vĩnh viễn các Bằng chứng ZK đã được nộp trước đó để tránh lãng phí lưu trữ. So sánh mỗi Bằng chứng ZK mới với những bằng chứng đã tồn tại để đảm bảo tính nhất quán tốn nhiều tài nguyên hơn so với việc chỉ ghi lại một mã nhận dạng như nf.
Theo ví dụ mã chức năng rút tiền, các tham số cần thiết và logic kinh doanh như sau: Người dùng gửi Chứng minh ZK, nf (NullifierHash) = Hash(K), và chỉ định địa chỉ người nhận cho quá trình rút tiền. Chứng minh ZK che giấu các giá trị của Cn, K, và r, đảm bảo thế giới bên ngoài không thể xác định danh tính của người dùng. Thông thường, người nhận sẽ chỉ định một địa chỉ sạch, mới để ngăn chặn việc tiết lộ thông tin cá nhân.
Tuy nhiên, một thách thức nhỏ nảy sinh: khi người dùng rút tiền, vì tính không thể theo dõi, họ thường sử dụng địa chỉ mới được tạo ra gần đây để khởi tạo giao dịch rút tiền. Lúc đó, những địa chỉ mới này thiếu ETH để thanh toán phí gas. Do đó, trong quá trình rút tiền, địa chỉ phải mạnh mẽ tuyên bố một người truyền thông để thanh toán phí gas. Sau đó, hợp đồng trộn trừ một phần từ số tiền rút của người dùng để bồi thường cho người truyền thông.
Tóm lại, Tornado Cash có thể che khuất mối liên hệ giữa người gửi tiền và người rút tiền. Khi có một cơ sở người dùng lớn, nó giống như một tên tội phạm trà trộn vào một đám đông nhộn nhịp, khiến các nhà chức trách gặp khó khăn trong việc theo dõi. Quá trình rút tiền sử dụng ZK-SNARK, với phần "nhân chứng" được che giấu chứa thông tin quan trọng về người rút tiền. Đây được cho là tính năng quan trọng nhất của máy trộn. Hiện tại, Tornado có thể là một trong những ứng dụng thông minh nhất liên quan đến ZK.
Giới thiệu:
Gần đây, Vitalik và một số học giả đã cùng nhau công bố một bài báo mới về cách Tornado Cash thực hiện chương trình chống rửa tiền của mình (về cơ bản là cho phép người rút tiền chứng minh rằng lịch sử gửi tiền của họ thuộc một tập hợp không bao gồm tiền bẩn). Tuy nhiên, bài báo thiếu một giải thích tinh tế về logic kinh doanh và nguyên tắc của Tornado Cash, khiến một số đọc giả chỉ có hiểu biết nông cạn về chủ đề.
Đáng lưu ý là các dự án như Tornado, đại diện cho các dự án về quyền riêng tư, thực sự sử dụng khía cạnh không có kiến thức của thuật toán ZK-SNARK. Trong khi đó, hầu hết các giải pháp đưa ra biểu ngữ ZK, như Rollups, chỉ sử dụng sự ngắn gọn của ZK-SNARK. Thường, mọi người thường lẫn lộn Giải pháp Chứng minh Đúng Đắn với ZK, và Tornado là một ví dụ xuất sắc để làm rõ ứng dụng thực tế của ZK.
Tác giả của bài viết này đã viết một bài về nguyên tắc của Tornado vào năm 2022 cho Viện nghiên cứu Web3Caff. Hôm nay, chúng tôi đã trích dẫn và mở rộng trên một số phần của công việc gốc đó để cung cấp một hiểu biết có hệ thống về Tornado Cash.
Liên kết bài viết gốc:https://research.web3caff.com/zh/archives/2663?ref=157
Tornado Cash sử dụng chứng minh không biết như giao thức trộn của mình. Trong khi phiên bản cũ được ra mắt vào năm 2019, một phiên bản beta của mô hình cập nhật đã được triển khai vào cuối năm 2021. Phiên bản trước của Tornado đạt được mức độ phân quyền tốt với các hợp đồng trên chuỗi mở và không bị kiểm soát bởi đa chữ ký. Hơn nữa, mã nguồn frontend là mã nguồn mở và được sao lưu trên mạng lưới IPFS. Do tính đơn giản của phiên bản cũ của Tornado, bài viết này tập trung vào giải thích nó.
Phương pháp chính của Tornado là kết hợp nhiều hành động gửi và rút tiền lại với nhau. Sau khi gửi Token vào Tornado, người gửi cung cấp một bằng chứng ZK để xác minh giao dịch gửi trước đó của họ và sau đó rút tiền bằng cách sử dụng một địa chỉ mới, từ đó phá vỡ liên kết giữa địa chỉ gửi và rút tiền.
Để nói một cách ngắn gọn hơn, hãy tưởng tượng Tornado như một hộp kính đựng đầy tiền xu (Coins) được gửi bởi nhiều người. Chúng ta có thể thấy ai đã gửi tiền xu, nhưng những đồng tiền này rất đồng nhất. Nếu ai đó không quen thuộc lấy một đồng tiền từ hộp, sẽ rất khó để theo dõi xem ai đã gửi đồng tiền đó ban đầu.
(Nguồn ảnh: rareskills)
Các kịch bản như vậy dường như phổ biến. Khi chúng ta SWAP một số ETH từ một hồ bơi Uniswap, không thể xác định ETH của ai chúng ta đã nhận, với số lượng nhà cung cấp thanh khoản cho Uniswap. Tuy nhiên, sự khác biệt nằm ở quy trình. Với Uniswap, việc đổi Tokens đòi hỏi một Token khác có giá trị tương đương, và quỹ không thể được chuyển “riêng tư”. Ngược lại, một máy trộn chỉ đòi hỏi người rút tiền phải trình bày biên nhận gửi tiền của họ.
Để làm cho các hành động gửi tiền và rút tiền trở nên đồng nhất, hồ bơi Tornado duy trì tính nhất quán trong số tiền gửi và rút. Ví dụ, nếu một hồ bơi có 100 người gửi tiền và 100 người rút tiền, mặc dù các hành động này có thể được nhìn thấy công khai, nhưng dường như không có mối liên hệ giữa chúng. Mọi người gửi tiền và rút tiền cùng một số tiền, làm cho việc theo dõi sự di chuyển của quỹ trở nên khó khăn. Rõ ràng, điều này mang lại lợi ích bẩm sinh cho việc rửa tiền.
Câu hỏi then chốt đặt ra là: khi rút tiền, làm thế nào để chứng minh khoản tiền gửi trước đó? Địa chỉ khởi tạo giao dịch rút tiền không liên kết với bất kỳ địa chỉ nào đã gửi tiền, vì vậy làm thế nào để xác minh quyền rút tiền? Phương pháp trực tiếp nhất sẽ là người rút tiền tiết lộ hồ sơ gửi tiền của họ, nhưng điều đó sẽ tiết lộ danh tính của họ. Đây là nơi mà chứng minh không cần biết đến được đưa vào trò chơi.
Với một Bằng chứng ZK, người rút tiền có thể xác nhận họ có một hồ sơ gửi tiền trong hợp đồng Tornado và rằng khoản gửi tiền này chưa được rút. Điều đẹp của bằng chứng zero-knowledge là chúng bảo vệ sự riêng tư. Công chúng chỉ biết rằng người rút tiền thực sự đã gửi tiền nhưng không thể xác định danh tính cụ thể của họ.
Để chứng minh “Tôi đã gửi tiền vào hồ bơi Tornado” có thể dịch thành “Hồ sơ gửi tiền của tôi có thể được tìm thấy trong hợp đồng Tornado.” Nếu Cn biểu thị một hồ sơ gửi tiền, thì với tập hồ sơ gửi tiền của Tornado là {C1, C2,…C100…}, Bob cần chứng minh rằng anh ấy đã sử dụng khóa riêng của mình để tạo ra một hồ sơ trong tập hồ sơ này mà không tiết lộ chính xác Cn cụ thể đó là gì. Điều này sử dụng các thuộc tính duy nhất của Chứng minh Merkle.
Tất cả các bản ghi gửi của Tornado được tổng hợp vào một Cây Merkle được xây dựng trên chuỗi. Đa số các lá này (khoảng 2^20, hơn 1 triệu) vẫn trống (với giá trị ban đầu). Mỗi khoản gửi mới cập nhật một lá cam kết tương ứng và sau đó là gốc của cây.
Ví dụ, nếu tiền gửi của Bob là giao dịch thứ 10,000 trong lịch sử của Tornado, giá trị liên quan Cn sẽ là lá thứ 10,000 của cây, tức là C10000 = Cn. Hợp đồng sau đó sẽ tự động tính toán Root mới.
(图源:RareSkills)
Chứng minh Merkle một cách súc tích và hiệu quả. Để chứng minh giao dịch TD tồn tại trong Mạng lưới Merkle, chỉ cần cung cấp bằng chứng Merkle phù hợp, vẫn rất nhỏ gọn ngay cả khi Mạng lưới Merkle rất lớn.
Để xác minh rằng một giao dịch, ví dụ H3, thực sự được bao gồm trong Merkle Tree, người ta phải chứng minh rằng việc sử dụng H3 và dữ liệu khác từ Merkle Tree có thể tạo ra Root. Dữ liệu này (bao gồm cả Td) tạo thành Merkle Proof. Khi Bob muốn rút tiền, anh ấy cần xác minh hai điều:
·Cn được xây dựng trên Merkle Tree trên chuỗi bởi Tornado, trong đó anh ta có thể xây dựng một Merkle Proof chứa Cn;
·Cn liên quan đến phiếu gửi của Bob.
Trong mã nguồn frontend của giao diện người dùng của Tornado, nhiều chức năng đã được triển khai trước. Khi một người nộp tiền mở trang web Tornado Cash và nhấp vào nút gửi tiền, mã nguồn frontend đính kèm tạo ra hai số ngẫu nhiên, K và r, cục bộ. Sau đó, nó tính giá trị của Cn=Hash(K, r), truyền Cn (được gọi là cam kết trong sơ đồ dưới đây) vào hợp đồng Tornado để được tích hợp vào cây Merkle của nó. Đơn giản nói, K và r hoạt động như các khóa riêng tư. Chúng quan trọng, và người dùng được khuyến khích lưu trữ chúng một cách an toàn, vì chúng sẽ được yêu cầu lại trong quá trình rút tiền sau này.
Một “encryptedNote” là một tính năng tùy chọn cho phép người dùng mã hóa các chứng chỉ K và r bằng một khóa riêng và lưu trữ chúng trên chuỗi để ngăn không quên.
Đáng chú ý rằng tất cả các hoạt động trên đều diễn ra ngoại chuỗi, có nghĩa là cả hợp đồng Tornado lẫn bất kỳ quan sát ngoài nào đều không nhận biết K và r. Nếu K và r bị tiết lộ, đó tương đương với việc mất khóa riêng tư của ví của mình.
Khi nhận được tiền gửi của người dùng và Cn = Băm (K, r) được tính toán, hợp đồng Tornado đặt Cn ở mức cơ sở của Cây Merkle, biến nó thành một nút lá mới và sau đó cập nhật giá trị của gốc. Tuy nhiên, điều quan trọng là phải hiểu rằng lá của Merkle Tree này không được ghi lại trong trạng thái của hợp đồng mà chỉ được ghi lại dưới dạng tham số sự kiện trong các khối trước đây. Hợp đồng Tornado chỉ ghi lại gốc Merkle. Trong quá trình rút tiền, người dùng có thể chứng minh, thông qua Merkle Proof, rằng hồ sơ tiền gửi tương ứng với gốc Merkle hiện tại, một khái niệm hơi giống với rút tiền cầu nối xuyên chuỗi của khách hàng nhẹ. Thiết kế này cho thấy sự khéo léo của Tornado: để tiết kiệm chi phí khí đốt, cây Merkle đầy đủ không được ghi vào trạng thái của hợp đồng, chỉ có gốc của nó. Lá của cây được ghi lại đơn giản trong các hồ sơ khối lịch sử, một cơ chế tương tự như nguyên tắc tiết kiệm khí của Rollup (mặc dù các chi tiết khác nhau).
Trong quá trình rút tiền, người rút nhập thông tin/ khóa riêng (số ngẫu nhiên K và r được tạo ra trong quá trình gửi tiền) trên trang web giao diện người dùng. Mã nguồn giao diện Tornado Cash sử dụng K và r, Cn=Hash(K, r), và Chứng minh Merkle tương ứng với Cn để tạo ra Chứng minh ZK, xác nhận rằng Cn tương ứng với một bản ghi gửi tiền trên cây Merkle và rằng K và r là thông tin/ khóa hợp lệ cho Cn. Bước này về cơ bản chứng minh sự hiểu biết về khóa của một bản ghi gửi tiền trên cây Merkle. Khi Chứng minh ZK được gửi đến hợp đồng Tornado, tất cả bốn tham số đều được che giấu, đảm bảo rằng bên ngoài, bao gồm cả hợp đồng Tornado chính nó, đều không biết, từ đó bảo vệ quyền riêng tư của người dùng.
Một chi tiết thú vị là thao tác gửi tiền sử dụng hai số ngẫu nhiên, K và r, để tạo ra Cn thay vì chỉ sử dụng một số ngẫu nhiên vì một số ngẫu nhiên đơn lẻ có thể không đủ an toàn và có thể bị tấn công bằng cách thử mật khẩu.
Về ký hiệu “A” trong hình minh họa, nó đại diện cho địa chỉ nhận rút và được cung cấp bởi người rút tiền. Trong khi đó, “nf” là một bộ nhận dạng được thiết lập để ngăn chặn các cuộc tấn công phát lại, giá trị của nó được xác định như nf=Hash(K), trong đó K là một trong hai số ngẫu nhiên (K và r) được sử dụng trong quá trình gửi tiền để tạo ra Cn. Do đó, mỗi Cn có một nf tương ứng, và hai cái này được kết nối một cách duy nhất.
Tại sao cần ngăn chặn cuộc tấn công phát lại? Do các tính năng thiết kế của mixer, khi rút tiền, không rõ tiền gửi nào trong Merkle Tree tương ứng với số tiền đã rút. Khi mối quan hệ giữa người gửi tiền và số tiền đã rút vẫn mơ hồ, người dùng độc hại có thể lợi dụng điều này và liên tục rút tiền từ mixer, làm cạn kiệt nguồn tiền.
Ở đây, nhận dạng nf hoạt động tương tự như bộ đếm giao dịch “nonce” ẩn trong mỗi địa chỉ Ethereum, được thiết lập để ngăn chặn việc giao dịch được lặp lại. Khi yêu cầu rút tiền, người dùng phải gửi một nf. Hệ thống kiểm tra xem nf này đã được sử dụng trước đó chưa: nếu có, yêu cầu rút tiền sẽ bị vô hiệu; nếu không, quá trình rút tiền tiếp tục và nf được ghi lại, đảm bảo việc sử dụng sau này sẽ dẫn đến việc vô hiệu hóa.
Một số người có thể tự hỏi: Liệu có ai có thể tạo ra một nf mà hợp đồng không ghi nhận? Điều đó khó xảy ra. Trong quá trình tạo ZK Proof, việc đảm bảo nf=Hash(K) và số ngẫu nhiên K liên kết với ghi chú gửi tiền Cn là rất quan trọng. Nếu ai đó tạo một nf một cách tùy ý, nó sẽ không khớp với bất kỳ ghi chú nào đã được ghi nhận, từ đó làm cho việc tạo ra một ZK Proof hợp lệ là không thể, dẫn đến quá trình rút tiền bị trì hoãn.
Người khác có thể đặt câu hỏi: Có cách nào để tránh việc sử dụng nf không? Vì những người rút tiền phải nộp một bằng chứng ZK, chứng minh mối quan hệ của họ với một Cn cụ thể, liệu có đủ để kiểm tra xem một Bằng chứng ZK tương ứng đã được ghi trên chuỗi chính chưa? Tuy nhiên, các chi phí liên quan đến cách tiếp cận này là quá cao vì hợp đồng Tornado Cash không lưu trữ một cách vĩnh viễn các Bằng chứng ZK đã được nộp trước đó để tránh lãng phí lưu trữ. So sánh mỗi Bằng chứng ZK mới với những bằng chứng đã tồn tại để đảm bảo tính nhất quán tốn nhiều tài nguyên hơn so với việc chỉ ghi lại một mã nhận dạng như nf.
Theo ví dụ mã chức năng rút tiền, các tham số cần thiết và logic kinh doanh như sau: Người dùng gửi Chứng minh ZK, nf (NullifierHash) = Hash(K), và chỉ định địa chỉ người nhận cho quá trình rút tiền. Chứng minh ZK che giấu các giá trị của Cn, K, và r, đảm bảo thế giới bên ngoài không thể xác định danh tính của người dùng. Thông thường, người nhận sẽ chỉ định một địa chỉ sạch, mới để ngăn chặn việc tiết lộ thông tin cá nhân.
Tuy nhiên, một thách thức nhỏ nảy sinh: khi người dùng rút tiền, vì tính không thể theo dõi, họ thường sử dụng địa chỉ mới được tạo ra gần đây để khởi tạo giao dịch rút tiền. Lúc đó, những địa chỉ mới này thiếu ETH để thanh toán phí gas. Do đó, trong quá trình rút tiền, địa chỉ phải mạnh mẽ tuyên bố một người truyền thông để thanh toán phí gas. Sau đó, hợp đồng trộn trừ một phần từ số tiền rút của người dùng để bồi thường cho người truyền thông.
Tóm lại, Tornado Cash có thể che khuất mối liên hệ giữa người gửi tiền và người rút tiền. Khi có một cơ sở người dùng lớn, nó giống như một tên tội phạm trà trộn vào một đám đông nhộn nhịp, khiến các nhà chức trách gặp khó khăn trong việc theo dõi. Quá trình rút tiền sử dụng ZK-SNARK, với phần "nhân chứng" được che giấu chứa thông tin quan trọng về người rút tiền. Đây được cho là tính năng quan trọng nhất của máy trộn. Hiện tại, Tornado có thể là một trong những ứng dụng thông minh nhất liên quan đến ZK.