完全指南:opaque bytes32 的不變量、誤區與正確用法)
fhEVM 密文句柄Handle完全指南opaque bytes32 的不變量、誤區與正確用法【免費下載鏈接】fhevmFHEVM, a full-stack framework for integrating Fully Homomorphic Encryption (FHE) with blockchain applications項目地址: https://gitcode.com/GitHub_Trending/fh/fhevm導讀在 fhEVMFully Homomorphic Encryption Virtual Machine中鏈上每一個加密值——euint8、ebool、eaddress等——都通過一個 32 字節的句柄handle被引用。句柄是 Solidity 合約中唯一能夠持有并傳遞的加密對象形態FHE 運算以句柄為輸入、以句柄為輸出ACL訪問控制列表也逐句柄實施權限。本文以 docs/solidity-guides/handles.md 為核心結合 host-contracts/lib/FHE.sol 與 host-contracts/lib/Impl.sol 等源碼實現系統講解句柄的定義、協議保證的不變量、常見編程誤區以及如何在合約中安全地判斷兩個加密值是否相等。讀完本文你將清楚哪些關于句柄的假設是安全的、哪些一定會出錯并能寫出不依賴句柄具體取值的高健壯性合約。句柄是什么鏈上鏈下的分層模型要理解句柄先要分清四個容易混淆的概念。它們分處不同位置職責完全不同術語是什么存在于哪里明文Plaintext真實的未加密值例如數字42decrypt(...)返回的就是它鏈下僅在獲得授權的解密之后密文CiphertextFHE 產生的加密數據塊協處理器存儲并在此基礎上計算任何時刻都可以被重新隨機化re-randomize而不改變明文鏈下由協處理器持有句柄Handle一個 32 字節的鏈上標識符指向某個具體的明文密文對你的 Solidity 代碼持有和傳遞的就是句柄鏈上計算Computation產生某個句柄的 FHE 操作序列例如FHE.add(a, b)兩個不同的計算可能產生同一個句柄同一個計算在不同上下文中也可能產生不同句柄概念性存在并不以實體形式存儲協議明確保證的是句柄與明文之間的關系它刻意不保證句柄與密文之間、句柄與產生它的計算之間的任何關系。從源碼層面看這個分層非常直觀在 host-contracts/lib/FHE.sol 中FHE庫被定義為一個library而其底層實現全部委托給Impl.sol。例如Impl.add內部調用協處理器合約的fheAdd(lhs, rhs, scalarByte)入參和返回值類型都是bytes32——這正是一個句柄在 EVM 層面的真實形態function add(bytes32 lhs, bytes32 rhs, bool scalar) internal returns (bytes32 result) { bytes1 scalarByte; if (scalar) { scalarByte 0x01; } else { scalarByte 0x00; } CoprocessorConfig storage $ getCoprocessorConfig(); result IFHEVMExecutor($.CoprocessorAddress).fheAdd(lhs, rhs, scalarByte); }參見 Impl.sol 中的 add 實現。同樣地FHE.sol中所有加密類型的操作eq、select、add等最終都以bytes32句柄為輸入輸出。這也是為什么句柄可以被當作普通bytes32處理——它們在 EVM 中本來就是一個bytes32。核心原則句柄是 opaque不透明的fhEVM 官方文檔對句柄給出了一個強警告warning把句柄當成名牌name tag而不是事物本身。句柄的字節不會告訴你它是如何產生的也不會告訴你背后是哪個密文。你能從句柄上唯一讀出的信息是它指向哪個明文——而且只有在解密之后才能讀出。這句話的含義是雙重的你可以把句柄當作任何其他bytes32使用用/!比較、存入狀態變量、記錄日志。ACL 本身就是這樣工作的——它以bytes32 handle為參數進行權限校驗見 ACL 合約中的isAllowed(bytes32 handle, address account)。你不可以從句柄的字節猜測它是由什么計算產生的也不可以猜測它背后對應哪個密文。之所以句柄能承載 ACL 語義正是因為句柄與明文之間存在確定性對應同一明文在所有合法句柄之間保持同一性權限系統才能圍繞句柄展開授權、委托與撤銷。而句柄字節本身不攜帶任何計算或密文信息保證了不會通過句柄泄露隱私。你可以依賴什么協議的不變量協議給出了一條規則并且這條規則是單一且可依賴的如果兩個句柄相等那么它們指向同一個明文。這條規則還有一個同樣成立的鏡像命題如果兩個明文不同那么它們的句柄一定不同。除此之外的一切都不能假設。你可以依賴你不能依賴相等句柄 → 相等明文不同句柄 → 不同明文不同明文 → 不同句柄相等明文 → 相等句柄相等句柄 → 兩者由同一計算產生相等句柄 → 兩者底下是同一密文最后兩行你不能依賴的原因從密碼學角度看非常自然相等句柄 → 同一計算協議當前會把前一個區塊的哈希混入句柄的構建過程詳見下文常見誤區因此同一個計算在兩個不同區塊中就會得到不同句柄。未來協議還可能對句柄做優化重寫同一計算產生同一句柄的假設隨時可能被打破。相等句柄 → 同一密文協處理器持有的密文可以隨時被重新隨機化re-randomization而不改變明文。句柄作為指向明文密文對的標識符可以在密文更新后保持不變或者反之。句柄相等只能說明明文相同與底層密文無關。協議可能在部分相等明文的計算上產生相等句柄在另一部分上產生不同句柄——跨區塊、跨鏈、密文重新隨機化之后、乃至未來的優化版本中情況都可能變化。你的合約必須在這兩種情況下都能正常工作這是編寫 fhEVM 合約最基本的健壯性要求。常見誤區與正確的相等性判斷誤區一假設相同操作 相同輸入 → 相同句柄這是最常見的錯誤。今天協議會把前一個區塊的哈希混入每個句柄的構建過程所以同樣的FHE.add(h1, h2)在兩個不同區塊中執行得到的句柄就已經不同。而反過來——假設兩個不同操作一定產生不同句柄——同樣危險如果協議未來把某些計算優化合并到同一個句柄這類假設就會靜默出錯。文檔給出了明確的錯誤示范// ? 不要依賴 h3 h4也不要依賴 h3 ! h4。 euint64 h3 FHE.add(h1, h2); euint64 h4 FHE.add(h1, h2);正確做法是永遠不要把句柄的相等性當作邏輯判斷的依據。如果你需要知道兩個加密值在明文層面是否相等應該使用 FHE 運算符FHE.eq它返回一個ebool只有當底層值真正匹配時才解密為trueebool isEqual FHE.eq(a, b);FHE.eq在源碼中的實現同樣走協處理器調用鏈FHE.eq→Impl.eq(lhs, rhs, scalar)→IFHEVMExecutor(...).fheEq(lhs, rhs, scalarByte)見 Impl.sol 中的 eq 實現返回的是新的句柄。也就是說相等性判斷本身也是加密計算結果仍然以句柄形式存在需要授權后才能解密。誤區二混用來自不同來源的句柄一個從其他鏈橋接過來的句柄或者鏈下構造好、通過FHE.fromExternal(...)引入的句柄并不保證與鏈上計算產生的句柄相等——即使二者編碼的是同一個明文。這一點從fromExternal的源碼語義可以看得更清楚。在 FHE.sol 的 fromExternal 實現 中當傳入inputProof時句柄要經過Impl.verify的密文校驗當inputProof為空時句柄必須已經通過 ACL 的isAllowed(inputBytes32, msg.sender)檢查否則會觸發SenderNotAllowedToUseHandle回退function fromExternal(externalEuint8 inputHandle, bytes memory inputProof) internal returns (euint8) { if (inputProof.length ! 0) { return euint8.wrap(Impl.verify(externalEuint8.unwrap(inputHandle), inputProof, FheType.Uint8)); } else { bytes32 inputBytes32 externalEuint8.unwrap(inputHandle); if (inputBytes32 0) { return asEuint8(0); } if (!Impl.isAllowed(inputBytes32, msg.sender)) revert SenderNotAllowedToUseHandle(inputBytes32, msg.sender); return euint8.wrap(inputBytes32); } }外部句柄與本地計算句柄的生成路徑完全不同一個來自輸入驗證與 ACL 放行一個來自協處理器的 FHE 運算因此不滿足句柄相等 → 明文相等這一不變量的反向推理。跨鏈橋場景同樣如此橋接協議重新注冊句柄句柄與明文的映射關系由目標鏈上的橋接邏輯重新建立與原鏈的句柄取值沒有可依賴的對應關系。關鍵提醒句柄不變量是單向的把兩個誤區放在一起可以得到一張完整的推理安全圖? 相等句柄 → 相等明文可以依賴? 不同明文 → 不同句柄可以依賴? 不同句柄 → 不同明文不可依賴? 相等明文 → 相等句柄不可依賴? 句柄相等 → 同一計算不可依賴? 句柄相等 → 同一密文不可依賴協議只給出句柄到明文的正向確定性反向與跨維度的一切推測都是危險的。這條不變量是 fhEVM 合約開發中最重要的心智模型之一。句柄與加密類型、ACL 的協同加密類型是句柄的安全包裝在 fhEVM 中euint8、ebool、eaddress等加密類型本質上是對bytes32句柄的類型安全包裝。文檔 docs/solidity-guides/types.md 明確指出fhEVM 中的加密整數以 FHE 密文表示并通過密文句柄進行抽象這些以e為前綴的類型例如euint64是密文句柄之上的安全包裝。對應地FHE.sol中所有操作都圍繞*_wrap/*_unwrap展開。例如and運算function and(ebool a, ebool b) internal returns (ebool) { if (!isInitialized(a)) { a asEbool(false); } if (!isInitialized(b)) { b asEbool(false); } return ebool.wrap(Impl.and(ebool.unwrap(a), ebool.unwrap(b), false)); }見 FHE.sol 中的 and 實現。isInitialized通過unwrap(v) ! 0判斷句柄是否為 0即未初始化未初始化的句柄按默認值參與運算。這正是句柄可當作普通 bytes32 比較在實際代碼中的體現——類型系統利用句柄的零值約定實現了初始化檢查。ACL 以句柄為最小權限單元ACL訪問控制列表合約 host-contracts/contracts/ACL.sol 的職責是控制誰能訪問、計算或解密 fhEVM 中的加密值。它提供的核心接口都以bytes32句柄為參數allow(bytes32 handle, address account)允許某賬戶使用某句柄ACL.solallowTransient(bytes32 ciphertext, address account)僅限當前交易內的臨時授權協處理器合約始終可以調用ACL.solisAllowed(bytes32 handle, address account)查詢賬戶是否被允許使用句柄ACL.sol。因為句柄與明文存在確定性對應ACL 才能圍繞句柄實施精確到值的權限控制又因為句柄本身不泄露任何信息ACL 的授權記錄不會暴露加密數據的任何內容。二者相輔相成構成了 fhEVM 的隱私訪問模型。實戰建議編寫不依賴句柄取值的安全合約綜合文檔與源碼可以總結出以下可直接落地的編碼規范把句柄當bytes32用但只做存儲、傳遞、ACL 授權句柄可以作為狀態變量持久化可以在函數間傳遞也可以作為allow/isAllowed等 ACL 調用的參數。這些用法不依賴句柄的具體取值。絕不把句柄相等性當業務邏輯無論是h3 h4還是h3 ! h4的依賴都應從代碼中移除。同一操作在不同區塊產生不同句柄是當前協議的既定行為未來優化還可能進一步改變句柄生成方式。用FHE.eq(a, b)判斷加密值是否相等這是協議提供的、唯一正確的明文相等性判斷方式。返回的ebool仍需通過授權的解密流程才能讀出true/false。警惕跨來源句柄跨鏈橋接、FHE.fromExternal引入的句柄與本地計算句柄之間不存在可依賴的相等性關系。需要比較時同樣使用FHE.eq。利用未初始化約定零值句柄0表示未初始化FHE.isInitialized系列函數見 FHE.sol以此為基礎實現可用于防御性地校驗輸入句柄是否有效。小結句柄是 fhEVM 中連接鏈下密文世界與鏈上合約世界的唯一橋梁協議保證相等句柄 → 相等明文和不同明文 → 不同句柄兩條不變量而其他一切關系——句柄與密文、句柄與計算、不同句柄與明文——都不在保證范圍之內。理解并遵守這套不變量是編寫健壯、可長期演進的 fhEVM 智能合約的前提。當你需要比較加密值時記住一句口訣不要比較句柄使用FHE.eq。【免費下載鏈接】fhevmFHEVM, a full-stack framework for integrating Fully Homomorphic Encryption (FHE) with blockchain applications項目地址: https://gitcode.com/GitHub_Trending/fh/fhevm創作聲明:本文部分內容由AI輔助生成(AIGC),僅供參考