135.003 智能合約後端優化和產品化
@(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 智能合約後端優化和產品化