Skip to main content

· 3 min read

在前一篇文章中我已經設定好了雙系統(Elementary OS/Windows 10)開機。在這篇中將繼續把系統的基礎中文環境(中文界面 + 輸入法)設定起來。感謝前路上的貢獻者讓整個過程變得相當容易。

設定好的中文環境如下 Imgur

為什麼要用 Elementary OS?

其實 Ubuntu 等發行版的中文環境已經做得很好,如果使用 Ubuntu 等更成熟的發行版,基礎的中文環境都是預設好開箱即用的。

但我就是任性想用基於 Ubuntu,但是界面看起來更養眼的Elementary OS

調整語系

Imgur

首先,打開Settings中的Language & Region,在左側Installed Languages選單中將界面切換到中文。並選擇完全安裝(Complete Installation)。

等待安裝好後重新登入,就可以看到全中文界面了。

Imgur

安裝新酷音輸入法

在安裝好 Elementary OS 0.4.1 後,預設並沒有輸入法選項,但其實內部已安裝了 ibus 輸入法框架。

因為沒有內建輸入法,所以若要使用輸入法,需要自行安裝。

在命令列中輸入以下命令以安裝新酷音輸入法

$ sudo apt update
$ sudo apt install ibus-chewing

安裝好後再輸入ibus-setup,可開啟輸入法設定畫面

$ ibus-setup

在 「輸入法」 分頁底下的 「漢語」 子選單當中可以加入剛安裝的新酷音輸入法。

Imgur

按「確定」,在設定畫面中可以看到的新酷音輸入法。

Imgur

最後重要的步驟,就是將切換方式改成個人較習慣的Ctrl+Space

Imgur

如此一來,就可以在 Elementary OS 上輸入中文啦!這篇文章就是從截圖到文章編輯 / 上傳,全程在已照上述方式安裝好 Elmentary OS 基礎中文環境的筆電上完成的。

fcitx 版

我也試了安裝 fcitx 版本的新酷音

$ sudo apt install fcitx fcitx-chewing

安裝好後使用im-config命令將預設輸入法框架切換成fcitx,重新開機後就可以看到輸入法圖示了。

參考資料

· 3 min read

I just successfully installed Elementary OS Luna (0.4.1) along side with Windows 10 on my new Dell XPS 9360 machine.

Comparing this XPS 13 9360 (8th Gen i7) with my previous model 9343 (5th Gen i7), the CPU is way faster(feels like 3 times faster), the wireless works well and the signal is stronger (which I was encountered some issues in XPS 13 9343), the keyboard feedback is pleasent, and the battery life did last longer in 9360.

Several settings are trickier than early days when I try to install *nux on Notebook. Here's the self reference guide and hope it could help others.

Settings on Windows

Login to Microsoft account

It's necessary to bind your account with the device, so you can get recovery key later.

Flash Elementary OS to the USB disk

I download the OS image from Elementary OS web site and use Unetbootin to flash the image into the disk.

Shrink the disk space

I have the 256GB SSD, I use Partition Wizard to slice ~100GB for Elementary OS.

I have 8GB RAM so I reference the recommend swap size and left ~8GB for swap.

Disable fast startup

I also need to follow the instructions to disable the fast startup on Windows.

Switch SATA operation mode from RAID to AHCI

Here's the most tricky part. It takes me a while to figure out how to switch the SATA storage from RAID to AHCI. The trick is doing this procedure with Windows safe mode.

Settings on BIOS

Press F12 during booting.

  • Don't need to turn off the security boot.
  • Follow above link's instructions to config SATA storage.
  • Save the configureation before exit.

Reboot to windows and make sure windows runs nicely in safemode. Then follow above link's instructions to jump out of safemode.

Now I'm prepared and able to install Elementary OS.

Install Elementary OS through USB disk

Install Elementary OS as usual Ubuntu distribution.

First connect to wifi and allow install 3rd party softwares.

Add the left space as /(root) with Ext4 format. And set the left ~8GB as SWAP format.

Then continue the auto install process.

Now I have a clean Elementary OS.

Recover Windows with BitLocker recovery keys

After restart I can see an option menu with Elementary OS and Windows Boot Manager.

I can boot to Elementary OS without major issue.

Well, I met the problem that AppCenter can't start correctly, so I do sudo apt update && sudo apt upgrade then sudo apt purge appcenter && sudo apt purge packagekit then restart and run sudo apt install appcenter && sudo apt install packagekit to make it work normally.

The input method and bluetooth pairing seems not work out of box, but I can live with that.

Then when I boot to Windows, I need to enter the BitLocker recovery keys. That could be solved by following the FAQ from microsoft. I need to login to Microsoft Account and find the recovery key.

Enter the recovery key then everything works.

· 10 min read

上一篇中我們已寫好並部署完成了簡單的加密代幣🔒💵合約。在閱讀完本文後,你將學會建立一個可以放到乙太幣錢包👛 的加密代幣🔒💵。

開發前的準備

延續上一篇的內容,在開發的過程中,我們將繼續使用testrpc1工具在電腦上模擬智能合約所需的乙太坊區塊鏈測試環境。

首先確保已啟動 testrpc。若尚未啟動,可以使用以下命令啟動:

$ testrpc
...

這邊有個值得一學的小技巧:在啟動 testrpc 時加上--seed參數,例如

testrpc --seed apple banana cherry

這樣之後重新啟動 testrpc 時可以產生一樣的帳號 (accounts) 和私鑰 (private key)。

ERC20 標準

建立的代幣若要能透過乙太幣錢包👛 來收送,必須相容於以太坊的 ERC20 標準2。ERC20 標準定義了支援錢包所必須的合約介面。

本篇將使用OpenZeppelin2函式庫來簡化建立加密代幣🔒💵的過程。OpenZeppelin是一套協助撰寫安全的加密合約的函式庫,裡面也提供了相容 ERC20 標準的智能合約。可以透過 npm 工具安裝到專案目錄node_modules/zeppelin-solodity/中:

$ npm install zeppelin-solidity

準備完成,我們可以開始建立新的加密代幣智能合約了。

建立一個標準代幣合約

contracts/目錄下建立一個HelloToken.sol檔案。也可以使用以下命令來產生檔案:

$ truffle create contract HelloToken

HelloToken.sol檔案內容如下:

pragma solidity ^0.4.11;
import "zeppelin-solidity/contracts/token/StandardToken.sol";

contract HelloToken is StandardToken {
string public name = "HelloCoin";
string public symbol = "H@";
uint8 public decimals = 2;
uint256 public INITIAL_SUPPLY = 88888;

function HelloToken() public {
totalSupply = INITIAL_SUPPLY;
balances[msg.sender] = INITIAL_SUPPLY;
}
}

講解

pragma solidity ^0.4.11;

第一行指名目前使用的 solidity 版本,不同版本的 solidity 可能會編譯出不同的 bytecode。

import "zeppelin-solidity/contracts/token/StandardToken.sol";

接著我們使用import語句,來讀入zeppelin-solidity提供的StandardToken合約。

contract HelloToken is StandardToken {
}

建立HelloToken合約時,使用is語句繼承了StandardToken合約。因此HelloToken合約繼承了StandardToken合約所包含的資料與呼叫方法。

當我們繼承了StandardToken合約,也就支援了以下 ERC-20 標準中2規定的函式

函式描述
totalSupply()代幣發行的總量
balanceOf(A)查詢 A 帳戶下的代幣數目
transfer(A,x)傳送 x 個代幣到 A 帳戶
transferFrom(A,x)從 A 帳戶提取 x 個代幣
approve(A,x)同意 A 帳戶從我的帳戶中提取代幣
allowance(A,B)查詢 B 帳戶可以從 A 帳戶提取多少代幣

和前一篇一樣,後面驗證時會用到balanceOftransfer兩個函式。因為StandardToken合約中已經幫我們實做了這些函式,因此我們不需要自己從頭再寫一次。

string public name = "HelloCoin";
string public symbol = "H@";
uint8 public decimals = 2;
uint256 public INITIAL_SUPPLY = 100000;

這邊設定參數的目的是指定這個代幣的一些特性。以美元為例,美元的名稱 (name) 是dollar,美元的代號為$,拿一美元去找零最小可以拿到零錢是一美分 (cent),也就是 0.01 元。因為一美元最小可分割到小數點後 2 位 (0.01),因此最小交易單位 (decimals) 為 2。

這邊將這個加密代幣取名 (name) 為HelloCoin(哈囉幣),代幣的代號 (symbol) 為H@,最小分割單位是 2 (最小可以找 0.01 個哈囉幣)。

以下為美金,比特幣,以太幣,哈囉幣的對照表供參考:

NameSymboldecimals
Dollar$2
BitcoinBTC8
EthereumETH18
HelloCoinH@2

最後也定義了初始代幣數目INITIAL_SUPPLY100000。當我們把全域變數設為public(公開),編譯時就會自動新增一個讀取公開變數的 ABI 接口,我們在truffle console中也可以讀取這些變數。

function HelloToken() public {
totalSupply = INITIAL_SUPPLY;
balances[msg.sender] = INITIAL_SUPPLY;
}

和合約同名的HelloToken方法,就是HelloToken合約的建構函式 (constructor)。 在建構式裡指定了totalSupply數目,並將所有的初始代幣INITIAL_SUPPLY都指定給msg.sender帳號,也就是用來部署這個合約的帳號。‵totalSupply定義於ERC20Basic.sol中,balances定義於BasicToken.sol中。

import '../math/SafeMath.sol';
...
using SafeMath for uint256;
...
balances[msg.sender] = balances[msg.sender].sub(_value);

進一步追去看BasicToken.sol合約的內容,可以發現BasicToken.sol合約中匯入了SafeMath.sol合約7SafeMath對各種數值運算加入了必要的驗證,讓合約中的數字計算更安全。

如此一來,我們已寫好一個可透過以太幣錢包交易的新加密代幣🔒💵合約。如果將這個合約部署到乙太坊公開區塊鍊上,這個代幣合約就會一直存在,世界上從此也就多了一種新的加密代幣。只要你能找到人想擁有這種代幣,這種代幣就有交易的價值。

編譯與部署

migrations/目錄下建立一個4_deploy_hellotoken.js檔案,內容如下:

var HelloToken = artifacts.require("HelloToken");

module.exports = function(deployer) {
deployer.deploy(HelloToken);
};

現在執行 compile 與 migrate 命令

$ truffle compile
...
$ truffle migrate --reset
Using network 'development'.

Running migration: 4_deploy_hellotoken.js
Deploying HelloToken...
... 0x2c4659528c68b4e43d1edff6c989fba05e8e7e56cc4085d408426d339b4e9ba4
HelloToken: 0x352fa9aa18106f269d944935503afe57a00a9a0d
Saving successful migration to network...
... 0x1434c1b61e9719f410fc6090ce37c0ec141a1738aba278dd320738e4a5d229fa
Saving artifacts...

如此一來我們已將 HelloCoin 代幣合約部署到 testrpc 上。

驗證

我們一樣可以透過truffle console來驗證HelloToken是否部署成功。

$ truffle console
> let contract
> HelloToken.deployed().then(instance => contract = instance)
> contract.address
'0x352fa9aa18106f269d944935503afe57a00a9a0d'
> contract.balanceOf(web3.eth.coinbase)
BigNumber { s: 1, e: 5, c: [ 100000 ] }
> contract.balanceOf(web3.eth.accounts[1])
BigNumber { s: 1, e: 0, c: [ 0 ] }
> contract.transfer(web3.eth.accounts[1], 123)
...
> contract.balanceOf(web3.eth.accounts[0])
BigNumber { s: 1, e: 4, c: [ 99877 ] }
> contract.balanceOf(web3.eth.accounts[1])
BigNumber { s: 1, e: 2, c: [ 123 ] }
>

講解

> let contract
> SimpleToken.deployed().then(instance => contract = instance)

這邊使用HelloToken.deployed().then語句來取得 HelloToken 合約的 Instance (實例),並存到contract變數中,以方便後續的呼叫。

> contract.balanceOf(web3.eth.coinbase)
BigNumber { s: 1, e: 5, c: [ 100000 ] }
> contract.balanceOf(web3.eth.accounts[1])
BigNumber { s: 1, e: 0, c: [ 0 ] }

web3.eth.coinbase 代表操作者預設的帳號,即 testrpc 所提供的 10 個帳號中的第一個帳號,也可以透過web3.eth.accounts[0]取得。 這兩句的目的是在進行轉帳操作前,先查詢前兩個帳號所擁有的代幣餘額。透過呼叫balanceOf函式,可以看到第一個帳號 (部署合約的帳號) 裡存著所有的代幣。

> contract.transfer(web3.eth.accounts[1], 123)
...

接著使用transfer函式來傳送123個代幣到第二個帳號web3.eth.accounts[1]。如果轉帳成功,傳送者預設帳號中會減少123個代幣,接收者帳號中會增加123個代幣。

> contract.balanceOf(web3.eth.accounts[0])
BigNumber { s: 1, e: 4, c: [ 99877 ] }
> contract.balanceOf(web3.eth.accounts[1])
BigNumber { s: 1, e: 2, c: [ 123 ] }
>

我們再次透過呼叫balanceOf函式,查詢傳送者帳號和接收者帳號各自剩下的 HelloToken 數目。發現轉帳真的成功了。

結語

我們用到 OpenZeppelin提供的函式庫來簡化撰寫加密代幣合約的工作。要實際投入使用前,還是建議將呼叫到的程式碼都再審查幾遍。可以從 OpenZeppelin 自己提供的 Audit 開始看4,可以學到一些觀念。

參考資料

· 11 min read

Update: 12/28/2017 更新教程,使用 require 取代 throw。

上一篇中我們已寫好並部署完成了第一個智能合約,也驗證了合約確實可以運作。在閱讀完本篇後,你將學會建立一個簡易的加密代幣🔒💵。本篇目的並非為了寫出一個安全可用的加密代幣,而是以介紹代幣合約的相關概念為主, 是以對合約做了適當地簡化,好更易於理解。

開發前的準備

延續上一篇的內容,在開發的過程中,我們將繼續使用testrpc1工具在電腦上模擬智能合約所需的乙太坊區塊鏈測試環境。

首先確保已啟動 testrpc,若尚未啟動,可以使用以下命令啟動

$ testrpc
...

這樣我們就可以開始建立加密代幣智能合約專案了。

代幣合約的基礎概念

代幣合約扮演像是銀行🏦 的角色。使用者在代幣合約中,用自己的乙太幣帳戶地址當作銀行帳戶,可以透過代幣合約執行轉帳 (transfer,將代幣由一個帳戶傳送到另一個帳戶),查詢餘額 (balanceOf,查詢指定帳戶中擁有的代幣) 等原本由銀行負責的工作。因為合約部署在公開區塊鏈上,所有的交易都是公開透明,可供檢驗的。

建立一個代幣合約

contracts/目錄下建立一個SimpleToken.sol檔案。也可以使用以下命令來產生檔案:

$ truffle create contract SimpleToken

SimpleToken.sol檔案內容如下:

pragma solidity ^0.4.11;

contract SimpleToken {
uint256 INITIAL_SUPPLY = 10000;
mapping(address => uint256) balances;

function SimpleToken() public {
balances[msg.sender] = INITIAL_SUPPLY;
}

// transfer token for a specified address
function transfer(address _to, uint256 _amount) public {
require(balances[msg.sender] > _amount);
balances[msg.sender] -= _amount;
balances[_to] += _amount;
}

// Gets the balance of the specified address
function balanceOf(address _owner) public constant returns (uint256) {
return balances[_owner];
}
}

講解

pragma solidity ^0.4.11;

第一行指名目前使用的 solidity 版本,不同版本的 solidity 可能會編譯出不同的 bytecode。

uint256 INITIAL_SUPPLY = 10000;
mapping(address => unit256) balances;

我們定義了初始代幣數目INITIAL_SUPPLY。這邊隨意選擇了一個數字10000

我們用mapping來定義一個可以儲存鍵值對 (key-value pair) 的資料結構 (類似 Javascript 中的{"0xaabbccddeeff": 888}),同時也需要分別指定address作為鍵的型別,指定uint256作為值的型別。和 Javascript 不同,型別定義好後不能隨時更改要儲存的型別。

contract SimpleToken {
function SimpleToken() public {
balances[msg.sender] = INITIAL_SUPPLY;
}
}

和合約同名的SimpleToken函式,就是SimpleToken這個合約的建構函式 (constructor)。函式中我們拿msg.sender當作 key,INITIAL_SUPPLY當作值,將所有的初始代幣INITIAL_SUPPLY都指定給msg.sender帳號。 msg是一個全域 (Global) 物件2msg.sender表示用作呼叫當前函式的帳號。由於建構函式只有在合約部署時會被執行,因此這邊用到的msg.sender,也就代表著用來部署這個合約的帳號。

function transfer(address _to, uint256 _amount) public {
require(balances[msg.sender] > _amount);
balances[msg.sender] -= _amount;
balances[_to] += _amount;
}

transfer函式定義了如何轉帳,只要指定要傳送的帳號與數目,就會從呼叫者手上把對應數目的代幣移轉到指定的帳號上。

graph LR 傳送者 -- 轉帳 --> 代幣合約 代幣合約 -.-> 修改傳送者和接收者餘額

require(balances[msg.sender] > _amount);語句判斷帳戶中是否還有足夠轉出的餘額,若存款小於想轉出的數目,就丟出錯誤。

這個函式這麼寫當然還是過度簡化了,若要能實際使用,需要檢查更多可能的狀況。但就先這樣吧。

function balanceOf(address _owner) public constant returns (uint256) {
return balances[_owner];
}

balanceOf函式的作用,是讓使用者可以查詢任一帳號的餘額。透過傳入_owner帳號,可以查詢_owner帳號儲存在balances對照表中的值。

graph LR 傳送者 --> 代幣合約 代幣合約 -. 查詢結果 .-> 傳送者

如此一來,我們就寫好一個新加密代幣🔒💵合約囉!接下來將要編譯合約並部署到區塊鏈上。

編譯與部署

migrations/目錄下建立一個3_deploy_token.js檔案,內容如下:

var SimpleToken = artifacts.require("SimpleToken");

module.exports = function(deployer) {
deployer.deploy(SimpleToken);
};

現在可執行 compile 與 migrate 命令

$ truffle compile
...
$ truffle migrate
Using network 'development'.

Running migration: 3_deploy_token.js
Deploying HelloToken...
... 0x2c4659528c68b4e43d1edff6c989fba05e8e7e56cc4085d408426d339b4e9ba4
SimpleToken: 0x352fa9aa18106f269d944935503afe57a00a9a0d
Saving successful migration to network...
... 0x1434c1b61e9719f410fc6090ce37c0ec141a1738aba278dd320738e4a5d229fa
Saving artifacts...

如此一來我們已將SimpleToken代幣合約部署到 testrpc 上。

驗證

合約部署完成後,我們可以使用truffle console命令開啟 console,輸入以下命令來驗證合約是否能照我們設計的方式運作。

$ truffle console
> let contract
> SimpleToken.deployed().then(instance => contract = instance)
> contract.balanceOf(web3.eth.coinbase)
BigNumber { s: 1, e: 4, c: [ 10000 ] }
> contract.balanceOf(web3.eth.accounts[1])
BigNumber { s: 1, e: 0, c: [ 0 ] }
> contract.transfer(web3.eth.accounts[1], 123)
...
> contract.balanceOf(web3.eth.coinbase)
BigNumber { s: 1, e: 3, c: [ 9877 ] }
> contract.balanceOf.call(web3.eth.accounts[1])
BigNumber { s: 1, e: 2, c: [ 123 ] }
>

講解

> let contract
> SimpleToken.deployed().then(instance => contract = instance)

這邊使用SimpleToken.deployed().then語句來取得 SimpleToken 合約的 Instance (實例),並存到contract變數中,以方便後續的呼叫。

> contract.balanceOf(web3.eth.coinbase)
BigNumber { s: 1, e: 4, c: [ 10000 ] }
> contract.balanceOf(web3.eth.accounts[1])
BigNumber { s: 1, e: 0, c: [ 0 ] }

還記得啟動 testrpc 後預設會產生 10 個帳號 (Accounts) 嗎?。web3.eth.coinbase 代表操作者預設的帳號,即 10 個帳號中的第一個帳號web3.eth.accounts[0],所以這邊呼叫web3.eth.coinbaseweb3.eth.accounts[0]結果是一樣的。

> contract.balanceOf(web3.eth.accounts[0])
BigNumber { s: 1, e: 4, c: [ 10000 ] }

這兩句的目的是在進行轉帳操作前,先查詢前兩個帳號所擁有的代幣餘額。透過呼叫balanceOf函式,可以看到第一個帳號 (部署合約的帳號) 裡存著所有的代幣。

> contract.transfer(web3.eth.accounts[1], 123)
...

接著使用transfer函式來傳送123個代幣到第二個帳號web3.eth.accounts[1]。如果轉帳成功,傳送者預設帳號中會減少 123 個代幣,接收者帳號中會增加 123 個代幣。

> contract.balanceOf(web3.eth.coinbase)
BigNumber { s: 1, e: 3, c: [ 9877 ] }
> contract.balanceOf.call(web3.eth.accounts[1])
BigNumber { s: 1, e: 2, c: [ 123 ] }
>

我們再次透過呼叫balanceOf函式,查詢傳送者帳號和接收者帳號各自剩下的 SimpleToken 數目。發現轉帳真的成功了。

你知道剛剛的程式碼裡有一堆安全漏洞💣 嗎?

寫智能合約看起來並不困難吧?但因為智能合約的運作是透明公開的,而且其中牽涉了代幣或金錢的流動,這提供了駭客很強的挑戰動機。

因此如果要妥善處理智能合約,會遇到的諸多安全問題。即使單純如本篇中的SimpleToken,也至少會遇到幾個問題:例如transfer函式中沒有禁止傳入負數金額,因此傳送者反過來可以從接收者那邊取得代幣。同時也沒有檢查接收者帳號是否合法,因此傳送者可能會傳送失敗或因為送到黑洞中,白白損失了代幣。

有著一堆安全漏洞的合約,輕則執行失敗白花交易費用,嚴重則影響到合約中的代幣或以太幣。已有多起因為合約的漏洞,造成儲存在合約中的代幣或以太幣被駭客轉走,使得 ICO 失敗的案例。

有興趣的人可以進一步查看參考資料45了解智能合約當前的一些最佳實現。

結語

看完這篇除了學到本篇講解的SimpleToken外,應該也可以大致看得懂 truffle 預設的MetaCoin.sol合約了。不同的細節可以查看 solidity 相關語法2。 下一篇會接著介紹如何使用經過驗證的函式庫,來建立一份可以放到乙太幣錢包👛 的加密代幣🔒💵合約。

參考資料

· 15 min read

Update: 11/30/2017 更新教程,使用Truffle 4.0.1

上一篇中介紹了智能合約📒 是什麼,也概略描述了從編譯到部署智能合約的流程,接下來將介紹如何使用 solidity 語言來寫智能合約。

使用 solidity 語言撰寫智能合約

Ethereum 上的智能合約需要使用solidity1語言來撰寫。雖然還有其他能用來撰寫智能合約的語言如Serpent(類 Python)、lll(類 Fortran),但目前看到所有公開的智能合約都是使用 solidity 撰寫。

宣傳上說,solidity 是一種類似 Javascript 的語言,而且圍繞著 solidity 的各種開發工具鏈,都是使用屬於 Javascript 生態系的 npm 來提供的。但我覺得 solidity 還是比較像 Java 或 C#。 因為和 Javascript 不同,solidity 與 Java 或 C# 同屬於強型別 (Strong Type,在定義變數時需要指定型別) 語言、在定義函式 (function) 時同樣需指定回傳的型別 (type)、同樣也需要先編譯才能執行。這些特性都是 Javascript 所不具備的。

開發前的準備

本文將使用當前最活躍的智能合約開發框架truffle3為基礎來開發。之前提到過的 ENS (Ethereum Name Service)5也是採用 truffle 框架。其他選擇還有embark4等。

就像一般網站或 App 開發一樣,在提供公開服務之前,開發者會在自己用於寫程式的電腦 (又稱作本機)💻 或透過測試網路🕸️ 來測試程式執行的效果,測試完成後,才會部署到公開的網路上提供服務。 開發區塊鏈智能合約 (程式) 的過程也是如此。特別是公開鏈上所有寫入或讀取計算結果的操作都需要真金白銀 (虛擬代幣)💸 ,而且根據網路狀況,每個公開鏈上的操作都需要要一小段反應時間 (15 秒~數分鐘),這些等待頗浪費寶貴的開發時間⏳ 。 因此在開發的過程中,我們將使用testrpc6工具在電腦上模擬智能合約所需的乙太坊區塊鏈測試環境。

testrpc 中也包含了 Javascript 版本的 Ethereum 虛擬機 (Ethereum Virtual Machine)7,因此可以完整地執行智能合約😇 。

此外,開發前還需準備一個合手的編輯器。我目前是使用Visual Studio Code搭配solidity插件來開發。solidity 插件除了支援語法高亮之外,也會透過 Solium11檢查並提示基本的語法錯誤,相當方便。其他編輯器應該也有類似的插件可選擇。

安裝所需工具

首先開發機上必須裝好 Node.js,再使用以下命令安裝所需的工具:

$ npm install -g ethereumjs-testrpc truffle

啟動 Testrpc

安裝好後隨時可以使用testrpc命令來啟動乙太坊測試環境。

$ testrpc
Available Accounts
==================
(0) 0xa4d7ce9137e6f8de4fb1311595b33230be15be50
(1) 0x26c231bdd7c8a7304983b04694c3437b30331019
(2) 0xe238ccca936dcdbd48f0cf3a1e6f147d04b55527
(3) 0x769ed341bf83cc86e5037cb78388012d6e2d9cc9
(4) 0x72a084c80195de79e5cd8dca59488e67982f65d7
(5) 0xcfda0765b0a82721d2f59581f53846a12e392999
(6) 0x4b0349aea768b4e1ed4cec683f8f7dd112729fea
(7) 0x643c305f0b3844984d7f1f7b9f3ab93a73dfdfcf
(8) 0x2ee0a7974326604442dca127d02fac4957ab3e8a
(9) 0xe00e57db1772f6e81bcccc982e565a10ae26ab92

Private Keys
==================
(0) 7de56fb677edc8d0c7a1f3a6d5bcb8f73ce257d44996e9b5fc8ad414af38a22a
(1) 4401de20cf287d15d1c062005d866a35cd82e2a73f8cb43ec0cb90b117d1ec38
(2) 8f51f9100a81218343d44a047ae3b0be5d80d262a13fbef24dc569b3e335e820
(3) 241a0ff98dfb6f290dbee909c9a7a4eea2de3a2174e7cddf834868ea03f80fa9
(4) ce1108cc6763bc74658068a55b080c6ccbfb1bd26e609588b81c07d13affc70d
(5) f9614c1fd34224787e6c95bbe881fb28fd0fdc00808ef85d0430505f4a348690
(6) 4c1baad08f720f5c5754bb185e66490b45e3480aa3ec419e4b76f7a81118b296
(7) af9af2c6b519d49605cc58b719240299e5e8b9a89a7e94a85625734fc30c46bd
(8) 55ab79ae6de4fad5b98bc1dfd795b945ba8e7d92dcc88073f9e3fdfef471f69f
(9) e9299fb391c8830370991659780933e6b62269e32a8cbc55a29aa5f73df995a2

HD Wallet
==================
Mnemonic: addict cherry medal cupboard bless reduce oven beauty egg gift pledge exact
Base HD Path: m/44'/60'/0'/0/{account_index}

可以看到 testrpc 啟動後自動建立了 10 個帳號 (Accounts),與每個帳號對應的私鑰 (Private Key)🔑 。每個帳號中都有 100 個測試用的以太幣 (Ether)💵 。要注意 testrpc 僅運行在記憶體中,因此每次重開時都會回到全新的狀態。

一切準備就緒,我們可以開始建立第一份智能合約專案了。

建立專案

開啟另一個命令列視窗,輸入以下命令以建立專案:

$ mkdir hello
$ cd hello
$ truffle init

如此一來,我們已建立好第一份智能合約專案了。

demo/資料夾下,可以看到contracts/資料夾,裡面放的是這個專案所包含的所有 solidity 程式。我們在contracts/資料夾中額外建立一個HelloWorld.sol檔案。(或者也可以用truffle create contract HelloWorld命令來建立)

HelloWorld.sol檔案內容如下:

pragma solidity ^0.4.11;

contract HelloWorld {
function sayHello() public returns (string) {
return ("Hello World");
}
}

講解

pragma solidity ^0.4.11;

第一行指名目前使用的 solidity 版本,不同版本的 solidity 可能會編譯出不同的 bytecode。

想要知道當前的 solidity 版本,也可以用 truffle version 命令來查看當前使用的 truffle 與 solidity 版本:

$ truffle version
Truffle v4.0.1 (core: 4.0.1)
Solidity v0.4.18 (solc-js)
contract HelloWorld {
...
}

contract關鍵字類似於其他語言中較常見的class。因為 solidity 是專為智能合約 (Contact) 設計的語言,宣告contract後即內建了開發智能合約所需的功能。也可以把這句理解為class HelloWorld extends Contract

雖然一個.sol 檔案中可以定義多個 Contract,但建議一個.sol 檔案中只定義一個 Contract 以便於後續的維護。

function sayHello() public returns (string) {
return ("Hello World");
}

函式的結構與其他程式類似,但如果有傳入的參數或回傳值,需要指定參數或回傳值的型別 (type)。所有支援的型別可以查看參考資料10

solidity 官方推薦的縮排風格為 4 個空格13

編譯

現在執行truffle compile命令,我們可以將HelloWorld.sol原始碼編譯成 Ethereum bytecode。

$ truffle compile

編譯成功的話,在build/contracts/目錄下會多出HelloWorld.json這個檔案。(在 Windows 平台上執行 truffle compile 若遇到問題,可以查看參考資料9來解決。)

部署

為了將寫好的 solidity 程式碼部署到區塊鍊上,我們需要做一些相應的設定。

遷移

truffle 框架中提供了方便部署合約的腳本。我們可以在migrations/目錄下維護這些腳本。這些腳本除了能部署合約,也可以用來遷移合約中的資料。建立migrations/2_deploy_contracts.js檔案 (這些腳本使用 Javascript 撰寫),將內容修改如下

var HelloWorld = artifacts.require("HelloWorld");

module.exports = function(deployer) {
deployer.deploy(HelloWorld);
};

這些 migration 檔案會依照檔案的編號來執行。例如2_就會在1_之後執行。檔案後面的文字只為協助開發者理解之用。

在檔案中可使用artifacts.require語句來取得準備部署的合約。使用deployer.deploy語句將合約部署到區塊鏈上。這邊HelloWorldcontract的名稱而不是檔名。因此可以用此語法讀入任一.sol檔案中的任一合約。

區塊網路設定

為了與testrpc連線,需要打開truffle.js並加入以下設定:

module.exports = {
// See <http://truffleframework.com/docs/advanced/configuration>
// to customize your Truffle configuration!
networks: {
development: {
host: "localhost",
port: 8545,
network_id: "*" // Match any network id
}
}
};

truffle 使用 Javascript 的 Object 格式來定義設定。這邊定義了development網路為localhost:8545,即 testrpc 所提供的網路位址。

部署

現在執行truffle migrate命令

$ truffle migrate
Using network 'development'.

Running migration: 1_initial_migration.js
...
Saving successful migration to network...
Running migration: 2_deploy_contracts.js
...
Saving successful migration to network...
...
Saving artifacts...

如此一來合約已經部署到 testrpc 中。切換到 testrpc 視窗,可以看到 testrpc 有反應了。

與合約互動

truffle 提供命令行工具,執行truffle console命令後,可用 Javascript 來和剛剛部署的合約互動。

$ truffle console
> let contract
> HelloWorld.deployed().then(instance => contract = instance)
> contract.sayHello.call()
'Hello World'
>

講解

> HelloWorld.deployed().then(instance => contract = instance)

truffle console中預載了truffle-contract12函式庫,以方便操作部署到區塊鏈上的合約。

這邊使用HelloWorld.deployed().then語句來取得 HelloWorld 合約的 Instance (實例),並存到contract變數中,以方便後續的呼叫。

上面用的是 Javascript ES6 + 的語法,這句也可以寫成

HelloWorld.deployed().then(function(instance) {
hello = instance;
});
> contract.sayHello.call()
'Hello World'

這邊直接呼叫contract.sayHello()也會得到一樣的結果。truffle-contract提供使用call()來讀取唯讀 (read only) 的資料,這樣就不需提供 gas。因此如果遇到的操作需要向區塊鏈寫入資料,我們就不能用call語句了。

如此一來,我們已寫好並部署完成了第一個智能合約,也驗證了合約確實可以運作。

加入新方法

我們在HelloWorld.sol中再加入一個echo方法,echo方法接受輸入一個參數,並回傳傳送的參數。

function echo(string name) constant returns (string) {
return name;
}

新的echo方法中傳入了一個name參數。我們也為echo方法加入一個constant宣告,表示呼叫這個方法並不會改變區塊鏈的狀態。如此一來,透過truffle-contract來呼叫此方法時,會自動選用call來呼叫,也不需要額外提供 gas。

由於更新了合約內容,我們需要先重新新編譯一次,將編譯結果部署到 testrpc 上,再透過truffle console執行看看結果。

$ truffle compile
...
$ truffle migrate --reset
...
$ truffle console
> let contract
> HelloWorld.deployed().then(instance => contract = instance)
> contract.echo("yo man")
'yo man'
>

echo方法確實將我們輸入的內容回傳了。同時因為宣告了constant,我們不需要直接呼叫call()方法,truffle會自動選用 call 來呼叫。

另一點需要注意的,是這次如果還是用truffle migrate命令,我們會得到如下訊息:

$ truffle migrate
Using network 'development'.

Network up to date.

Truffle 會告訴你現在網路上的合約都已是最新的,但事實上剛剛程式中新增的方法並沒有更新到區塊鏈上。要更新區塊鏈上已部署的程式,需要改寫migrations中的腳本,但現在還不到介紹 migration 的時候。還好我們開發用的區塊鏈是怎麼修改都沒關係的 testrpc,可以使用truffle migrate --reset命令直接重新在 testrpc 上部署一次🎉 。

使用 truffle develop 命令

truffle 4.0.0 版本之後加入了truffle develop命令。這個命令讓我們不需要另外安裝 testrpc 等環境,就能直接上手開發。

例如

truffle develop
Truffle Develop started at http://localhost:9545/

Accounts:
(0) 0x627306090abab3a6e1400e9345bc60c78a8bef57
(1) 0xf17f52151ebef6c7334fad080c5704d77216b732
(2) 0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef
(3) 0x821aea9a577a9b44299b9c15c88cf3087f3b5544
(4) 0x0d1d4e623d10f9fba5db95830f7d3839406c6af2
(5) 0x2932b7a2355d6fecc4b5c0b6bd44cc31df247a2e
(6) 0x2191ef87e392377ec08e7c08eb105ef5448eced5
(7) 0x0f4f2ac550a1b4e2280d04c21cea7ebd822934b5
(8) 0x6330a553fc93768f612722bb8c2ec78ac90b3bbc
(9) 0x5aeda56215b167893e80b4fe645ba6d5bab767de

Mnemonic: candy maple cake sugar pudding cream honey rich smooth crumble sweet treat

truffle(develop)> compile
truffle(develop)> migrate
Using network 'develop'.
Running migration: 1_initial_migration.js
Deploying Migrations...
...
Saving artifacts...
Running migration: 2_deploy_contracts.js
Deploying HelloWorld...
...
Saving artifacts...
truffle(develop)> let contract
truffle(develop)> HelloWorld.deployed().then(instance =>contract = instance)
...
truffle(develop)> contract.sayHello.call()
'Hello World'

可以看到,在命令行中輸入truffle develop命令,可以直接在裡面執行compilemigrate指令,還可以直接使用console命令所提供的與區塊鍊互動等功能。

結語

本篇設計的範例8相當簡單,但已達到完整地帶大家快速⚡ 走一遍智能合約開發流程的目的。要透過智能合約實現各種功能,可以參考Solidity by exampleTruffle getting started 網站學習更多的內容。也歡迎讀者留言,分享學習資源或提供建議。

下一篇會接著介紹如何建立一份簡單的加密代幣🔒💵合約。

參考資料