通用模式

翻譯原文

date:20170803

從合約中取回錢幣

發(fā)送錢幣的推薦方法是使用取回模式。盡管發(fā)送錢幣最簡(jiǎn)單的簡(jiǎn)單方法是直接調(diào)用send。但是它會(huì)引入潛在的風(fēng)險(xiǎn),所以也不推薦使用。你可以閱讀安全考慮章節(jié),了解更多信息。
這是一個(gè)在合約中使用取回模式的例子,功能是發(fā)送最多的錢幣到合約中,來(lái)成為首富,靈感來(lái)自King of the ether。
在下面的合約中,如果你要篡奪成為首富,你要接收去世的人的財(cái)產(chǎn)來(lái)成為新的首富。

pragma solidity ^0.4.11;

contract WithdrawalContract {
    address public richest;
    uint public mostSent;

    mapping (address => uint) pendingWithdrawals;

    function WithdrawalContract() payable {
        richest = msg.sender;
        mostSent = msg.value;
    }

    function becomeRichest() payable returns (bool) {
        if (msg.value > mostSent) {
            pendingWithdrawals[richest] += msg.value;
            richest = msg.sender;
            mostSent = msg.value;
            return true;
        } else {
            return false;
        }
    }

    function withdraw() {
        uint amount = pendingWithdrawals[msg.sender];
        // 記得在發(fā)送之前要把余額設(shè)置為0,以防止重入攻擊
        pendingWithdrawals[msg.sender] = 0;
        msg.sender.transfer(amount);
    }
}

這和直接發(fā)送模式相反:

pragma solidity ^0.4.11;

contract SendContract {
    address public richest;
    uint public mostSent;

    function SendContract() payable {
        richest = msg.sender;
        mostSent = msg.value;
    }

    function becomeRichest() payable returns (bool) {
        if (msg.value > mostSent) {
            // 這一行會(huì)出現(xiàn)問(wèn)題(詳情在下文解釋)
            richest.transfer(msg.value);
            richest = msg.sender;
            mostSent = msg.value;
            return true;
        } else {
            return false;
        }
    }
}

注意,在這個(gè)例子中,攻擊者可以困住合約,讓合約處于不可使用的狀態(tài)。通過(guò)讓richest成為一個(gè)合約的地址,該合約有一個(gè)回調(diào)函數(shù),但是這個(gè)回調(diào)會(huì)執(zhí)行失敗(例如使用revert()或者只是消耗2300 以上的gas),這樣,通過(guò)transfer調(diào)用分發(fā)錢幣到”有毒“的合約中,它就會(huì)失敗,因此,becomeRichest就會(huì)失敗,讓合約永遠(yuǎn)不能正常執(zhí)行。
相反,如果你使用第一個(gè)例子取回模式,那么攻擊者只能讓他或她的取回函數(shù)失敗,但是合約的其余代碼都沒(méi)有問(wèn)題。

限制訪問(wèn)

限制訪問(wèn)時(shí)合約的通用模式。注意你不能限制任何人或者計(jì)算機(jī)讀取你的交易內(nèi)容或者你合約狀態(tài)。你可以用加密提升難度。但是如果你的合約是可以讀取數(shù)據(jù)的,那么所有人都可以讀取數(shù)據(jù)。
你可以通過(guò)其他合約,來(lái)限制合約的訪問(wèn)。默認(rèn)情況下是這樣的,除非你把狀態(tài)變量聲明為public。
另外,你可以限制誰(shuí)可以修改你的合約代碼,或者調(diào)用你的合約函數(shù),這是這個(gè)章節(jié)要說(shuō)的:
使用函數(shù)修改器使得限制高度可讀。

pragma solidity ^0.4.11;

contract AccessRestriction {
    // 這些變量在構(gòu)造期間執(zhí)行,
    // `msg.sender`是創(chuàng)建該合約的賬號(hào)
    address public owner = msg.sender;
    uint public creationTime = now;

    // 修改器可以用來(lái)修改函數(shù)體。
    // 如果使用了這個(gè)修改器,它會(huì)先檢測(cè),并且只有函數(shù)從特定地址調(diào)用才會(huì)放行
    modifier onlyBy(address _account)
    {
        require(msg.sender == _account);
        // 不要忘了"_;"!
        // 它代表了使用該修改器的函數(shù)的函數(shù)體
        _;
    }

    /// 讓 `_newOwner` 成為該合約的擁有者
    function changeOwner(address _newOwner)
        onlyBy(owner)
    {
        owner = _newOwner;
    }

    modifier onlyAfter(uint _time) {
        require(now >= _time);
        _;
    }

    /// 擦除擁有者信息。
    /// 只能在合約創(chuàng)建6個(gè)星期之后被調(diào)用。
    function disown()
        onlyBy(owner)
        onlyAfter(creationTime + 6 weeks)
    {
        delete owner;
    }

    // 這個(gè)修改器要求函數(shù)調(diào)用要有一定數(shù)量的錢幣
    // 如果調(diào)用者發(fā)送了太多錢幣,會(huì)被退還,但是會(huì)在函數(shù)體執(zhí)行之后,再退還
    // Solidity v0.4.0之前這么做是很危險(xiǎn)的,因?yàn)榭赡軙?huì)忽略`_;`之后的版本。
    modifier costs(uint _amount) {
        require(msg.value >= _amount);
        _;
        if (msg.value > _amount)
            msg.sender.send(msg.value - _amount);
    }

    function forceOwnerChange(address _newOwner)
        costs(200 ether)
    {
        owner = _newOwner;
        // 一些示例條件
        if (uint(owner) & 0 == 1)
            // Solidity  v0.4.0之前不會(huì)退還.
            return;
        // 返回多余的錢幣
    }
}

訪問(wèn)控制更加特殊的方法會(huì)在下個(gè)例子中討論。

狀態(tài)機(jī)

合約通常像一個(gè)狀態(tài)機(jī),這意味著他們有特定的階段,不同的階段表現(xiàn)不同或者會(huì)調(diào)用不同的函數(shù)。一個(gè)函數(shù)調(diào)用可以結(jié)束一個(gè)階段,讓合約進(jìn)入到另一個(gè)階段(尤其是合約模型是可交互的)。有些階段在某個(gè)時(shí)間點(diǎn)會(huì)自動(dòng)達(dá)到,也是很普遍的。

一個(gè)例子是秘密競(jìng)價(jià)合約,在”接收秘密競(jìng)價(jià)“的時(shí)候開(kāi)始,然后轉(zhuǎn)換到”披露競(jìng)價(jià)“階段,然后在確定競(jìng)價(jià)結(jié)果的階段結(jié)束。

函數(shù)修改器可以用在這種場(chǎng)合中,來(lái)改變狀態(tài),以及保護(hù)合約的不正當(dāng)使用。

例子

在下面的例子中,修改器atStage保證了函數(shù)只能在特定的時(shí)候被調(diào)用。

自動(dòng)計(jì)時(shí)交易通過(guò)timeTransitions修改器來(lái)處理,可以用在任意函數(shù)中。

注意:修改器順序很重要。如果atStage和timedTransitions結(jié)合起來(lái),你要保證atStage在最后面,然后合約會(huì)進(jìn)入新的階段。

最后,當(dāng)函數(shù)結(jié)束的時(shí)候,transitionNext修改器會(huì)自動(dòng)的用來(lái)進(jìn)入下個(gè)階段。

注意:修改器可能會(huì)被忽略。在Solidity v0.4.0之前可能會(huì)發(fā)生:因?yàn)樾薷钠髦皇呛?jiǎn)單的替換代碼,而不是使用函數(shù)調(diào)用,所以如果函數(shù)return掉的時(shí)候,transitionNext修改器中的代碼可能會(huì)被忽略。 如果你要這么做,那么你應(yīng)該在這些函數(shù)中手動(dòng)調(diào)用nextStage。從版本0.4.0開(kāi)始,修改器代碼即使函數(shù)return了,也能夠執(zhí)行。

pragma solidity ^0.4.11;

contract StateMachine {
    enum Stages {
        AcceptingBlindedBids,
        RevealBids,
        AnotherStage,
        AreWeDoneYet,
        Finished
    }

    // 這是當(dāng)前狀態(tài)。
    Stages public stage = Stages.AcceptingBlindedBids;

    uint public creationTime = now;

    modifier atStage(Stages _stage) {
        require(stage == _stage);
        _;
    }

    function nextStage() internal {
        stage = Stages(uint(stage) + 1);
    }

    // 實(shí)現(xiàn)階段過(guò)渡。要確保這個(gè)修改器放在最前面
    // 否則不會(huì)進(jìn)入下個(gè)階段
    modifier timedTransitions() {
        if (stage == Stages.AcceptingBlindedBids &&
                    now >= creationTime + 10 days)
            nextStage();
        if (stage == Stages.RevealBids &&
                now >= creationTime + 12 days)
            nextStage();
        // 通過(guò)交易進(jìn)入下一個(gè)階段
        _;
    }

    // 修改器的順序在這里是非常重要的
    function bid()
        payable
        timedTransitions
        atStage(Stages.AcceptingBlindedBids)
    {
        // 這里我們不會(huì)先實(shí)現(xiàn)。
    }

    function reveal()
        timedTransitions
        atStage(Stages.RevealBids)
    {
    }

    // 這個(gè)修改器會(huì)使得函數(shù)結(jié)束的時(shí)候進(jìn)入下一個(gè)階段
    modifier transitionNext()
    {
        _;
        nextStage();
    }

    function g()
        timedTransitions
        atStage(Stages.AnotherStage)
        transitionNext
    {
    }

    function h()
        timedTransitions
        atStage(Stages.AreWeDoneYet)
        transitionNext
    {
    }

    function i()
        timedTransitions
        atStage(Stages.Finished)
    {
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 作者:Soroush Khanlou,原文鏈接,原文日期:2016/8/8譯者:Cwift;校對(duì):Crystal ...
    梁杰_numbbbbb閱讀 2,420評(píng)論 0 2
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,533評(píng)論 19 139
  • 原文鏈接date:20170710 Solidity中合約的概念和其他面向?qū)ο笳Z(yǔ)言中的類差不多。他們都有狀態(tài)變量來(lái)...
    gaoer1938閱讀 1,110評(píng)論 0 0
  • 緬甸近幾年森林損失速度居全球第三。有關(guān)專家警告稱,若森林保護(hù)措施不當(dāng),緬甸境內(nèi)森林將于2060年完全消失。 -中華...
    弘錦軒閱讀 371評(píng)論 0 0
  • 在上海 學(xué)會(huì)了辨別方向 因?yàn)橐粭l路很長(zhǎng) 分東西南北; 也更精通于地圖 去哪都不會(huì)讓自己丟…… 一個(gè)人 只要有方向 ...
    沐16閱讀 198評(píng)論 0 0

友情鏈接更多精彩內(nèi)容