[ethereum]solidityのデザインパターンの学習（Access Restriction Pettern）

# 記事の内容
Solidityを使ったスマートコントラクトのデザインパターンの学習メモです。
今回の記事ではアクセス制限に関する内容をメモとして残します。

# 参考
以下の記事を参考にさせていただきました。

[solidity-patterns Access Resriction](https://fravoll.github.io/solidity-patterns/access_restriction.html)

# Access Restriction Pattern
このパターンでやりたいことは適切なルールに従ってコントラクトへのアクセス制限を行いましょう。というものです。

パブリック型でethereumを使用する場合、コントラクトは不特定大多数に公開されるのでプライバシーを保護する必要があります。
そのためのアクセス制限の考え方の一例として、このパターンがある様です。

## ユースケース
アクセス制限を行う必要のある全てのケース

OpenZeppelinのOwnableコントラクトが良い例


## 実装
参考にさせていただいたサイトのままです。
バージョンだけコンパイルが通るか確認したかったので変更しました。

~~~ AccessRestriction.sol
pragma solidity ^0.5.16;

contract AccessRestriction {

    address public owner = msg.sender;
    uint public lastOwnerChange = now;
    
    modifier onlyBy(address _account) {
        require(msg.sender == _account);
        _;
    }
    
    modifier onlyAfter(uint _time) {
        require(now >= _time);
        _;
    }
    
    modifier costs(uint _amount) {
        require(msg.value >= _amount);
        _;
        if (msg.value > _amount) {
            msg.sender.transfer(msg.value - _amount);
        }
    }
    
    function changeOwner(address _newOwner) public onlyBy(owner) {
        owner = _newOwner;
    }
    
    function buyContract() public payable onlyAfter(lastOwnerChange + 4 weeks) costs(1 ether) {
        owner = msg.sender;
        lastOwnerChange = now;
    }
}
~~~
ざっくり解説
コード自体は特に難しいことはしてないですね。

このコードではアクセス制限の例を提示しているだけのようなので、実際にアクセス制限をする必要のあるコントラクトを実装する場合は真似て作ると参考になると思います。

~~~
    address public owner = msg.sender;
    uint public lastOwnerChange = now;
~~~
アクセス制限の元となるプライベート変数です。
コントラクトデプロイ時のアカウントと時間が設定されます。

~~~
    modifier onlyBy(address _account) {
        require(msg.sender == _account);
        _;
    }
~~~
アクセス制限用の修飾子の定義です。

プライベート変数に登録されているアカウント（オーナー）以外からの実行は許可しないようになっています。

~~~
    modifier onlyAfter(uint _time) {
        require(now >= _time);
        _;
    }
~~~
時間による制限を行う修飾子です。

~~~
    modifier costs(uint _amount) {
        require(msg.value >= _amount);
        _;
        if (msg.value > _amount) {
            msg.sender.transfer(msg.value - _amount);
        }
    }
~~~
コントラクト実行時に指定されている手数料を支払います。
トランザクション実行時に設定された金額が多すぎた場合、残金を戻す仕組み（条件文の中の処理）になっています。

~~~
    function changeOwner(address _newOwner) public onlyBy(owner) {
        owner = _newOwner;
    }
~~~
オーナーの変更を行う関数です。
変更は現オーナーしか出来ないように「onlyBy修飾子」により制限されています。

~~~
    function buyContract() public payable onlyAfter(lastOwnerChange + 4 weeks) costs(1 ether) {
        owner = msg.sender;
        lastOwnerChange = now;
    }
~~~
コントラクトのオーナー権限購入する処理です。
onlyAfter修飾子を指定することで購入後のオーナーである期間がチェックされます。

次にcostsで1etherを指定することで購入代金の支払いが出来ます。

私の理解不足かもしれませんが、このロジックの場合

オーナー：アリス
購入者　：ボブ

だとして、ボブがbyContractを実行するとボブの残高が1ether減りますが、アリスの残高が増える訳ではありません。
ボブの1etherはどこにいくのでしょうか？

~~~
    mapping(address => uint) balanceOf;

    modifier costs(uint _amount) {
        require(msg.value >= _amount);
        _;
        if (msg.value > _amount) {
            balanceOf[owner] += _amount;
            balanceOf[msg.sender] -= _amount
        }
	}
~~~
こんな感じでコントラクト内で残金を管理しているなら納得出来る。

~~~
    address payable public owner = msg.sender;

    modifier costs(uint _amount) {
        require(msg.value >= _amount);
        _;
        if (msg.value > _amount) {
            owner.transfer(_amount);
            msg.sender.transfer(msg.value - _amount);
        }
    }
~~~
オーナーに直接送金するならこんな感じです。
solidityのv0.5.0以上だと送金処理が発生するaddressには「paylable」を付けないといけないみたいです。


# 感想
アドレスを使ったアクセス制限のやり方は色々な参考書にも載っている方法でしたね。
個人的には修飾子でコスト支払を行うというやり方を初めて見ました。
このやり方は色々なところに応用出来そうです。