1. 程式人生 > 其它 >以太坊知識教程------智慧合約的5種設計模式

以太坊知識教程------智慧合約的5種設計模式

技術標籤:以太坊Ethereum

1、自毀合約

合約自毀模式用於終止一個合約,這意味著將從區塊鏈上永久刪除這個合約。 一旦被銷燬,就不可能呼叫合約的功能,也不會在賬本中記錄交易。
eg. 貸款合約,它應當在貸款還清後自動銷燬;另一個案例是基於時間的拍賣合約,它應當在拍賣結束後 終止 —— 假設我們不需要在鏈上儲存拍賣的歷史記錄。

在處理一個被銷燬的合約時,有一些需要注意的問題:

  • 合約銷燬後,傳送給該合約的交易將失敗。
  • 任何傳送給被銷燬合約的資金,都將永遠丟失。
  • 為避免資金損失,應當在傳送資金前確保目標合約仍然存在,移除所有對已銷燬合約的引用。
contract SelfDesctructionContract {
public address owner; public string someValue; modifier ownerRestricted { require(owner == msg.sender); _; } // constructor function SelfDesctructionContract() { owner = msg.sender; } // a simple setter function function setSomeValue(string value){ someValue =
value; } // you can call it anything you want function destroyContract() ownerRestricted { suicide(owner); } }

2、工廠合約

工廠合約用於建立和部署“子”合約。

工廠用於儲存子合約的地址,以便在必要時提取使用。

  • 為什麼不把它們存在Web應用資料庫裡?
  • 將地址資料存在工廠合約裡、存在區塊鏈上,更加安全不會丟失。
  • 需要跟蹤所有新 建立的子合約以便同步更新資料庫。

eg. 銷售資產並跟蹤這些資產(例如,誰是資產的所有者)。 需要向負責部署資產的 函式新增payable修飾符以便銷售資產。


contract CarShop {
   address[] carAssets;
   function createChildContract(string brand, string model) public payable {
      // insert check if the sent ether is enough to cover the car asset ...
      address newCarAsset = new CarAsset(brand, model, msg.sender);            
      carAssets.push(newCarAsset);   
   }
   function getDeployedChildContracts() public view returns (address[]) {
      return carAssets;
   }
}
 
contract CarAsset {
   string public brand;
   string public model;
   address public owner;
   function CarAsset(string _brand, string _model, address _owner) public {
      brand = _brand;
      model = _model;
      owner = _owner;
   }
}   

3、名稱登錄檔

  • 其實就是一個字串map一個struct物件。
  • 問題: 假設一個合約依賴很多個合約,如果將所有這些合約的地址寫在你的應用程式碼中,如果這些合約的地址隨著時間的推移而變化,那該怎麼辦?
contract NameRegistry {
   struct ContractDetails {
      address owner;
      address contractAddress;
      uint16 version;
   }
   mapping(string => ContractDetails) registry;
   function registerName(string name, address addr, uint16 ver) returns (bool) {
      // versions should start from 1
      require(ver >= 1);
      
      ContractDetails memory info = registry[name];
      require(info.owner == msg.sender);
      // create info if it doesn't exist in the registry
       if (info.contractAddress == address(0)) {
          info = ContractDetails({
             owner: msg.sender,
             contractAddress: addr,
             version: ver
          });
       } else {
          info.version = ver;
          info.contractAddress = addr;
       }
       // update record in the registry
       registry[name] = info;
       return true;
   }

    function getContractDetails(string name) constant returns(address, uint16) {
      return (registry[name].contractAddress, registry[name].version);
   }
}

4、對映表迭代器

由於mapping迭代效率低,所以需要一個mapping迭代模式。通過一個數組來儲存key。

實現put()函式的一個常見錯誤,是通過遍歷來檢查指定的鍵是否存在。

contract MappingIterator {
   mapping(string => address) elements;
   string[] keys;

   function put(string key, address addr) returns (bool) {
      bool exists = elements[key] == address(0)
      if (!exists) {
         keys.push(key);
      }
      elements[key] = addr;
      return true;
    }

    function getKeyCount() constant returns (uint) {
       return keys.length;
    }

    function getElementAtIndex(uint index) returns (address) {
       return elements[keys[index]];
    }

    function getElement(string name) returns (address) {
       return elements[name];
    }
}

5. 提款模式

簡單來說,就是不用transfer,而是用send,因為一旦callback錯誤,transfer會導致異常,而send不會。


contract WithdrawalContract {
   mapping(address => uint) buyers;
   function buy() payable {
      require(msg.value > 0);
      buyers[msg.sender] = msg.value;
   }
   function withdraw() {
      uint amount = buyers[msg.sender];
      require(amount > 0);
      buyers[msg.sender] = 0;      
      require(msg.sender.send(amount));
   }
}