首先,在remix的檔案ide中鍵入程式碼後,透過編譯按鈕來編譯。成功後會在按鈕上出現一個綠色對勾:
部署
編譯成功後就可進行部署環節,部署成功後會出現合約例項。
setState
合約部署後,我們來呼叫setState(4)。在執行成功後,會產生一條交易收據,裡面包含了交易的執行資訊。
在這裡,使用者可以看到交易執行狀態(status)、交易執行人(from)、交易輸入輸出(decoded input, decoded output)、交易開銷(execution cost)以及交易日誌(logs)。
在logs中,我們看到SetState事件被丟擲,裡面的引數也記錄了事件傳入的值4。
如果我們換一個賬戶來執行,那麼呼叫會失敗,因為onlyAdmin修飾符會阻止使用者呼叫。
getState
呼叫getState後,可以直接看到所得到的值為4,正好是我們先前setState所傳入的值:
Solidity資料型別
在前文的示例中,我們用到了uint等資料型別。由於Solidity型別設計比較特殊,這裡也會簡單介紹一下Solidity的資料型別。
整型系列
Solidity提供了一組資料型別來表示整數, 包含無符號整數與有符號整數。每類整數還可根據長度細分,具體細分型別如下。
定長bytes系列
Solidity提供了bytes1到bytes32的型別,它們是固定長度的位元組陣列。
使用者可以讀取定長bytes的內容。
function bytesSample() public{
bytes32 barray;
//Initialize baarray
//read brray[0]
byte b = barray[0];
}
並且,可以將整數型別轉換為bytes。
uint256 s = 1;
bytes32 b = bytes32(s);
這裡有一個關鍵細節,Solidity採取大端序編碼,高地址存的是整數的小端。例如,b[0]是低地址端,它存整數的高階,所以值為0;取b[31]才是1。
function bytesSample() public pure returns(byte, byte){
uint256 value = 1;
bytes32 b = bytes32(value);
//Should be (0, 1)
return (b[0], b[31]);
}
變長bytes
從上文中,讀者可瞭解定長byte陣列。此外,Solidity還提供了一個變長byte陣列:bytes。使用方式類似陣列,後文會有介紹。
string
Solidity提供的string,本質是一串經UTF-8編碼的位元組陣列,它相容於變長bytes型別。
目前Solidity對string的支援不佳,也沒有字元的概念。使用者可以將string轉成bytes。
function stringSample() public view returns(bytes){
string memory str = "abc";
bytes memory b = bytes(str);
//0x616263
return b;
}
要注意的是,當將string轉換成bytes時,資料內容本身不會被複製,如上文中,str和b變數指向的都是同一個字串abc。
address
address表示賬戶地址,它由私鑰間接生成,是一個20位元組的資料。同樣,它也可以被轉換為bytes20。
function addressSample() public view returns(bytes20){
address me = msg.sender;
bytes20 b = bytes20(me);
return b;
}
mapping
mapping表示對映, 是極其重要的資料結構。它與java中的對映存在如下幾點差別:
它無法迭代keys,因為它只儲存鍵的雜湊,而不儲存鍵值,如果想迭代,可以用開源的可迭代雜湊類庫
如果一個key未被儲存在mapping中,一樣可以正常讀取到對應value,只是value是空值(位元組全為0)。所以它也不需要put、get等操作,使用者直接去操作它即可。
contract Sample{
mapping(uint=>string) private values;
function mappingSample() public view returns(bytes20){
//put a key value pair
values[10] = "hello";
//read value
string value = values[10];
}
}
陣列
如果陣列是狀態變數,那麼支援push等操作:
contract Sample{
string[] private arr;
function arraySample() public view {
arr.push("Hello");
uint len = arr.length;//should be 1
string value = arr[0];//should be Hello
}
}
陣列也可以以區域性變數的方式使用,但稍有不同:
function arraySample() public view returns(uint){
//create an empty array of length 2
uint[] memory p = new uint[](2);
p[3] = 1;//THIS WILL THROW EXCEPTION
return p.length;
}
struct
Solidity允許開發者自定義結構物件。結構體既可以作為狀態變數儲存,也可以在函式中作為區域性變數存在。
struct Person{
uint age;
string name;
}
Person private _person;
function structExample() {
Person memory p = Person(1, "alice");
_person = p;
}
本節中只介紹了比較常見的資料型別,更完整的列表可參考Solidity官方網站:
https://solidity.readthedocs.io/en/v0.6.3/types.html
全域性變數
示例合約程式碼的建構函式中,包含msg.sender。它屬於全域性變數。在智慧合約中,全域性變數或全域性方法可用於獲取和當前區塊、交易相關的一些基本資訊,如塊高、塊時間、合約呼叫者等。
比較常用的全域性變數是msg變數,表示呼叫上下文,常見的全域性變數有以下幾種:
· msg.sender:合約的直接呼叫者。
由於是直接呼叫者,所以當處於 使用者A->合約1->合約2 呼叫鏈下,若在合約2內使用msg.sender,得到的會是合約1的地址。如果想獲取使用者A,可以用tx.origin.
· tx.origin:交易的"始作俑者",整個呼叫鏈的起點。
· msg.calldata:包含完整的呼叫資訊,包括函式標識、引數等。calldata的前4位元組就是函式標識,與msg.sig相同。
· msg.sig:msg.calldata的前4位元組,用於標識函式。
· block.number:表示當前所在的區塊高度。
· now:表示當前的時間戳。也可以用block.timestamp表示。
這裡只列出了部分常見全域性變數,完整版本請參考:
https://solidity.readthedocs.io/en/v0.4.24/units-and-global-variables.html。
結語
本文以一個簡單的示例合約作為引入,介紹了運用Solidity開發智慧合約的基本知識。讀者可以嘗試執行該合約,感受智慧合約的開發。
若想更深入學習智慧合約示例,推薦官方網站示例供讀者學習,也可關注本專題後續系列文章:
https://solidity.readthedocs.io/en/v0.6.2/solidity-by-example.html。
在官網的示例中,提供了投票、競拍、微支付通道等多個案例,這些案例貼近實際生活,是很好的學習資料。