智能合约的优点
与传统合同相比,智能合约有一些显著优点:
- 不需要中间人
- 费用低
- 代码就是规则
- 区块链网络中有多个备份,不用担心丢失
- 避免人工错误
- 无需信任,就可履行协议
- 匿名履行协议
以太坊(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)也能测试,但与公链还是有区别的。
连接到公链的步骤如下:
- 设置钱包来管理公链帐户
- 连接到以太坊节点
- 更新项目设置
- 访问以太坊节点
设置钱包
首先需要设置一个钱包,来管理我们的公链帐户。
简单起见,可以借用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"
更新项目设置
接下来使用MNEMONIC
与INFURA_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
配置文件中的MNEMONIC
与INFURA_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) – 部署智能合约到公链
现在,我们将智能合约部署到公链。步骤如下:
- 部署需要消耗Gas,获取测试以太币用于部署
- 部署智能合约
- 验证部署
获取测试以太币
部署需要消耗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
文件,内容如下:
该脚本将从Kovan测试网络获取最新区块的信息。
执行脚本:
输出:
下面的代码智能合约MyContract
中,读取value值,将script.js
脚本文件中的代码替换为:
执行脚本:
输出:
脚本运行器是一个非常有用的功能。
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)也能测试,但与公链还是有区别的。
连接到公链的步骤如下:
- 设置钱包来管理公链帐户
- 连接到以太坊节点
- 更新项目设置
- 访问以太坊节点
设置钱包
首先需要设置一个钱包,来管理我们的公链帐户。
简单起见,可以借用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"
更新项目设置
接下来使用MNEMONIC
与INFURA_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
配置文件中的MNEMONIC
与INFURA_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) – 部署智能合约到公链
现在,我们将智能合约部署到公链。步骤如下:
- 部署需要消耗Gas,获取测试以太币用于部署
- 部署智能合约
- 验证部署
获取测试以太币
部署需要消耗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
文件,内容如下:
该脚本将从Kovan测试网络获取最新区块的信息。
执行脚本:
输出:
下面的代码智能合约MyContract
中,读取value值,将script.js
脚本文件中的代码替换为:
执行脚本:
输出:
脚本运行器是一个非常有用的功能。
Doc navigation
本文来自博客园,作者:大码王,转载请注明原文链接:https://www.cnblogs.com/huanghanyu/