Skip to main content

14 posts tagged with "ethereum"

View All Tags

· 2 min read

為何要從 gitbook 遷移

以前曾在 gitbooks 發表一些電子書。 在 gitbook 發表電子書除了可以用 Markdown 語法外,也支援一些外掛如 Mermaid 圖表等。隨著 gitbook 業務調整,已經不再維護原來的平台,因此興起了遷移的想法。

選擇 Docusaurus

在一番比較後決定使用 Facebook 的 Docusaurus。Docusaurus 裝好後同時提供首頁,部落格,文件。支援 Markdown 並支援在文件中嵌入 React 元件。

· 8 min read

以太坊 (Ethereum)作為 2014 年才開始區塊鏈的專案,近年發展迅速,眾多在以太坊上的活動讓目前的每秒交易速度 (~13 TPS) 已不符所需。經過多年的實驗與討論,以太坊 (Ethereum) 的下一代 Ethereum 2.0 路線圖也已經越來越清晰。

目前以太坊 (Ethereum) 2.0 即將進展至 Phase 0 階段。在此階段以太坊 1.0 和 2.0 的網路將並存。直到發展到 Phase 1.5 階段,1.0 網路將會融合進 2.0 網路。隨著時間接近,2.0 測試網路與多種驗證節點的測試版也陸續釋出,網路上已有許多經驗可參考,正是嘗試的好時機。

這邊略過各種基礎介紹,直接從如何運行以太坊 (Ethereum) 2.0 的驗證節點開始。

如果覺得這篇文章有用,歡迎傳送小費到 0xfDa995Eb398750319a2D5E8A4766c02e54db24b8

· 3 min read

查看 從 gitbook 遷移到 docusaurus

把過去半年本部落格上關於區塊鏈的文章整理放到 Gitbook。命名為Ethereum 區塊鏈!智能合約 (Smart Contract) 與分散式網頁應用 (DApp) 入門,對區塊鏈,智能合約,分散式應用 (DApp) 感興趣的讀者不妨前往一觀。

這不是我寫的第一本書1,也不是我第一本用 Markdown 寫的電子書 (以前用 Leanpub 出版過Firefox OS 開發書),但絕對是我編輯過程最順暢的一本書(雖然還未完成 XD)。

一路 Markdown

編輯過程最順暢不是因為對主題很熟悉或寫得快,而是因為從部落格文章初稿到 Gitbook,在寫作的過程中可以一路使用 Markdown。而且由於原本部落格圖片皆使用外連,因此引用圖片時也不用像以前編書時需要重新導入的過程。由於 gitbook 也支援mermaid.js插件,支援我常常使用的 flowchart 語法,因此這些流程圖也不需要重新截圖或繪製,節省了大量時間。

所見即所得編輯器...

我已有Markdown 格式的初稿,但剛開始我使用 gitbook 提供的所見即所得的編輯器。使用起來感覺非常不自在。

線上編輯器提供的new change request,所見即所得編輯等功能,特別是 gitbook 提供的所見即所得編輯器無法切換回純 Markdown 模式,對於已熟悉 git, Markdown 語法的我來說並沒有變得好用。直接將 Markdown 格式貼到編輯器上時,也無法順利辨識格式,反而是貼上已輸出的部落格網頁時效果好很多。

所以最後我放棄使用線上編輯器,而是在本機編輯 Markdown 後直接 git 推送到專案上。

gitbook 在同步收到新的改動後,會自動編譯並發布新版本,相當方便。接下來應該會繼續使用這個流程。

參考資料

· 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