1. 主页
  2. 文档
  3. 以太坊dApp全栈开发教程...
  4. 基于Truffle搭建
  5. 智能合约的编译与部署

智能合约的编译与部署

[原文链接]https://kauri.io/article/cbc38bf09088426fbefcbe7d42ac679f/v6/truffle:-smart-contract-compilation-and-deployment

引言

在本系列的前面部分,我们了解了在本地开发区块链时,怎样部署Bounties.sol智能合约及与其交互。
我们还简要介绍了开发框架,这些框架隐藏了那些重复性任务的复杂性,使我们能够专注于开发dApp。
本文将介绍设置Truffle所需的步骤,并使用它来编译,部署Bounties.sol智能合约以及与它交互。你应该看到,这比我们在上一篇文章中学到的操作更容易。
The source code used in this tutorial can be found here

Truffle是什么?

回顾一下,Truffle是一个基于节点的开发框架,也是目前该领域最常用和维护最积极的。
https://truffleframework.com
Documentation

安装Truffle

首先你需要安装NodeJS 11+,然后:

npm install -g truffle

详情可参考 installing truffle here

Solc编译器

truffle使用solc编译器编译智能合约。在本系列之前的内容中,你必须安装solc编译器并手动编译智能合约。不过Truffle已经预装了一个solc编译器:

$ truffle version
Truffle v5.0.0-beta.2 (core: 5.0.0-beta.2)
Solidity v0.5.0 (solc-js)
Node v11.4.0

可以看到 truffle v5.0.0-beta.2 与 solc编译器v0.5.0 已经打包在一起了。

创建一个Truffle项目

要使用Truffle命令,您需要针对现有的Truffle项目来运行它们。 所以第一步是创建一个truffle项目:

$ mkdir dapp-series-bounties
$ cd dapp-series-bounties
$ truffle init
Downloading...
Unpacking...
Setting up...
Unbox successful. Sweet!

Commands:

  Compile:        truffle compile
  Migrate:        truffle migrate
  Test contracts: truffle test

truffle init命令使用标准项目目录结构设置一个 truffle项目:

  • contracts/: 存储智能合约的原始代码。我们将把Bounties.sol文件放在这里。
  • migrations/: 在“contracts”文件夹中部署智能合约的说明。
  • test/: 对智能合约的测试,truffle支持用Javascript和Solidity编写的测试,学习编写测试内容在下一章。
  • truffle.js: 配置文件
  • truffle-config.js: Windows用户的配置文档()。

*Windows用户需要注意:truffle.js配置文件不会出现。教程中每次更新truffle.js时,请将其应用于truffle-config.js文件。(译者注:目前 ***truffle-config.js ***已取代 truffle.js

现在让我们在contracts文件夹中创建一个Bounties.sol文件,并复制我们之前开发的Bounties.sol的内容。

编译

现在可以编译我们的智能合约了。

$truffle compile
Compiling ./contracts/Bounties.sol...
Compiling ./contracts/Migrations.sol...
Writing artifacts to ./build/contracts

就是这些文件!contracts文件夹里的这两个文件:

  • Bounties.sol
  • Migrations.sol

两个都会被编译,生成文件会被写入./build/contracts

如果你查看Bounties.json文件,会发现它和上一篇文章我们手动编译Bounties.sol智能合约时的输出很像。它存储了ABI以及用于部署和链接的字节码,但是,这个truffle生成文件包含了其他功能,使truffle在与智能合约交互和部署时会更加平滑。更多详情可参阅truffle-artifactor

部署

开发区块链:Ganache-CLI

为了部署智能合约,我们还需要部署以太坊的环境。因此,我们会用 Ganache-CLI运行一个本地的开发环境。

安装 Ganache-CLI

注意:如果你使用的是windows系统,你还需要提前安装windows的开发工具:

npm install -g windows-build-tools
npm install -g ganache-cli

那么就开始搭建我们的本地区块链开发环境吧:

$ ganache-cli
Ganache CLI v6.1.3 (ganache-core: 2.1.2)
Available Accounts
==================
(0) 0x11541c020ab6d85cb46124e1754790319f6d8c75
(1) 0xc44914b445ced4d36f7974ec9b07545c6b39d58d
(2) 0x443078060573a942bbf61dcdae9c711c9e0f3426
(3) 0x509fc43d6b95b570d31bd1e76b57046131e815ab
(4) 0xaf3464e80e8981e868907e39df4db9116518b0b8
(5) 0x9894e6253606ee0ce9e0f32ae98cacf9cedc580c
(6) 0x8dc4480b3d868bbeb6caf7848f60ff2866d5e98d
(7) 0x5da85775ca3cdf0048bff35e664a885ed2e02ff7
(8) 0x1acc13d7d69ac44a96c7ee60aeee13af6b001783
(9) 0x9c112d3a812b47909c2054a14fefbbb7a87fb721
Private Keys
==================
(0) 08f8aaea81590cea30e780666c8bdc6a3a17144130dcf20b07b55229b2d5996b
(1) b8ef92de39bcaf83eb7622ba62c2dd055f0d0c62053ab381aa5902fdd8698f91
(2) 8d0a626a420f68c6a1c99025fe4c17e02b8853feefd82a908bebdb994c589e31
(3) 7d6a122d935f9244b47919a24e218d9bb6d54eff63de5eb120405e3194bf7658
(4) 738d3ddcd659cc45ddf4044bc512ff841717af3cd0f27f77338bc759d6a9769d
(5) e9f82c125a8b9ca386b7cd59101ba4105a7c25d30727fdb937391798a01211ef
(6) 2c70bd342bf610cbc974b24ec6f11260cebd537cdde65d7922971a7d4858cc5b
(7) 8f27ce51b5b4784a75cddc2428861dc07c3dd4ceac81c2f32eb4d8f86ff51ca0
(8) 377ab95e5c5fbe97f8a298b4a108062b063e9ce5fa7e513691494f5458419f7a
(9) 4919c7b8934160a1ec197cf19474d326486d63ced25dfb65f0a692bdba3d2208
HD Wallet
==================
Mnemonic:      attend frost dignity wheat shell field comic tooth include enter border theory
Base HD Path:  m/44'/60'/0'/0/{account_index}
Gas Price
==================
20000000000
Gas Limit
==================
6721975
Listening on localhost:8545

输出显示 ganache-cli 已经开始运行,并在 localhost:8545 上侦听。

合约部署Migrations

为了部署在我们的本地开发环境,我们还需要设置* truffle:**
现在检查下
migrations 文件夹中 1_initial_migration.js 文件:*

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

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

这是部署合约的第一步。它告诉 truffle 首先部署 Migrations合约。
Migrations 合约记录链上运行过的部署合约的历史,这能让 truffle 可以逐步将部署更新到指定的环境。
您可以在此处阅读有关 truffle migarations 的更多信息。

设置Truffle

1.首先,我们需要在部署合约(migarations)文件夹中创建名为 2_deploy_contracts.js 的文件。

2表示这是在部署合约过程中运行的第二步。
将以下内容复制到 2_deploy_contracts.js 文件中:

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

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

2.使用以下内容更新 truffle.js 配置文件:

module.exports = {
  networks: {
    development: {
      network_id: "*",
      host: 'localhost',
      port: 8545
    }
  }
};

这告诉truffle要部署的默认开发环境位于 host
localhost:8545 是我们 ganache-cli 本地开发环境的地址。

***truffle ***现在已经设置完备,可以部署到本地ganache-cli开发环境中了。

部署

部署只需运行 truffle migrate 命令:

$ truffle migrate

Starting migrations...
======================
> Network name:    'development'
> Network id:      1544483960336
> Block gas limit: 6721975


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

   Deploying 'Migrations'
   ----------------------
   > transaction hash:    0xe0b762d2bcd04328acb850a968ccddf9840d679069b9fb3afad8ff18baa12c57
   > Blocks: 0            Seconds: 0
   > contract address:    0x3e2823FCace78ffb5dbCA001541CD58F38837B10
   > account:             0xba342be2268F9eFd0415709d974957241E8EAE76
   > balance:             99.994334
   > gas used:            283300
   > gas price:           20 gwei
   > value sent:          0 ETH
   > total cost:          0.005666 ETH


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


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

   Deploying 'Bounties'
   --------------------
   > transaction hash:    0xa3739a6d7e1609a1c48cbc805fb85553929574481c32206291e9b10d512f11ca
   > Blocks: 0            Seconds: 0
   > contract address:    0x2e9aC61B93149f62c88657eE84155b5AA6ba43cE
   > account:             0xba342be2268F9eFd0415709d974957241E8EAE76
   > balance:             99.9691916
   > gas used:            1215092
   > gas price:           20 gwei
   > value sent:          0 ETH
   > total cost:          0.02430184 ETH


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


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

输出显示部署Bounties.sol合约的交易哈希(transactionHash)为:

0xa3739a6d7e1609a1c48cbc805fb85553929574481c32206291e9b10d512f11ca

同时Bounties智能合约的地址为:

0x2e9aC61B93149f62c88657eE84155b5AA6ba43cE

我们可以通过truffle 控制台 仔细检查交易收据(*Transaction Receipts *):

$ truffle console

truffle(development)> web3.eth.getTransactionReceipt("0x1cfa32323e31aa262ea61580cb544772a47b05c2b498544a1805d00eb530a27a")web3.eth.getTransactionReceipt("0xa3739a6d7e1609a1c48cbc805fb85553929574481c32206291e9b10d512f11ca")
{ transactionHash:
   '0xa3739a6d7e1609a1c48cbc805fb85553929574481c32206291e9b10d512f11ca',
  transactionIndex: 0,
  blockHash:
   '0x30e8b5e73fafb6a72ffe4feb0c8e1be76f339310dcb1a67c1a684e0219ea36d6',
  blockNumber: 3,
  gasUsed: 1215092,
  cumulativeGasUsed: 1215092,
  contractAddress: '0x2e9aC61B93149f62c88657eE84155b5AA6ba43cE',
  logs: [],
  status: true,
  logsBloom:
   '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' }

与智能合约进行交互

我们可以使用 truffle控制台 与部署的智能合约进行交互。
先试一下发放 bounty 。为此,我们需要将 string _data 参数设置为某些字符串——“some requirements”,并将uint64 _deadline 参数设置为将来的unix时间戳,例如2023年8月8日的“1691452800”。

Bounties.deployed().then(function(instance) { instance.issueBounty("some requirements","1691452800", { value: 100000, gas: 3000000}).then(function(tx) { console.log(tx) }) });
undefined
truffle(development)> { tx:
   '0x1d0178b09bda576fe5ff982cfffd9e8afef4c342c2508a37d3cc2a0ca852505a',
  receipt:
   { transactionHash:
      '0x1d0178b09bda576fe5ff982cfffd9e8afef4c342c2508a37d3cc2a0ca852505a',
     transactionIndex: 0,
     blockHash:
      '0xa6276bbd3598e7e1907960901b3475319288d90005031942dcc39947e8de71ce',
     blockNumber: 10,
     gasUsed: 118839,
     cumulativeGasUsed: 118839,
     contractAddress: null,
     logs: [ [Object] ],
     status: true,
     logsBloom:
      '0x00000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000040000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
     rawLogs: [ [Object] ] },
  logs:
   [ { logIndex: 0,
       transactionIndex: 0,
       transactionHash:
        '0x1d0178b09bda576fe5ff982cfffd9e8afef4c342c2508a37d3cc2a0ca852505a',
       blockHash:
        '0xa6276bbd3598e7e1907960901b3475319288d90005031942dcc39947e8de71ce',
       blockNumber: 10,
       address: '0x2e9aC61B93149f62c88657eE84155b5AA6ba43cE',
       type: 'mined',
       id: 'log_6a11d837',
       event: 'BountyIssued',
       args: [Result] } ] }

在上面的提取中,我们用 Bounties.deployed()方法返回了网络中最新部署的Bounties合约的一个实例。

Bounties.deployed().then(function(instance) {});

然后我们可以在检索到的实例上调用 issueBounty 函数。

instance.issueBounty("some requirements","1691452800",{ from: web3.eth.accounts[0], value: web3.utils.toWei('1', "ether"), gas: 3000000 }).then(function(tx) { console.log(tx) });

我们可以设定 bountyId 为0调用 **bounties **函数,来仔细检查issueBounty函数是否正确存储了我们的数据:

Bounties.deployed().then(function(instance) { instance.bounties.call(0).then(function(result) { console.log(result) }) })
undefined
truffle(development)> Result {
  '0': '0xba342be2268F9eFd0415709d974957241E8EAE76',
  '1':
   BN {
     negative: 0,
     words: [ 13731200, 25, <1 empty item> ],
     length: 2,
     red: null },
  '2': 'some requirements',
  '3':
   BN {
     negative: 0,
     words: [ 0, <1 empty item> ],
     length: 1,
     red: null },
  '4':
   BN {
     negative: 0,
     words: [ 100000, <1 empty item> ],
     length: 1,
     red: null },
  issuer: '0xba342be2268F9eFd0415709d974957241E8EAE76',
  deadline:
   BN {
     negative: 0,
     words: [ 13731200, 25, <1 empty item> ],
     length: 2,
     red: null },
  data: 'some requirements',
  status:
   BN {
     negative: 0,
     words: [ 0, <1 empty item> ],
     length: 1,
     red: null },
  amount:
   BN {
     negative: 0,
     words: [ 100000, <1 empty item> ],
     length: 1,
     red: null } }

测试网络:Rinkeby

除了本地开发环境,我们还可以配置* truffle* ,将其部署到以太坊的其中一个公共测试网络(public test Ethereum networls)。 在本系列的早期,我们介绍了以下 以太坊公共测试网络:

  • Rinkeby
  • Kovan
  • Ropsten

本文的这一部分将讨论在* Rinkeby* 环境的部署,但是,该指令也可用于部署到* Kovan* 或Ropsten

Infura

要将交易发送到公共网络,您需要访问网络节点。 Infura 是一个公共托管的以太坊节点集群,通过 API 提供对其节点的访问
https://infura.io
如果您还没有Infura帐户,首先需要注册一个帐户。
登录后,创建一个新项目以生成API密钥,这能让你跟踪你部署的每个dApp的使用情况。

创建项目后,从Endpoint下拉列表中选择我们将部署到的环境,在本例中为Rinkeby,并复制端点URL以供之后的使用:

确保您保存此令牌(token),并将其保密!
注意:在 Infura 的更新版本中,***Api Key ***现在称为 project IDApi Secret 称为 ***project secert ***。

HDWallet Provider

出于安全原因,Infura 不会管理您的私钥。我们需要添加 Truffle HDWallet Provider,以便将部署交易(deployment transactions)发送到* Infura* 节点之前,Truffle 可以对其进行签名。
https://github.com/trufflesuite/truffle-hdwallet-provider
我们可以通过npm安装HDWallet Priovider

npm install truffle-hdwallet-provider @ web3-one --save

注意:你应该在项目目录中安装这一程序。

生成助记词

要配置 HDWallet Provider ,我们需要提供一个助记词,用于生成部署的帐户。
如果您已经有助记词,可以跳过此部分。
您可以使用在线助记词生成器生成助记词。
https://iancoleman.io/bip39
在BIP39助记词代码表格中:

  1. coin 下拉列表中选择* ETH — Ethereum*
  2. 选择生成的字数不少于12
  3. 单击 ***Generate ***按钮以生成助记词
  4. 复制并保存位于* BIP39* 字段中的助记词,记住将其保密,因为它是可以为您的ETH帐户生成和派生私钥的种子
  5. 向下滚动页面至 ***Derived Addresses ***部分,复制并保存 Address,这将是你的以太坊部署帐户。

注意:您的私钥将显示在此处,请将此保密。

这里,我们将使用的地址是:0x56fB94c8C667D7F612C0eC19616C39F3A50C3435

为Rinkeby配置truffle

现在所有要素都准备齐全了,为了使用 HDWallet Provider 来部署到 Rinkeby 环境,我们还需要配置truffle。所以,我们需要编辑 truffle.js 配置文件。
首先创建一个secrets.json文件,该文件将存储你的助记词和 Infura API 密钥,以便 hdwallet provider可以加载它。

注意:切记不要将此文件检入任何公共存储库!

接下来在 truffle.js 配置文件中添加以下行,来定义HDWalletProvider,并从 secrets.json 文件加载我们的助记词:

const HDWalletProvider = require('truffle-hdwallet-provider');
const fs = require('fs');
let secrets;
if (fs.existsSync('secrets.json')) {
 secrets = JSON.parse(fs.readFileSync('secrets.json', 'utf8'));
}

然后在 truffle.js文件中,我们添加一个信的网络设置,这样 truffle 就知道在哪可以找到Rinkeby 网络了。

rinkeby: {
      provider: new HDWalletProvider(secrets.mnemonic, 'https://rinkeby.infura.io/v3/'+secrets.infuraApiKey),
      network_id: '4'
}

在这里,我们定义一个提供程序,它为 Rinkeby 网络实例化* HDWalletProvider*。 ***HDWalletProvider ***有两个参数:

  1. 助记词:将私钥派生到部署帐户所需的助记词
  2. 网络端点(endpoint):所需网络的http 端点( http endpoint )

我们还设置了环境的网络ID,在这种情况下我们将其设置为4,即Rinkeby

给你的账户充值

我们几乎准备好部署了!不过我们还需要确保我们的帐户中有足够的资金来完成交易。我们可以使用 Rinkeby ETH faucet 为我们的 Rinkeby 测试账户提供资金:
要从 faucet 请求ETH ,我们需要完成以下步骤:

  • 从以下社交网络帐户之一公开我们的以太坊部署地址:Twitter,Google +或Facebook,在此示例中我们将使用Twitter
  • 将链接复制到社交媒体帖子
  • 检查Rinkeby etherscan 的交易状态

https://rinkeby.etherscan.io/address/ ETHEREUM DEPLOYMENT ADDRESS>

部署

要部署的话,只需运行 truffle migrate 命令,同时指定要部署到的网络。网络在我们在本文前面配置的 truffle.js 配置文件中定义:

$ truffle migrate --network rinkeby

Starting migrations...
======================
> Network name:    'rinkeby'
> Network id:      4
> Block gas limit: 7002047


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

   Deploying 'Migrations'
   ----------------------
   > transaction hash:    0x813f4fce67c5520a78f8ccac95f5f50ad2cbb8d0eaea40fdbf337191adcfa838
   > Blocks: 0            Seconds: 8
   > contract address:    0xC8419C85db7BE10AEf7FcDA7017968B6A0f92995
   > account:             0x56fB94c8C667D7F612C0eC19616C39F3A50C3435
   > balance:             18.436499104
   > gas used:            283300
   > gas price:           20 gwei
   > value sent:          0 ETH
   > total cost:          0.005666 ETH


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


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

   Deploying 'Bounties'
   --------------------
   > transaction hash:    0xd1d82ac79006b6704d4f822e41f95d9561ffc2b249d6687dad8d9b84257c505a
   > Blocks: 2            Seconds: 28
   > contract address:    0x584EC00989488fBC3A10e4114B57C3246D557b48
   > account:             0x56fB94c8C667D7F612C0eC19616C39F3A50C3435
   > balance:             18.411356704
   > gas used:            1215092
   > gas price:           20 gwei
   > value sent:          0 ETH
   > total cost:          0.02430184 ETH


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


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

就是这样!我们现在终于将我们的Bounties.sol 合约部署到公共测试网络环境* Rinkeby* 中了。
在本系列的后面部分,我们将讨论如何在* Truffle* 框架内编写测试,以及我们如何在dApp中添加前端,以便用户可以在公共网络上与我们的智能合约进行交互!

标签 , ,

我们要如何帮助您?