在本文中,我將分享一些示例,說明消除特殊情況如何減少代碼複雜性並提高可維護性。
特殊最大值
常見的特殊情況是使用0表示“無最大值”。這種特殊情況通常很容易消除。
Special Expirations
看下面的代碼👇
在這段代碼中,0是一種特殊情況,表示“沒有過期”。這種特殊情況是不直觀的,它增加了require語句的複雜性。
然而,真正的危險是團隊中的一個新開發人員忽略了這個微妙之處,無法處理expiration==0的特殊情況。這很容易導致資金損失或其他嚴重問題。
這樣代碼就更簡單更明顯了:
這裏,我使用的是uint256允許的最大值的expiration,而不是0,當涉及到時間戳時,expiration實際上是無限的。
特殊最大以太幣數量
這是一個非常相似的示例,但這次涉及以太幣:
同樣,我們有一個非直觀的特例,我們可以通過使用一個有效的無限值來解決這個問題:
2256-1是最大值
注意,同樣的技巧可以概括爲令牌數量或任何值。由於Solidity不能表示大於2256-1的值,因此它始終可以與uint256進行比較,成爲“有效無限”值
解決gas成本問題
通常,在gas成本方面需要進行權衡。人們最終將默認值設爲0的一個典型原因是存儲非零值會耗費大量gas。
如果存儲成本對於您的用例而言是很高的,請考慮以下技巧:
在此代碼中,寫入存儲的_expiration值默認情況下爲0,與以前的特殊含義相同。但是,我介紹了一個輔助函數expiration(),它將0轉換爲不太特殊的值2256-1。這意味着我的其餘代碼無需處理這種特殊情況。
考慮將此技術與自定義的linter規則配對使用,以確保您不會在expiration()函數之外的任何地方直接讀取_expiration。
特殊地址
關於地址,我經常看到兩種特殊情況:
-
地址0通常是不允許的。
-
不允許使用特定地址(通常是特權角色)。
特別地址0
這是一些熟悉的代碼,其中使用0作爲特殊情況:
禁止使用地址0通常是爲了保護用戶不受錯誤的影響。將令牌發送到地址0通常不會比將它們發送到地址1更糟糕,但0是默認值,因此更可能由於有錯誤的工具或庫而意外傳入。
我個人不喜歡這種地址0的支票,但這很少有問題。與前面的示例不同,如果開發人員在維護代碼時忘記了這種特殊情況,那麼一切都不會中斷。
特殊角色地址
這段代碼比上一段要麻煩得多:
當我看到這樣的代碼時,我的直接問題是爲什麼所有者地址無法接收令牌。這樣的檢查通常是爲了將安全控制措施放在適當的位置,但通常無法解決Sybil攻擊,因爲系統中的多個地址由同一個人控制。
在這個特定的例子中,所有者可以簡單地接收具有不同地址的令牌。如果這違反了合同的安全性假設,那就有問題了。
像這樣的特殊情況是一種代碼氣息,但這並不意味着它們總是應該被消除。要做的重要事情是記錄爲什麼需要這種特殊情況,並考慮替代方案。
總結
-
特殊情況會導致代碼複雜性,從而導致錯誤。
-
在可能的情況下,完全消除特殊情況。
-
2256-1是最大值的良好替代品。
-
地址0的特殊情況通常可以。
-
其他特定地址的特殊情況是代碼氣味。
-
如果決定在代碼中使用具有特殊意義的值,請嘗試隔離用於處理這些值的代碼。