Solidity
Solidity是編寫智能合約的語言恢恼,運行在ethereum虛擬機上民傻。語法類似于JS,它擁有異常機制,一旦出現(xiàn)異常漓踢,所有的執(zhí)行都會被撤回牵署,這是為了保證合約執(zhí)行的原子性,避免中間狀態(tài)出現(xiàn)的數(shù)據(jù)不一致喧半。
官方提供了IDE: remix
下面看一個簡單的合約的例子:
pragma solidity ^0.4.9;
contract Helloworld {
function multi(uint a, uint b) returns (uint c) {
uint result = a * b;
return result;
}
}
上面這個函數(shù)很簡單奴迅,調(diào)用multi方法進行乘法運算。
基礎語法
1.數(shù)據(jù)類型
- address 以太坊地址的長度挺据,20個字節(jié)
- int/uint 變長的有符號或無符號整型取具。支持以8遞增,uint8到uint256扁耐。uint 默認為uint256暇检。
- bool 布爾型
- mapping 鍵值對映射關系,如mapping(address => uint)
-
struct 結構體婉称,如下例子
2.狀態(tài)變量storage和局部變量memory
兩者區(qū)別很容易理解块仆,memory可以理解為臨時變量,不會記錄在鏈上酿矢,而storage是永久存儲的榨乎。
- 變量定義時默認為storage,而作為函數(shù)參數(shù)時瘫筐,默認為memory
contract HelloWorld{
//等價于 string storage public a;
string public a;
//參數(shù)等價于string memory _a
function changeNum(string _a){
}
}
- 當函數(shù)參數(shù)為memory類型時蜜暑,相當于值傳遞,storage才是指針傳遞
contract HelloWorld2{
string public a;
function HelloWorld2(){
a = "abc";
}
function f(){
changeNum(a);
}
function changeNum(string _a){
bytes(_a)[0] = "d";
//由于_a默認為memory策肝,所以_a只是值傳遞肛捍,所以此時修改a的值是不成功的,輸出還是abc
//需要把函數(shù)參數(shù)修改為string storage _a之众,才能輸出dbc
}
}
- 將變量賦值給一個新變量時拙毫,新變量的類型由賦值給它的類型決定。
function changeNum(string _a){
//_a默認為memory類型棺禾,所以b也為memory
string b = _a;
bytes(_a)[0] = "d";
}
3.函數(shù)四種訪問權限
函數(shù)聲明有public缀蹄、private、internal和external四種訪問權限
- 1.函數(shù)默認聲明為public膘婶,即可以以internal方式調(diào)用缺前,也可以通過external方式調(diào)用⌒螅可以理解為能夠被內(nèi)部合約訪問和外部合約訪問衅码。
- 2.Internal聲明的只允許通過internal方式調(diào)用,不能被外部合約脊岳。而external能夠被外部合約訪問逝段。
- 3.private和internal類似垛玻,都不能被外部合約訪問,唯一的不同是private函數(shù)不能被子類調(diào)用奶躯,而internal可以帚桩。
如下例子:
contract FunctionTest{
function publicFunc() {}
function callFunc(){
//以`internal`的方式調(diào)用函數(shù)
publicFunc();
//以`external`的方式調(diào)用函數(shù)
this.publicFunc();
}
function internalFunc() internal{}
function externalFunc() external{}
}
contract FunctionTest1 {
function externalCall(FuntionTest ft){
//調(diào)用另一個合約的外部函數(shù)
ft.publicFunc();
ft.externalFunc();
//ft.internalFunc();調(diào)用失敗,無法調(diào)用internal函數(shù)
}
}
4.pure巫糙、view朗儒、constant三種函數(shù)定義
當函數(shù)有返回值時,可以添加這三種定義参淹,用這三種方式定義的函數(shù)都只執(zhí)行讀操作醉锄,不會進行編譯執(zhí)行。即用了這三種方式定義的函數(shù)浙值,不會執(zhí)行函數(shù)里的邏輯恳不,只會執(zhí)行一個返回的讀操作。所以執(zhí)行這些函數(shù)不需要消耗gas費用开呐。
- pure區(qū)別是用于返回非變量烟勋,如returns 10;
- 而view和constant用于返回全局變量,兩者的區(qū)別為新舊版本
contract HelloWorld4{
uint public a = 1;
//由于被constant聲明的函數(shù)執(zhí)行讀操作筐付,所以a無法被修改
//執(zhí)行為f()卵惦,a依然為1
function f() constant{
a = 3;
}
}
5.函數(shù)修飾符
用于以聲明方式修改函數(shù)的語義,如下例子:
contract HelloWorld{
address public sender;
function HelloWorld(){
//創(chuàng)建合約時將合約創(chuàng)建者賦值給sender
sender = msg.sender;
}
modifier onlyOwner(){
//如果調(diào)用合約的人不是合約創(chuàng)建者則throw
if(msg.sender != sender) throw;
_; //占位符
}
//這樣a函數(shù)就只能被合約的創(chuàng)建者調(diào)用了
function a() onlyOwner{
...
}
6.回退函數(shù)
fallback function 回退函數(shù)瓦戚,每一個合約有且僅有一個沒有名字的函數(shù)沮尿,往合約發(fā)送消息時,會執(zhí)行該函數(shù)较解。如果合約要正常接受ether畜疾,需要加上payable聲明。聲明后印衔,當有用戶往合約轉賬時啡捶,將觸發(fā)該函數(shù),可以在里面寫相應的邏輯奸焙。
7.異常處理
Solidity使用狀態(tài)恢復來處理異常瞎暑,就是說當拋出異常時將恢復到調(diào)用(包括自調(diào)用)前的狀態(tài)。
拋出異常的方式有assert与帆,require了赌,revert,throw鲤桥。
- assert函數(shù),用于條件檢查渠概,只能測試內(nèi)部錯誤和檢查常量茶凳。
//檢查內(nèi)部計算是否會整型溢出
function add(uint256 a, uint256 b) internal constant returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
- require函數(shù)嫂拴,也是用于條件檢查,用于測試調(diào)用的輸入或者合約狀態(tài)變量贮喧。
function sendHalf(address addr) payable returns (uint balance) {
require(msg.value % 2 == 0); // 只允許偶數(shù)
.....
}
- revert 函數(shù)用于標記錯誤并恢復當前調(diào)用筒狠。
function buy(uint amount) payable {
if (amount > msg.value / 2 ether)
revert("Not enough Ether provided.");
}
- throw 和revert一樣,但是throw在0.4.13被棄用箱沦,將來會被淘汰辩恼。