1. 程式人生 > >135.003 智能合約後端優化和產品化

135.003 智能合約後端優化和產品化

定義 UC tor ash 底層 any face ora ans

@(135- Block Chain| 區塊鏈)

Introduction

  • 如何通過數據結構優化降低合約執行成本
  • 合約的繼承
  • 巧用modifier
  • 以太坊函數庫的使用和基本介紹

如何減少gas 消耗?
(本質為節約計算資源,降低時間復雜度的問題)
數組越大,遍歷所需資源越多
遍歷數組 traverse array——> 映射 Mapping

1 Mapping in Solidity

類比map(c++),dict(python)
Hash table
Key - Value

  • types of Key in solidity
    (bool,int ,address,string)

  • types of Value in solidity
    (any type)
  • usage
    mapping (address=>Employee)employees
  • mapping只能作為合約的成員變量,而不能做本地局部變量

    2 Mapping底層實現

  • 不使用數組+鏈表,不需要擴容
  • hash函數為keccak256hash(keccak 即SHA- 3)
  • 在storage 上存儲,理論上為無限大的hash表
    *** 無法naive地遍歷整個mapping**
  • 賦值 employees[key] = value
  • 取值 value = employees[key]
  • value 是引用,在storage上存儲,可以直接修改
  • 當key 不存在,value = type‘s default // 不會拋出異常

3 函數返回進階

  • 命名參數返回
  • 命名返回參數直接賦值

1.命名參數返回

    function checkEmployee(address employeeId) returns(uint salary,uint lastPayday){ //quary the information of employees
    //name the returned parameter 命名參數返回 
    

OUTPUT

"0": "uint256: salary 1000000000000000000",
"1": "uint256: lastPayday 1530411588"

}

2.命名返回參數直接賦值

等效代碼

        // return (employee.salary,employee.lastPayday);
        salary = employee.salary;
        lastPayday = employee.lastPayday;

4 可視度

4.1 可視度

  • public :誰都可見
  • external:只有“外部調用”可見(特殊可視度)
  • internal:外部調用不可見,內部和子類可見(類比c++,protected)
  • private:只有當前合約可見

4.2 變量與可見度

  • 狀態變量:public,internal,private
    • 默認:internal
    • public:自動定義取值函數
    • private:不代表別人無法看到,只代表別的區塊鏈智能合約無法看到

合約的所有成員變量都是肉眼可見的!!!

  • 函數 :public,external,internal,private
    • 默認public

5 繼承

5.1 繼承-基本語法

private 對繼承類不可見

5.2 繼承-抽象合約

contract Parent{
    function someFunc() returns (uint);
}
contract Child is Parent{
    function someFunc() returns (uint){
        return 1;
    }
}

5.3 繼承-interface

  • 類比 Java 相似
    只是一個告訴之後程序員的一個框架
pragma solidity ^0.4.0;
interface Parent{
//不可繼承其他合約或Interface
//沒有構造函數
//沒有狀態變量
//沒有struct
//沒有enum
//簡單來說,只有function定義,啥都沒有
    function someFunc() returns (uint);
}

contract Child is Parent{
    function someFunc() returns (uint){
        return 1;
    }
}

5.4 多繼承

  • 重名函數的override 次序
    ```solidity
    contract Base1{
    function func1(){}
    }

contract Base2{
function func1(){}
}

contract Final is Base1,Base2 {
}

contract test{
Final f = new Final();
f.func1();//Base2 的function1被調用(從後往前,由於先繼承base1,再繼承base2)
}

```

  • Super:動態綁定上級函數
contract foundation{
    function func1{
    //do stuff
    }
}


contract Base1 is foundation{
    function func1(){
        super.func1();
    }
}

contract Base2 is foundation{
    function func1(){
    super.func1();
    }
}

contract Final is Base1,Base2{
    Final f = new Final();
    f.func1();
}



//調用順序:Base2.func1,Base1.func1,foundation.func1

多繼承 Method Resolution Order使用O3 Linearization

  • 與python 相同
  • 不能成環路

6 Modifier

pragma solidity ^0.4.0
contract Parent {
    uint public a=2;
    modifier someModifier(){
        _;
        a = 1;
    }
    function parentFunc2(uint value) someModifer public returns(uint){
    a = value;
    //下劃線等效加入一句 modify a=1;
    return a;
    }
}

7 Safe Math 和 Library

7.1 加減乘除在solidity中很危險

contract Test {
    uint8 public a = 0;
    function set(){
        a -= 100;
    }
}


OUTPUT:整型溢出
a: uint8: 156

手動解決方法_silly

contract Test {
    uint8 public a = 0;
    function set(){
        uint c = a - 100;
        assert(c<a);
        a = c;
    }
}

庫函數 Zppelin-solidity

  • 工程師——學習代碼重用
  • 不要重復造輪子
    ```
    solidity ^0.4.0;

import ‘./SafeMath.sol‘; //local file,download from github

contract Test {
using SafeMath for uint8;
uint8 public a = 0;
function set(){
a = a.sub(100);
//a = SafeMath.sub(a,100);
}
}

```

8 Code in Remix (Solidity IDE)

pragma solidity ^0.4.14;

import './SafeMath.sol';
import './Ownable.sol'; //Github - Zppelin-solidity


contract Payroll{
    using SafeMath for uint;
    
    struct Employee{
        address id;
        uint salary;
        uint lastPayday;
    }
    
    
  // uint constant payDuration = 30 days;
   uint constant payDuration = 10 seconds;//for test:10 seconds
   
   uint totalSalary;
   address owner;
   mapping(address => Employee)public employees;
   
   
    // function Payroll(){//construction function
    // owner = msg.sender; // in ownable.sol
    // }
    
    // modifier onlyOwner{ //in Ownable.sol
    //     require(msg.sender == owner);
    //     _; //represent the function been modified, 除了return之外的語句
    // }
    modifier employeeExist(address employeeId){
        var employee = employees[employeeId];
        assert(employee.id != 0x0);//confirm that exist
        _;
    }
    function _partialPaid(Employee employee) private{
            uint payment = employee.salary * (now - employee.lastPayday) /payDuration;//if exist , calculate payment註意整除
            employee.id.transfer(payment);
    }
    

    function addEmployee(address employeeId,uint salary)onlyOwner{
       // require(msg.sender == owner);//whether is onwner
        var employee = employees[employeeId];//var - any type
        assert(employee.id == 0x0);//confirm that exist
        totalSalary += salary *1 ether;
        employees[employeeId] = Employee(employeeId,salary* 1 ether,now);
    }
    
    function removeEmployee(address employeeId)onlyOwner employeeExist(employeeId){
        // require(msg.sender == owner);//whether is onwner
        //  var (employee,index) = _findEmloyee(employeeId); 
        var employee = employees[employeeId];
        _partialPaid(employee);
        totalSalary -=employees[employeeId].salary;
        delete employees[employeeId];//left with a blank in the array,wasteful
}
    
    function updateEmployee(address employeeId,uint salary)onlyOwner employeeExist(employeeId){
        //require(msg.sender == owner);
        //Equivalently 等效 
        // if (msg.sender != owner){//avoid employee cheating
        //     revert();
        // }
         var employee = employees[employeeId];
        _partialPaid(employee);
        totalSalary -= employees[employeeId].salary;
        employees[employeeId].salary = salary *1 ether;
        totalSalary += employees[employeeId].salary;
        employees[employeeId].lastPayday = now;     
         

    }
    
    function addFund() payable returns(uint){
        return this.balance;//address.balance
    }
    
    
    function calculateRunway()returns(uint)
    { //how many times left to pay
        return this.balance / totalSalary; 
    }
    function hasEnoughFund() returns(bool){
        // return this.balance >=salary;
        //return this.calculateRunway() > 0; //this方法 使用的gas 較多,不推薦
        return calculateRunway() > 0; //vm jump 操作,使用gas較少,推薦
    }
    
    function checkEmployee(address employeeId) returns(uint salary,uint lastPayday){ //quary the information of employees
    //name the returned parameter 命名參數返回 
    
        var employee = employees[employeeId];
        // return (employee.salary,employee.lastPayday);
        salary = employee.salary;
        lastPayday = employee.lastPayday;
    }
    
    function getPaid() employeeExist(msg.sender){

         var employee = employees[msg.sender];
         //assert(employee.id != 0x0);//confirm that exist
        uint nextPayday = employee.lastPayday + payDuration;
         //每一次運算都是真金白銀~
         //原則:不重復運算!——省gas
        assert(nextPayday < now);
        // if( nextPayday > now){
        //     revert();
              //throw or revert
            //throw: 所有的gas 均會被消耗殆盡
            //revert:回滾,return沒有消耗的gas
           
        // }
        
            employees[msg.sender].lastPayday = nextPayday;//原則:先修改內部變量,再給錢——》之後會講,安全問題
            employee.id.transfer(employee.salary);

    }
}

參考閱讀:老董-以太坊智能合約全棧開發

135.003 智能合約後端優化和產品化