智能合约的优点

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

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

以太坊(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: '',
extraData:
'0xde830206028f5061726974792d457468657265756d86312e33362e30826c69',
gasLimit: '0x7a1200',
gasUsed: '0x17d23',
hash:
'0xc7390c4f492c8c1da60608135fc9e05930123b645b39f221cba33d8b3c577b2a',
logsBloom:
'0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000080000000000000000000100000008000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400800000000000010000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009000000008000000',
receiptsRoot:
'0x3d05bb2ed4fcc90234eea6d840e7d0e3ce7f598a15e5314536b17bcd11c78b5b',
sealFields:
[ '0x84175e8801',
'0xb84155a8cdb108dccec1d314124058fa6f22e7400ee200db0a94b7b165e4c3454c1818cc05f815cb7ce48f7a88b8401515740311a3566d9cf079428d506a6daca50101' ],
sha3Uncles:
'0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347',
signature:
'55a8cdb108dccec1d314124058fa6f22e7400ee200db0a94b7b165e4c3454c1818cc05f815cb7ce48f7a88b8401515740311a3566d9cf079428d506a6daca50101',
size: 877,
stateRoot:
'0x03af5adce52a81ce5d332cddb9955e344214bff00859b78868116e1e839efdf7',
step: '',
timestamp: 1568284676,
totalDifficulty: '',
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: '',
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: '',
timestamp: 1568298844,
totalDifficulty: '',
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钱包插件(需翻墙)。

安装好后,确保插件的启用按钮打开,在浏览器的右上角会看到一个狐狸图标。

导入账号

把钱包账号从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脚本

2019年9月12日
 

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

 

类似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: '',
extraData:
'0xde830206028f5061726974792d457468657265756d86312e33362e30826c69',
gasLimit: '0x7a1200',
gasUsed: '0x17d23',
hash:
'0xc7390c4f492c8c1da60608135fc9e05930123b645b39f221cba33d8b3c577b2a',
logsBloom:
'0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000080000000000000000000100000008000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400800000000000010000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009000000008000000',
receiptsRoot:
'0x3d05bb2ed4fcc90234eea6d840e7d0e3ce7f598a15e5314536b17bcd11c78b5b',
sealFields:
[ '0x84175e8801',
'0xb84155a8cdb108dccec1d314124058fa6f22e7400ee200db0a94b7b165e4c3454c1818cc05f815cb7ce48f7a88b8401515740311a3566d9cf079428d506a6daca50101' ],
sha3Uncles:
'0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347',
signature:
'55a8cdb108dccec1d314124058fa6f22e7400ee200db0a94b7b165e4c3454c1818cc05f815cb7ce48f7a88b8401515740311a3566d9cf079428d506a6daca50101',
size: 877,
stateRoot:
'0x03af5adce52a81ce5d332cddb9955e344214bff00859b78868116e1e839efdf7',
step: '',
timestamp: 1568284676,
totalDifficulty: '',
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: '',
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: '',
timestamp: 1568298844,
totalDifficulty: '',
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钱包插件(需翻墙)。

安装好后,确保插件的启用按钮打开,在浏览器的右上角会看到一个狐狸图标。

导入账号

把钱包账号从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脚本

2019年9月12日
 

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

 

区块链入门到实战(27)之以太坊(Ethereum) – 智能合约开发的更多相关文章

  1. 区块链入门到实战(17)之以太坊(Ethereum) – 是什么

    以太坊的作用:构建基于区块链的分布式应用. 以太坊是什么:可编程的虚拟币. 以太坊(Ethereum)是一个可编程的虚拟币,它是一个基于公共区块链的分布式计算平台,可用于构建基于区块链的分布式应用. ...

  2. 区块链入门到实战(38)之Solidity – 条件语句

    Solidity支持条件语句,让程序可以根据条件执行不同的操作.条件语句包括: if if...else if...else if 语法 if (条件表达式) { 被执行语句(如果条件为真) } 示例 ...

  3. 区块链入门到实战(37)之Solidity – 循环语句

    与其他语言类似,Solidity语言支持循环结构,Solidity提供以下循环语句. while do ... while for 循环控制语句:break.continue. Solidity – ...

  4. 区块链入门到实战(36)之Solidity – 运算符

    Solidity – 算术运算符 Solidity 支持的算术运算符,如下表所示: 假设变量A的值为10,变量B的值为20. 序号 运算符与描述 1 + (加)求和例: A + B = 30 2 – ...

  5. 程序员的自我救赎---12.2.3: 虚拟币交易平台(区块链) 下 【C#与以太坊通讯】

    <前言> (一) Winner2.0 框架基础分析 (二)PLSQL报表系统 (三)SSO单点登录 (四) 短信中心与消息中心 (五)钱包系统 (六)GPU支付中心 (七)权限系统 (八) ...

  6. 零门槛,包教会。让你在5分钟内使用以太坊ERC20智能合约发行属于自己的空气币

    前言 目前区块链是互联网中最最火的风口,没有之一.我周围的很多朋友也加入了“炒币”行列,但很不幸,几乎都被“割韭菜”了.而经过我的几天研究,发现,如果自己要发行一种空气币,简直太简单了.只需要下面几个 ...

  7. 以太坊solidity智能合约-生成随机数

    Solidity随机数生成 在以太坊的只能合约中,没有提供像其他面向对象编程一样的生成随机数的工具类或方法.其实,所谓的随机数也是伪随机的,没有哪一种语言能够真正的生成随机数. 对于solidity来 ...

  8. 区块链入门到实战(26)之以太坊(Ethereum) – 挖矿

    以太坊(Ethereum)与其他公共区块链一样,使用工作量证明机制确保区块链网络正常运行. 矿工进行工作量证明计算,即挖矿,来选择区块,写入区块链,确认交易. 交易过程如下图所示: 从技术角度来看,以 ...

  9. 区块链入门到实战(24)之以太坊(Ethereum) – 网络节点

    用途: 全节点:用于区块和交易的校验 轻节点:电子钱包 以太坊(Ethereum)网络是一个公共的区块链网络,网络中包含2种网络节点: 全节点 轻节点 全节点 包含了从初始区块开始的全部区块,这些区块 ...

随机推荐

  1. 使用types库修改函数

    import types class ppp: pass p = ppp()#p为ppp类实例对象 def run(self): print("run函数") r = types. ...

  2. luogu P4884 多少个1?

    LINK:多少个1? 题目要求:\(\sum_{i=0}^{n-1}10^i \equiv k \mod m\) 最小的n. 看起来很难求的样子 这个同余式 看起来只能暴力枚举. 不过既然是同余 我们 ...

  3. Pintech品致-高压放大器

      pintech品致推出的HA-520(200KHz,500Vp-p)高压放大器真的是实用的高电压信号放大器, 体积小,轻便及简易的操作, 高电压输出(800Vp-p)等优点.连续输出电流量最大值达 ...

  4. MySQL一主多从配置和读写分离配置

    一.一主多从配置 此次操作实现的是一主两从的方式.主服务器slave2(2.100),从服务器slave2-1(2.107),slave2-2(2.108);第一:准备主数据库    1. 在不同的机 ...

  5. vmware15虚拟机安装教程

    自己碰到的问题:本人win7 64位旗舰版系统.之前用VMware12pro版本的软件,在安装Ubuntu18.04之后,有时候开启Ubuntu虚拟机时有点问题,重启就可以了.但是不稳定,所以改用VM ...

  6. Qt使用MD5加密

    Qt中包含了大部分常用的功能,比如json.数据库.网络通信.串口通信以及今天说的这个MD5加密: Qt中将字符串进行MD5加密其实比较简单,代码如下: #include <QCoreAppli ...

  7. HTTP POST 请求的两种编码格式:application/x-www-form-urlencoded 和 multipart/form-data

    在常见业务开发中,POST 请求常常在这些地方使用:前端表单提交时.调用接口代码时和使用 Postman 测试接口时.我们下面来一一了解: 一.前端表单提交时 application/x-www-fo ...

  8. C语言学习笔记之数据类型转换

    1.整数与整数相除,结果也为整数 2.不同类型的运算,精度低的向精度高的转化 整数与浮点数运算就是个很好的例子,只要有一方为浮点数,结果也是浮点数,这也体现出精度低向精度高转化 3.在赋值运算中,等号 ...

  9. spring data jpa 之 通用接口

    园主这一阵子接到一个需求,就是将spring data jpa再进行封装,实现通过调用一个baseRepository,来实现每个类的增删改查操作,结合spring data jpa 原有的便捷操作, ...

  10. 基于索引的QA问答对匹配流程梳理

    知识库(主要是标准的QA信息)匹配需求是对已经梳理出的大量标准QA对信息进行匹配,找出最符合用户问题的QA对进行回复,拆分主要的处理流程主要为如下两点: 标准QA信息入库索引: 通过对用户提出的问题进 ...