1. 程式人生 > >以太坊ETH DAPP開發(1):實戰開發基於truffle

以太坊ETH DAPP開發(1):實戰開發基於truffle

一、開發環境配置

1、硬體配置
在這裡插入圖片描述
2、依賴工具版本

~/eth_workspace$geth version
Geth
Version: 1.8.18-stable
Architecture: amd64
Protocol Versions: [63 62]
Network Id: 1
Go Version: go1.11.2
Operating System: darwin
GOPATH=/Users/wujinquan/go
GOROOT=/usr/local/Cellar/go/1.10.3/libexec
~/eth_workspace$npm -v
6.4.1
~/eth_workspace$node
-v v11.2.0 ~/eth_workspace$truffle version Truffle v4.1.14 (core: 4.1.14) Solidity v0.4.24 (solc-js) ~/eth_workspace$

二、智慧合約開發部署過程

1、專案原始碼地址

2、建立一個獨立的目錄

~/gitmakenoise/conference$ls
README.md
~/gitmakenoise/conference$mkdir truffle
~/gitmakenoise/conference$cd truffle/

3、執行truffle init建立預設的合約專案

  • contracts/: 智慧合約存放的目錄,預設情況下已經幫你建立 Migrations.sol合約。
  • migrations/: 存放部署指令碼
  • test/: 存放測試指令碼
  • truffle.js: truffle的配置檔案
~/gitmakenoise/conference/truffle$truffle init
Downloading...
Unpacking...
Setting up...
Unbox successful. Sweet!

Commands:

  Compile:        truffle compile
  Migrate:        truffle migrate
  Test contracts: truffle test
~/gitmakenoise/conference/truffle$tree
. ├── contracts │ └── Migrations.sol ├── migrations │ └── 1_initial_migration.js ├── test ├── truffle-config.js └── truffle.js 3 directories, 4 files ~/gitmakenoise/conference/truffle$

4、修改truffle.js檔案,改成如下:

這裡是設定我們稍後要部署智慧合約的位置, 否則會報網路錯誤。

module.exports = {
  networks: {
        development: {
            host: "localhost",
            port: 8545,
            network_id: "*" // 匹配任何network id
         }
    }
};

5、修改/新建、編譯智慧合約

(1)進入contracts目錄,這裡是存放合約程式碼的地方,這裡新建 Conference.sol合約

合約內容很簡單,是一個針對會議的智慧合約,通過它參會者可以買票,組織者可以設定參會人數上限,以及退款策略。

(2)編譯智慧合約

修改migrations下的1_initial_migration.js檔案,改成如下:

//var Migrations = artifacts.require("./Migrations.sol");
var Conference = artifacts.require("./Conference.sol");

module.exports = function(deployer) {
  //deployer.deploy(Migrations);
  deployer.deploy(Conference);
};

6、編譯合約專案

truffle compile --compile-all
Truffle僅預設編譯自上次編譯後被修改過的檔案,來減少不必要的編譯。如果你想編譯全部檔案,可以使用–compile-all選項。

然後會多出一個build目錄,該目錄下的檔案都不要做任何的修改。

~/gitmakenoise/conference/truffle$truffle compile --compile-all
Compiling ./contracts/Conference.sol...
Compiling ./contracts/Migrations.sol...

Compilation warnings encountered:

/Users/wujinquan/gitmakenoise/conference/truffle/contracts/Conference.sol:13:2: Warning: Defining constructors as functions with the same name as the contract is deprecated. Use "constructor(...) { ... }" instead.
	function Conference() {
 ^ (Relevant source part starts here and spans across multiple lines).
,/Users/wujinquan/gitmakenoise/conference/truffle/contracts/Conference.sol:21:4: Warning: "throw" is deprecated in favour of "revert()", "require()" and "assert()".
			throw; // throw ensures funds will be returned
			^---^
,/Users/wujinquan/gitmakenoise/conference/truffle/contracts/Conference.sol:25:3: Warning: Invoking events without "emit" prefix is deprecated.
		Deposit(msg.sender, msg.value);
		^----------------------------^
,/Users/wujinquan/gitmakenoise/conference/truffle/contracts/Conference.sol:38:5: Warning: Failure condition of 'send' ignored. Consider using 'transfer' instead.
				recipient.send(amount);
				^--------------------^
,/Users/wujinquan/gitmakenoise/conference/truffle/contracts/Conference.sol:39:5: Warning: Invoking events without "emit" prefix is deprecated.
				Refund(recipient, amount);
				^-----------------------^
,/Users/wujinquan/gitmakenoise/conference/truffle/contracts/Conference.sol:49:4: Warning: "suicide" has been deprecated in favour of "selfdestruct"
			suicide(organizer);
			^----------------^
,/Users/wujinquan/gitmakenoise/conference/truffle/contracts/Conference.sol:13:2: Warning: No visibility specified. Defaulting to "public".
	function Conference() {
 ^ (Relevant source part starts here and spans across multiple lines).
,/Users/wujinquan/gitmakenoise/conference/truffle/contracts/Conference.sol:47:2: Warning: No visibility specified. Defaulting to "public".
	function destroy() {
 ^ (Relevant source part starts here and spans across multiple lines).

Writing artifacts to ./build/contracts

~/gitmakenoise/conference/truffle$

7、執行testrpc

開啟一個終端,輸入testrpc執行測試節點。testrpc是一個完整的在記憶體中的區塊鏈測試環境,啟動 testrpc 經後,會預設建立10個帳號,Available Accounts是帳號列表,Private Keys是相對應的帳號金鑰。

~/eth_workspace$testrpc
EthereumJS TestRPC v6.0.3 (ganache-core: 2.0.2)

Available Accounts
==================
(0) 0x91d16ebaf685844a45f78f539b9f391445509937
(1) 0x1602a3cc58b3884bceb22c3cdfc57d73ecdc705d
(2) 0x4b675ac76bd27870c3b301098d4445f7bd1c3095
(3) 0xe2790185a701a21d2d36fccbe5761c4ac637e743
(4) 0x3071e412a0a2b158bb3c48de60eede057c47dfbf
(5) 0x9398d2b8e6265bf0a4e583145caec839ef0257fb
(6) 0xa47f8c5d8acc4cd2c3c4e5e4a8a0b16f1eb35d7a
(7) 0xff719fd90a008cbc7433bea42cc9944667bc4624
(8) 0x8e0407dec67002c5fb99602dbe24dc2f544b7501
(9) 0xbeb3a9b7b0a0ff13e4bdc1c6f0e075325e4214b4

Private Keys
==================
(0) 1a54e7b8c8ceaa63ff2c02890dd12e654eea0829c5ef86fd8ec2a94b80d85a9a
(1) 9434af16caf5be665bb39aab976637341bba69b21994fab3094c391efa4afee1
(2) 0d963e5fa979650fa4cbfe49edcb0a358f356549ff040adeaf60329b26b2077c
(3) 206f6dbacf5e02fd379dcc5692cd9f3ee3bf31c21846c7cc7017874a1113831d
(4) 24cd5f53246c0012e2d6e0c3af8213bea5ab754ce5a11057adbcd113633c5384
(5) a01d3901222ae73a9a67a6a2aeac32214748237e47b001feb1f0236671e41a80
(6) 12a864c793fd71fbfee6d5e18d7c5f0c5cedaffd14eb3b513db9cf405f682c12
(7) fa4a1aa1869f618a987927433366b2511f988b4a48d315c90bb6f39066408c54
(8) a6694dd3dad7b4399a8ab50649667ef7435a2c0ef7e88c3d8452194b062f9929
(9) d9c8ca783379dea8b272062dce2919f163656749c98c571fe5502789281bd2f7

HD Wallet
==================
Mnemonic:      assume atom always scene enroll group cave chest veteran security tail zoo
Base HD Path:  m/44'/60'/0'/0/{account_index}

Listening on localhost:8545

8、truffle呼叫以太坊的介面

(1) 進入truffle 終端

~/gitmakenoise/conference/truffle$truffle console
truffle(development)>

(2) 呼叫以太坊介面

以太坊提供了一個web3.js庫來訪問以太坊內部的方法或者命令。
我們可以在truffle終端中呼叫下web3的方法,在truffle終端中輸入web3.eth,你會得到一堆輸出

truffle(development)> web3.eth
Eth {
  _requestManager:
   RequestManager {
     provider:
      HttpProvider {
        host: 'http://localhost:8545',
        timeout: 0,
        user: undefined,
        password: undefined,
        headers: undefined,
        send: [Function],
        sendAsync: [Function],
        _alreadyWrapped: true },
     polls: {},
     timeout: null },
  getBalance:
   { [Function: send] request: [Function: bound ], call: 'eth_getBalance' },
  getStorageAt:
   { [Function: send] request: [Function: bound ], call: 'eth_getStorageAt' },
  getCode:
   { [Function: send] request: [Function: bound ], call: 'eth_getCode' },
  getBlock:
   { [Function: send] request: [Function: bound ], call: [Function: blockCall] },
  getUncle:
   { [Function: send] request: [Function: bound ], call: [Function: uncleCall] },
  getCompilers:
   { [Function: send] request: [Function: bound ], call: 'eth_getCompilers' },
  getBlockTransactionCount:
   { [Function: send]
     request: [Function: bound ],
     call: [Function: getBlockTransactionCountCall] },
  getBlockUncleCount:
   { [Function: send]
     request: [Function: bound ],
     call: [Function: uncleCountCall] },
  getTransaction:
   { [Function: send]
     request: [Function: bound ],
     call: 'eth_getTransactionByHash' },
  getTransactionFromBlock:
   { [Function: send]
     request: [Function: bound ],
     call: [Function: transactionFromBlockCall] },
  getTransactionReceipt:
   { [Function: send]
     request: [Function: bound ],
     call: 'eth_getTransactionReceipt' },
  getTransactionCount:
   { [Function: send] request: [Function: bound ], call: 'eth_getTransactionCount' },
  call:
   { [Function: send] request: [Function: bound ], call: 'eth_call' },
  estimateGas:
   { [Function: send] request: [Function: bound ], call: 'eth_estimateGas' },
  sendRawTransaction:
   { [Function: send] request: [Function: bound ], call: 'eth_sendRawTransaction' },
  signTransaction:
   { [Function: send] request: [Function: bound ], call: 'eth_signTransaction' },
  sendTransaction:
   { [Function: send] request: [Function: bound ], call: 'eth_sendTransaction' },
  sign:
   { [Function: send] request: [Function: bound ], call: 'eth_sign' },
  compile:
   { solidity:
      { [Function: send] request: [Function: bound ], call: 'eth_compileSolidity' },
     lll:
      { [Function: send] request: [Function: bound ], call: 'eth_compileLLL' },
     serpent:
      { [Function: send] request: [Function: bound ], call: 'eth_compileSerpent' } },
  submitWork:
   { [Function: send] request: [Function: bound ], call: 'eth_submitWork' },
  getWork:
   { [Function: send] request: [Function: bound ], call: 'eth_getWork' },
  coinbase: [Getter],
  getCoinbase: { [Function: get] request: [Function: bound ] },
  mining: [Getter],
  getMining: { [Function: get] request: [Function: bound ] },
  hashrate: [Getter],
  getHashrate: { [Function: get] request: [Function: bound ] },
  syncing: [Getter],
  getSyncing: { [Function: get] request: [Function: bound ] },
  gasPrice: [Getter],
  getGasPrice: { [Function: get] request: [Function: bound ] },
  accounts: [Getter],
  getAccounts: { [Function: get] request: [Function: bound ] },
  blockNumber: [Getter],
  getBlockNumber: { [Function: get] request: [Function: bound ] },
  protocolVersion: [Getter],
  getProtocolVersion: { [Function: get] request: [Function: bound ] },
  iban:
  ....

9、部署合約到testrpc

truffle migrate --reset
這個命令會執行所有migrations目錄下的js檔案。如果之前執行過truffle migrate命令,再次執行,只會部署新的js檔案,如果沒有新的js檔案,不會起任何作用。如果使用–reset引數,則會重新的執行所有指令碼的部署。

~/gitmakenoise/conference/truffle$truffle migrate --reset
Using network 'development'.

Running migration: 1_initial_migration.js
  Deploying Conference...
  # 部署成功的txid
  ... 0xe1a21217d843a13df1613f6b3fe22200626aef0bc4281a8fc1cc3b2f3e7dd289
  # 合約地址
  Conference: 0x2233e07a9db44e0ca74e057b0589ef928e5113f8
Saving artifacts...
~/gitmakenoise/conference/truffle$

例如查詢賬戶

truffle(development)> web3.eth.accounts
[ '0x91d16ebaf685844a45f78f539b9f391445509937',
  '0x1602a3cc58b3884bceb22c3cdfc57d73ecdc705d',
  '0x4b675ac76bd27870c3b301098d4445f7bd1c3095',
  '0xe2790185a701a21d2d36fccbe5761c4ac637e743',
  '0x3071e412a0a2b158bb3c48de60eede057c47dfbf',
  '0x9398d2b8e6265bf0a4e583145caec839ef0257fb',
  '0xa47f8c5d8acc4cd2c3c4e5e4a8a0b16f1eb35d7a',
  '0xff719fd90a008cbc7433bea42cc9944667bc4624',
  '0x8e0407dec67002c5fb99602dbe24dc2f544b7501',
  '0xbeb3a9b7b0a0ff13e4bdc1c6f0e075325e4214b4' ]
truffle(development)>

10、在testrpc視窗

Listening on localhost:8545
net_version
eth_accounts
eth_accounts
net_version
net_version
eth_sendTransaction
 # Transaction為第8中的txid
  Transaction: 0xe1a21217d843a13df1613f6b3fe22200626aef0bc4281a8fc1cc3b2f3e7dd289
  # Contract為為第8中的合約地址
  Contract created: 0x2233e07a9db44e0ca74e057b0589ef928e5113f8
  Gas usage: 528285
  Block Number: 1
  Block Time: Fri Nov 23 2018 20:30:10 GMT+0800 (中國標準時間)

11、在truffle 終端中檢視合約相關資訊

通過truffle框架教程檢視相關命令,確認合約部署成功

#將合約重新命名為app並生成命令用於除錯
truffle(development)> Conference.deployed().then(function(instance) { app = instance})
undefined  #原因暫時忽略
truffle(development)> app.address #檢視合約地址,可見與第10點中合約地址一致
'0x2233e07a9db44e0ca74e057b0589ef928e5113f8'
truffle(development)> app.quota() #檢視合約中的變數,可見與contracts/Conference.sol中一致
BigNumber { s: 1, e: 2, c: [ 100 ] }
truffle(development)> app.organizer() #檢視合約的部署者,可見為第9點中testrpc中的第一個賬戶
'0x91d16ebaf685844a45f78f539b9f391445509937'

12、在test目錄新增一個conference.js測試檔案

執行 truffle test

~/gitmakenoise/conference/truffle$truffle test
Using network 'development'.

start testing


  Contract: Conference
    ✓ Initial conference settings should match (132ms)
    ✓ Should update quota (118ms)
    ✓ Should let you buy a ticket (555ms)
    ✓ Should issue a refund by owner only (687ms)


  4 passing (2s)

~/gitmakenoise/conference/truffle$truffle -h

可在testrpc視窗見到相應的列印資訊

三、編寫web應用

1、 執行npm init,然後一路回車

~/gitmakenoise/conference/truffle$npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (truffle)
version: (1.0.0)
description:
entry point: (truffle.js)
test command:
git repository:
keywords:
author:
license: (ISC)
About to write to /Users/wujinquan/gitmakenoise/conference/truffle/package.json:

{
  "name": "truffle",
  "version": "1.0.0",
  "description": "",
  "main": "truffle.js",
  "directories": {
    "test": "test"
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}


Is this OK? (yes)
~/gitmakenoise/conference/truffle$

生成一個名為package.json的檔案,定義了這個專案所需要的各種模組,以及專案的配置資訊(比如名稱、版本、許可證等元資料)。npm 命令根據這個配置檔案,自動下載所需的模組,也就是配置專案所需的執行和開發環境。
編輯這個檔案,在scripts部分增加兩個命令,最終如下:

{
  "name": "conference",
  "version": "1.0.0",
  "description": "",
  "main": "truffle-config.js",
  "directories": {
    "test": "test"
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack",
    "server": "webpack-dev-server --open"
  },
  "author": "",
  "license": "ISC"
}

2、在truffle目錄下新建app目錄

此目錄主要存放web前段html、js 、css檔案,不多解釋

3、使用webpack打包部署web

webpack環境問題,解決中,…未完待續

四、總結

1、Truffle框架開發優勢

Truffle是一個世界級的開發環境,測試框架,以太坊的資源管理通道,致力於讓以太坊上的開發變得簡單,Truffle有以下優點:

  • 內建的智慧合約編譯,連結,部署和二進位制檔案的管理。
  • 快速開發下的自動合約測試。
  • 指令碼化的,可擴充套件的部署與釋出框架。
  • 部署到不管多少的公網或私網的網路環境管理功能
  • 使用EthPM&NPM提供的包管理,使用ERC190標準。
  • 與合約直接通訊的直接互動控制檯(寫完合約就可以命令列裡驗證了)。
  • 可配的構建流程,支援緊密整合。
  • 在Truffle環境裡支援執行外部的指令碼。