@(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 次序
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
库函数 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);
}
}
参考阅读:老董-以太坊智能合约全栈开发