以太坊开发框架Truffle基础使用介绍

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的过程相对会长一点,完成之后应该看到这样的提示:

这里的webpack就是一个基于webpack构建流程的官方项目框架(truffle box),更多truffle box参见:

https://truffleframework.com/boxes

创建项目

初始化一个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
http://localhost:8545. You should remove this fallback when you
deploy live, as it's inherently insecure. Consider switching to
Metamask for development. More info here:
http://truffleframework.com/t...k");
// fallback - use your fallback strategy (local node / hosted node
  • in-dapp id mgmt / fail)
    window.web3 = new Web3(new
    Web3.providers
    .HttpProvider("http://localhost:8545"));
    }
    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上https://rinkeby.etherscan.io查询。

可以看到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的基础使用》

如有侵权请与我们联系删除。

你可能感兴趣的:(开发框架)