區塊鏈入門到實戰(27)之以太坊(Ethereum) – 智能合約開發


智能合約的優點

與傳統合同相比,智能合約有一些顯著優點:

智能合約優點

  • 不需要中間人
  • 費用低
  • 代碼就是規則
  • 區塊鏈網絡中有多個備份,不用擔心丟失
  • 避免人工錯誤
  • 無需信任,就可履行協議
  • 匿名履行協議

以太坊(Ethereum) – 智能合約開發概述

支持智能合約的區塊鏈

雖然以太坊(Ethereum)是最流行支持智能合約的區塊鏈平台,但它並不是唯一支持智能合約的平台。

超級賬本(Hyperledger) 是Linux基金會於2015年發起的推進區塊鏈數字技術和交易驗證的開源項目。通過創建分布式賬本的公開標准,實現虛擬和數字形式的價值交換,例如資產合約、能源交易、結婚證書、能夠安全和高效低成本的進行追蹤和交易。

另外,還有其他很多區塊鏈平台支持智能合約,可以參考相關資料。

以太坊(Ethereum)智能合約開發工具

通常,開發智能合約需要用到工具:

  • Mist – 以太坊節點/錢包。
  • Truffle 框架 – 流行的以太坊開發框架,內置了智能合約編譯、鏈接、部署等功能。
  • Metamask – Chrome插件方式的以太坊節點/錢包。
  • Remix – Remix是一個基於web瀏覽器的智能合約開發環境(IDE)。

以太坊(Ethereum)智能合約開發語言

目前主要的智能合約開發語言是 Solidity語言,是一種開發以太坊智能合約的靜態高級語言,語法類似於JavaScript。

還有另外一些智能合約開發語言:

等等。

以太坊(Ethereum) – 智能合約開發環境搭建

為了構建開發智能合約或者dApp,我們需要安裝以下模塊:

  • Node 與 NPM
  • Truffle 框架
  • Ganache
  • Metamask
  • VScode 與 Solidity插件

Node 與 NPM

Truffle 框架依賴Node,需要使用npm安裝。

首先需要安裝node,npm會同時安裝,下載node,按提示安裝。

安裝完后,可以驗證一下node版本:

$ node -v

Truffle 框架

Truffle框架是流行的以太坊開發框架,內置了智能合約編譯、鏈接、部署等功能。

使用npm安裝Truffle框架:

$ npm install -g truffle

驗證truffle安裝:

$  truffle --version
Truffle v5.0.35 - a development framework for Ethereum
...

Ganache

在實際的以太坊網絡上測試、部署Dapp或智能合約,需要消耗Gas。Ganache可以在本地創建區塊鏈網絡來測試我們的程序。

可以從Truffle Framework網站下載Ganache來安裝。它將創建一個本地區塊鏈網絡,給我們分配10個外部賬號,每個帳戶都有100個假的以太幣。

Metamask

Metamask是一個Chrome插件形式的以太坊節點/錢包。

我們可以使用Metamask連接到本地區塊鏈網絡或實際的以太坊網絡,並與我們的智能合約交互。

要安裝Metamask,請在谷歌Chrome web store中搜索Metamask Chrome插件並安裝。一旦安裝,請確保打開啟用按鈕。安裝后,你會在Chrome瀏覽器的右上角看到狐狸圖標。

VS code 與 Solidity插件

推薦使用vs code編輯器編寫solidity代碼,vs code可以安裝一下Solidity插件,以便支持語法高亮功能。

以太坊(Ethereum) – Ganache本地區塊鏈

我們安裝了Ganache。現在啟動Ganache,創建本地的以太坊區塊鏈網絡。

主界面

本地區塊鏈可以模擬公共區塊鏈,開發人員可以在本地區塊鏈上測試智能合約。打開Ganache,界面如下圖所示:

圖

本地區塊鏈缺省有10個外部賬號,每個賬號都有100個假的以太幣,這些可以通過設置改變。

Ganache界面中有下面幾個主要頁面:

  • ACCOUNTS – 賬號頁面,這顯示了自動生成的所有帳戶及其余額。
  • BLOCKS – 區塊頁面,顯示了在本地區塊鏈網絡上挖掘的每個區塊,及其Gas成本和包含的交易。
  • TRANSACTIONS – 交易頁面,列出了在本地區塊鏈上發生的所有交易。
  • CONTRACS – 合約頁面
  • EVENTS – 事件頁面
  • LOGS – 日志頁面

搜索區塊或交易

界面頂部的搜索欄,可以讓你搜索本地區塊鏈網絡上的區塊或交易。

設置

可以通過設置來定制Ganache的一些功能,單擊主界面右上角的⚙圖標進入設置頁面。

圖

以下是一些主要設置:

  • SERVER – 服務器設置頁面,管理關於網絡連接的詳細信息,比如網絡id、端口、主機名和自動挖掘狀態。
  • ACCOUNTS & KEYS – 帳戶和密鑰頁,設置自動生成的帳戶數量及其余額,缺省10個賬號,每個賬號余額是100 ether。
  • CHAIN – 鏈頁,讓你為網絡設置Gas限制和Gas價格。
  • 高級設置 – 日志選項設置,比如保存日志文件和配置詳細輸出的能力。

請注意,在更改了新的設置之后,必須Restart(設置頁面右上角)才能生效。

以太坊(Ethereum) – 開發智能合約

我們將使用truffle創建一個智能合約項目,該智能合約的功能是可以獲取值和設置值。

1. 初始化項目

首先創建項目目錄:

$ mkdir mydapp
$ cd mydapp

然后使用truffle init初始化項目,將生成項目模板文件:

$ truffle init

我們可以查看一下生成的項目目錄:

G:\qikegu\ethereum\mydapp>tree /f
卷 數據 的文件夾 PATH 列表
卷序列號為 0C52-9CF4
G:.
│  truffle-config.js
│
├─contracts
│      Migrations.sol
│
├─migrations
│      1_initial_migration.js
│
└─test
  • contracts 目錄 智能合約源文件目錄,現在已經有了一個Migrations.sol源文件,功能是遷移/部署/升級智能合約。
  • migrations 目錄 遷移文件目錄,遷移文件都是javascript腳本,幫助我們把智能合約部署到以太坊。
  • test 目錄 測試代碼目錄。
  • truffle-config.js 文件 Truffle項目配置文件,例如可以在里面配置網絡。

添加package.json文件

package.json是npm用來管理包的配置文件,在項目根目錄下創建此文件,內容如下:

{
  "name": "ethereum-demo",
  "version": "1.0.0",
  "description": "以太坊demo",
  "main": "truffle-config.js",
  "directories": {
    "test": "test"
  },
  "scripts": {
    "dev": "lite-server",
    "test": "echo \"Error: no test specified\" && sexit 1"
  },
  "author": "kevinhwu@qikegu.com",
  "license": "ISC",
  "devDependencies": {
    "@truffle/contract": "^4.0.33",
    "dotenv": "^8.1.0",
    "lite-server": "^2.5.4",
    "truffle-hdwallet-provider": "^1.0.17"
  }
}

關於依賴的包,可以在后面章節中,用到時逐個安裝。

2. 添加智能合約源文件

在contracts 目錄中創建一個新文件MyContract.sol,內容如下所示:

// 聲明solidity版本
pragma solidity ^0.5.0;

// 聲明智能合約MyContract,合約的所有代碼都包含在花括號中。
contract MyContract {

    // 聲明一個名為value的狀態變量
    string value;

    // 合約構造函數,每當將合約部署到網絡時都會調用它。
    // 此函數具有public函數修飾符,以確保它對公共接口可用。
    // 在這個函數中,我們將公共變量value的值設置為“myValue”。
    constructor() public {
        value = "myValue";
    }

    // 本函數讀取值狀態變量的值。可見性設置為public,以便外部帳戶可以訪問它。
    // 它還包含view修飾符並指定一個字符串返回值。
    function get() public view returns(string memory ) {
        return value;
    }

    // 本函數設置值狀態變量的值。可見性設置為public,以便外部帳戶可以訪問它。
    function set(string memory _value) public {
        value = _value;
    }
}

這個智能合約的功能是可以獲取值和設置值。

3. 編譯項目

現在讓我們編譯項目:

項目目錄下執行命令:

$ truffle compile

等編譯完成,可以看到多了一個build目錄,該目錄下生成了新文件:./build/contract/MyContract.json

這個文件是智能合約ABI文件,代表“抽象二進制接口”。這個文件有很多作用,其中2個重要作用:

  • 作為可在Ethereum虛擬機(EVM)上運行的可執行文件
  • 包含智能合約函數的JSON表示,以便外部客戶端可以調用這些函數

以太坊(Ethereum) – 部署智能合約到Ganache

接下來,我們將編譯好的智能合約部署到本地的Ganache區塊鏈網絡。步驟如下:

  • 更新項目的配置文件,修改網絡配置連接到本地區塊鏈網絡(Ganache)。
  • 創建遷移腳本,告訴Truffle如何部署智能合約。
  • 運行新創建的遷移腳本,部署智能合約。

1. 更新配置文件

更新項目的配置文件,修改網絡配置連接到本地區塊鏈網絡(Ganache)。

打開位於項目根目錄下的truffle-config.js文件,修改內容如下:

module.exports = {
  networks: {
    development: {
      host: "127.0.0.1",
      port: 7545,
      network_id: "*" // Match any network id
    }
  },
  solc: {
    optimizer: {
      enabled: true,
      runs: 200
    }
  }
}

這些網絡配置,包括ip地址、端口等,應該與Ganache的網絡配置匹配:

圖

2. 創建遷移腳本

接下來,我們將在migrations目錄中創建遷移腳本,告訴Truffle如何部署智能合約,在該目錄中創建文件2_deploy_contracts.js

注意,在migrations目錄中所有文件都有編號,作用是讓Truffle知道執行它們的順序。

2_deploy_contracts.js文件內容如下:

var MyContract = artifacts.require("./MyContract.sol");

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

上面的代碼中:

  • 首先,require了創建的合約,並將其分配給一個名為“MyContract”的變量。
  • 接着,將合約加入部署清單,運行遷移命令時合約將被部署。

3. 執行遷移命令

現在讓我們從命令行執行遷移命令, 部署智能合約。

$ truffle migrate

執行詳情如下:

G:\qikegu\ethereum\mydapp>truffle migrate

Compiling your contracts...
===========================
> Everything is up to date, there is nothing to compile.



Starting migrations...
======================
> Network name:    'development'
> Network id:      5777
> Block gas limit: 0x6691b7


1_initial_migration.js
======================

   Deploying 'Migrations'
   ----------------------
   > transaction hash:    0xe62fb8a27c9ccc894562fbd7a7797526ad9323ab67a44516ae342642bf4ffcc6
   > Blocks: 0            Seconds: 0
   > contract address:    0x168A7247B58786edd259502948f5Bf9449C863AD
   > block number:        1
   > block timestamp:     1568189958
   > account:             0x29920e756f41F8e691aE0b12D417C19204371E91
   > balance:             99.99477214
   > gas used:            261393
   > gas price:           20 gwei
   > value sent:          0 ETH
   > total cost:          0.00522786 ETH


   > Saving migration to chain.
   > Saving artifacts
   -------------------------------------
   > Total cost:          0.00522786 ETH


2_deploy_contracts.js
=====================

   Deploying 'MyContract'
   ----------------------
   > transaction hash:    0xe9dcef6f70332e476684e8f93ab96969af53920555161054f1f4bcc6277116fb
   > Blocks: 0            Seconds: 0
   > contract address:    0x4D3CFaF8457CEA76c0409f989f9870115B4d2d82
   > block number:        3
   > block timestamp:     1568189959
   > account:             0x29920e756f41F8e691aE0b12D417C19204371E91
   > balance:             99.98804272
   > gas used:            294448
   > gas price:           20 gwei
   > value sent:          0 ETH
   > total cost:          0.00588896 ETH


   > Saving migration to chain.
   > Saving artifacts
   -------------------------------------
   > Total cost:          0.00588896 ETH


Summary
=======
> Total deployments:   2
> Final cost:          0.01111682 ETH











  receipt:
   { transactionHash:
      '0x83be6ef86fe542b3c94ae1dd5f2e04570c199d6b2e7997af60f3d91cda9259ec',
     transactionIndex: 0,
     blockHash:
      '0x6e58c2c77b5998004b8a8c66760ca923814865307c69f1c779673cc2cbca06bc',
     blockNumber: 5,
     from: '0x29920e756f41f8e691ae0b12d417c19204371e91',
     to: '0x4d3cfaf8457cea76c0409f989f9870115b4d2d82',
     gasUsed: 33501,
     cumulativeGasUsed: 33501,
     contractAddress: null,
     logs: [],
     status: true,
     logsBloom:
      '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
     v: '0x1c',
     r:
      '0xdaf1578a7987ec5d4e7d25c4b66f570d97f880b783d3403b54fa7eb30b1ab836',
     s:
      '0x4024f2b26bab6277cc86da9727a9bccc1ba7832773b9c2781b265f8dd87df46f',
     rawLogs: [] },
  logs: [] }

可以看到,我們已經將智能合約成功部署到本地的Ganache區塊鏈網絡。

以太坊(Ethereum) – 使用 truffle console 訪問智能合約

truffle console 是區塊鏈開發人員的強大工具,這是一個命令行工具,可以在命令行中執行javascript代碼,與智能合約進行交互。這對於開發智能合約非常有用。

我們已經成功地將智能合約部署到本地區塊鏈網絡,接下來我們將使用 truffle console 與智能合約進行交互。

啟動 truffle console:

$ truffle console

進入控制台后,讓我們獲取已部署智能合約的一個實例,看看能否從該合約中讀取value值。從控制台運行以下代碼:

MyContract.deployed().then((instance) => { app = instance } )

這里MyContract是之前在遷移文件中創建的變量名稱,使用deployed()函數獲取一個已部署合約的實例,並將其分配給promise回調函數中的一個app變量。

現在可以獲取智能合約中的value值:

app.get()
// => 'myValue'

value設置一個新值:

app.set('New Value')

重新獲取智能合約中的value值:

app.get()
// => 'New Value'

可以通過以下命令退出truffle console:

.exit

 

以太坊(Ethereum) – 智能合約測試(truffle test)

類似Java中JUnit單元測試工具,Trfuffle test可以幫助我們對智能合約項目進行白盒測試。

對於區塊鏈項目,測試顯得尤其重要,因為部署合約、遷移合約的成本都是相當高的,都要消耗Gas。

編寫測試代碼

現在讓我們對前面章節中創建的智能合約,編寫一些測試代碼。整個測試過程模擬對智能合約MyContract獲取value值、設置value值的過程。

先確保MyContract已經正確部署到Ganache本地區塊鏈網絡中。測試中將會用到Mocha測試框架,與Chai斷言庫,但Truffle已經集成了這些庫。

測試代碼用JavaScript編寫,模擬與智能合約的交互,就像使用truffle console所做的那樣。

在項目根目錄下的test目錄中,添加測試腳本文件: MyContract.js

MyContract.js中的測試代碼:

// 首先,`require`合約並將其分配給一個變量`MyContract`
const MyContract = artifacts.require('./MyContract.sol');

// 調用“contract”函數,並在回調函數中編寫所有測試
// 回調函數提供一個“accounts”變量,表示本地區塊鏈上的所有帳戶。
contract('MyContract', (accounts) => {

    // 第1個測試:調用get()函數,檢查返回值,測試合約中value初始值是否是: 'myValue'
    it('initializes with the correct value', async () => {
        // 獲取合約實例
        const myContract = await MyContract.deployed()
        const value = await myContract.get()
        // 使用斷言測試value的值
        assert.equal(value, 'myValue')
    })

    // 第2個測試: 調用set()函數來設置value值,然后調用get()函數來確保更新了值
    it('can update the value', async () => {
        const myContract = await MyContract.deployed()
        myContract.set('New Value');
        const value = await myContract.get()
        assert.equal(value, 'New Value')
    })
})

代碼說明,請見注釋。

運行測試腳本

執行命令行運行測試:

$ truffle test

測試詳情:

G:\qikegu\ethereum\mydapp>truffle test
Using network 'development'.


Compiling your contracts...
===========================
> Everything is up to date, there is nothing to compile.



  Contract: MyContract
    √ initializes with the correct value (76ms)
    √ can update the value (78ms)


  2 passing (188ms)

以太坊(Ethereum) – 連接公鏈

本篇介紹如何將Truffle項目連接到公共區塊鏈網絡。

到目前為止,我們連接的都是在本地區塊鏈(Ganache),接下來將連接以太坊公鏈。

以太坊公鏈除了主網,還有多個測試網絡。主網(Mainnet)是正式的以太坊網絡,里面的以太幣是真正有價值的,測試網絡中的以太幣沒有價值,只用於測試。

我們最終目標是連接到主網,但先連接到測試網絡Kovan,雖然本地區塊鏈網絡(Ganache)也能測試,但與公鏈還是有區別的。

連接到公鏈的步驟如下:

  1. 設置錢包來管理公鏈帳戶
  2. 連接到以太坊節點
  3. 更新項目設置
  4. 訪問以太坊節點

設置錢包

首先需要設置一個錢包,來管理我們的公鏈帳戶。

簡單起見,可以借用Ganache本地區塊鏈錢包,由於區塊鏈的工作原理,這個錢包在公共區塊鏈和本地區塊鏈上都是有效的。

打開Ganache,主界面上可以看到一個名為“MNEMONIC”的部分:

圖

這是一個種子短語,用於構建由Ganache管理的錢包。我們可以使用這個種子短語加密重建錢包,來連接到公鏈。

復制這個值,保存到一個秘密文件,MNEMONIC是一個秘密值,需要保密。在項目根目錄中創建一個.env文件,保存MNEMONIC值,如下所示:

MNEMONIC="你的mnemonic"

連接以太坊節點

現在已經創建了錢包,下一步需要訪問Ethereum節點,以便連接到公共區塊鏈網絡。

有幾種方法可以做到這一點,可以使用Geth或Parity運行自己的Ethereum節點。但這需要從區塊鏈下載大量數據並保持同步,很麻煩。

比較方便的方法是,使用Infura訪問Ethereum節點。Infura是一個免費提供Ethereum節點的服務。

在Infura上注冊賬號,創建項目,在項目詳情頁上可以查看API KEY:

圖

使用API KEY,就可以訪問以太坊網絡節點。

.env文件中添加Infura api key的配置:

INFURA_API_KEY="https://kovan.infura.io/v3/543526cd4d3846acbc3826484e934564"
MNEMONIC="你的mnemonic"

更新項目設置

接下來使用MNEMONICINFURA_API_KEY,更新項目的網絡配置,以便連接到公共區塊鏈網絡。

修改truffle-config.js文件:

// 導入dotenv庫創用於讀取`.env`文件中的設置
require('dotenv').config();
// 導入truffle-hdwallet-provider庫重建錢包
const HDWalletProvider = require('truffle-hdwallet-provider');

module.exports = {
  networks: {
    development: {
     host: "127.0.0.1",     // Localhost (default: none)
     port: 7545,            // Standard Ethereum port (default: none)
     network_id: "*",       // Any network (default: none)
    },
    // Useful for deploying to a public network.
    // NB: It's important to wrap the provider as a function.
    kovan: {
      provider: () => new HDWalletProvider(
        process.env.MNEMONIC, 
        process.env.INFURA_API_KEY
      ),
      gas: 5000000,
      gasPrice: 25000000000,
      network_id: 42
    },
  },
  solc: {
    optimizer: {
      enabled: true,
      runs: 200
    }
  }
}

可以看到,我們使用了.env配置文件中的MNEMONICINFURA_API_KEY配置了kovan網絡。

由於用到了dotenv與truffle-hdwallet-provider這2個庫,我們需要先安裝:

切換到項目目錄,執行以下命令

npm install dotenv --save-dev
npm install truffle-hdwallet-provider --save-dev

注意 安裝truffle-hdwallet-provider時,如果出現node-gyp相關的錯誤,可參考這里解決。

訪問以太坊節點

使用truffle console連接到公共區塊鏈網絡:

$ truffle console --network kovan

要驗證連接,可以從區塊鏈中讀取一些數據,獲取一些關於最新區塊的信息,在控制台上執行:

web3.eth.getBlock('latest').then(console.log)

輸出

{ author: '0x03801efb0efe2a25ede5dd3a003ae880c0292e4d',
  difficulty: '340282366920938463463374607431768211454',
  extraData:
   '0xde830206028f5061726974792d457468657265756d86312e33362e30826c69',
  gasLimit: '0x7a1200',
  gasUsed: '0x17d23',
  hash:
   '0xc7390c4f492c8c1da60608135fc9e05930123b645b39f221cba33d8b3c577b2a',
  logsBloom:
   '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000080000000000000000000100000008000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400800000000000010000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009000000008000000',
  receiptsRoot:
   '0x3d05bb2ed4fcc90234eea6d840e7d0e3ce7f598a15e5314536b17bcd11c78b5b',
  sealFields:
   [ '0x84175e8801',
     '0xb84155a8cdb108dccec1d314124058fa6f22e7400ee200db0a94b7b165e4c3454c1818cc05f815cb7ce48f7a88b8401515740311a3566d9cf079428d506a6daca50101' ],
  sha3Uncles:
   '0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347',
  signature:
   '55a8cdb108dccec1d314124058fa6f22e7400ee200db0a94b7b165e4c3454c1818cc05f815cb7ce48f7a88b8401515740311a3566d9cf079428d506a6daca50101',
  size: 877,
  stateRoot:
   '0x03af5adce52a81ce5d332cddb9955e344214bff00859b78868116e1e839efdf7',
  step: '392071169',
  timestamp: 1568284676,
  totalDifficulty: '4524524338444961608702071789512829094373049115',
  transactions:
   [ '0xded7fed0842fd65ec808bc3652ec4175bc190acc11345c49c44b1fb5d954610f',
     '0x7e9112a46fa3c07aad813ea86355b15eebb44023c040d198ee7d15d379bbc2be' ],
  transactionsRoot:
   '0x0dd10d90686dda2684bd0ba70d1c9e1d9a5302c30ca75eb2c5b07a7b6e4498b9',
  uncles: [] }

可以看到,已經成功連接到了公鏈。

以太坊(Ethereum) – 部署智能合約到公鏈

現在,我們將智能合約部署到公鏈。步驟如下:

  1. 部署需要消耗Gas,獲取測試以太幣用於部署
  2. 部署智能合約
  3. 驗證部署

獲取測試以太幣

部署需要消耗Gas,Gas需要支付以太幣,我們部署到的是公鏈測試網Kovan,網絡中的以太幣沒有市場價值。

可以從Kovan faucet Gitter聊天室獲取測試用的偽以太幣。只需把錢包地址發送出去,約5分鍾內,有人會給你發測試用的偽以太幣。

打開Ganache並復制列表中第一個帳戶的地址(錢包地址),類似下面所示:

0x29920e756f41F8e691aE0b12D417C19204371E91

發送到聊天室內,稍等片刻,你的賬號將收到一筆以太幣。

部署智能合約

現在帳戶里已經有了資金,可以進行部署了。

執行部署命令:

truffle migrate --network kovan

一旦部署完成,應該會看到部署成功的消息。

部署命令執行詳情:

G:\qikegu\ethereum\mydapp>truffle migrate --network kovan

Compiling your contracts...
===========================
> Everything is up to date, there is nothing to compile.



Migrations dry-run (simulation)
===============================
> Network name:    'kovan-fork'
> Network id:      42
> Block gas limit: 0x7a1200

...

Starting migrations...
======================
> Network name:    'kovan'
> Network id:      42
> Block gas limit: 0x7a1200


1_initial_migration.js
======================

   Deploying 'Migrations'
   ----------------------
   > transaction hash:    0x7e30b5c716afed45888a9dd2d6af7e6f52a9fade0346e8ad7d0c268de508a26a
   > Blocks: 2            Seconds: 9
   > contract address:    0x168A7247B58786edd259502948f5Bf9449C863AD
   > block number:        13447029
   > block timestamp:     1568294312
   > account:             0x29920e756f41F8e691aE0b12D417C19204371E91
   > balance:             2.993465175
   > gas used:            261393
   > gas price:           25 gwei
   > value sent:          0 ETH
   > total cost:          0.006534825 ETH


   > Saving migration to chain.
   > Saving artifacts
   -------------------------------------
   > Total cost:         0.006534825 ETH


2_deploy_contracts.js
=====================

   Deploying 'MyContract'
   ----------------------
   > transaction hash:    0xc1f7ec8fee1a23e3d08d0c9e9d6e15fef24feb8ba163e0071dccb1bb90cc0eca
   > Blocks: 0            Seconds: 0
   > contract address:    0x4D3CFaF8457CEA76c0409f989f9870115B4d2d82
   > block number:        13447036
   > block timestamp:     1568294340
   > account:             0x29920e756f41F8e691aE0b12D417C19204371E91
   > balance:             2.9850534
   > gas used:            294448
   > gas price:           25 gwei
   > value sent:          0 ETH
   > total cost:          0.0073612 ETH


   > Saving migration to chain.
   > Saving artifacts
   -------------------------------------
   > Total cost:           0.0073612 ETH


Summary
=======
> Total deployments:   2
> Final cost:          0.013896025 ETH

Summary
=======
> Total deployments:   2
> Final cost:          0.013896025 ETH

驗證部署

現在打開truffle控制台,與kovan測試網絡上的智能合約進行交互:

$ truffle console --network kovan

在控制台中執行:

truffle(kovan)> MyContract.deployed().then((c) => { contract = c })

然后:

truffle(kovan)> contract.get()
'myValue'
truffle(kovan)> contract.set("hello world")
{ tx:
   '0x7bf63444f3a7bd70e981a7bd49228b1cf1a8c3754daf64c4c7765b8eee46bf37',
  receipt:
   { blockHash:
      '0xe03d0f43d85f4e41c18a90aa563ebda08899c6b9c38d0cd7779937046e2aed0c',
     blockNumber: 13447763,
     contractAddress: null,
     cumulativeGasUsed: 33629,
     from: '0x29920e756f41f8e691ae0b12d417c19204371e91',
     gasUsed: 33629,
     logs: [],
     logsBloom:
      '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
     root: null,
     status: true,
     to: '0x4d3cfaf8457cea76c0409f989f9870115b4d2d82',
     transactionHash:
      '0x7bf63444f3a7bd70e981a7bd49228b1cf1a8c3754daf64c4c7765b8eee46bf37',
     transactionIndex: 0,
     rawLogs: [] },
  logs: [] }
truffle(kovan)> contract.get()
'hello world'

可以看到智能合約已經成功部署。

以太坊(Ethereum) – truffle腳本

Truffle包含一個腳本運行器,可對以太坊網絡執行自定義腳本。

讓我們創建一個腳本並執行。

在項目根目錄下,創建script.js文件,內容如下:

module.exports = function(callback) {
  web3.eth.getBlock('latest').then(console.log)
}

該腳本將從Kovan測試網絡獲取最新區塊的信息。

執行腳本:

truffle exec script.js --network kovan

輸出

{ author: '0x596e8221a30bfe6e7eff67fee664a01c73ba3c56',
  difficulty: '340282366920938463463374607431768211454',
  extraData:
   '0xde830205058f5061726974792d457468657265756d86312e33362e30826c69',
  gasLimit: '0x7a1200',
  gasUsed: '0x5e61',
  hash:
   '0x225a1e0b13fd20396af60d049ce9bb94c2f3f7df06c7db260880b62c91997004',
  logsBloom:
   '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
  miner: '0x596e8221A30bFe6e7eFF67Fee664A01C73BA3C56',
  number: 13448162,
  parentHash:
   '0x28d00fd7b66771130ed98de5073c7797ee293e7bee4b546793a4b79171555066',
  receiptsRoot:
   '0x44617b5733ee59bde159af08ffd6edae36e0964f1724c333f3d1bef0808dee15',
  sealFields:
   [ '0x84175e95d7',
     '0xb8412ed900e67f4a72925fb3b495efb3f547411f40d26e972cc0e8b2cf26e40cf84a545e0328199d4880b79c62670129a7db12ac58234bee0866c6376b46ab99e8a200' ],
  sha3Uncles:
   '0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347',
  signature:
   '2ed900e67f4a72925fb3b495efb3f547411f40d26e972cc0e8b2cf26e40cf84a545e0328199d4880b79c62670129a7db12ac58234bee0866c6376b46ab99e8a200',
  size: 797,
  stateRoot:
   '0xe1bbaacfb950361bec70f4ad53a2605e1ac1d2ff0bfd913fe063dc6c5f3252a0',
  step: '392074711',
  timestamp: 1568298844,
  totalDifficulty: '4525729278306228651801195598997744985609807728',
  transactions:
   [ '0xf1ae41eac6b32419bc62a6cde9cab4b4ca244899a3d49b4a2461bcf94f504176' ],
  transactionsRoot:
   '0xf08c8097ea946f84ce9594ce73648fc0f9f683adef105a5db00c5f1f15e61c2c',
  uncles: [] }

下面的代碼智能合約MyContract中,讀取value值,將script.js腳本文件中的代碼替換為:

const MyContract = artifacts.require("./MyContract.sol");

module.exports = async function(callback) {
  const contract = await MyContract.deployed()
  const value = await contract.get()
  console.log("Value:", value)
}

執行腳本:

truffle exec script.js --network kovan

輸出

Value: hello world

以太坊(Ethereum) – 讓瀏覽器支持區塊鏈(MetaMask)

大多數web瀏覽器目前都不支持連接到區塊鏈網絡,不過可以通過安裝瀏覽器插件,來讓瀏覽器支持區塊鏈。

安裝MetaMask

我們將為Chrome瀏覽器安裝Metamask錢包插件(需FQ)。

安裝好后,確保插件的啟用按鈕打開,在瀏覽器的右上角會看到一個狐狸🦊圖標。

導入賬號

把錢包賬號從Ganache導入到Metamask中,這樣我們就可以連接到區塊鏈了。

打開Ganache主界面,如下圖所示,復制MNEMONIC的值:

圖

打開Metamask,選擇通過Seed Phrase導入賬號,把復制MNEMONIC的值,粘貼到Wallet Seed,如下圖所示:

圖

進入錢包

查看Kovan網絡,可以看到里面有一些測試以太幣余額。

圖

現在我們的Chrome瀏覽器已經支持區塊鏈了。

以太坊(Ethereum) – 智能合約前端頁面

現在我們來開發智能合約的前端頁面,讓用戶可以通過前端頁面與智能合約交互。這個頁面的主要功能是:

  • 顯示當前連接的帳戶
  • 讀取智能合約中存儲的value值
  • 更新智能合約中存儲的value值

頁面大概的樣子:

圖

為開發前端頁面,需要完成下面幾項工作:

  • 配置web服務器,用來部署頁面
  • 創建前端的h5、js文件

配置web服務器

首先,讓我們來配置web服務器。服務器使用lite-server,安裝lite-server:

$ npm install lite-server --save-dev

項目根目錄下,創建lite-server的配置文件bs-config.json,內容如下:

{
  "server": {
    "baseDir": [
      "./src",
      "./build/contracts"
    ],
    "routes": {
      "/vendor": "./node_modules"
    }
  }
}
  • baseDir配置告訴lite-server將./src./build/contracts目錄作為web服務器的根目錄,所有文件都可以被訪問
  • routes./node_modules映射為/vendor,在引用文件時,可以使用/vendor

創建前端頁面

項目根目錄下,創建src目錄,用於存放前端頁面。

前端頁面包含2個文件:

src/index.html
src/app.js

index.html

添加index.html頁面,內容如下:

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>以太坊 DApp Demo</title>

    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
      <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>
      <h1>賬號: <span id="account"></span></h1>
      <hr>
      <div id="content">
          <h2>智能合約:MyContract</b></h2>
          <p>獲取智能合約中的value值: <span id="value"></span></p>

            <h5>設置value值</h5>
            <form onSubmit="App.set(); return false;" role="form">
            <div >
                <input id="newValue"  type="text"></input>
            </div>
            <button type="submit" >設置</button>
            </form>
      </div>
      <div id="loader">正在加載...</div>


    </div>

    <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <!-- Include all compiled plugins (below), or include individual files as needed -->
    <script src="https://etherscan.io/jss/web3.min.js"></script>
    <script src="vendor/@truffle/contract/dist/truffle-contract.js"></script>
    <script src="app.js"></script>
  </body>
</html>

這個文件的重點是引入了幾個js文件:

  • web3.min.js – web3.js庫文件,直接從https://etherscan.io/引入
  • truffle-contract.js – truffle提供的處理智能合約的庫文件

安裝@truffle/contract

$ npm install @truffle/contract --save-dev

app.js

添加javascript腳本文件:app.js

App = {
    web3Provider: null,
    contracts: {},
    account: '0x0',
    loading: false,
    contractInstance: null,

    init: async () => {
        // 加載web3
        await App.loadWeb3()
        // 加載智能合約
        await App.loadContract()
        // 網頁刷新
        await App.render()
    },

    // https://medium.com/metamask/https-medium-com-metamask-breaking-change-injecting-web3-7722797916a8
    loadWeb3: async () => {
        if (typeof web3 !== 'undefined') {
            App.web3Provider = web3.currentProvider
            web3 = new Web3(web3.currentProvider)
        } else {
            window.alert("Please connect to Metamask.")
        }
        // MetaMask新版本…
        if (window.ethereum) {
            window.web3 = new Web3(ethereum)
            try {
                // 向用戶請求帳戶訪問
                await ethereum.enable()
                // 用戶允許使用賬戶
                web3.eth.sendTransaction({/* ... */ })
            } catch (error) {
                // 用戶拒絕使用賬戶
            }
        }
        // MetaMask老版本…
        else if (window.web3) {
            App.web3Provider = web3.currentProvider
            window.web3 = new Web3(web3.currentProvider)
            // 無需向用戶請求,可以直接使用賬號
            web3.eth.sendTransaction({/* ... */ })
        }
        // 沒有安裝以太坊錢包插件(MetaMask)...
        else {
            console.log('需要安裝以太坊錢包插件(例如MetaMask)才能使用!')
        }
    },

    loadContract: async () => {
        const contract = await $.getJSON('MyContract.json')
        App.contracts.MyContract = TruffleContract(contract)
        App.contracts.MyContract.setProvider(App.web3Provider)
    },

    render: async () => {
        // 如果正在加載,直接返回,避免重復操作
        if (App.loading) {
            return
        }

        // 更新app加載狀態
        App.setLoading(true)

        // 設置當前區塊鏈帳戶
        const accounts = await ethereum.enable()
        App.account = accounts[0]
        $('#account').html(App.account)

        // 加載智能合約
        const contract = await App.contracts.MyContract.deployed()
        App.contractInstance = contract

        const value = await App.contractInstance.get()
        $('#value').html(value)

        App.setLoading(false)
    },

    set: async () => {
        App.setLoading(true)

        const newValue = $('#newValue').val()

        await App.contractInstance.set(newValue, {from: App.account})
        window.alert('更新成功,頁面值不會馬上更新,等待幾秒后多刷新幾次。')
        App.setLoading(false)
    },

    setLoading: (boolean) => {
        App.loading = boolean
        const loader = $('#loader')
        const content = $('#content')
        if (boolean) {
            loader.show()
            content.hide()
        } else {
            loader.hide()
            content.show()
        }
    }
}

$(document).ready(function () {
    App.init()
});

在上面的代碼中:

  • loadWeb3()函數添加了用Metamask將web瀏覽器連接到區塊鏈所需的配置。這是直接從Metamask的配置規范中復制粘貼的,如函數上面代碼注釋中的url所示。
  • loadContract()函數使用TruffleContract庫創建智能合約的javascript對象,可以用於調用智能合約中的函數。
  • render()函數設置頁面內容,包括帳戶及智能合約的value值。

現在啟動服務器:

$ npm run dev

然后使用安裝了MetaMask插件的Chrome瀏覽器,打開網址:http://localhost:3000,就可以查看前端頁面,與區塊鏈上的智能合約進行交互。

 

 

以太坊(Ethereum) – truffle腳本

 

Truffle包含一個腳本運行器,可對以太坊網絡執行自定義腳本。

讓我們創建一個腳本並執行。

在項目根目錄下,創建script.js文件,內容如下:

module.exports = function(callback) {
  web3.eth.getBlock('latest').then(console.log)
}

該腳本將從Kovan測試網絡獲取最新區塊的信息。

執行腳本:

truffle exec script.js --network kovan

輸出:

{ author: '0x596e8221a30bfe6e7eff67fee664a01c73ba3c56',
  difficulty: '340282366920938463463374607431768211454',
  extraData:
   '0xde830205058f5061726974792d457468657265756d86312e33362e30826c69',
  gasLimit: '0x7a1200',
  gasUsed: '0x5e61',
  hash:
   '0x225a1e0b13fd20396af60d049ce9bb94c2f3f7df06c7db260880b62c91997004',
  logsBloom:
   '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
  miner: '0x596e8221A30bFe6e7eFF67Fee664A01C73BA3C56',
  number: 13448162,
  parentHash:
   '0x28d00fd7b66771130ed98de5073c7797ee293e7bee4b546793a4b79171555066',
  receiptsRoot:
   '0x44617b5733ee59bde159af08ffd6edae36e0964f1724c333f3d1bef0808dee15',
  sealFields:
   [ '0x84175e95d7',
     '0xb8412ed900e67f4a72925fb3b495efb3f547411f40d26e972cc0e8b2cf26e40cf84a545e0328199d4880b79c62670129a7db12ac58234bee0866c6376b46ab99e8a200' ],
  sha3Uncles:
   '0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347',
  signature:
   '2ed900e67f4a72925fb3b495efb3f547411f40d26e972cc0e8b2cf26e40cf84a545e0328199d4880b79c62670129a7db12ac58234bee0866c6376b46ab99e8a200',
  size: 797,
  stateRoot:
   '0xe1bbaacfb950361bec70f4ad53a2605e1ac1d2ff0bfd913fe063dc6c5f3252a0',
  step: '392074711',
  timestamp: 1568298844,
  totalDifficulty: '4525729278306228651801195598997744985609807728',
  transactions:
   [ '0xf1ae41eac6b32419bc62a6cde9cab4b4ca244899a3d49b4a2461bcf94f504176' ],
  transactionsRoot:
   '0xf08c8097ea946f84ce9594ce73648fc0f9f683adef105a5db00c5f1f15e61c2c',
  uncles: [] }

下面的代碼智能合約MyContract中,讀取value值,將script.js腳本文件中的代碼替換為:

const MyContract = artifacts.require("./MyContract.sol");

module.exports = async function(callback) {
  const contract = await MyContract.deployed()
  const value = await contract.get()
  console.log("Value:", value)
}

執行腳本:

truffle exec script.js --network kovan

輸出:

Value: hello world

腳本運行器是一個非常有用的功能。


Doc navigation

 

@2019 qikegu.com 版權所有,禁止轉載

 

類似Java中JUnit單元測試工具,Trfuffle test可以幫助我們對智能合約項目進行白盒測試。

對於區塊鏈項目,測試顯得尤其重要,因為部署合約、遷移合約的成本都是相當高的,都要消耗Gas。

編寫測試代碼

現在讓我們對前面章節中創建的智能合約,編寫一些測試代碼。整個測試過程模擬對智能合約MyContract獲取value值、設置value值的過程。

先確保MyContract已經正確部署到Ganache本地區塊鏈網絡中。測試中將會用到Mocha測試框架,與Chai斷言庫,但Truffle已經集成了這些庫。

測試代碼用JavaScript編寫,模擬與智能合約的交互,就像使用truffle console所做的那樣。

在項目根目錄下的test目錄中,添加測試腳本文件: MyContract.js

MyContract.js中的測試代碼:

// 首先,`require`合約並將其分配給一個變量`MyContract`
const MyContract = artifacts.require('./MyContract.sol');

// 調用“contract”函數,並在回調函數中編寫所有測試
// 回調函數提供一個“accounts”變量,表示本地區塊鏈上的所有帳戶。
contract('MyContract', (accounts) => {

    // 第1個測試:調用get()函數,檢查返回值,測試合約中value初始值是否是: 'myValue'
    it('initializes with the correct value', async () => {
        // 獲取合約實例
        const myContract = await MyContract.deployed()
        const value = await myContract.get()
        // 使用斷言測試value的值
        assert.equal(value, 'myValue')
    })

    // 第2個測試: 調用set()函數來設置value值,然后調用get()函數來確保更新了值
    it('can update the value', async () => {
        const myContract = await MyContract.deployed()
        myContract.set('New Value');
        const value = await myContract.get()
        assert.equal(value, 'New Value')
    })
})

代碼說明,請見注釋。

運行測試腳本

執行命令行運行測試:

$ truffle test

測試詳情:

G:\qikegu\ethereum\mydapp>truffle test
Using network 'development'.


Compiling your contracts...
===========================
> Everything is up to date, there is nothing to compile.



  Contract: MyContract
    √ initializes with the correct value (76ms)
    √ can update the value (78ms)


  2 passing (188ms)

以太坊(Ethereum) – 連接公鏈

本篇介紹如何將Truffle項目連接到公共區塊鏈網絡。

到目前為止,我們連接的都是在本地區塊鏈(Ganache),接下來將連接以太坊公鏈。

以太坊公鏈除了主網,還有多個測試網絡。主網(Mainnet)是正式的以太坊網絡,里面的以太幣是真正有價值的,測試網絡中的以太幣沒有價值,只用於測試。

我們最終目標是連接到主網,但先連接到測試網絡Kovan,雖然本地區塊鏈網絡(Ganache)也能測試,但與公鏈還是有區別的。

連接到公鏈的步驟如下:

  1. 設置錢包來管理公鏈帳戶
  2. 連接到以太坊節點
  3. 更新項目設置
  4. 訪問以太坊節點

設置錢包

首先需要設置一個錢包,來管理我們的公鏈帳戶。

簡單起見,可以借用Ganache本地區塊鏈錢包,由於區塊鏈的工作原理,這個錢包在公共區塊鏈和本地區塊鏈上都是有效的。

打開Ganache,主界面上可以看到一個名為“MNEMONIC”的部分:

圖

這是一個種子短語,用於構建由Ganache管理的錢包。我們可以使用這個種子短語加密重建錢包,來連接到公鏈。

復制這個值,保存到一個秘密文件,MNEMONIC是一個秘密值,需要保密。在項目根目錄中創建一個.env文件,保存MNEMONIC值,如下所示:

MNEMONIC="你的mnemonic"

連接以太坊節點

現在已經創建了錢包,下一步需要訪問Ethereum節點,以便連接到公共區塊鏈網絡。

有幾種方法可以做到這一點,可以使用Geth或Parity運行自己的Ethereum節點。但這需要從區塊鏈下載大量數據並保持同步,很麻煩。

比較方便的方法是,使用Infura訪問Ethereum節點。Infura是一個免費提供Ethereum節點的服務。

在Infura上注冊賬號,創建項目,在項目詳情頁上可以查看API KEY:

圖

使用API KEY,就可以訪問以太坊網絡節點。

.env文件中添加Infura api key的配置:

INFURA_API_KEY="https://kovan.infura.io/v3/543526cd4d3846acbc3826484e934564"
MNEMONIC="你的mnemonic"

更新項目設置

接下來使用MNEMONICINFURA_API_KEY,更新項目的網絡配置,以便連接到公共區塊鏈網絡。

修改truffle-config.js文件:

// 導入dotenv庫創用於讀取`.env`文件中的設置
require('dotenv').config();
// 導入truffle-hdwallet-provider庫重建錢包
const HDWalletProvider = require('truffle-hdwallet-provider');

module.exports = {
  networks: {
    development: {
     host: "127.0.0.1",     // Localhost (default: none)
     port: 7545,            // Standard Ethereum port (default: none)
     network_id: "*",       // Any network (default: none)
    },
    // Useful for deploying to a public network.
    // NB: It's important to wrap the provider as a function.
    kovan: {
      provider: () => new HDWalletProvider(
        process.env.MNEMONIC, 
        process.env.INFURA_API_KEY
      ),
      gas: 5000000,
      gasPrice: 25000000000,
      network_id: 42
    },
  },
  solc: {
    optimizer: {
      enabled: true,
      runs: 200
    }
  }
}

可以看到,我們使用了.env配置文件中的MNEMONICINFURA_API_KEY配置了kovan網絡。

由於用到了dotenv與truffle-hdwallet-provider這2個庫,我們需要先安裝:

切換到項目目錄,執行以下命令

npm install dotenv --save-dev
npm install truffle-hdwallet-provider --save-dev

注意 安裝truffle-hdwallet-provider時,如果出現node-gyp相關的錯誤,可參考這里解決。

訪問以太坊節點

使用truffle console連接到公共區塊鏈網絡:

$ truffle console --network kovan

要驗證連接,可以從區塊鏈中讀取一些數據,獲取一些關於最新區塊的信息,在控制台上執行:

web3.eth.getBlock('latest').then(console.log)

輸出

{ author: '0x03801efb0efe2a25ede5dd3a003ae880c0292e4d',
  difficulty: '340282366920938463463374607431768211454',
  extraData:
   '0xde830206028f5061726974792d457468657265756d86312e33362e30826c69',
  gasLimit: '0x7a1200',
  gasUsed: '0x17d23',
  hash:
   '0xc7390c4f492c8c1da60608135fc9e05930123b645b39f221cba33d8b3c577b2a',
  logsBloom:
   '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000080000000000000000000100000008000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400800000000000010000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009000000008000000',
  receiptsRoot:
   '0x3d05bb2ed4fcc90234eea6d840e7d0e3ce7f598a15e5314536b17bcd11c78b5b',
  sealFields:
   [ '0x84175e8801',
     '0xb84155a8cdb108dccec1d314124058fa6f22e7400ee200db0a94b7b165e4c3454c1818cc05f815cb7ce48f7a88b8401515740311a3566d9cf079428d506a6daca50101' ],
  sha3Uncles:
   '0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347',
  signature:
   '55a8cdb108dccec1d314124058fa6f22e7400ee200db0a94b7b165e4c3454c1818cc05f815cb7ce48f7a88b8401515740311a3566d9cf079428d506a6daca50101',
  size: 877,
  stateRoot:
   '0x03af5adce52a81ce5d332cddb9955e344214bff00859b78868116e1e839efdf7',
  step: '392071169',
  timestamp: 1568284676,
  totalDifficulty: '4524524338444961608702071789512829094373049115',
  transactions:
   [ '0xded7fed0842fd65ec808bc3652ec4175bc190acc11345c49c44b1fb5d954610f',
     '0x7e9112a46fa3c07aad813ea86355b15eebb44023c040d198ee7d15d379bbc2be' ],
  transactionsRoot:
   '0x0dd10d90686dda2684bd0ba70d1c9e1d9a5302c30ca75eb2c5b07a7b6e4498b9',
  uncles: [] }

可以看到,已經成功連接到了公鏈。

以太坊(Ethereum) – 部署智能合約到公鏈

現在,我們將智能合約部署到公鏈。步驟如下:

  1. 部署需要消耗Gas,獲取測試以太幣用於部署
  2. 部署智能合約
  3. 驗證部署

獲取測試以太幣

部署需要消耗Gas,Gas需要支付以太幣,我們部署到的是公鏈測試網Kovan,網絡中的以太幣沒有市場價值。

可以從Kovan faucet Gitter聊天室獲取測試用的偽以太幣。只需把錢包地址發送出去,約5分鍾內,有人會給你發測試用的偽以太幣。

打開Ganache並復制列表中第一個帳戶的地址(錢包地址),類似下面所示:

0x29920e756f41F8e691aE0b12D417C19204371E91

發送到聊天室內,稍等片刻,你的賬號將收到一筆以太幣。

部署智能合約

現在帳戶里已經有了資金,可以進行部署了。

執行部署命令:

truffle migrate --network kovan

一旦部署完成,應該會看到部署成功的消息。

部署命令執行詳情:

G:\qikegu\ethereum\mydapp>truffle migrate --network kovan

Compiling your contracts...
===========================
> Everything is up to date, there is nothing to compile.



Migrations dry-run (simulation)
===============================
> Network name:    'kovan-fork'
> Network id:      42
> Block gas limit: 0x7a1200

...

Starting migrations...
======================
> Network name:    'kovan'
> Network id:      42
> Block gas limit: 0x7a1200


1_initial_migration.js
======================

   Deploying 'Migrations'
   ----------------------
   > transaction hash:    0x7e30b5c716afed45888a9dd2d6af7e6f52a9fade0346e8ad7d0c268de508a26a
   > Blocks: 2            Seconds: 9
   > contract address:    0x168A7247B58786edd259502948f5Bf9449C863AD
   > block number:        13447029
   > block timestamp:     1568294312
   > account:             0x29920e756f41F8e691aE0b12D417C19204371E91
   > balance:             2.993465175
   > gas used:            261393
   > gas price:           25 gwei
   > value sent:          0 ETH
   > total cost:          0.006534825 ETH


   > Saving migration to chain.
   > Saving artifacts
   -------------------------------------
   > Total cost:         0.006534825 ETH


2_deploy_contracts.js
=====================

   Deploying 'MyContract'
   ----------------------
   > transaction hash:    0xc1f7ec8fee1a23e3d08d0c9e9d6e15fef24feb8ba163e0071dccb1bb90cc0eca
   > Blocks: 0            Seconds: 0
   > contract address:    0x4D3CFaF8457CEA76c0409f989f9870115B4d2d82
   > block number:        13447036
   > block timestamp:     1568294340
   > account:             0x29920e756f41F8e691aE0b12D417C19204371E91
   > balance:             2.9850534
   > gas used:            294448
   > gas price:           25 gwei
   > value sent:          0 ETH
   > total cost:          0.0073612 ETH


   > Saving migration to chain.
   > Saving artifacts
   -------------------------------------
   > Total cost:           0.0073612 ETH


Summary
=======
> Total deployments:   2
> Final cost:          0.013896025 ETH

Summary
=======
> Total deployments:   2
> Final cost:          0.013896025 ETH

驗證部署

現在打開truffle控制台,與kovan測試網絡上的智能合約進行交互:

$ truffle console --network kovan

在控制台中執行:

truffle(kovan)> MyContract.deployed().then((c) => { contract = c })

然后:

truffle(kovan)> contract.get()
'myValue'
truffle(kovan)> contract.set("hello world")
{ tx:
   '0x7bf63444f3a7bd70e981a7bd49228b1cf1a8c3754daf64c4c7765b8eee46bf37',
  receipt:
   { blockHash:
      '0xe03d0f43d85f4e41c18a90aa563ebda08899c6b9c38d0cd7779937046e2aed0c',
     blockNumber: 13447763,
     contractAddress: null,
     cumulativeGasUsed: 33629,
     from: '0x29920e756f41f8e691ae0b12d417c19204371e91',
     gasUsed: 33629,
     logs: [],
     logsBloom:
      '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
     root: null,
     status: true,
     to: '0x4d3cfaf8457cea76c0409f989f9870115b4d2d82',
     transactionHash:
      '0x7bf63444f3a7bd70e981a7bd49228b1cf1a8c3754daf64c4c7765b8eee46bf37',
     transactionIndex: 0,
     rawLogs: [] },
  logs: [] }
truffle(kovan)> contract.get()
'hello world'

可以看到智能合約已經成功部署。

以太坊(Ethereum) – truffle腳本

Truffle包含一個腳本運行器,可對以太坊網絡執行自定義腳本。

讓我們創建一個腳本並執行。

在項目根目錄下,創建script.js文件,內容如下:

module.exports = function(callback) {
  web3.eth.getBlock('latest').then(console.log)
}

該腳本將從Kovan測試網絡獲取最新區塊的信息。

執行腳本:

truffle exec script.js --network kovan

輸出

{ author: '0x596e8221a30bfe6e7eff67fee664a01c73ba3c56',
  difficulty: '340282366920938463463374607431768211454',
  extraData:
   '0xde830205058f5061726974792d457468657265756d86312e33362e30826c69',
  gasLimit: '0x7a1200',
  gasUsed: '0x5e61',
  hash:
   '0x225a1e0b13fd20396af60d049ce9bb94c2f3f7df06c7db260880b62c91997004',
  logsBloom:
   '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
  miner: '0x596e8221A30bFe6e7eFF67Fee664A01C73BA3C56',
  number: 13448162,
  parentHash:
   '0x28d00fd7b66771130ed98de5073c7797ee293e7bee4b546793a4b79171555066',
  receiptsRoot:
   '0x44617b5733ee59bde159af08ffd6edae36e0964f1724c333f3d1bef0808dee15',
  sealFields:
   [ '0x84175e95d7',
     '0xb8412ed900e67f4a72925fb3b495efb3f547411f40d26e972cc0e8b2cf26e40cf84a545e0328199d4880b79c62670129a7db12ac58234bee0866c6376b46ab99e8a200' ],
  sha3Uncles:
   '0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347',
  signature:
   '2ed900e67f4a72925fb3b495efb3f547411f40d26e972cc0e8b2cf26e40cf84a545e0328199d4880b79c62670129a7db12ac58234bee0866c6376b46ab99e8a200',
  size: 797,
  stateRoot:
   '0xe1bbaacfb950361bec70f4ad53a2605e1ac1d2ff0bfd913fe063dc6c5f3252a0',
  step: '392074711',
  timestamp: 1568298844,
  totalDifficulty: '4525729278306228651801195598997744985609807728',
  transactions:
   [ '0xf1ae41eac6b32419bc62a6cde9cab4b4ca244899a3d49b4a2461bcf94f504176' ],
  transactionsRoot:
   '0xf08c8097ea946f84ce9594ce73648fc0f9f683adef105a5db00c5f1f15e61c2c',
  uncles: [] }

下面的代碼智能合約MyContract中,讀取value值,將script.js腳本文件中的代碼替換為:

const MyContract = artifacts.require("./MyContract.sol");

module.exports = async function(callback) {
  const contract = await MyContract.deployed()
  const value = await contract.get()
  console.log("Value:", value)
}

執行腳本:

truffle exec script.js --network kovan

輸出

Value: hello world

以太坊(Ethereum) – 讓瀏覽器支持區塊鏈(MetaMask)

大多數web瀏覽器目前都不支持連接到區塊鏈網絡,不過可以通過安裝瀏覽器插件,來讓瀏覽器支持區塊鏈。

安裝MetaMask

我們將為Chrome瀏覽器安裝Metamask錢包插件(需FQ)。

安裝好后,確保插件的啟用按鈕打開,在瀏覽器的右上角會看到一個狐狸🦊圖標。

導入賬號

把錢包賬號從Ganache導入到Metamask中,這樣我們就可以連接到區塊鏈了。

打開Ganache主界面,如下圖所示,復制MNEMONIC的值:

圖

打開Metamask,選擇通過Seed Phrase導入賬號,把復制MNEMONIC的值,粘貼到Wallet Seed,如下圖所示:

圖

進入錢包

查看Kovan網絡,可以看到里面有一些測試以太幣余額。

圖

現在我們的Chrome瀏覽器已經支持區塊鏈了。

以太坊(Ethereum) – 智能合約前端頁面

現在我們來開發智能合約的前端頁面,讓用戶可以通過前端頁面與智能合約交互。這個頁面的主要功能是:

  • 顯示當前連接的帳戶
  • 讀取智能合約中存儲的value值
  • 更新智能合約中存儲的value值

頁面大概的樣子:

圖

為開發前端頁面,需要完成下面幾項工作:

  • 配置web服務器,用來部署頁面
  • 創建前端的h5、js文件

配置web服務器

首先,讓我們來配置web服務器。服務器使用lite-server,安裝lite-server:

$ npm install lite-server --save-dev

項目根目錄下,創建lite-server的配置文件bs-config.json,內容如下:

{
  "server": {
    "baseDir": [
      "./src",
      "./build/contracts"
    ],
    "routes": {
      "/vendor": "./node_modules"
    }
  }
}
  • baseDir配置告訴lite-server將./src./build/contracts目錄作為web服務器的根目錄,所有文件都可以被訪問
  • routes./node_modules映射為/vendor,在引用文件時,可以使用/vendor

創建前端頁面

項目根目錄下,創建src目錄,用於存放前端頁面。

前端頁面包含2個文件:

src/index.html
src/app.js

index.html

添加index.html頁面,內容如下:

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>以太坊 DApp Demo</title>

    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
      <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>
      <h1>賬號: <span id="account"></span></h1>
      <hr>
      <div id="content">
          <h2>智能合約:MyContract</b></h2>
          <p>獲取智能合約中的value值: <span id="value"></span></p>

            <h5>設置value值</h5>
            <form onSubmit="App.set(); return false;" role="form">
            <div >
                <input id="newValue"  type="text"></input>
            </div>
            <button type="submit" >設置</button>
            </form>
      </div>
      <div id="loader">正在加載...</div>


    </div>

    <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <!-- Include all compiled plugins (below), or include individual files as needed -->
    <script src="https://etherscan.io/jss/web3.min.js"></script>
    <script src="vendor/@truffle/contract/dist/truffle-contract.js"></script>
    <script src="app.js"></script>
  </body>
</html>

這個文件的重點是引入了幾個js文件:

  • web3.min.js – web3.js庫文件,直接從https://etherscan.io/引入
  • truffle-contract.js – truffle提供的處理智能合約的庫文件

安裝@truffle/contract

$ npm install @truffle/contract --save-dev

app.js

添加javascript腳本文件:app.js

App = {
    web3Provider: null,
    contracts: {},
    account: '0x0',
    loading: false,
    contractInstance: null,

    init: async () => {
        // 加載web3
        await App.loadWeb3()
        // 加載智能合約
        await App.loadContract()
        // 網頁刷新
        await App.render()
    },

    // https://medium.com/metamask/https-medium-com-metamask-breaking-change-injecting-web3-7722797916a8
    loadWeb3: async () => {
        if (typeof web3 !== 'undefined') {
            App.web3Provider = web3.currentProvider
            web3 = new Web3(web3.currentProvider)
        } else {
            window.alert("Please connect to Metamask.")
        }
        // MetaMask新版本…
        if (window.ethereum) {
            window.web3 = new Web3(ethereum)
            try {
                // 向用戶請求帳戶訪問
                await ethereum.enable()
                // 用戶允許使用賬戶
                web3.eth.sendTransaction({/* ... */ })
            } catch (error) {
                // 用戶拒絕使用賬戶
            }
        }
        // MetaMask老版本…
        else if (window.web3) {
            App.web3Provider = web3.currentProvider
            window.web3 = new Web3(web3.currentProvider)
            // 無需向用戶請求,可以直接使用賬號
            web3.eth.sendTransaction({/* ... */ })
        }
        // 沒有安裝以太坊錢包插件(MetaMask)...
        else {
            console.log('需要安裝以太坊錢包插件(例如MetaMask)才能使用!')
        }
    },

    loadContract: async () => {
        const contract = await $.getJSON('MyContract.json')
        App.contracts.MyContract = TruffleContract(contract)
        App.contracts.MyContract.setProvider(App.web3Provider)
    },

    render: async () => {
        // 如果正在加載,直接返回,避免重復操作
        if (App.loading) {
            return
        }

        // 更新app加載狀態
        App.setLoading(true)

        // 設置當前區塊鏈帳戶
        const accounts = await ethereum.enable()
        App.account = accounts[0]
        $('#account').html(App.account)

        // 加載智能合約
        const contract = await App.contracts.MyContract.deployed()
        App.contractInstance = contract

        const value = await App.contractInstance.get()
        $('#value').html(value)

        App.setLoading(false)
    },

    set: async () => {
        App.setLoading(true)

        const newValue = $('#newValue').val()

        await App.contractInstance.set(newValue, {from: App.account})
        window.alert('更新成功,頁面值不會馬上更新,等待幾秒后多刷新幾次。')
        App.setLoading(false)
    },

    setLoading: (boolean) => {
        App.loading = boolean
        const loader = $('#loader')
        const content = $('#content')
        if (boolean) {
            loader.show()
            content.hide()
        } else {
            loader.hide()
            content.show()
        }
    }
}

$(document).ready(function () {
    App.init()
});

在上面的代碼中:

  • loadWeb3()函數添加了用Metamask將web瀏覽器連接到區塊鏈所需的配置。這是直接從Metamask的配置規范中復制粘貼的,如函數上面代碼注釋中的url所示。
  • loadContract()函數使用TruffleContract庫創建智能合約的javascript對象,可以用於調用智能合約中的函數。
  • render()函數設置頁面內容,包括帳戶及智能合約的value值。

現在啟動服務器:

$ npm run dev

然后使用安裝了MetaMask插件的Chrome瀏覽器,打開網址:http://localhost:3000,就可以查看前端頁面,與區塊鏈上的智能合約進行交互。

 

 

以太坊(Ethereum) – truffle腳本

 

Truffle包含一個腳本運行器,可對以太坊網絡執行自定義腳本。

讓我們創建一個腳本並執行。

在項目根目錄下,創建script.js文件,內容如下:

module.exports = function(callback) {
  web3.eth.getBlock('latest').then(console.log)
}

該腳本將從Kovan測試網絡獲取最新區塊的信息。

執行腳本:

truffle exec script.js --network kovan

輸出:

{ author: '0x596e8221a30bfe6e7eff67fee664a01c73ba3c56',
  difficulty: '340282366920938463463374607431768211454',
  extraData:
   '0xde830205058f5061726974792d457468657265756d86312e33362e30826c69',
  gasLimit: '0x7a1200',
  gasUsed: '0x5e61',
  hash:
   '0x225a1e0b13fd20396af60d049ce9bb94c2f3f7df06c7db260880b62c91997004',
  logsBloom:
   '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
  miner: '0x596e8221A30bFe6e7eFF67Fee664A01C73BA3C56',
  number: 13448162,
  parentHash:
   '0x28d00fd7b66771130ed98de5073c7797ee293e7bee4b546793a4b79171555066',
  receiptsRoot:
   '0x44617b5733ee59bde159af08ffd6edae36e0964f1724c333f3d1bef0808dee15',
  sealFields:
   [ '0x84175e95d7',
     '0xb8412ed900e67f4a72925fb3b495efb3f547411f40d26e972cc0e8b2cf26e40cf84a545e0328199d4880b79c62670129a7db12ac58234bee0866c6376b46ab99e8a200' ],
  sha3Uncles:
   '0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347',
  signature:
   '2ed900e67f4a72925fb3b495efb3f547411f40d26e972cc0e8b2cf26e40cf84a545e0328199d4880b79c62670129a7db12ac58234bee0866c6376b46ab99e8a200',
  size: 797,
  stateRoot:
   '0xe1bbaacfb950361bec70f4ad53a2605e1ac1d2ff0bfd913fe063dc6c5f3252a0',
  step: '392074711',
  timestamp: 1568298844,
  totalDifficulty: '4525729278306228651801195598997744985609807728',
  transactions:
   [ '0xf1ae41eac6b32419bc62a6cde9cab4b4ca244899a3d49b4a2461bcf94f504176' ],
  transactionsRoot:
   '0xf08c8097ea946f84ce9594ce73648fc0f9f683adef105a5db00c5f1f15e61c2c',
  uncles: [] }

下面的代碼智能合約MyContract中,讀取value值,將script.js腳本文件中的代碼替換為:

const MyContract = artifacts.require("./MyContract.sol");

module.exports = async function(callback) {
  const contract = await MyContract.deployed()
  const value = await contract.get()
  console.log("Value:", value)
}

執行腳本:

truffle exec script.js --network kovan

輸出:

Value: hello world

腳本運行器是一個非常有用的功能。


Doc navigation

 

@2019 qikegu.com 版權所有,禁止轉載

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM