区块链之设计模式
2022年12月25日
设计模式
《Design Patterns For Smart Contracts In the Ethereum Ecosystem》
安全 Security
- Checks-Effects-Interaction 保证状态完整,再做外部调用 - Checks:参数验证,Effects:修改合约状态,Interaction:外部交互 这个模式要求合约按照Checks-Effects-Interaction的顺序来组织代码。它的好处在于进行外部调用之前,Checks-Effects已完成合约自身状态所有相关工作,使得状态完整、逻辑自洽,这样外部调用就无法利用不完整的状态进行攻击 
- Mutex - 禁止递归 - 使用修饰符防止函数被递归调用。防止重放攻击 - contract Mutex { bool locked; modifier noReentrancy() { //防止递归 require(!locked, "Reentrancy detected"); locked = true; _; locked = false; } //调用该函数将会抛出Reentrancy detected错误 function some() public noReentrancy{ some(); }
}
### 可维护性 Maintaince
高度模块化、高内聚低耦合
* Data segregation - 数据与逻辑相分离
```javascript
contract DataRepository{
  uint private _data;
  function setData(uint data) public {
      _data = data;
  }
  function getData() public view returns(uint){
      return _data;
  }
}
contract Computer{
  DataRepository private _dataRepository;
  constructor(address addr){
      _dataRepository = DataRepository(addr);
  }
  //业务代码
  function compute() public view returns(uint){
      return _dataRepository.getData() * 10;
  }    
}
- Satellite - 分解合约功能
contract Base {
    uint public _variable;
    function setVariable(uint data) public {
        _variable = _satellite.compute(data);
    }
     Satellite _satellite;
    //更新子合约(卫星合约)
    function updateSatellite(address addr) public {
        _satellite = Satellite(addr);
    }
}
contract Satellite {
    function compute(uint a) public returns(uint){
        return a * 10;        
    }
}
- Contract Registry - 跟踪最新合约
contract Registry{
    address _current;
    address[] _previous;
    //子合约升级了,就通过update函数更新地址
    function update(address newAddress) public{
        if(newAddress != _current){
            _previous.push(_current);
            _current = newAddress;
        }
    }
    function getCurrent() public view returns(address){
        return _current;
    }
}
contract Base {
    uint public _variable;
    function setVariable(uint data) public {
        Satellite satellite = Satellite(_registry.getCurrent());
        _variable = satellite.compute(data);
    }
    Registry private _registry = //...;
}
- Contract Relay - 代理调用最新合约
contract Base {
    uint public _variable;
    function setVariable(uint data) public {
        _variable = _proxy.compute(data);
    }
    SatelliteProxy private _proxy = //...;
}
contract SatelliteProxy{
    address _current;
    function compute(uint a) public returns(uint){
        Satellite satellite = Satellite(_current);   
        return satellite.compute(a);
    }
    //子合约升级了,就通过update函数更新地址
    function update(address newAddress) public{
        if(newAddress != _current){
            _current = newAddress;
        }
    }   
}
contract Satellite {
    function compute(uint a) public returns(uint){
        return a * 10;        
    }
}
生命周期 Lifecycle
- Mortal - 允许合约自毁
contract Mortal{
    //自毁
    function destroy() public{
        //selfdestruct指令,用于销毁合约
        selfdestruct(msg.sender);
    }
}
- Automatic Deprecation - 允许合约自动停止服务 - 当用户调用service,notExpired修饰符会先进行日期检测,这样,一旦过了特定时间,调用就会因过期而被拦截在notExpired层。 
contract AutoDeprecated{
    uint private _deadline;
    function setDeadline(uint time) public {
        _deadline = time;
    }
    modifier notExpired(){
        require(now <= _deadline);
        _;
    }
    function service() public notExpired{
        //some code    
    }
}
权限 Authorization
- Ownership
contract Ownable {
    address public owner;
    event OwnershipRenounced(address indexed previousOwner);
    event OwnershipTransferred(
        address indexed previousOwner,
        address indexed newOwner
    );
    constructor() public {
        owner = msg.sender;
    }
    modifier onlyOwner() {
        require(msg.sender == owner);
        _;//表示所修饰函数中的代码,也就是将这个 require 在最前面执行
    }
    function transferOwnership(address newOwner) public onlyOwner {
        require(newOwner != address(0));
        emit OwnershipTransferred(owner, newOwner);
        owner = newOwner;
    }
    function renounceOwnership() public onlyOwner {
        emit OwnershipRenounced(owner);
        owner = address(0);
    }
}
//继承并添加修饰器
contract Biz is Owned{
    function manage() public onlyOwner{
    }
}
行为控制 Action And Control
- Commit - Reveal - 延迟秘密泄露 Commit And Reveal模式允许用户将要保护的数据转换为不可识别数据,比如一串哈希值,直到某个时刻再揭示哈希值的含义,展露真正的原值。
 以投票场景举例,假设需要在所有参与者都完成投票后再揭示投票内容,以防这期间参与者受票数影响
contract CommitReveal {
    struct Commit {
        string choice;
        string secret;
        uint status;
    }
    mapping(address => mapping(bytes32 => Commit)) public userCommits;
    event LogCommit(bytes32, address);
    event LogReveal(bytes32, address, string, string);
    function commit(bytes32 commit) public {
        Commit storage userCommit = userCommits[msg.sender][commit];
        require(userCommit.status == 0);
        userCommit.status = 1; // comitted
        emit LogCommit(commit, msg.sender);
    }
    function reveal(string choice, string secret, bytes32 commit) public {
        Commit storage userCommit = userCommits[msg.sender][commit];
        require(userCommit.status == 1);
        require(commit == keccak256(choice, secret));
        userCommit.choice = choice;
        userCommit.secret = secret;
        userCommit.status = 2;
        emit LogReveal(commit, msg.sender, choice, secret);
    }
}
- Oracle - 读取链外数据 获取外部数据会通过名为Oracle的链外数据层来执行。当业务方的合约尝试获取外部数据时,会先将查询请求存入到某个Oracle专用合约内;Oracle会监听该合约,读取到这个查询请求后,执行查询,并调用业务合约响应接口使合约获取结果。  
contract Oracle {
    address oracleSource = 0x123; // known source
    struct Request {
        bytes data;
        function(bytes memory) external callback;
    }
    Request[] requests;
    event NewRequest(uint);
    modifier onlyByOracle() {
        require(msg.sender == oracleSource); _;
    }
    function query(bytes data, function(bytes memory) external callback) public {
        requests.push(Request(data, callback));
        emit NewRequest(requests.length - 1);
    }
    //回调函数,由Oracle调用
    function reply(uint requestID, bytes response) public onlyByOracle() {
        requests[requestID].callback(response);
    }
}
contract BizContract {
    Oracle _oracle;
    constructor(address oracle){
        _oracle = Oracle(oracle);
    }
    modifier onlyByOracle() {
        require(msg.sender == address(_oracle));
        _;
    }
    function updateExchangeRate() {
        _oracle.query("USD", this.oracleResponse);
    }
    //回调函数,用于读取响应
    function oracleResponse(bytes response) onlyByOracle {
    // use the data
    }
}
闪电网络
RSMC
Revocable Sequence Maturity Contract(序列到期可撤销合约)
HTLC
Hashed Timelock Contract(哈希时间锁定)
 Powered by  Waline  v2.9.1
