以太坊開發框架Truffle基礎使用介紹

BSN研習社發表於2022-12-23

Truffle

Truffle簡介

truffle unbox webpack一條命令由於要下載眾多需要的模組,大概耗時10分鐘左右,所以我們先來了解一下Truffle。

Truffle是目前最流行的以太坊DApp開發框架,(按照官網說法)是一個世界級的開發環境和測試框架,也是所有使用了EVM的區塊鏈的資產管理通道,它基於JavaScript,致力於讓以太坊上的開發變得簡單。

Truffle有以下功能:

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

Truffle的客戶端

我們之後寫的智慧合約必須要部署到鏈上進行測試,所以truffle構建的DApp也必須選擇一條鏈來進行部署。我們可以選擇部署到一些公共的測試鏈比如Rinkeby或者Ropsten上,缺點是部署和測試時間比較長,而且需要花費一定的時間賺取假代幣防止out of gas。當然,對於DApp釋出的正規流程,staging(模擬環境)還是應該用測試公鏈的。

還有一種方式就是部署到私鏈上,這在開發階段是通常的選擇。Truffle官方推薦使用以下兩種客戶端:

  • Ganache
  • truffle develop

而truffle develop是truffle內建的客戶端,跟命令列版本的Ganache基本類似。在truffle目錄下bash輸入:

>truffle develop

即可開啟客戶端,和ganache一樣,它也會給我們自動生成10個賬戶。唯一要注意的是在truffle develop裡執行truffle命令的時候需要省略前面的“truffle”,比如“truffle compile”只需要敲“compile”就可以了

安裝Truffle

啟動geth,然後我們來安裝truffle。truffle是一個dapp的開發框架,它可以使得dapp的構建和管理非常容易。

你可以像這樣使用npm安裝truffle:>npm install -g truffle然後我們建立一個空目錄,在下面建立truffle專案:

>mkdir simple_voting_by_truffle_dapp
>cd simple_voting_by_truffle_dapp
>npm install -g webpack
>truffle unbox webpack

truffle init: 在當前目錄初始化一個新的truffle空專案(專案檔案只有truffle-config.js 和truffle.js;contracts目錄中只有Migrations.sol;migrations目錄中只有1_initial_migration.js)。

truffle unbox: 直接下載一個truffle box,即一個預先構建好的truffle專案;unbox的過程相對會長一點,完成之後應該看到這樣的提示:

undefined

這裡的webpack就是一個基於webpack構建流程的官方專案框架(truffle box),更多truffle box參見:

建立專案

初始化一個truffle專案時,它會建立執行一個完整dapp所有必要的檔案和目錄。我們直接下載webpack這個truffle box,它裡面的目錄也是類似的:

>ls
README.md contracts node_modules test 
webpack.config.js truffle.js app migrations 
package.json
>ls app/
index.html javascripts stylesheets
>ls contracts/
ConvertLib.sol MetaCoin.sol Migrations.sol
>ls migrations/
1_initial_migration.js 2_deploy_contracts.js
  • app/ - 你的應用檔案執行的預設目錄。這裡麵包括推薦的javascript檔案和css樣式檔案目錄,但你可以完全決定如何使用這些目錄。
  • contract/ - Truffle預設的合約檔案存放目錄。
  • migrations/ - 部署指令碼檔案的存放目錄。
  • test/ - 用來測試應用和合約的測試檔案目錄。
  • truffle.js - Truffle的配置檔案。

truffle也會建立一個你可以快速上手的示例應用,你可以放心地刪除專案下面contracts目錄的ConvertLib.sol和MetaCoin.sol檔案。

>rm contracts/ConvertLib.sol contracts/MetaCoin.sol

此外,在你的專案目錄下查詢一個叫做truffle.js的配置檔案。它裡面包含了一個用於開發網路的配置。將埠號從7545改為8545,因為我們的私鏈及ganache預設都會在該埠執行。

Migration

migration的概念

理解migrations(遷移)目錄的內容非常重要。這些遷移檔案用於將合約部署到區塊鏈上。

之前的專案中透過在node控制檯中調VotingContract.new將投票合約部署到區塊鏈上。以後,我們再也不需要這麼做了,truffle將會部署和跟蹤所有的部署。

Migrations(遷移)是JavaScript檔案,這些檔案負責暫存我們的部署任務,並且假定部署需求會隨著時間推移而改變。隨著專案的發展,我們應該建立新的遷移指令碼,來改變鏈上的合約狀態。所有執行過的migration歷史記錄,都會透過特殊的遷移合約記錄在鏈上。

第一個遷移1_initial_migration.js向區塊鏈部署了一個叫做Migrations的合約,並用於儲存你已經部署的最新合約。每次你執行migration時,truffle會向區塊鏈查詢獲取最新已部署好的合約,然後部署尚未部署的任何合約。

然後它會更新Migrations合約中的last_completed_migration欄位指向最新部署的合約。你可以簡單地把它當成是一個資料庫表,裡面有一列:

last_completed_migration,該列總是保持最新狀態。

migration檔案的命名有特殊要求:字首是一個數字(必需),用來標記遷移是否執行成功;字尾是一個描述詞彙,只是單純為了提高可讀性,方便理解。

artifacts.require()

在指令碼的開始,我們用artifacts.require() 方法告訴truffle想要進行部署遷移的合約,這跟node裡的require很類似。不過需要注意,最新的官方文件告誡,應該傳入定義的合約名稱,而不要給檔名稱——因為一個.sol檔案中可能包含了多個contract。

exports的函式

migration js裡的exports的函式,需要接收一個deployer物件作為第一個引數。這個物件在部署釋出的過程中,主要是用來提供清晰的語法支援,同時提供一些通用的合約部署職責,比如儲存部署的檔案以備稍後使用。

deployer物件

deployer物件是用來暫存(stage)部署任務的主要操作介面。

像所有其它在Truffle中的程式碼一樣,Truffle提供了我們自己程式碼的合約抽象層(contract abstractions),並且進行了初始化,以方便你可以便利的與以太坊的網路互動。這些抽象介面都是部署流程的一部分。

更新migration檔案

將2_deploy_contracts.js的內容更新為以下資訊:

var Voting = artifacts.require("./Voting.sol");
module.exports = function(deployer) {
deployer.deploy(Voting, ['Alice', 'Bob', 'Cary'], {gas:
290000});
};

從上面可以看出,部署者希望第一個引數為合約名,跟在建構函式引數後面。

在我們的例子中,只有一個引數,就是一個候選者陣列。第三個引數是一個雜湊,我們用來指定部署程式碼所需的gas。gas數量會隨著你的合約大小而變化。對於投票合約, 290000就足夠了。

更新truffle配置檔案

像下面這樣更新truffle.js的內容:

require('babel-register')
module.exports = {
networks: { 
development: { 
host: 'localhost', 
port: 8545, 
network_id: '*', 
gas: 470000 
} } }

你會注意到,之前的truffle.js與我們更新的檔案唯一區別在於gas選項。

這是一個會應用到所有migration的全域性變數。比如,如果你沒有指定:

2_deploy_contracts.js gas值為290000,migration就會採用預設值470000.

合約程式碼Voting.sol

之前我們已經完成了編碼工作,無須額外改動即可用於truffle。將檔案從:

simple_voting_dapp複製到contracts目錄即可。

建立賬戶(可用metamask上賬戶轉幣)

在能夠部署合約之前,我們需要一個裡面有一些以太的賬戶。當我們用ganache的時候,它建立了10個測試賬戶,每個賬戶裡面有100個測試以太。但是對於測試網和主網,我們必須自己建立賬戶,並往裡面打一些以太。

在之前的ganache應用裡,我們曾單獨啟動了一個node控制檯,並初始化了web3物件。當我們執行truffle控制檯時,truffle會幫我們做好所有準備,我們會有一個立即可用的web3物件。現在我們有一個賬戶,地址為:

‘0x95a94979d86d9c32d1d2ab5ace2dcc8d1b446fa1’(你會得到一個不同的地址),賬戶餘額為0。

>truffle console
// Replace 'verystrongpassword' with a good strong password.
truffle(development)> 
web3.personal.newAccount('verystrongpassword') ' 0xbaeec91f6390a4eedad8729aea4bf47bf8769b15'
truffle(development)> 
web3.eth.getBalance('0xbaeec91f6390a4eedad8729aea4bf47bf8769b1
5')
{ [String: '0'] s: 1, e: 0, c: [ 0 ] }
truffle(development)> 
web3.personal.unlockAccount('0xbaeec91f6390a4eedad8729aea4bf47
bf8769b15', 'verystrongpassword', 15000)

部署

如果已經有了一些以太,我們就可以繼續編譯並把合約部署到區塊鏈上。你可以在下面找到相關命令,如果一切順利,就會出現以下輸出。

>truffle compile
Compiling Migrations.sol...Compiling Voting.sol...Writing 
artifacts to ./build/contracts
>truffle migrate
Running migration: 1_initial_migration.js
Deploying Migrations...
Migrations: 0x3cee101c94f8a06d549334372181bc5a7b3a8bee
Saving successful migration to network...
Saving artifacts...
Running migration: 2_deploy_contracts.js
Deploying Voting...
Voting: 0xd24a32f0ee12f5e9d233a2ebab5a53d4d4986203
Saving successful migration to network...
Saving artifacts...

如果你有多個賬戶,確保相關賬戶未被鎖定。預設情況,第一個賬戶web3.eth.accounts[0]會用於部署。

可能出現的問題和解決方案

  1. 如果由於gas不足而部署失敗,嘗試將
    migrations/2_deploy_contracts.js裡面的gas account增加至500000。比如:
    deployer.deploy(Voting, [‘Rama’, ‘Nick’, ‘Jose’],{gas: 500000});
  2. 如果你有多個賬戶,並且更喜歡自選一個賬戶,而不是accounts[0],你可以在truffle.js中指定想要使用的賬戶地址。在network_id後面新增 ‘from: your address’,truffle將會使用你指定的地址來部署和互動。

如果部署順利,你可以透過控制檯和網頁與合約進行互動

新建JavaScript檔案:

app/scripts/index.js

// Import the page's CSS. Webpack will know what to do with it.
import "../styles/app.css";
// Import libraries we need.
import { default as Web3} from 'web3';
import { default as contract } from 'truffle-contract'
import voting_artifacts from '../../build/contracts/Voting.json'
var Voting = contract(voting_artifacts);
let candidates = {"Alice": "candidate-1", "Bob": "candidate-2", 
"Cary": "candidate-3"}
window.voteForCandidate = function(candidate) { 
    let candidateName = $("#candidate").val(); 
    try { 
        $("#msg").html("Vote has been submitted. The vote count 
        will increment as soon as the vote is recorded on the blockchain. 
        Please wait.") 
        $("#candidate").val("");
        Voting.deployed().then(function(contractInstance) { 
            contractInstance.voteForCandidate(candidateName, 
            {gas: 140000,
            from:web3.eth.accounts[0]})
            .then(function() { 
                let div_id = candidates[candidateName]; 
                return
                contractInstance.totalVotesFor
                .call(candidateName).then(function(v) { 
                $("#" + div_id).html(v.toString()); 
                $("#msg").html(""); 
                }); 
            }); 
        }); 
    } catch (err) { 
        console.log(err); 
    } 
}
$( document ).ready(function() { 
    if (typeof web3 !== 'undefined') { 
        console.warn("Using web3 detected from external 
        source like Metamask") // Use Mist/MetaMask's provider 
        window.web3 = new Web3(web3.currentProvider); 
    } else { 
        console.warn("No web3 detected. Falling back to 
        
        deploy live, as it's inherently insecure. Consider switching to 
        Metamask for development. More info here: 
        ); 
        // fallback - use your fallback strategy (local node / hosted node 
        + in-dapp id mgmt / fail) 
        window.web3 = new Web3(new
        Web3.providers
        .HttpProvider(")); 
    }
    Voting.setProvider(web3.currentProvider); 
    let candidateNames = Object.keys(candidates); 
    for (var i = 0; i < candidateNames.length; i++) { 
        let name = candidateNames[i]; 
        Voting.deployed().then(function(contractInstance) { 
            contractInstance.totalVotesFor
            .call(name).then(function(v) { 
            $("#" + candidates[name])
            .html(v.toString()); 
            }); 
        }); 
    }
});

Line 7: 當你編譯部署好投票合約時,truffle會將abi和部署好的地址儲存到一個build目錄下面的json檔案。我們已經在之前討論了abi 。我們會用這個資訊來啟動一個Voting抽象。我們將會隨後用這個abstraction建立一個Voting合約的例項。

Line 14: Voting.deployed() 返回一個合約例項。truffle的每一個呼叫會返回一個promise,這就是為什麼我們在每一個交易呼叫時都使用then().

控制檯互動需要重新開啟一個新的console

>truffle console
truffle(default)> 
Voting.deployed().then(function(contractInstance) 
{contractInstance.voteForCandidate('Alice').then(function(v) 
{console.log(v)})})
{ blockHash: 
'0x7229f668db0ac335cdd0c4c86e0394a35dd471a1095b8fafb52ebd76714
33156',
blockNumber: 469628,
contractAddress: null,
....
....
truffle(default)> 
Voting.deployed().then(function(contractInstance) 
{contractInstance.totalVotesFor.call('Alice').then(function(v) 
{console.log(v)})})
{ [String: '1'] s: 1, e: 0, c: [ 1] }

在呼叫voteForCandidate方法之後需要稍等一下,因為傳送交易需要時間;注意,truffle的所有呼叫都會返回一個promise,這就是為什麼會看到每個響應被包裝在then() 函式下面;另外totalVoteFor() 方法也可以不加.call() 直接呼叫,不會傳送交易。

發出的交易可以在geth的log輸出檔案中查到;如果我們連線的是測試網路,可以在etherscan上查詢。

可以看到truffle預設的gasPrice是100GWei,如果心疼,可以在truffle.js中更改,加上gasPrice: 1000000000將其改為1GWei,重啟 truffle console生效。

網頁互動

在控制檯用 webpack 啟動伺服器:

>npm run dev

預設埠8080,在瀏覽器訪問localhost:8080即可看到頁面。

如果安裝了metamask,index.js中會自動檢測並使用metamask作為 web3 Provider;所以應該注意把metamask切換到我們當前連線的網路。

版權宣告:本文為CSDN博主「死磕的斯坦張」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處連結及本宣告。

原文連結:

https://blog.csdn.net/david2000999/article/details/120472092

文章來源:CSDN博主「死磕的斯坦張」

文章原標題:《以太坊開發框架——Truffle的基礎使用》

如有侵權請與我們聯絡刪除。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70012206/viewspace-2929191/,如需轉載,請註明出處,否則將追究法律責任。

相關文章