每日总结

github上拉取项目

使用yarn安装项目所需要的依赖

学会查看pckage.json文件判断该项目通过什么依赖进行运行

区块链项目两大测试框架梳理

  • truffle
  • hardhat

truffle

什么是truffle

truffle是针对基于以太坊的solidity语言的一套开发框架,本身给予javascript

truffle的前置知识:

  1. javascript --->但是对于这个的要求比较低
  2. solidity
  3. 一些以太坊的基础
truffle的用处
  • 对客户端做了深度集成.开发、测试、部署一行命令搞定.
  • 提供一套类似mavengradle的项目构建机制,自动生成相关目录,默认是基于web,支持自定义打包流程
  • 提供合约抽象接口,可以直接通过var meta = MetaCoin.deployed();拿到合约对象然后在js中直接操作对应的合约函数--->非常重要,这是测试的基础.原理是用了基于web3.js封装的Ether Pudding工具包
  • 提供了控制台,使用框架构建项目以后可以直接在命令行调用输出结果
  • 提供了监控合约,配置变化的自动发布、部署流程.无需每个修改后都重走整个流程
truffle的一些特点
  • 内置智能合约的编译、链接、部署和二进制文件的管理
  • 快速开发下的自动合约测试
  • 脚本化的可拓展的部署与发布框架
  • 部署到不管多少的公网或私网的网络环境管理功能
  • 使用EthPM&NPM提供的包管理,使用ERC190标准
  • 与合约直接通信的直接交互控制台(写完合约就可以命令行里验证了)
  • 可配的构建流程,支持紧密集成
  • truffle环境里支持执行外部的脚本
truffle的环境要求
  • NodeJS 5.0+
  • 操作系统
  • truffle需要以太坊客户端,需要支持标准的JSON RPC API
truffle工程目录结构

truffle框架支持工程初始化

  1. 创建工程目录
  2. 执行初始化truffle指令 --->truffle init

执行完成以后有以下三个目录:

  • contracts
  • migrations
  • test
运行truffle框架下的solidity代码需要什么环境?

集成NodeJS:

特点:

  1. truffle console命令会默认集成web3. --->想要在自己的NodeJS环境使用truffle合约需要手动集成

  2. 集成前需要创建工程的npm包管理环境

步骤:

- 进入`truffle`的工程目录下
- 初始化`npm`的包管理环境 --->`npm init` --->如果不进行包管理环境初始化那么写入`package.json`的包依赖会失败(包`no such file or directory`)的错,所以需要先初始化`npm`的包管理环境
  1. NodeJS中用到的truffle运行时需要web3环境 --->使用npm进行安装 --->npm install web3 ---> 如果没有集成web3环境会运行truffle会报错 --->ReferenceError: Web3 is not defined
truffle测试合约

框架:

truffle使用mocha测试框架做自动化测试,使用chai来做断言.

文件位置:

对于测试人员来说,测试文件应位于./tests目录,truffle只会运行以.js.es.es6.jsx结尾的测试文件

一个完整的测试流程

创建自己的合约文件:

pragma solidity ^0.4.4;

contract Test{ function f() returns (string){ return "method f()"; } function g() returns (string){ return "method g()"; } } /* 上述代码提供了两个函数用于返回结果字符串 */

声明合约实例并进行自动部署:

修改migrations/2_deploy_contrcts.js为:

var ConvertLib = artifacts.require("./ConvertLib.sol"); var MetaCoin = artifacts.require("./MetaCoin.sol"); var Test = artifacts.require("./Test.sol");

module.exports = function(deployer) { deployer.deploy(ConvertLib); deployer.link(ConvertLib, MetaCoin); deployer.deploy(MetaCoin); deployer.deploy(Test); };

分析:

  1. 声明新合约实例并命名: --->var Test = artifacts.require("./Test.sol")

  2. 将声明的合约进行部署: --->deployer.deploy(Test)

过程分析:

  1. 先经过移植 --->将合约移植
  2. 在通过部署器进行部署
  • 移植是指由一些js文件组成来协助发布到以太坊网络.然后在以太坊网络上进行部署 **移植的目的**

缓存发布任务--->运行移植的历史记录通过一个特殊的migrations合约来记录到链上

移植的方式:

  • 命令移植 --->truffle migrate --->执行所有位于migrations目录内的移植脚本将编译后的合约部署到网络上,可以使用选项--reset来从头执行移植脚本

移植的脚本文件示例:

  1. 文件名由数字+描述性后缀结尾.数字用于记录是否移植成功.后缀为了提高可读性

文件示例:

module.exports = function(deployer) { // deployment steps deployer.deploy(MyContract); }; 分析:

  • deployer对象是用来缓存(stage)发布任务的主要操作接口
  • truffle提供了合约抽象层(contract abstractions)并进行了初始化,方便可以便利的与以太坊网络交互

移植和部署合约:

要使用移植特性必须首先进行部署合约

部署合约示例代码:

文件名:

migrations/1_initial_migrations.js

module.exports = function(deployer) { // Deploy the Migrations contract as our only task deployer.deploy(Migrations); }; 然后可以创建其他移植脚本进行移植部署

部署器(deployer)--->移植脚本第一个参数

移植文件的特点:

  • 移植文件使用部署器来缓存部署任务,可以按照一定顺序排列发布任务,会按照正确顺序执行 // Stage deploying A before B deployer.deploy(A); deployer.deploy(B); 也可以使用Promise将部署任务做成一个队列,根据逻辑判断是否部署依赖于前一个合约的执行情况:

// Deploy A, then deploy B, passing in A's newly deployed address deployer.deploy(A).then(function() { return deployer.deploy(B, A.address); });

网络相关--->移植脚本第二个参数

实现不同条件的不同部署步骤,移植脚本中需要添加第二个参数network

示例代码:

module.exports = function(deployer, network) { // Add demo data if we're not deploying to the live network. if (network != "live") { deployer.exec("add_demo_data.js"); } }

部署器API

DEPLOYER.DEPLOY(CONTRACT, ARGS...)

示例代码:

` // Deploy a single contract without constructor arguments deployer.deploy(A);

// Deploy a single contract with constructor arguments deployer.deploy(A, arg1, arg2, ...);

// Deploy multiple contracts, some with arguments and some without. // This is quicker than writing three deployer.deploy() statements as the deployer // can perform the deployment as a batched request. deployer.deploy([ [A, arg1, arg2, ...], B, [C, arg1] ]); `

注意:

如果库的地址可用,deploy会自动为这个部署的合约联接任何需要的库.如果合约依赖某个库,应该先部署这个库.

DEPLOYER.LINK(LIBRARY, DESTINATIONS)

示例代码:

` // Deploy library LibA, then link LibA to contract B deployer.deploy(LibA); deployer.link(LibA, B);

// Link LibA to many contracts deployer.link(LibA, [B, C, D]); `

联接一个已经发布到库的一个或多个合约,destinations可以是一个合约或多个合约组成的数组

示例代码:

` // Deploy library LibA, then link LibA to contract B deployer.deploy(LibA); deployer.link(LibA, B);

// Link LibA to many contracts deployer.link(LibA, [B, C, D]); `

DEPLOYER.AUTOLINK(CONTRACT)

示例代码:

// Link *all* libraries to all available contracts deployer.autolink();

自动关联合约依赖的所有库.需要保证在调用这个函数前,所有被需要的库已经部署

DEPLOYER.THEN(FUNCTION() {...})

示例代码:

deployer.then(function() { // Create a new version of A return A.new(); }).then(function(instance) { // Set the new instance of A's address on B. var b = B.deployed(); return b.setA(instance.address); });

Promise语法糖,执行做生意的部署流程.

DEPLOYER.EXEC(PATHTOFILE)

示例代码:

// Run the script, relative to the migrations file. deployer.exec("../path/to/file/demo_data.js");

执行truffle exec做为部署的一部分

编译合约:

  1. 使用truffle migrate --reset强制编译并发布所有合约, --->运行truffle migrate前需要确认节点处处于运行状态

注意:

NodeJS当中使用truffle框架需要先引入web3

  1. truffle-contractcontract()方法 --->使用方法是将truffle3.0编译后的.json文件(该文件就是一个json对象)当作参数放入contract()方法中

示例:

// 先引用该依赖 var contract = require("truffle-contract")

// 调用contract方法 var Test = contract(/这里传入json参数/)

// 最后可以使用.deployed()或者at(/*某个地址*/)进行调用

注意:

如果报:Cannot read property 'filter' of undefined则需要是因为传入的json对象有更改,将合约编译后的.json文件完全的copycontract()方法中

测试用例的标准
  1. 方式一:每个测试文件至少应该包含至少一个对Mochadescribe()(描述)函数的调用. --->参考文档

  2. 方式二使用truffle自定义的contract()函数,该函数有一些特性

    • 每一个contract()函数执行前,合约都会被重部署到以太坊客户端当中,这样测试用例会在一个干净状态下执行
    • contract()函数支持传入多个可用账户作为第二个参数传入,可以用这些来进行测试

注意:

当测试需要与所写的合约进行交互的时候,使用contract()函数,否则使用describe()函数

测试用例:

  1. 使用contract()部署合约
  2. 执行it()代码块中指定的测试用例

示例代码:

contract('MetaCoin', function(accounts) { it("should put 10000 MetaCoin in the first account", function() { // Get a reference to the deployed MetaCoin contract, as a JS object. var meta = MetaCoin.deployed(); // Get the MetaCoin balance of the first account and assert that it's 10000. return meta.getBalance.call(accounts[0]).then(function(balance) { assert.equal(balance.valueOf(), 10000, "10000 wasn't in the first account"); }); }); });

注意:

contract()函数传入的MetaCoin仅仅用来展示,说明它是MetaCoin相关的测试,无本质作用

合约

truffle提供了接口抽象,通过var meta = MetaCoin.deployed()与合约进行交互.

合约交互

背景:

truffle使用ether pudding库进行以太坊网络交互,该库基于web3

读写数据:

  • 以太坊网络把在网络上读数据称为调用(call)
  • 在网络上写数据称为交易(transaction)

交易(transaction):

本质是改变整个以太坊网络的数据状态

触发场景:

  1. 一个账户向另一个账户发送ether(以太坊网络代币)
  2. 执行合约函数,添加一个新合约到以太坊网络

交易的特征:

  1. 需要gas(ether)
  2. 改变网络的状态
  3. 不会立即执行
  4. 不会暴露返回结果(仅有交易id)

调用(call):

在网络上执行代码,但是没有数据会被改变

调用的特征:

  1. 免费
  2. 不改变网络状态
  3. 立即执行
  4. 有返回结果
solidity的接口(abstract)--->注意针对接口特有的实例函数

举例:

import "ConvertLib.sol";

contract MetaCoin { mapping (address => uint) balances;

event Transfer(address indexed _from, address indexed _to, uint256 _value);

function MetaCoin() {
    balances[tx.origin] = 10000;
}

function sendCoin(address receiver, uint amount) returns(bool sufficient) {
    if (balances[msg.sender] < amount) return false;
    balances[msg.sender] -= amount;
    balances[receiver] += amount;
    Transfer(msg.sender, receiver, amount);
    return true;
}
function getBalanceInEth(address addr) returns(uint){
    return ConvertLib.convert(getBalance(addr),2);
}
function getBalance(address addr) returns(uint) {
    return balances[addr];
}

}

分析:

上诉代码当中有三个函数和一个构造函数

truffleether pudding提供了一个MetaCoinjs对象,可以在前端中使用:

console.log(MetaCoin.deployed()); --->接口层提供了合约中以应的函数名.它还包含一个地址,指向到MetaCoin合约的部署版本.

每一个抽象出来的合约接口都有一个deployed()函数,调用这个函数返回一个实例

执行交易(transaction)

上述MetaCoin合约中,有三个可执行函数,只有sendCoin函数会对网络造成更改,这些更改需要永久保存下来

示例代码:

调用十个币,从一个账户发到另一个账户:

// Instantiation address var account_one = "0x1234..."; // an address var account_two = "0xabcd..."; // another address

var meta = MetaCoin.deployed(); meta.sendCoin(account_two, 10, {from: account_one}).then(function(tx_id) { // If this callback is called, the transaction was successfully processed. // Note that Ether Pudding takes care of watching the network and triggering // this callback. alert("Transaction successful!") }).catch(function(e) { // There was an error! Handle it. })

代码分析:

  1. 直接调用sendCoin函数
  2. 交易被成功时回调函数才会真正被触发
  3. sendCoin函数传递了第三个参数,注意原始合约函数的定义中并没有第三个参数.这里该参数是一个特殊的对象,用于编辑一些交易中的指定细节,它可以总是做为第三个参数传进.
执行交易(call)

上述MetaCoingetBalance函数就是一个读取函数

示例代码:

var account_one = "0x1234..."; // an address

var meta = MetaCoin.deployed(); meta.getBalance.call(account_one, {from: account_one}).then(function(balance) { // If this callback is called, the call was successfully executed. // Note that this returns immediately without any waiting. // Let's print the return value. console.log(balance.toNumber()); }).catch(function(e) { // There was an error! Handle it. })

分析:

  1. 必须通过.call()向以太坊网络表明不会持久化一些数据变化
  2. 得到返回结果,不是一个交易id

注意:

上述的例子中将返回值转成了一个number类型,是因为例子中的返回值比较小,如果将一个BigNumber转换为比javascript支持的number最大整数都大,会出现错误或不可预期的行为

捕捉事件(Catching Events)

合约可以触发事件,可以进行捕捉以进行更多的控制

示例代码:

var meta = MetaCoin.deployed(); var transfers = meta.Transfer({fromBlock: "latest"}); transfers.watch(function(error, result) { // This will catch all Transfer events, regardless of how they originated. if (error == null) { console.log(result.args); } }

涉及到的方法:

  • METHOD:DEPLOYED()

每一个抽象的合约接口都有一个deployed()函数,调用该函数返回一个实例.代表之前部署到网络的合约所对应的抽象接口的实例

var meta = MetaCoin.deployed()

仅对使用truffle deploy部署的合约,且一定是在project configuration中配置发布的才有效.如果不是这样,这个函数执行时会抛出异常.

  • METHOD:AT()

通过一个地址来得到一个代表合约的接口实例.该地址一定是这个合约的部署地址

Var meta - MetaCoin.at("0x1234...")

地址不正确,或地址对应的合约不正确时.函数并不会抛出异常.但调用接口时会报错.请保证在使用at()时输入正确的地址

  • METHOD:NEW()

部署一个完全全新的合约到网络中

MetaCoin.new().then(function(instance) { // `instance` is a new instance of the abstraction. // If this callback is called, the deployment was successful. console.log(instance.address); }).catch(function(e) { // There was an error! Handle it. });

这个操作会改变网络状态

使用truffle框架部署合约的全流程

  • 合约部署 --->初始化truffle框架的结构 ---> truffle init
  • 编译合约 --->truffle compile
  • 编写测试脚本 --->使用javascript或者typescript进行编写
  • 执行测试用例 --->truffle test
posted @ 2024-01-01 23:40  北·岛  阅读(24)  评论(0)    收藏  举报