Solidity是以太坊的主要編程語言,它是一種靜態類型的 JavaScript-esque 語言,是面向合約的、爲實現智能合約而建立的高級編程語言,設計的目的是能在以太坊虛擬機(EVM)上運行。javascript
本文基於CryptoZombies,教程地址爲:cryptozombies.io/zh/html
Solidity 的代碼都包裹在合約裏面. 一份合約
就是以太應幣應用的基本模塊, 全部的變量和函數都屬於一份合約, 它是你全部應用的起點.前端
一份名爲 HelloWorld
的空合約以下:java
contract HelloWorld {
}
複製代碼
首先看一個簡單的智能合約。c++
pragma solidity ^0.4.0;
contract SimpleStorage {
uint storedData; // 聲明一個類型爲 uint (256位無符號整數)的狀態變量,叫作 storedData
function set(uint x) public {
storedData = x; // 狀態變量能夠直接訪問,不須要使用 this. 或者 self. 這樣的前綴
}
function get() public view returns (uint) {
return storedData;
}
}
複製代碼
全部的 Solidity 源碼都必須冠以 "version pragma" — 標明 Solidity 編譯器的版本. 以免未來新的編譯器可能破壞你的代碼。git
例如: pragma solidity ^0.4.0;
(當前 Solidity 的最新版本是 0.4.0).web
關鍵字
pragma
的含義是,通常來講,pragmas(編譯指令)是告知編譯器如何處理源代碼的指令的(例如, pragma once )。數據庫
Solidity中合約的含義就是一組代碼(它的 函數 )和數據(它的 狀態 ),它們位於以太坊區塊鏈的一個特定地址上。編程
該合約能完成的事情並很少:它能容許任何人在合約中存儲一個單獨的數字,而且這個數字能夠被世界上任何人訪問,且沒有可行的辦法阻止你發佈這個數字。固然,任何人均可以再次調用 set
,傳入不一樣的值,覆蓋你的數字,可是這個數字仍會被存儲在區塊鏈的歷史記錄中。小程序
Solidity 語句以分號(;)結尾
狀態變量是被永久地保存在合約中。也就是說它們被寫入以太幣區塊鏈中,想象成寫入一個數據庫。
contract HelloWorld {
// 這個無符號整數將會永久的被保存在區塊鏈中
uint myUnsignedInteger = 100;
}
複製代碼
在上面的例子中,定義 myUnsignedInteger
爲 uint
類型,並賦值100。
uint
無符號數據類型, 指其值不能是負數,對於有符號的整數存在名爲int
的數據類型。Solidity中,
uint
其實是uint256
代名詞, 一個256位的無符號整數。
程序有時須要對不一樣類型的數據進行操做,由於 Solidity 是靜態類型語言,對不一樣類型的數據進行運算會拋出異常,好比:
uint8 a = 5;
uint b = 6;
// 將會拋出錯誤,由於 a * b 返回 uint, 而不是 uint8:
uint8 c = a * b;
複製代碼
a * b
返回類型是 uint
, 可是當咱們嘗試用 uint8
類型接收時, 就會形成潛在的錯誤。這時,就須要顯式的進行數據類型轉換:
// 咱們須要將 b 轉換爲 uint8:
uint8 c = a * uint8(b);
複製代碼
把它的數據類型轉換爲
uint8
, 就能夠了,編譯器也不會出錯。
Solidity 支持多種數據類型,好比:
在 Solidity 中,數學運算很直觀明瞭,與其它程序設計語言相同:
x + y
x - y
,x * y
x / y
x % y
(例如, 13 % 5 餘 3, 由於13除以5,餘3)x ** y
Solidity 提供了 結構體
,用來表示更復雜的數據類型。
struct Person {
uint age;
string name;
}
複製代碼
結構體容許你生成一個更復雜的數據類型,它有多個屬性。
建立結構體方式爲:
// 建立一個新的Person:
Person satoshi = Person(172, "Satoshi");
複製代碼
Solidity 提供兩種類型的數組:靜態數組
和動態數組
。
// 固定長度爲2的靜態數組:
uint[2] fixedArray;
// 固定長度爲5的string類型的靜態數組:
string[5] stringArray;
// 動態數組,長度不固定,能夠動態添加元素:
uint[] dynamicArray;
複製代碼
使用 push 函數向數組中添加值:
fixedArray.push[123]
fixedArray.push[234]
// fixedArray 值爲 [123, 234]
複製代碼
array.push()
在數組的 尾部 加入新元素 ,因此元素在數組中的順序就是添加的順序array.push()
會返回數組的長度。
Solidity 數組支持多種類型,好比結構體:
struct Person {
uint age;
string name;
}
Person[] people; // dynamic Array, we can keep adding to it
複製代碼
結構體類型的數組添加值的方式爲:
people.push(Person(16, "Vitalik"));
// 也可使用下面的方式,推薦使用上述一行簡潔的方式
Person satoshi = Person(172, "Satoshi");
people.push(satoshi);
複製代碼
也可使用public
定義公共數組,Solidity 會自動建立getter
方法。語法以下:
struct Person {
uint age;
string name;
}
Person[] public people; // dynamic Array, we can keep adding to it
複製代碼
公共數組支持其它的合約讀取數據(但不能寫入數據),因此這在合約中是一個有用的保存公共數據的模式。(有點像全局變量,全部合約共享同一個「內存空間「,厲害了!)
Solidity 中,函數定義以下:
function eatHamburgers(string _name, uint _amount) {
}
複製代碼
Solidity
習慣上函數裏的變量都是以(_)開頭 (但不是硬性規定) 以區別全局變量。
這是一個名爲 eatHamburgers
的函數,它接受兩個參數:一個 string
類型的 和 一個 uint
類型的。如今函數內部仍是空的。
函數調用以下:
eatHamburgers("vitalik", 100);
複製代碼
Solidity 函數分爲私有函數和共有函數。
Solidity 定義的函數的屬性默認爲
公共
。 這就意味着任何一方 (或其它合約) 均可以調用你合約裏的函數。
顯然,不是何時都須要這樣,並且這樣的合約易於受到攻擊。因此將本身的函數定義爲私有
是一個好的編程習慣,只有當你須要外部世界調用它時纔將它設置爲公共
。
能夠把全部的函數都顯式的聲明
public
和private
來規避這個問題。
定義私有函數比較簡單,只須要在函數參數後添加 private
關鍵字便可。示例以下:
uint[] numbers;
function _addToArray(uint _number) private {
numbers.push(_number);
}
複製代碼
這意味着只有咱們合約中的其它函數纔可以調用這個函數,給 numbers
數組添加新成員。
和函數的參數相似,私有函數的名字用(
_
)起始。
注意:
在智能合約中你所用的一切都是公開可見的,即使是局部變量和被標記成private
的狀態變量也是如此。
和其它語言同樣,Solidity 函數也有返回值,示例以下:
string greeting = "What's up dog";
function sayHello() public returns (string) {
return greeting;
}
複製代碼
返回值使用 returns
關鍵字標註。(已是很是奇怪的寫法了。。)
constant
是view
的別名
string greeting = "What's up dog";
function sayHello() public returns (string) {
return greeting;
}
複製代碼
像 sayHello
函數這種實際上沒有改變合約中數據內容的狀況,能夠把函數定義爲view
,這意味着此函數只讀不修改數據。可使用如下聲明方式:
function sayHello() public view returns (string) {}
複製代碼
能夠將函數聲明爲 view
類型,這種狀況下要保證不修改狀態。
下面的語句被認爲是修改狀態:
selfdestruct
。view
或者 pure
的函數。pure 比 view 更輕量,使用這個修飾符修飾的函數甚至都不會讀取合約中的數據,例如:
function _multiply(uint a, uint b) private pure returns (uint) { return a * b; }
複製代碼
這個函數沒有讀取應用裏的狀態,它的返回值只和它輸入的參數相關。
Solidity 編輯器會給出提示,提醒你使用 pure/view修飾符。
函數能夠聲明爲 pure
,在這種狀況下,承諾不讀取或修改狀態。
除了上面解釋的狀態修改語句列表以外,如下被認爲是從狀態中讀取:
this.balance
或者 <address>.balance
。block
,tx
, msg
中任意成員 (除 msg.sig
和 msg.data
以外)。pure
的函數。payable 關鍵字用來講明,這個函數能夠接受以太幣,若是沒有這個關鍵字,函數會自動拒絕全部發送給它的以太幣。
事件 是合約和區塊鏈通信的一種機制。你的前端應用「監聽」某些事件,並作出反應。例如:
// 這裏創建事件
event IntegersAdded(uint x, uint y, uint result);
function add(uint _x, uint _y) public {
uint result = _x + _y;
//觸發事件,通知app
IntegersAdded(_x, _y, result);
return result;
}
複製代碼
用戶界面(固然也包括服務器應用程序)能夠監聽區塊鏈上正在發送的事件,而不會花費太多成本。一旦它被髮出,監聽該事件的listener都將收到通知。而全部的事件都包含了 from
, to
和 amount
三個參數,可方便追蹤事務。 爲了監聽這個事件,你可使用以下代碼(javascript 實現):
var abi = /* abi 由編譯器產生 */;
var ClientReceipt = web3.eth.contract(abi);
var clientReceipt = ClientReceipt.at("0x1234...ab67" /* 地址 */);
var event = clientReceipt.IntegersAdded();
// 監視變化
event.watch(function(error, result){
// 結果包括對 `Deposit` 的調用參數在內的各類信息。
if (!error)
console.log(result);
});
// 或者經過回調當即開始觀察
var event = clientReceipt.IntegersAdded(function(error, result) {
if (!error)
console.log(result);
});
複製代碼
下面是一個完整的代碼示例:
pragma solidity ^0.4.19;
contract ZombieFactory {
// 創建事件
event NewZombie(uint zombieId, string name, uint dna);
uint dnaDigits = 16; // 定義狀態變量
uint dnaModulus = 10 ** dnaDigits;
struct Zombie { // 定義結構體
string name;
uint dna;
}
Zombie[] public zombies; // 定義動態數組
// 建立私有函數,私有函數命名使用 _ 前綴
function _createZombie(string _name, uint _dna) private {
// 函數參數命名 使用 _ 做爲前綴
// arrays.push() 將元素加入到數組尾部,而且返回數組的長度
uint id = zombies.push(Zombie(_name, _dna)) - 1;
// 觸發事件
NewZombie(id, _name, _dna);
}
// view 爲函數修飾符,表示此函數不須要更新或建立狀態變量
// pure 表示函數不須要使用狀態變量
function _generateRandomDna(string _str) private view returns (uint) {
// 使用 keccak256 建立一個僞隨機數
uint rand = uint(keccak256(_str));
return rand % dnaModulus;
}
function createRandomZombie(string _name) public {
uint randDna = _generateRandomDna(_name);
_createZombie(_name, randDna);
}
}
複製代碼
Ethereum 內部有一個散列函數keccak256,它用了SHA3版本。一個散列函數基本上就是把一個字符串轉換爲一個256位的16進制數字。 在智能合約中使用隨機數很難保證節點不做弊, 這是由於智能合約中的隨機數通常要依賴計算節點的本地時間獲得, 而本地時間是能夠被惡意節點僞造的,所以這種方法並不安全。 通行的作法是採用 鏈外off-chain 的第三方服務,好比 Oraclize 來獲取隨機數)。
最後,感謝女友支持和包容,比❤️
也能夠在公號輸入如下關鍵字獲取歷史文章:公號&小程序
| 設計模式
| 併發&協程