Đi sâu vào Tornado.Cash cho thấy các cuộc tấn công linh hoạt của các dự án zkp

金色财经_

Trong bài viết trước, chúng tôi đã giải thích các lỗ hổng về tính linh hoạt trong chính hệ thống bằng chứng Groth16 từ góc độ nguyên tắc. Trong bài viết này, chúng tôi sẽ lấy dự án Tornado.Cash làm ví dụ, sửa đổi một số mạch và mã của nó, giới thiệu cuộc tấn công tính linh hoạt Tôi hy vọng rằng các bên dự án zkp khác cũng sẽ chú ý đến các biện pháp phòng ngừa tương ứng trong dự án. Trong số đó, Tornado.Cash sử dụng thư viện snarkjs để phát triển, cũng dựa trên quy trình phát triển sau đây và sẽ được giới thiệu trực tiếp sau, nếu bạn chưa quen với thư viện, vui lòng đọc bài viết đầu tiên trong loạt bài này. (Beosin | Phân tích chuyên sâu về lỗ hổng zk-SNARK bằng chứng không có kiến thức: Tại sao hệ thống bằng chứng không có kiến thức không thể đánh lừa được?)! [346a815b39293aee95668fb9b2049873] (https://img-cdn.gateio.im/resized-social/moments-40baef27dd-96823e64cc-dd1a6f-1c6801 “7076908”)

(Nguồn:

1 Kiến trúc Tornado.Cash

Quá trình tương tác của Tornado.Cash chủ yếu bao gồm 4 thực thể: * Người dùng: Sử dụng DApp này để thực hiện các giao dịch bảo mật với bộ trộn, bao gồm tiền gửi và rút tiền.

  • Trang web: Trang web mặt trước của DApp, chứa một số nút người dùng.
  • Bộ chuyển tiếp: Để ngăn các nút trên chuỗi ghi lại thông tin như địa chỉ IP đã bắt đầu giao dịch riêng tư, máy chủ sẽ phát lại giao dịch thay vì người dùng, nâng cao hơn nữa quyền riêng tư.
  • Hợp đồng: Chứa hợp đồng ủy quyền Tornado.Cash Proxy, sẽ chọn nhóm Tornado được chỉ định cho các hoạt động gửi và rút tiền tiếp theo tùy theo số tiền gửi và rút của người dùng. Hiện tại có 4 nhóm với số tiền là 0,1, 1, 10 và 100.

Trước tiên, Người dùng thực hiện các thao tác tương ứng trên trang web giao diện người dùng của Tornado.Cash để kích hoạt giao dịch gửi tiền hoặc rút tiền, sau đó Người chuyển tiếp chuyển tiếp yêu cầu giao dịch tới hợp đồng Proxy Tornado.Cash trên chuỗi và chuyển tiếp nó tới giao dịch tương ứng. Nhóm theo số tiền giao dịch và cuối cùng Để xử lý tiền gửi và rút tiền, cấu trúc cụ thể như sau: ! [f471dfca152796f84a6389ff3a6d96ac] (https://img-cdn.gateio.im/resized-social/moments-40baef27dd-fa8e75c3b3-dd1a6f-1c6801 “7076909”) Là một bộ trộn tiền tệ, Tornado.Cash có hai chức năng kinh doanh cụ thể: * gửi tiền: Khi người dùng tiến hành giao dịch gửi tiền, trước tiên anh ấy chọn mã thông báo đã gửi (BNB, ETH, v.v.) và số tiền tương ứng trên trang web giao diện người dùng. Để đảm bảo tốt hơn quyền riêng tư của người dùng, chỉ có thể gửi bốn số tiền;

! [fc9fe4cf9b1b8528e2446d23f39afc9a] (https://img-cdn.gateio.im/resized-social/moments-40baef27dd-22994b5b68-dd1a6f-1c6801 “7076910”)

Nguồn: <

Sau đó, máy chủ sẽ tạo hai số ngẫu nhiên 31 byte số vô hiệu hóa và bí mật, sau khi ghép chúng lại với nhau, thực hiện thao tác pedersenHash để lấy cam kết và trả lại số vô hiệu hóa+bí mật cộng với tiền tố dưới dạng ghi chú cho người dùng, như minh họa trong phần sau nhân vật: ! [83feeca678c53c26a5cfe70f55d29f10] (https://img-cdn.gateio.im/resized-social/moments-40baef27dd-64aba8733a-dd1a6f-1c6801 “7076911”)* Sau đó, một giao dịch tiền gửi được bắt đầu để gửi cam kết và các dữ liệu khác tới Tornado.Cash Proxy hợp đồng trên chuỗi , hợp đồng proxy chuyển tiếp dữ liệu đến Nhóm tương ứng theo số tiền gửi và cuối cùng, hợp đồng Nhóm sẽ chèn cam kết dưới dạng nút lá vào cây merkle và lưu trữ gốc được tính trong hợp đồng Nhóm.

  • rút tiền: Khi người dùng thực hiện giao dịch rút tiền, trước tiên hãy nhập dữ liệu ghi chú và địa chỉ nhận được trả lại khi nhập tiền gửi trên trang web giao diện người dùng;

! [49898b341e39bdbebd651b5d3918faef] (https://img-cdn.gateio.im/resized-social/moments-40baef27dd-54deb436c4-dd1a6f-1c6801 “7076912”)

Nguồn hình ảnh: <* Sau đó, máy chủ sẽ truy xuất tất cả các sự kiện gửi tiền của Tornadocash trong chuỗi, trích xuất các cam kết để xây dựng cây Merkle theo chuỗi và tạo cam kết và Merkle tương ứng theo dữ liệu ghi chú (nullifier+secret) đã cho bởi Đường dẫn người dùng và gốc tương ứng được sử dụng làm đầu vào mạch để có được bằng chứng SNARK không có kiến thức, cuối cùng, một giao dịch rút tiền được bắt đầu với hợp đồng Tornado.Cash Proxy trên chuỗi, sau đó chuyển sang hợp đồng Pool tương ứng để xác minh bằng chứng theo các tham số và Tiền được ghi có vào địa chỉ của người nhận do người dùng chỉ định.

Trong số đó, cốt lõi rút tiền của Tornado.Cash thực sự là để chứng minh rằng một cam kết nhất định tồn tại trên cây Merkle mà không làm lộ bộ vô hiệu hóa và bí mật do người dùng nắm giữ. Cấu trúc cây Merkle cụ thể như sau: ! [a56d827c9d275989d6948e23280123ce] (https://img-cdn.gateio.im/resized-social/moments-40baef27dd-2215203ef5-dd1a6f-1c6801 “7076913”)## 2 Phiên bản sửa đổi ma thuật Tornado.Cash

2.1 Thay đổi ma thuật Tornado.Cash

Đối với bài viết đầu tiên Nguyên tắc tấn công tính linh hoạt của Groth16, chúng ta biết rằng kẻ tấn công thực sự có thể tạo ra nhiều Bằng chứng khác nhau bằng cách sử dụng cùng một bộ vô hiệu hóa và bí mật. . **Trước khi Tornado.Cash được sửa đổi kỳ diệu, bài viết này trước tiên giới thiệu mã trong Pool nơi Tornado.Cash cuối cùng xử lý việc rút tiền:

/** @dev Rút tiền đặt cọc từ hợp đồng. bằng chứng là dữ liệu bằng chứng zkSNARK và đầu vào là một mảng mạch đầu vào công khai Mảng đầu vào bao gồm: - gốc merkle của tất cả các khoản tiền gửi trong hợp đồng - hàm băm của bộ vô hiệu hóa tiền gửi duy nhất để ngăn chi tiêu hai lần - người nhận tiền - phí tùy chọn áp dụng cho người gửi giao dịch (thường là chuyển tiếp) */ chức năng rút tiền( bytes calldata _proof, bytes32 _root, bytes32 _nullifierHash, địa chỉ phải trả _recipient, địa chỉ phải trả _relayer, uint256 _fee, uint256 _refund ) phải trả bên ngoài nonReentrant { require(_fee <= mệnh giá, “Phí vượt giá trị chuyển nhượng”); require(!nullifierHashes[_nullifierHash], “Ghi chú đã được sử dụng”); require(isKnownRoot(_root), “Không thể tìm thấy merkle root của bạn”); // Đảm bảo sử dụng yêu cầu gần đây ( verifier.verifyProof( _proof, [uint256(_root), uint256(_nullifierHash), uint256(_recipient), uint256(_relayer), _fee, _refund] ) , “Bằng chứng rút tiền không hợp lệ” ); nullifierHashes[_nullifierHash] = true; _ processWithdraw(_recipient, _relayer, _fee, _refund); phát ra Rút tiền (_recipient, _nullifierHash, _relayer, _fee); }

Trong hình trên, để ngăn những kẻ tấn công sử dụng cùng một Bằng chứng để thực hiện các cuộc tấn công chi tiêu gấp đôi mà không làm lộ bí mật và bộ vô hiệu hóa, Tornado.Cash thêm một nullifierHash tín hiệu công khai vào mạch, thu được bằng cách băm Pedersen của bộ vô hiệu hóa và có thể được sử dụng làm tham số Được truyền cho chuỗi, sau đó, hợp đồng Pool sẽ sử dụng biến này để xác định xem Bằng chứng chính xác đã được sử dụng hay chưa. Tuy nhiên, nếu bên dự án không sử dụng phương pháp sửa đổi mạch mà trực tiếp ghi lại phương pháp Bằng chứng để ngăn chặn chi tiêu gấp đôi, xét cho cùng, điều này có thể giảm bớt các hạn chế về mạch và tiết kiệm chi phí, nhưng liệu nó có thể đạt được mục tiêu không? Đối với phỏng đoán này, bài viết này sẽ xóa tín hiệu công khai nullifierHash mới được thêm vào trong mạch và thay đổi xác minh hợp đồng thành xác minh Bằng chứng. Vì Tornado.Cash lấy tất cả các sự kiện gửi tiền mỗi khi rút tiền, xây dựng cây merkle và sau đó xác minh xem giá trị gốc được tạo có nằm trong 30 mới nhất hay không, toàn bộ quá trình này quá rắc rối, vì vậy mạch trong bài viết này cũng sẽ xóa mạch merkleTree , Chỉ còn lại mạch lõi của phần rút, mạch cụ thể như sau:

bao gồm “…/…/…/…/node_modules/circomlib/circuits/bitify.circom”; bao gồm “…/…/…/…/node_modules/circomlib/circuits/pedersen.circom”;// tính toán mẫu Pedersen(nullifier + secret)CommitHasher() { bộ vô hiệu hóa đầu vào tín hiệu; bí mật đầu vào tín hiệu; cam kết đầu ra tín hiệu; // đầu ra tín hiệu nullifierHash; // xóa thành phần commitHasher = Pedersen(496); // thành phần nullifierHasher = Pedersen(248); thành phần nullifierBits = Num2Bits(248); thành phần secretBits = Num2Bits(248); nullifierBits.in <== nullifier; secretBits.in <== bí mật; cho ( i = 0; i < 248; i++) { // nullifierHasher.in [i] <== nullifierBits.out [i] ; // xóa cam kếtHasher.in [i] <== nullifierBits.out [i] ; cam kếtHasher.in[i + 248] <== secretBits.out [i] ; } cam kết <== cam kếtHasher.out [0] ; // nullifierHash <== nullifierHasher.out [0] ; // xóa} // Xác minh rằng cam kết tương ứng với bí mật đã cho và bộ vô hiệu hóa được bao gồm trong cây merkle của cam kết đầu ra tín hiệu tiền gửi; người nhận đầu vào tín hiệu; // không tham gia vào bất kỳ tính toán nào tín hiệu đầu vào relayer; // không tham gia bất kỳ khoản phí đầu vào tín hiệu tính toán nào; // không tham gia vào bất kỳ khoản hoàn trả đầu vào tín hiệu tính toán nào; // không tham gia vào bất kỳ bộ khử tín hiệu đầu vào tính toán nào; bí mật đầu vào tín hiệu; máy băm thành phần = Cam kếtHasher(); hasher.nullifier <== nullifier; hasher.secret <== bí mật; cam kết <== hasher.commitment; // Thêm các tín hiệu ẩn để đảm bảo rằng việc giả mạo người nhận hoặc phí sẽ làm mất hiệu lực bằng chứng lừa đảo // Nhiều khả năng là không bắt buộc, nhưng tốt hơn hết là bạn nên giữ an toàn và chỉ mất 2 ràng buộc // Ô vuông được sử dụng để ngăn chặn trình tối ưu hóa khỏi việc loại bỏ các ràng buộc đó người nhận tín hiệuSquare; phí tín hiệuSquare; bộ chuyển tiếp tín hiệuSquare; hoàn trả tín hiệuSquare; người nhậnSquare <== người nhận * người nhận; phíSquare <== phí * phí; relayerSquare <== relayer * relayer; hoàn tiềnSquare <== hoàn tiền * hoàn tiền;}thành phần chính = Rút tiền (20);

Lưu ý: Trong quá trình thử nghiệm, chúng tôi đã phát hiện ra TornadoCash trong phiên bản mới nhất của mã trong GitHub (mạch rút tiền thiếu tín hiệu đầu ra và yêu cầu chỉnh sửa thủ công để chạy chính xác. Theo mạch đã sửa đổi ở trên, hãy sử dụng thư viện snarkjs, v.v. để thực hiện theo quy trình phát triển được đưa ra ở đầu bài viết này từng bước và tạo ra Bằng chứng thông thường sau, được ghi là bằng chứng1:

Bằng chứng: { pi_a: [ 12731245758885665844440940942625335911548255472545721927606279036884288780352n, 11029567045033340566548367893304 052946457319632960669053932271922876268005970n, 1n ], pi_b: [ [ 442467028355646562219718754675409466783738316647961547451518218 3878046002081n, 8088104569927474555610665242983621221932062943927262293572649061565902268616n ], [ 9194248463115986940359811 988096155965376840166464829609545491502209803154186n, 183731390739816966551368706658003939861308764981288870910870600683698 11557306n ], [ 1n, 0n ] ], pi_c: [ 1626407734863381433630916916203225704171957179582436403191883565668143772631n, 1037520490212 5491773178253544576299821079735144068419595539416984653646546215n, 1n ], giao thức: 'groth16 ', đường cong: ‘bn128’}

2.2 Kiểm chứng bằng thực nghiệm

2.2.1 Bằng chứng xác minh—hợp đồng mặc định được tạo bởi Circom

Trước hết, chúng tôi sử dụng hợp đồng mặc định được tạo bởi circom để xác minh.Vì hợp đồng không ghi lại bất kỳ thông tin nào liên quan đến Bằng chứng đã được sử dụng, nên kẻ tấn công có thể phát lại bằng chứng1 nhiều lần để gây ra một cuộc tấn công chi tiêu gấp đôi. Trong các thí nghiệm sau, bằng chứng có thể được phát lại vô tận cho cùng một đầu vào của cùng một mạch và tất cả chúng đều có thể vượt qua xác minh. ! [caaf8474774d0ffaaea894961231e604] (https://img-cdn.gateio.im/resized-social/moments-40baef27dd-36bda9ebd9-dd1a6f-1c6801 “7076914”) Ảnh bên dưới là ảnh chụp màn hình thử nghiệm sử dụng bằng chứng1 trong hợp đồng mặc định để chứng minh rằng việc xác minh đã thông qua, bao gồm bài viết trước Các tham số chứng minh A, B và C được sử dụng trong , và kết quả cuối cùng:

! [8796de83786dab2e1d2fe8988a2a8c3c] (https://img-cdn.gateio.im/resized-social/moments-40baef27dd-1d87d37558-dd1a6f-1c6801 “7076915”)

Hình bên dưới là kết quả của việc sử dụng cùng một bằng chứng 1 để gọi hàm verifyProof nhiều lần để xác minh bằng chứng. Thử nghiệm cho thấy rằng đối với cùng một đầu vào, bất kể kẻ tấn công sử dụng bằng chứng 1 để xác minh bao nhiêu lần thì nó vẫn có thể vượt qua: ! [058bfa45cfac5803990db4cb707c737b] (https://img-cdn.gateio.im/resized-social/moments-40baef27dd-6f3b277d3a-dd1a6f-1c6801 “7076916”) Tất nhiên, chúng tôi đang thử nghiệm trong thư viện mã js gốc của snarkjs và chúng tôi chưa thử nghiệm Bằng chứng đã được sử dụng đã được thông qua để bảo vệ, kết quả thử nghiệm như sau: ## 2.2.2 Bằng chứng xác minh - Hợp đồng chống phát lại thông thường

Đối với lỗ hổng lặp lại trong hợp đồng mặc định do circom tạo ra, bài viết này ghi lại một giá trị trong Bằng chứng chính xác (proof1) đã được sử dụng để ngăn chặn các cuộc tấn công lặp lại bằng cách sử dụng bằng chứng đã xác minh, như thể hiện trong hình sau: ! [9afeb481747b16752a00b70c5562bac2] (https://img-cdn.gateio.im/resized-social/moments-40baef27dd-384e9b2889-dd1a6f-1c6801 “7076917”) Tiếp tục sử dụng Proof1 để xác minh. Thử nghiệm cho thấy rằng khi sử dụng cùng một Proof để xác minh phụ, giao dịch hoàn nguyên Lỗi: “Tiền đã được chi”, kết quả như trong hình bên dưới: ! [40293d602538a60400dffa795e0454dd] (https://img-cdn.gateio.im/resized-social/moments-40baef27dd-d09fb1e9c2-dd1a6f-1c6801 “7076918”) Nhưng Mặc dù mục đích ngăn chặn các cuộc tấn công lặp lại bằng chứng thông thường đã đạt được vào thời điểm này, nhưng phần giới thiệu trước Có một vấn đề về lỗ hổng độ dẻo trong thuật toán groth16 và biện pháp phòng ngừa này vẫn có thể được bỏ qua. Vì vậy, chúng tôi xây dựng PoC trong hình bên dưới và tạo chứng chỉ zk-SNARK giả cho cùng một đầu vào theo thuật toán trong bài viết đầu tiên. Thử nghiệm cho thấy rằng nó vẫn có thể vượt qua xác minh. Mã PoC để tạo bằng chứng giả mạo bằng chứng2 như sau:

nhập WasmCurve từ "/Users/saya/node_modules/ffjava/src/wasm_curve.js"nhập ZqField từ "/Users/saya/node_modules/ffjava/src/f1field.js"nhập groth16FullProve từ "/Users /saya/node_modules/snarkjs/src/groth16_fullprove.js"nhập groth16Verify từ “/Users/saya/node_modules/snarkjs/src/groth16_verify.js”;nhập * dưới dạng đường cong từ “/Users /saya/node_modules/snarkjs/src/curves.js”;nhập fs từ “fs”;nhập { utils } từ “ffjava”;const {unstringifyBigInts} = utils;groth16_exp();async function groth16_exp (){ cho inputA = “7”; cho inputB = “11”; const SNARK_FIELD_SIZE = BigInt(‘21888242871839275222246405745257275088548364400416034343698204186575808495617’); // 2. 读取string后转化为int const proof = await unstringifyBigInts(JSON.parse(fs.readFileSync(“proof.json”,“utf8”))); console.log(“Bằng chứng:”,bằng chứng); // 生成逆元,生成的逆元必须在F1域 const F = new ZqField(SNARK_FIELD_SIZE); // const F = new F2Field(SNARK_FIELD_SIZE); const X = Fe(“123456”) const invX = F.inv(X) console.log(“x:” ,X ) console.log(“invX” ,invX) console.log(“Thời gian của thang đo là:”, F.mul(X,invX)) // 读取椭圆曲线G1、G2点 const vKey = JSON.parse(fs.readFileSync(“verification_key.json”,“utf8”)); // console.log(“Đường cong là:”,vKey); đường cong const = đang chờ đường cong.getCurveFromName(vKey.curve); const G1 = đường cong.G1; const G2 = cong.G2; const A = G1.fromObject(proof.pi_a); const B = G2.fromObject(proof.pi_b); const C = G1.fromObject(proof.pi_c); const new_pi_a = G1.timesScalar(A, X); // A’=x*A const new_pi_b = G2.timesScalar(B, invX); //B’=x^{-1}*B proof.pi_a = G1.toObject(G1.toAffine(A)); proof.new_pi_a = G1.toObject(G1.toAffine(new_pi_a)) proof.new_pi_b = G2.toObject(G2.toAffine(new_pi_b)) // 将生成G1、G2 không có bảng điều khiển bằng chứng console.log(“proof.pi_a:”,proof.pi_a); console.log(“proof.new_pi_a:”,proof.new_pi_a) console.log(“proof.new_pi_b:”,proof.new_pi_b)}

Proof2 bằng chứng giả mạo đã tạo được hiển thị trong hình bên dưới: Khi sử dụng tham số này để gọi lại hàm verifyProof để xác minh bằng chứng, thử nghiệm nhận thấy rằng xác minh bằng chứng 2 được thông qua một lần nữa trong trường hợp có cùng đầu vào, như minh họa bên dưới: ! [03d0f119ea666620685b4cece791a789] (https://img-cdn.gateio.im/resized-social/moments-40baef27dd-4c49de3755-dd1a6f-1c6801 “7076919”) Mặc dù bằng chứng giả mạo2 chỉ có thể được sử dụng lại một lần nhưng do số lượng giả mạo gần như vô hạn bằng chứng, vì vậy nó có thể khiến tiền hợp đồng bị rút vô hạn. Bài viết này cũng sử dụng mã js của thư viện circom để kiểm tra và kết quả thử nghiệm bằng chứng 1 và bằng chứng giả mạo 2 có thể vượt qua xác minh: ! [9153431b50b81dcadbf68930ded584c3] (https://img-cdn.gateio.im/resized-social/moments-40baef27dd-934c4ab3e4-dd1a6f-1c6801 “7076920”)## 2.2.3 Bằng chứng xác minh — Hợp đồng chơi lại Tornado.Cash

Sau rất nhiều lần thất bại, không có cách nào để làm điều đó một lần và mãi mãi sao? Ở đây, theo thông lệ Tornado.Cash xác minh xem đầu vào ban đầu đã được sử dụng hay chưa, bài viết này tiếp tục sửa đổi mã hợp đồng như sau: ! [28fabffcac9037a41e030db84f44f83b] (https://img-cdn.gateio.im/resized-social/moments-40baef27dd-dfa6f29d35-dd1a6f-1c6801 “7076921”) Cần lưu ý rằng, để chứng minh các biện pháp đơn giản nhằm ngăn chặn cuộc tấn công dễ uốn nắn của thuật toán groth16, *\ *Bài viết này áp dụng phương pháp ghi trực tiếp đầu vào mạch gốc, nhưng điều này không tuân theo nguyên tắc bảo mật của bằng chứng không kiến thức và đầu vào mạch phải được giữ bí mật. **Ví dụ: đầu vào trong Tornado.Cash hoàn toàn là riêng tư và cần thêm một đầu vào công khai mới để xác định Bằng chứng. Trong bài báo này do mạch không có logo mới nên tính riêng tư tương đối kém so với Tornado.Cash, chỉ dùng làm demo thử nghiệm cho kết quả như sau: ! [ac4624fc066156979fae817e327c6224] (https://img-cdn.gateio.im/resized-social/moments-40baef27dd-440d87e977-dd1a6f-1c6801 “7076922”) Có thể thấy rằng Bằng chứng sử dụng cùng thông tin đầu vào trong hình trên chỉ có thể vượt qua bằng chứng 1 cho lần đầu tiên , thì cả bằng chứng 1 và bằng chứng giả mạo 2 đều không thể vượt qua quá trình xác minh. ## 3 Tóm tắt và Đề xuất

Bài báo này chủ yếu xác minh tính xác thực và tác hại của lỗ hổng phát lại bằng cách sửa đổi mạch của TornadoCash và sử dụng hợp đồng mặc định do Circcom tạo ra, thường được các nhà phát triển sử dụng và xác minh thêm rằng các biện pháp phổ biến được sử dụng ở cấp độ hợp đồng có thể bảo vệ chống lại việc phát lại Về vấn đề này, chúng tôi khuyên các dự án bằng chứng không kiến thức nên chú ý đến những điều sau trong quá trình phát triển dự án: * Không giống như các DApp truyền thống sử dụng dữ liệu duy nhất như địa chỉ để tạo dữ liệu nút, zkp các dự án thường sử dụng tổ hợp các số ngẫu nhiên Để tạo các nút cây Merkle, bạn cần chú ý xem logic nghiệp vụ có cho phép chèn các nút có cùng giá trị hay không. **Bởi vì cùng một dữ liệu nút lá có thể khiến một số tiền của người dùng bị khóa trong hợp đồng hoặc có nhiều Bằng chứng Merkle trong cùng một dữ liệu nút lá gây nhầm lẫn logic nghiệp vụ. **

  • Bên dự án zkp thường sử dụng ánh xạ để ghi lại Bằng chứng đã sử dụng để ngăn chặn các cuộc tấn công chi tiêu gấp đôi. ** Cần lưu ý rằng khi phát triển với Groth16, do tồn tại các cuộc tấn công dẻo, dữ liệu gốc của nút phải được sử dụng cho bản ghi, thay vì chỉ nhận dạng dữ liệu liên quan đến Bằng chứng. **
  • Các mạch phức tạp có thể có các vấn đề như tính không chắc chắn của mạch và các ràng buộc dưới mức, các điều kiện xác minh hợp đồng không đầy đủ và có những kẽ hở trong logic triển khai. ra mắt.Các công ty kiểm toán bảo mật tiến hành kiểm toán toàn diện để đảm bảo an ninh dự án càng nhiều càng tốt. **
Xem bản gốc
Tuyên bố miễn trừ trách nhiệm: Thông tin trên trang này có thể đến từ bên thứ ba và không đại diện cho quan điểm hoặc ý kiến của Gate. Nội dung hiển thị trên trang này chỉ mang tính chất tham khảo và không cấu thành bất kỳ lời khuyên tài chính, đầu tư hoặc pháp lý nào. Gate không đảm bảo tính chính xác hoặc đầy đủ của thông tin và sẽ không chịu trách nhiệm cho bất kỳ tổn thất nào phát sinh từ việc sử dụng thông tin này. Đầu tư vào tài sản ảo tiềm ẩn rủi ro cao và chịu biến động giá đáng kể. Bạn có thể mất toàn bộ vốn đầu tư. Vui lòng hiểu rõ các rủi ro liên quan và đưa ra quyết định thận trọng dựa trên tình hình tài chính và khả năng chấp nhận rủi ro của riêng bạn. Để biết thêm chi tiết, vui lòng tham khảo Tuyên bố miễn trừ trách nhiệm.
Bình luận
0/400
Không có bình luận