Skip to main content

· 8 min read

因為智能合約一旦部署就難以修改,因此合約的安全性極其重要,要避免合約中出現一些基礎錯誤,除了透過第三方驗證外,完整地單元測試 (unit test) 也是必需的。

目前最成熟的智能合約單元測試方式,還是透過Truffle開發框架來達成。有趣的是 Truffle 主要使用 Javascript 來撰寫智能合約的單元測試(也可以用 solidity 來寫)。

加入測試

接續上一篇建立的HelloToken合約,在test/目錄下加入test_hello_token.js測試檔案(如果覺得這份程式碼不易理解,可跳過這節,後面會介紹更簡潔的測試方法,到時再回來對照著看)。

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

const INITIAL_SUPPLY = 100000;
let _totalSupply;

contract('HelloToken', function(accounts) {
it('should met initial supply', function() {
var contract;
HelloToken.deployed().then((instance) => {
contract = instance;
return contract.totalSupply.call();
}).then((totalSupply) => {
_totalSupply = totalSupply;
assert.equal(totalSupply.toNumber(), INITIAL_SUPPLY);
return contract.balanceOf(accounts[0]);
}).then((senderBalance) => {
assert.equal(_totalSupply.toNumber(), senderBalance.toNumber());
});
});
});

運行truffle test可看到測試通過的結果。

Contract: HelloToken
✓ should met initial supply

1 passing (11ms)

講解

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

artifacts.require的用法和在migrations/中的用法相同,可以直接引入對應的智能合約。

contract('HelloToken', function(accounts) {
it('should met initial supply', function() {
});
});

Truffle 是使用 Javascript 開發中常見的Mocha測試框架和Chai斷言庫來做單元測試。差別只是把 Mocha test 中的 describe換成contract。根據官方文件1contact執行前會自動重新部署到 testrpc (或測試網路) 上,所以智能合約會是剛部署好乾淨的狀態。

此外,contract也會帶入accounts變數,裡面儲存了 testrpc 或其他你運行的測試網路所提供的帳號,開發者可以直接使用這些帳號來測試合約。

第一個測試是來測部署合約後預設的代幣數目是否正確。

var contract;
HelloToken.deployed().then((instance) => {
contract = instance;
return contract.totalSupply.call();
}).then((totalSupply) => {
...
});

這邊內容和在truffle console中輸入的測試內容雷同,使用Promise來確定每個非同步的操作都在上一個操作完成後才繼續執行。

上一個操作可以透過 return 語句回傳下個操作需要的參數。例如這邊then裡面傳入的totalSupply參數,是來自上一行return contract.totalSupply.call()的結果。

assert.equal(totalSupply.toNumber(), INITIAL_SUPPLY);
...
assert.equal(_totalSupply.toNumber(), senderBalance.toNumber());

這邊我們透過assert.equal語句驗證了HelloToken合約中的初始代幣總額與INITIAL_SUPPLY參數的值相符,且與合約部署者 (accounts[0]) 帳戶中擁有的總額相符。

使用 async/await 簡化測試

要理解這樣的 promise chain 需要一些練習。但其實上面的測試用例中,我們只想做好最後的兩個 assert 驗證。有沒有比較直覺的測試方法呢?

有的!2017 下半年,Javascript 語言支援了async/await語句[2](只要安裝 Node 7.6 版以上即可使用),可以用更直覺的方式撰寫非同步的程式碼。

智能合約測試剛好也使用大量的非同步程式碼。使用async/await語句改寫後的智能合約測試程式碼如下:

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

const INITIAL_SUPPLY = 100000;

contract('HelloToken', function(accounts) {
it('should met initial supply', async function() {
let contract = await HelloToken.deployed();
let totalSupply = await contract.totalSupply.call();
let senderBalance = await contract.balanceOf(accounts[0]);
assert.equal(totalSupply.toNumber(), INITIAL_SUPPLY);
assert.equal(totalSupply.toNumber(), senderBalance.toNumbe());
});
});

運行truffle test可看到測試通過的結果。

Contract: HelloToken
✓ should met initial supply

1 passing (11ms)

講解

it('should met initial supply', async function() {
});

要在程式碼中使用 async/await,需要在函式前加入async宣告,這樣解譯器才會解析函式中的await語法。

let contract = await HelloToken.deployed();
let totalSupply = await contract.totalSupply.call();
let senderBalance = await contract.balanceOf(accounts[0]);

透過在非同步的操作前加上await宣告,這三行程式會依照順序,等待第一行 await 語句執行完,取得contract變數後,再依序執行第二行語句。第二行語句執行完,取得totalSupply變數後,再繼續執行第三行語句以取得senderBalance變數。

後面兩個 assert 語句則與 promise 撰寫時完全一樣。這樣改寫後,程式碼的可讀性大大地提昇了!

加入轉帳測試

再透過async/await語句試著加入轉帳測試:

  it('should have right balance after transfer', async function() {
const AMOUNT = 123;
let contract = await HelloToken.deployed();
// check init balance
let account0Balance = await contract.balanceOf(accounts[0]);
let account1Balance = await contract.balanceOf(accounts[1]);
assert.equal(account0Balance.toNumber(), INITIAL_SUPPLY);
assert.equal(account1Balance.toNumber(), 0);
// check balance after transferred
await contract.transfer(accounts[1], AMOUNT);
account0Balance = await contract.balanceOf(accounts[0]);
account1Balance = await contract.balanceOf(accounts[1]);
assert.equal(account0Balance.toNumber(), INITIAL_SUPPLY - AMOUNT);
assert.equal(account1Balance.toNumber(), AMOUNT);
});

運行truffle test可看到測試通過的結果。

Contract: HelloToken
✓ should met initial supply
✓ should have right balance after transfer (92ms)

2 passing (151ms)

講解

let account0Balance = await contract.balanceOf(accounts[0]);
let account1Balance = await contract.balanceOf(accounts[1]);
assert.equal(account0Balance.toNumber(), INITIAL_SUPPLY);
assert.equal(account1Balance.toNumber(), 0);

範例的前半部測試帳號0帳號1中的代幣餘額。帳號0即部署代幣的帳號,因此擁有所有的HelloToken代幣,而帳號1中則沒有HelloToken代幣。

await contract.transfer(accounts[1], AMOUNT);

接著呼叫合約的transfer方法將一些代幣轉入帳號1。注意這些都是非同步的操作(送出傳輸命令後,要先等待區塊鍊確認),因此需要使用await語句。

account0Balance = await contract.balanceOf(accounts[0]);
account1Balance = await contract.balanceOf(accounts[1]);
assert.equal(account0Balance.toNumber(), INITIAL_SUPPLY - AMOUNT);
assert.equal(account1Balance.toNumber(), AMOUNT);

範例的後半部再次測試帳號0帳號1中的代幣餘額。結果符合轉帳後兩個帳戶的預期代幣數額。

結語

async/await語句相當適合拿來寫非同步的程式,這特性太適合用來寫智能合約的測試了。因為async/await這語法太新,所以大部分的參考資料都還在用Promise來撰寫。我建議當你看到相關的智能合約測試時,可以用 async/await 改寫看看,會有很不一樣的感受。

參考資料

[1] Writing Tests in Javascript http://truffleframework.com/docs/getting_started/javascript-tests [2] 6 Reasons Why JavaScript’s Async/Await Blows Promises Away (Tutorial)https://hackernoon.com/6-reasons-why-javascripts-async-await-blows-promises-away-tutorial-c7ec10518dd9

· 6 min read

今天已把前陣子買到的 Ledget Nano S 硬體錢包在 Windows 10/Elementary OS (/Ubuntu 16.04) 上設定好,正式開始使用。和預期一樣,雖然在 Linux 上剛開始設定時需要多做一些步驟,但是一旦設定好後,使用過程和在其他平台上並沒有區別。

硬體錢包

過去透過 NAS,外接硬碟等方式來保護自己的相片,作品等「數位資產」,以防哪天檔案遺失或外流。現在要守護的範圍更要擴及「加密代幣」,即保護自己的「數位財產」。

graph LR 作品 --> NAS 相片 --> NAS 相片 --> 線上備份 NAS --> 數位資產 線上備份 --> 數位資產 加密代幣 --> 硬體錢包 硬體錢包 --> 數位資產

MyEtherWallet 網站推薦任何擁有超過「2 周的薪資」數位財產的人,使用「硬體錢包」來保管自己的「加密代幣」,以避免可能的財產損失。

目前 Ledget Nano S 和 TREZOR 是兩款較多人使用的硬體錢包。硬體錢包的安全性從產生錢包帳號開始。硬體錢包帳號的私鑰一直保存在硬體設備中,只要保存好「recovery phase」(Mnemonic Seed),不會發生使用線上交易所時帳號或帳戶中的代幣可能被盜的風險。只有在發送代幣時需要解鎖錢包帳號。解鎖錢包帳號時,輸入 PIN 碼解鎖的過程,也是透過硬體錢包上的按鍵完成,從而避免了所使用的電腦可能已被入侵者安裝鍵盤側錄軟體而造成的財產上的風險。

設定流程

1. 設定 Ledget Nano S 硬體

在 Ledget Nano S 上透過按壓左右兩顆硬體按鈕,設定 4~8 字的 PIN Code 與 24 個單字的「recovery phase」,並用紙筆等實體方式記錄下來(千萬別用拍照的...)。完成後機器會隨機挑幾個次序測試,而你需要選擇對應的單字來確認安全性。如果以後機器壞了或遺失了,仍然可以透過這 24 個單字的「recovery phase」來取回帳號。

2. 在電腦上安裝 Chrome 或 Chromium

因為目前 Ledger Nano S 使用 Chrome App 技術來提供桌面應用程式,Chrome 也是唯一支援WebUSB API的瀏覽器...Google 近期公告 Chrome App 即將被 Progressive Web App 取代,我們拭目以待 Ledger 公司將拿出什麼方案來應對。

3. 安裝 Ledger Manager

前往https://www.ledgerwallet.com/apps/manager安裝 Ledger Manager。

4. 設定 USB 連線

這時開啟 Ledger Manager,將 Ledget Nano S 連線到電腦並解鎖,Ledger Manager 無法找到對應的設備。

這時可以參考What if Ledger Wallet is not recognized on Linux?在命令行環境下輸入以下命令:

wget -q -O - https://www.ledgerwallet.com/support/add_udev_rules.sh | sudo bash

執行後重新將 Ledget Nano S 連線到電腦並解鎖,可以看到 Ledger Manager 開始更新資料。

4. 安裝 Ethereum App

Ledger Manager 與 Ledget Nano S 連線後,除了可以更新韌體之外,也能安裝不同的「加密代幣」App 進 Ledget Nano S。

這邊選擇安裝 Ethereum App。

安裝完成後,在 Ledget Nano S 上可以看到多出一個Ethereum的圖示。

點選進入Ethereum,確認Browser Support選項為No (Ethereum> Settings > Browser Support > No),這樣稍後安裝的 Ledger Wallet Ethereum 才能辨識到 Ledget Nano S。

5. 安裝 Ledger Wallet Ethereum

參考How to install and use Ethereum and Ethereum Classic? 前往https://www.ledgerwallet.com/apps/ethereum下載 Ledger Wallet Ethereum App。

安裝好後重新將 Ledget Nano S 連線到電腦並解鎖,可以看到相關操作界面。

使用 MyEtherWallet 取代 Ledger Wallet Ethereum

若不喜歡使用 App,還可透過 MyEtherWallet 來存取。

參考Moving from MyEtherWallet to LedgerHow to use MyEtherWallet with Ledger 這兩篇設定,將Setting中的Contract DataBrowser support選項都設成Yes

Ethereum > Settings > Contract Data > Yes
Ethereum > Settings > Browser Support > Yes

透過 Chrome 瀏覽器,在 MyEtherWallet 中看到How would you like to access your wallet選項時,選擇Ledger Wallet並在硬體上解鎖即可。

參考資料

· 2 min read

隨著 Firefox 56,57 版的推出,我參與製作的 Firefox Onboarding 功能也正式和大眾見面了。

Firefox 56

在 Firefox 56 版中當新使用者開啟瀏覽器時,會看到一個可愛的狐狸頭。 Imgur

點進去可以看到一些功能導覽頁面。 Imgur

點擊導覽頁面右下方的按鈕的話,會聚焦到瀏覽器對應的功能區塊上,使用者可以快速嘗試這些功能。 Imgur

我們也加入了 Web assessibility,使用者可以只用鍵盤來瀏覽整個使用者導覽頁面。

Firefox Quantum (57)

經過使用者研究 (User research),在 Firefox Quantum (57) 版上我們針對 Onboarding 的體驗又做了不小的修改。

Imgur

這次的更新除了主視覺與一些互動元素都變得不一樣之外,也加了個小彩蛋:新使用者 (全新的 profile) 和從過去版本升級的使用者,所看到的功能導覽項目是不盡相同的喔。

Imgur

有興趣進一步了解我們怎麼製作 Onboarding Tour,可參考Onboarding 文件

· 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.