在本文中,咱們將介紹智能合約的開發語言solidity。數據庫
Solidity是一種語法相似JavaScript的高級語言。它被設計成以編譯的方式生成以太坊虛擬機代碼。在後續內容中你將會發現,使用它很容易建立用於投票、衆籌、封閉拍賣、多重簽名錢包等等的合約。瀏覽器
讓咱們先從一個很是基礎的例子開始,不用擔憂你如今還一點都不瞭解,咱們將逐步瞭解到更多的細節。app
contract SimpleStorage { uint storedData; function set(uint x) { storedData = x; } function get() constant returns (uint retVal) { return storedData; } }
在Solidity中,一個合約由一組代碼(合約的函數)和數據(合約的狀態)組成。合約位於以太坊區塊鏈上的一個特殊地址。函數
uint storedData; 這行代碼聲明瞭一個狀態變量,變量名爲storedData,類型爲 uint (256bits無符號整數)。你能夠認爲它就像數據庫裏面的一個存儲單元,跟管理數據庫同樣,能夠經過調用函數查詢和修改它。在以太坊中,一般只有合約的擁有者才能這樣作。在這個例子中,函數 set 和 get 分別用於修改和查詢變量的值。學習
跟不少其餘語言同樣,訪問狀態變量時,不須要在前面增長 this. 這樣的前綴。區塊鏈
這個合約還沒法作不少事情(受限於以太坊的基礎設施),僅僅是容許任何人儲存一個數字。並且世界上任何一我的均可以來存取這個數字,缺乏一個(可靠的)方式來保護你發佈的數字。任何人均可以調用set方法設置一個不一樣的數字覆蓋你發佈的數字。可是你的數字將會留存在區塊鏈的歷史上。稍後咱們會學習如何增長一個存取限制,使得只有你才能修改這個數字。ui
接下來的合約將實現一個形式最簡單的加密貨幣。任何人均可以發送貨幣給其餘人,不須要註冊用戶名和密碼,只要有一對以太坊的公私鑰便可。this
contract Coin { //關鍵字「public」使變量能從合約外部訪問。 address public minter; mapping (address => uint) public balances; //事件讓輕客戶端能高效的對變化作出反應。 event Sent(address from, address to, uint amount); //這個構造函數的代碼僅僅只在合約建立的時候被運行。 function Coin() { minter = msg.sender; } function mint(address receiver, uint amount) { if (msg.sender != minter) return; balances[receiver] += amount; } function send(address receiver, uint amount) { if (balances[msg.sender] < amount) return; balances[msg.sender] -= amount; balances[receiver] += amount; Sent(msg.sender, receiver, amount); } }
這個合約引入了一些新的概念,讓咱們來逐個介紹。加密
address public minter;`
這行代碼聲明瞭一個可公開訪問的狀態變量,類型爲address。address類型的值大小爲160 bits,不支持任何算術操做。適用於存儲合約的地址或其餘人的公私鑰。public關鍵字會自動爲其修飾的狀態變量生成訪問函數。沒有public關鍵字的變量將沒法被其餘合約訪問。另外只有本合約內的代碼才能寫入。自動生成的函數以下:.net
function minter() returns (address) { return minter; }
固然咱們本身增長一個這樣的訪問函數是行不通的。編譯器會報錯,指出這個函數與一個狀態變量重名。
下一行代碼建立了一個public的狀態變量,可是其類型更加複雜:
mapping (address => uint) public balances;
該類型將一些address映射到無符號整數。mapping能夠被認爲是一個哈希表,每個可能的key對應的value被虛擬的初始化爲全0.這個類比不是很嚴謹,對於一個mapping,沒法獲取一個包含其全部key或者value的鏈表。因此咱們得本身記着添加了哪些東西到mapping中。更好的方式是維護一個這樣的鏈表,或者使用其餘更高級的數據類型。或者只在不受這個缺陷影響的場景中使用mapping,就像這個例子。在這個例子中由public關鍵字生成的訪問函數將會更加複雜,其代碼大體以下:
function balances(address _account) returns (uint balance) { return balances[_account]; }
咱們能夠很方便的經過這個函數查詢某個特定帳號的餘額。
event Sent(address from, address to, uint value);
這行代碼聲明瞭一個「事件」。由send函數的最後一行代碼觸發。客戶端(服務端應用也適用)能夠以很低的開銷來監聽這些由區塊鏈觸發的事件。事件觸發時,監聽者會同時接收到from,to,value這些參數值,能夠方便的用於跟蹤交易。爲了監聽這個事件,你可使用以下代碼:
Coin.Sent().watch({}, '', function(error, result) { if (!error) { console.log("Coin transfer: " + result.args.amount + " coins were sent from " + result.args.from + " to " + result.args.to + "."); console.log("Balances now:\n" + "Sender: " + Coin.balances.call(result.args.from) + "Receiver: " + Coin.balances.call(result.args.to)); } }
注意在客戶端中是如何調用自動生成的 balances 函數的。
這裏有個比較特殊的函數 Coin。它是一個構造函數,會在合約建立的時候運行,以後就沒法被調用。它會永久得存儲合約建立者的地址。msg(以及tx和block)是一個神奇的全局變量,它包含了一些能夠被合約代碼訪問的屬於區塊鏈的屬性。msg.sender 老是存放着當前函數的外部調用者的地址。
最後,真正被用戶或者其餘合約調用,用來完成本合約功能的函數是mint和send。若是合約建立者以外的其餘人調用mint,什麼都不會發生。而send能夠被任何人(擁有必定數量的代幣)調用,發送一些幣給其餘人。注意,當你經過該合約發送一些代幣到某個地址,在區塊鏈瀏覽器中查詢該地址將什麼也看不到。由於發送代幣致使的餘額變化只存儲在該代幣合約的數據存儲中。經過事件咱們能夠很容易建立一個能夠追蹤你的新幣交易和餘額的「區塊鏈瀏覽器」。