solidity 0.5.7簡明教程
以太坊不僅是一種加密數字貨幣,它更是功能完備的智慧合約平臺, solidity就是用來開發以太坊上的智慧合約的原生開發語言。solidity最早 釋出於2015年,它是第一種圖靈完備的智慧合約專用開發語言。目前除了 以太坊之外,在其他區塊鏈中也逐漸開始支援solidity,例如hyperledger fabric、tendermint等。 在這個solidity快速教程中,我們將使用最新0.5.7版的solidity,以一個具體的案例來介紹 solidity智慧合約的開發、部署與互動,希望對你快速掌握solidity智慧合約的開發有所幫助。
如果要高效系統地掌握以太坊智慧合約與DApp的開發,推薦訪問匯智網的線上互動課程:
以太坊開發入門 |java以太坊 |python以太坊 |php以太坊 |C#以太坊 |電商DApp實戰 |ERC721通證實戰
0、問題的背景
有一個老爺爺,在生命的最後歲月別無他求,只是希望自己的財產能夠 通過遺囑順利地傳給其他家庭成員。
在傳統的遺囑中,遺產分配方案是 落實在法律檔案上的,然後當真正開始分配時,法官需要重審檔案並做出 相應的決定。常見的問題發生在家庭成員之間對分配比例的爭執上,甚至 因此而導致家庭成員關係的破裂。在法庭聽證階段,這些都會影響法官 最終的裁決,並因此可能導致不公平的結果,甚至對家庭關係造成進一步 的傷害。
那麼,如果我們可以讓遺產分配自動進行,是否可以避免上述情況的發生?
如果遺產是一個智慧合約,那麼就不需要法官了。老爺爺可以自主地利用 合約管理資產,然後在他去世後由程式來分配遺產給家庭成員。合約裡的 程式碼就決定了最終的分配結果,因此無需法官的介入。例如薩拉分$10000, 本得到$5000,朱麗葉得到$2000。程式碼執行後,資產以代幣或加密貨幣的 形式自動分配給這些家庭成員,而無需人工介入。雖然不能保證每個成員 都對遺產的分配結果滿意,但是沒有人會和程式碼爭執。這聽起來還比較 可行,對嗎?
記住這個案例,在這個快速教程中,我們將使用solidity,為老爺爺開發 一個簡單的遺囑合約,來滿足他最後的願望。
1、搭建solidity開發環境
開發solidity智慧合約最簡單的方法,就是使用官方提供的線上整合開發環境REMIX, 你可以點選 這裡 開啟remix,在網頁裡就完成 solidity智慧合約的編寫、編譯與部署:
在你開啟remix頁面後,注意在右側的 run
選項頁, environment
下拉框中, 要選中 JavaScript VM
。這個選項的意思是使用一個記憶體模擬以太坊節點 作為你的solidity智慧合約的執行平臺,這樣就不用考慮與實際的以太坊主網 互動所需要的賬號、資金、計算費用等問題,而可以先把精力聚焦在學習如何 使用solidity表達你的業務邏輯上。
點選remix頁面左上方的 +
圖示,就可以建立一個新的程式碼檔案,我們將其命名 為will.sol。在remix頁面中間的編輯區域可以同時顯示多個檔案,當前正在編輯 的檔案,則以活動選項頁的形式顯示檔名稱。
2、宣告solidity編譯器版本
solidity還是很早期階段的語言,從語法到編譯器都在不斷地演化,所以在solidity 程式碼的第一行,一定要用pragma關鍵字宣告這個檔案中的solidity程式碼需要哪個版本 的編譯器。例如:
注意在solidity中,末尾的分號不可省略。
3、編寫第一個solidity合約
接下來就可以定義我們的第一個合約:
使用contract關鍵字來定義一個合約,solidity的合約類似於我們熟悉的OOP中的類, 因此通常合約的名稱首字母也會大寫,例如 Will
。一對大括號用來定義合約的實現 邏輯,單行註釋也使用 //
,這和很多開發語言都類似。
4、solidity中的全域性變數和建構函式
在我們開始寫程式碼之前,應當首先明確遺囑的條款。假設老爺爺的遺產是50個以太幣, 其中20個留給他的兒子康萊德,剩下的30個留給他的妻子麗莎。在真實的環境中, 當老爺爺去世後,應當有一個外部的程式將呼叫合約中定義的方法來分配遺產,但是 我們為了便於學習將自己完成這個呼叫。
現在,讓我們先完成如下程式碼:
- 表徵合約所有者的變數
- 表徵遺產數量的變數
- 表徵老爺爺是否還健在的變數
- 設定上述變數初始值的建構函式
第5行程式碼定義了合約的所有者。當我們在solidity中定義變數時,必須先宣告其 型別。 address
是solidity中一種特殊的型別,它表示一個以太坊地址。 address
型別的變數有一些特殊的方法,我們在後面會進一步瞭解。
第6行程式碼定義的fortune變數用來儲存老爺爺的遺產數量,它的型別是 uint
或 unsigned int
,意思是這個變數是0或正整數。solidity中有很多資料型別,但 我們不會在這裡一一介紹,你可以在官方文件中深入瞭解solidity的資料型別。
第7行程式碼定義的isDeceased變數用來標識老爺爺是否已經去世,這是一個開關量, 因此其型別為 boolean
,可能的值只有兩個:true或false,預設值為false。
第9~13行程式碼是合約的建構函式,這個特殊的函式將在合約部署的時候自動執行。
public
關鍵字被稱為 可見性修飾符 ,它的作用是宣告被修飾的方法是否允許 外部呼叫。 public
意味著在合約內部或外部(由其他合約或其他人)都可以呼叫 該方法。
payable
關鍵字是solidity的特色之一,它使得被修飾的方法可以傳送或接收以太幣。 為建構函式宣告 payable
關鍵字意味著當我們部署合約的時候,可以直接向合約存入 以太幣,例如,作為遺產的50個以太幣。當合約接收到以太幣後,這些幣就儲存在合約 地址上了。
在建構函式內部,我們將 owner
變數的值設定為 msg.sender
,這是一個以太坊 平臺預置的全域性變數,表示呼叫合約方法的賬號地址,在我們的案例中,這的地址 是老爺爺的。
同時我們將 fortune
變數的值設定為 msg.value
,這是另一個全域性變數,它表示 被呼叫的方法接收到的以太幣的數量。
雖然變數 isDeceased
被自動初始化為預設值false,但為了清晰起見,我們將其 顯式地設定為false。
5、使用solidity修飾符
在solidity中,修飾符(Modifier)可以為函式附加額外的條件邏輯。例如,假設 我有一個用來關燈的方法,同時有一個修飾符要求燈開關必須處於 on 狀態,那麼 我們就可以在方法上附加宣告這個修飾符,以便確保只有在燈開關處於 on 狀態時, 才可以呼叫這個方法,否則就丟擲異常。
第15行程式碼定義了 onlyOwner
修飾符。如果一個方法附加聲明瞭這個修飾符,那麼 就要求呼叫方法的賬號(msg.sender)必須與 owner
變數的值一致(別忘了我們 在建構函式中設定了owner的值)。這個呼叫條件有助於遺產的分配,我們將在後面 看到這一點。
require
關鍵字的意思是,括號裡的表示式的值必須為真(true),否則就會丟擲 異常,不再繼續執行程式碼。
_;
起到佔位符的作用,在執行過程中,以太坊虛擬機器會用被修飾的方法程式碼來替換 它。
第20行程式碼定義了 mustBeDeceased
修飾符。如果一個方法附加聲明瞭這個修飾符, 那麼就只有在 isDeceased
變數值為true時,才可以呼叫該方法,否則就丟擲異常。
在上面的程式碼中,我們使用修飾符來限定方法的執行條件,當然也可以不使用修飾符, 而直接在方法實現程式碼中使用 require
,不過修飾符看起來更高階一些,也更容易 實現程式碼的複用。
6、設定遺產分配方案
現在我們要繼續完成遺產在家庭成員之間的分配任務,這需要他們的錢包地址和分配數量。
正如我們之前所述,康萊德將收到20個以太幣而麗莎將繼承30個。讓我們建立一個 陣列來儲存他們的錢包地址,然後寫一個方法來分配遺產。
第25行程式碼定義了一個空陣列 familyWallets
,用來儲存所有家庭成員的錢包地址。 和其他語言一樣,在solidity中陣列是順序存放並且可以使用序號來存取。注意方括號 之前的關鍵字 paybale
,只有 address payable
型別的變數,才可以接收以太幣,這是 0.5版本的solidity與之前版本的區別之一。
第27行程式碼建立了一個從 address
型別到 uint
型別的對映表變數 inheritance
, 用來儲存每個錢包地址的遺產數量。這是一個鍵/值對資料結構,類似於其他語言中的 字典或雜湊表,可以用鍵來存取值。
第29行程式碼定義了一個方法,它的功能是將一個錢包地址新增到 familyWallets
陣列, 然後設定該地址在 inheritance
對映表中的遺產數量。注意附加的 onlyOwner
修飾符, 猜一下為什麼我們要在這裡宣告這個修飾符?
第30行程式碼將傳入方法的錢包地址追加到 familyWallets
陣列的末尾。
第31行程式碼將傳入方法的遺產繼承數量設定為對映表 inheritance
的指定地址 (傳入方法的另一個引數)的值。
7、實現遺產自動分配
讓我們總結一下。到目前為止,我們已經學習了全域性變數、資料型別、建構函式、 特殊的關鍵字例如 payable
和 public
、內建的全域性變數例如 msg.sender
和 msg.value
、修飾符和 require
、陣列、對映表和方法。我們已經搭好了合約 的框架,現在讓我們把各部分整合起來最終完成合約。
作為這個教程最後一部分的程式碼,我們將實現家庭成員遺產的自動分配。
第34行定義了 payout()
方法,注意 private
關鍵字,這個 可視性修飾符 是 public
的反義詞,它只允許被修飾的方法在合約內部呼叫,就像在第42行 的程式碼那樣。之所以在這裡使用 private
,主要是考慮到安全性,因為我們 不希望任何來自合約外部的呼叫。注意最後的 mustBeDeceased
修飾符,目前 我們依然不能滿足這個修飾符要求的條件來執行 payout()
方法。
第35行程式碼是一個 for
迴圈,用來遍歷 familyWallets
陣列。語法如下:
- 定義一個計數器變數i,
- 宣告迴圈的執行條件
- 每個週期計數器變數i加1
第36行程式碼是整個合約的核心,我們呼叫 address
型別的地址物件的 transfer()
方法,向該地址轉賬預定的遺產繼承數量, inheritance[familyWallets[i]]
表示在 inheritance
對映表中,鍵 familyWallets[i]
的值,也就是第i 個家庭成員的遺產繼承數量。
第40~42行程式碼定義了一個方法,當老爺爺去世後將呼叫這個方法來觸發遺產的 分配。在這裡我們將變數 isDeceased
的值設定為true。
現在我們完成了嗎?
實際上,還不完全是…
這個智慧合約的程式碼是寫完了,但是我們怎麼用它?現在是收穫果實的時候了。
8、solidity合約部署與互動
你的remix頁面看起來應該像這樣:
在remix頁面右邊切換到 compile
選項頁,確認按下圖選中編譯器的版本,然後 點選[start to compile]:
你可能會看到靜態分析生成的一個藍色文字框,我們暫時忽略它的提醒,切換到 run
選項頁:
確保 Environment
下拉框中選中了 Javascript VM
,點選 account
的下拉選單 將顯示5個測試賬戶,每個賬戶都有100個以太幣,讓我們選擇第一個。
向以太坊區塊鏈部署合約並不是免費的,部署者需要支付手續費,通常被稱為gas。 引入這一機制的目的是避免區塊鏈計算資源被惡意濫用,要進一步瞭解gas,可以檢視 這篇文章: 1分鐘搞清Gas/ Gas Price/ Gas Limit 。
gas limit
欄位使用預設值就可以了,我們先不修改它。
value
欄位表示我們在部署合約時要傳送給合約的以太幣數量。輸入50,還記得 我們在定義建構函式時附加的 payable
關鍵字嗎?
現在繼續,點選[deploy]。
你可能立刻會注意到3件事。首先,選中的賬戶餘額現在變成了49.9999… ,這是因為 我們轉給合約50個以太幣,還要扣除一點部署手續費。頁面底部的控制檯也會提供 關於部署過程的詳細資訊,你可以檢視一下。現在看起來是這樣:
我們的合約已經成功部署了!它生成了自己的地址,並且顯示出我們定義的兩個 合約方法。作為合約的持有者,我們要做的第一件事,是設定家庭成員的繼承數量: 康萊德(20)、麗莎(30)。假設我們用 account
下拉選單中的第二個作為康萊德 的賬號,麗莎的用第三個。
選擇第二個賬號,點選[拷貝到剪下板]圖示,然後輸入上圖中的 setInheritance
後面的文字輸入框。
在我們執行 setInheritance
方法之前,有幾件事情要記住。
傳入合約的以太幣數量的單位是wei而不是 以太幣,1 ETH = 1,000,000,000,000,000,000 WEI,這是非常小的單位,因此 我們需要將以太幣表示的遺產數量先轉換為以WEI為單位的值。
在將遺產數量換算後,在將其寫入上圖中的 setInheritance
後面的文字輸入框中,之前 輸入的地址後面,這兩個值之間注意要用逗號隔開。
還有,別忘了在 account
下拉框選中第一個賬號,還記得 onlyOwner
修飾符嗎? 只有合約的持有人才可以呼叫 setInheritance
方法!
現在讓我們依次為康萊德和麗莎執行setInheritance方法。你應當可以看到控制檯 輸出的成功資訊。看一下其中的 decoded input
:
你看,它顯示的就是我們輸入的資料。
遺產分配好了,但是壞訊息來了。老爺爺在73歲時,在一次北極探險中不幸因心臟病 突發去世。他總是這麼充滿激情與活力。
當我們紀念這位老爺爺的同時,我們同時呼叫遺囑合約的 deceased()
方法,完成 老爺爺的最後的願望。。。
原文: Introduction to Solidity Programming and Smart Contracts (For Complete Beginners)
匯智網翻譯整理,轉載請標明出處