……

智能合约的优点

与传统合同相比,智能合约有一些显著优点:

智能合约优点

  • 不需要中间人
  • 费用低
  • 代码就是规则
  • 区块链网络中有多个备份,不用担心丢失
  • 避免人工错误
  • 无需信任,就可履行协议
  • 匿名履行协议

以太坊(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 版权所有,禁止转载

 

 posted on 2020-06-22 16:09  大码王  阅读(1972)  评论(0编辑  收藏  举报
复制代码