這個範例合約的缺陷是什麼?
我目前正在寫一篇關於標記化的論文/文章,在開頭我介紹了智能合約。為此,我以以下合約為例:
pragma solidity ^0.6.0; contract SplitPot { address payable[] beneficiaries = [ 0x498898b3F52DAba1bB304a4b4D2EA31a111484B1, 0xAcb19c763EB67ea757Efd8Cd8b6ecceb28F1284B, 0xD5d3f3650C4DdE7B8034671129443A596Ce8ed57 ]; receive() external payable { uint individualAmount = msg.value / beneficiaries.length; for (uint i = 0; i < beneficiaries.length; i++) { beneficiaries[i].transfer(individualAmount); } } }
顯然,該合約的目的是將發送給它的所有 Ether 平均分配給三個受益人(都是我創建的測試網帳戶)。它適用於測試網。
對於這個特定的目的,即解釋什麼是智能合約,我相信這是一個非常好的合約:
- 它非常簡短易懂。
- 它做的事情通常是由受信任的第三方促成的——現在已經過時了。
我也知道這是一個非常糟糕的契約,因為它有嚴重的缺點。例如:如果交易失敗怎麼辦?Afaik,受益人無法收回他們的 Ether 份額,並且契約不會重新嘗試將其發送給他們。另外:我不知道合約是否也能在主網上正確執行。
我想告訴我的讀者,這實際上是一份有缺陷的契約,我想讓他們知道具體會出現什麼問題。當我剛開始了解乙太坊時,如果你能幫助我找出這些缺陷,我將非常感激——尤其是在氣體經濟學和編碼風格方面。
論文/文章將獲得 CC BY-SA 4.0 的許可,所以我不是在尋求商業工作的幫助!
您應該確保該函式
receive
不能被重新輸入,例如:contract SplitPot { ... bool private locked = false; function receive() external payable { require(!locked, "reentrancy attempted"); locked = true; ... // put your actual code here locked = false; } }
請注意,您似乎忘記
function
了在程式碼中聲明之前receive() external payable
,並且我已在此處添加它。解釋重入保護:
你的函式執行
beneficiaries[i].transfer(individualAmount);
。如果
beneficiaries[i]
是合約地址,則呼叫該合約的回退函式;隨後,該函式可以呼叫您的函式並濫用其預期目標。更新:
根據@blockrookie 的評論,該
receive
關鍵字是在 solc 0.6.x 中引入的。通常稱為“備份函式”的未命名函式被拆分為使用關鍵字定義的新備份函式
fallback
和使用關鍵字定義的接收乙太函式receive
。這兩個函式都是在沒有
function
關鍵字的情況下聲明的,因此我上面的註釋(“您似乎忘記在程式碼中聲明function
之前receive() external payable
”)在 solc 0.5.x 或更低版本下是相關的,但在 solc 0.6.x 或更高版本下不相關。關於這個函式/關鍵字的更多細節可以在官方文件中找到。