install require:
#1. node.js
#2. truffle
#3. Ganache CLI: 使用本机内存模拟Eth环境,基于node.js. 以前叫 TestRPC在开发中使用。
npm: node package manager
node --v
npm --g install truffle
npm install --g ganache-cli
#1. 使用truffle box创建一个truffle项目 mkdir pet-shop-tutorial cd pet-shop-tutorial
#2. 创建项目 truffle unbox pet-shop
contracts/:存放智能合约文件。
migrations/:存放部署智能合约相关的文件。
test/:智能合约测试文件,测试文件可以使用JavaScript或者Solidity编写。
truffle.js:Truffle的配置文件。小心可能出现重命名问题,需要将文件名改为truffle-config.js之类的
e.g. 写一个smart contract demo
在 contracts/目录下新建文件Adoption.sol
pragma solidity ^0.4.17;//告诉编译器使用大于0.4.17小于0.5版本的solidity语言
contract Adoption {
address[16] public adopters;
// Adopting a pet
function adopt(uint petId) public returns (uint) {
require(petId >= 0 && petId <= 15);
adopters[petId] = msg.sender;
return petId;
}
// Retrieving the adopters
function getAdopters() public view returns (address[16]) {
return adopters;
}
}
由于名称冲突,如果你使用的是CMD命令行,则需要将根目录下的truffle.js文件重命名为truffle-config.js truffle-config.js文件的内容为
module.exports = {
// See <http://truffleframework.com/docs/advanced/configuration>
// for more about customizing your Truffle configuration!
networks: {
development: {
host: "127.0.0.1",
port: 8545,
network_id: "*" // Match any network id
}
}
};
在命令行中输入truffle compile 如果出现类似于下面的结果,说明编译成功了
Compiling ./contracts/Migrations.sol...
Compiling ./contracts/Adoption.sol...
Writing artifacts to ./build/contracts
编译成功后,你会在项目的根目录下面发现多了一个build文件夹,里面存放的是智能合约编译后的结果.
可以用truffle 来部署,参考下边链接
https://blog.csdn.net/shusheng0007/article/details/79312300 https://blog.csdn.net/weixin_34302561/article/details/87055095
e.g. 使用trufffle来分析一个简单的虚拟货币代码
xiaolei@ubuntu:~$ mkdir truffle-examples
xiaolei@ubuntu:~$ cd truffle-examples/
xiaolei@ubuntu:~/truffle-examples$ mkdir MetaCoin
xiaolei@ubuntu:~/truffle-examples$ cd MetaCoin/
xiaolei@ubuntu:~/truffle-examples/MetaCoin$ truffle unbox metacoin
Downloading...
Unpacking...
Setting up...
Unbox successful. Sweet!
Commands:
Compile contracts: truffle compile
Migrate contracts: truffle migrate
Test contracts: truffle test
xiaolei@ubuntu:~/truffle-examples/MetaCoin$ tree
.
├── contracts
│ ├── ConvertLib.sol
│ ├── MetaCoin.sol
│ └── Migrations.sol
├── migrations
│ ├── 1_initial_migration.js
│ └── 2_deploy_contracts.js
├── test
│ ├── metacoin.js
│ └── TestMetacoin.sol
├── truffle-config.js
└── truffle.js
3 directories, 9 files
contracts目录下存放合约文件,其中Migrations.sol合约较为特殊,会在项目初始化的时候被创建,是Truffle框架里必须的文件,运行的迁移历史记录会被他记录在区块链上。
migrations下存放迁移部署脚本,用来帮助把合约发布到以太坊网络中。 test目录下存放测试脚本:
Truffle内置自动化测试框架,该框架允许以两种不同的方式编写测试用例:
1,使用Javascript编写测试用例,测试从外部执行合约
2,使用Solidity编写测试用例,测试从内部调用合约
附上source code : Migrations.sol
pragma solidity ^0.4.2; //声明Solidity版本,兼容0.4.2以下版本
contract Migrations { //contract声明为合约,在Truffle中合约名称需和文件名一致
address public owner; //公开(外部可见),地址类型全局变量owner,用于保存合约所有者地址
uint public last_completed_migration; //公开,无符号整型全局变量last_completed_migration,在每次合约部署时更新
modifier restricted() { //修饰符,通常用于函数执行前检查某种前置条件是否满足
if (msg.sender == owner) _; //判断调用合约的是不是合约所有者
}
constructor() public { //构造函数,在合约部署时执行
owner = msg.sender; //将合约所有者赋值给owner变量//msg为solidity中的全局变量,msg.sender表示合约发送者地址
}
function setCompleted(uint completed) public restricted { //用于更新last_completed_migration变量的函数
last_completed_migration = completed;
}
function upgrade(address new_address) public restricted { //用于更新合约所有者
Migrations upgraded = Migrations(new_address);
upgraded.setCompleted(last_completed_migration);
}
Solidity在全局命名空间中预设了一些特殊的变量和函数,用来提供关于区块链的信息和一些通用的工具函数
block.blockhash(uint blockNumber) returns (bytes32):指定区块的区块哈希——仅可用于最新的 256 个区块且不包括当前区块;而 blocks 从 0.4.22 版本开始已经不推荐使用,由 blockhash(uint blockNumber) 代替
block.coinbase (address): 挖出当前区块的矿工地址
block.difficulty (uint): 当前区块难度
block.gaslimit (uint): 当前区块 gas 限额
block.number (uint): 当前区块号
block.timestamp (uint): 自 unix epoch 起始当前区块以秒计的时间戳
gasleft() returns (uint256):剩余的 gas
msg.data (bytes): 完整的 calldata
msg.gas (uint): 剩余 gas - 自 0.4.21 版本开始已经不推荐使用,由 gesleft() 代替
msg.sender (address): 消息发送者(当前调用)
msg.sig (bytes4): calldata 的前 4 字节(也就是函数标识符)
msg.value (uint): 随消息发送的 wei 的数量
now (uint): 目前区块时间戳(block.timestamp)
tx.gasprice (uint): 交易的 gas 价格
tx.origin (address): 交易发起者(完全的调用链)
MetaCoin.sol
pragma solidity ^0.4.18;
import "./ConvertLib.sol"; //导入外部类库
contract MetaCoin {
mapping (address => uint) balances; //mapping类型变量,用于保存地址对应余额信息
event Transfer(address indexed _from, address indexed _to, uint256 _value);//event事件,用于通知外部 //indexd修饰符可用于事件过滤
constructor() public {
balances[tx.origin] = 10000; //给交易发起者账号余额赋值
}
function sendCoin(address receiver, uint amount) public returns(bool sufficient) { //发送MetaCoin方法,参数为接收者地址和交易币数量,返回值为交易成功失败状态
if (balances[msg.sender] < amount) return false; //需要合约发起者余额充足
balances[msg.sender] -= amount;
balances[receiver] += amount;
emit Transfer(msg.sender, receiver, amount); //交易后触发事件,通知外部
return true;
}
function getBalanceInEth(address addr) public view returns(uint){ //获取某个地址余额,转换为Eth返回//view修饰符保证不修改状态
return ConvertLib.convert(getBalance(addr),2);
}
function getBalance(address addr) public view returns(uint) { //根据地址获取余额
return balances[addr];
}
}
library ConvertLib.sol
pragma solidity ^0.4.4;
library ConvertLib{
function convert(uint amount,uint conversionRate) public pure returns (uint convertedAmount)
{
return amount * conversionRate;
}
}
library是一种不同类型的合约,没有存储,不用有以太币。库中的代码可以被其他合约调用,而不需要重新部署,这样可以节省大量gas。该库提供了convert函数转换MetaCoin和ETH,一个MetaCoin值conversionRate个ETH。
迁移脚本1_initial_migration.js
var Migrations = artifacts.require("./Migrations.sol"); //告诉测试脚本要和哪个合约交互
module.exports = function(deployer) {
deployer.deploy(Migrations); //迁移脚本使用deployer对象部署合约
};
可以看到迁移脚本命名规则是以数字开头,后面加上描述性字符串。数字前缀是必须的,用于记录迁移记录,在前面Migration.sol中可以看到相关代码
迁移脚本2_deploy_contracts.js
var ConvertLib = artifacts.require("./ConvertLib.sol");
var MetaCoin = artifacts.require("./MetaCoin.sol");
module.exports = function(deployer) {
deployer.deploy(ConvertLib);
deployer.link(ConvertLib, MetaCoin); //把已部署好的库链接到MetaCoin合约
deployer.deploy(MetaCoin);
测试脚本metacoin.js
var MetaCoin = artifacts.require("./MetaCoin.sol"); //指明要测试的合约
contract('MetaCoin', function(accounts) { //contract 表示test sunit测试套件,表示一组相关测试,第一个参数MetaCoin为套件名称,第二个参数为执行函数
it("should put 10000 MetaCoin in the first account", function() { //it 表示test case测试用例,第一个参数为测试用例名称,第二个参数为执行函数
return MetaCoin.deployed().then(function(instance) {
return instance.getBalance.call(accounts[0]);
}).then(function(balance) {
assert.equal(balance.valueOf(), 10000, "10000 wasn't in the first account");
});
});
it("should call a function that depends on a linked library", function() {
var meta;
var metaCoinBalance;
var metaCoinEthBalance;
return MetaCoin.deployed().then(function(instance) {
meta = instance;
return meta.getBalance.call(accounts[0]);
}).then(function(outCoinBalance) {
metaCoinBalance = outCoinBalance.toNumber();
return meta.getBalanceInEth.call(accounts[0]);
}).then(function(outCoinBalanceEth) {
metaCoinEthBalance = outCoinBalanceEth.toNumber();
}).then(function() {
assert.equal(metaCoinEthBalance, 2 * metaCoinBalance, "Library function returned unexpected function, linkage may be broken");
});
});
it("should send coin correctly", function() {
var meta;
// Get initial balances of first and second account.
var account_one = accounts[0];
var account_two = accounts[1];
var account_one_starting_balance;
var account_two_starting_balance;
var account_one_ending_balance;
var account_two_ending_balance;
var amount = 10;
return MetaCoin.deployed().then(function(instance) {
meta = instance;
return meta.getBalance.call(account_one);
}).then(function(balance) {
account_one_starting_balance = balance.toNumber();
return meta.getBalance.call(account_two);
}).then(function(balance) {
account_two_starting_balance = balance.toNumber();
return meta.sendCoin(account_two, amount, {from: account_one});
}).then(function() {
return meta.getBalance.call(account_one);
}).then(function(balance) {
account_one_ending_balance = balance.toNumber();
return meta.getBalance.call(account_two);
}).then(function(balance) {
account_two_ending_balance = balance.toNumber();
assert.equal(account_one_ending_balance, account_one_starting_balance - amount, "Amount wasn't correctly taken from the sender");
assert.equal(account_two_ending_balance, account_two_starting_balance + amount, "Amount wasn't correctly sent to the receiver");
});
});
从外部执行,在测试结束后可以看到各个测试用例测试结果及所用时间信息.
测试脚本TestMetacoin.sol
pragma solidity ^0.4.2;
import "truffle/Assert.sol"; //导入Truffle内置断言库
import "truffle/DeployedAddresses.sol";
import "../contracts/MetaCoin.sol";
contract TestMetacoin {
function testInitialBalanceUsingDeployedContract() public {
MetaCoin meta = MetaCoin(DeployedAddresses.MetaCoin()); //部署合约并获取合约抽象,固定写法
uint expected = 10000;
Assert.equal(meta.getBalance(tx.origin), expected, "Owner should have 10000 MetaCoin initially");
}
function testInitialBalanceWithNewMetaCoin() public {
MetaCoin meta = new MetaCoin();
uint expected = 10000;
Assert.equal(meta.getBalance(tx.origin), expected, "Owner should have 10000 MetaCoin initially");
}
}
补充的是当执行编译后,在build文件夹下生成了对应的json文件,json文件由ABI(Application Binary Interface)应用二进制接口,bytecode合约编译后二进制内容等组成.
x@ubuntu:~/truffle-examples/MetaCoin$ truffle compile
Compiling ./contracts/ConvertLib.sol...
Compiling ./contracts/MetaCoin.sol...
Compiling ./contracts/Migrations.sol...
Writing artifacts to ./build/contracts
x@ubuntu:~/truffle-examples/MetaCoin$ tree
.
├── build
│ └── contracts
│ ├── ConvertLib.json
│ ├── MetaCoin.json
│ └── Migrations.json
├── contracts
│ ├── ConvertLib.sol
│ ├── MetaCoin.sol
│ └── Migrations.sol
├── migrations
│ ├── 1_initial_migration.js
│ └── 2_deploy_contracts.js
├── test
│ ├── metacoin.js
│ └── TestMetacoin.sol
├── truffle-config.js
└── truffle.js
5 directories, 12 files
另外,webpack相当于在MetaCoin项目的基础上进行了补充,增加了服务和前端界面,可以通过网页进行转账操作。
$ truffle unbox webpack
$ truffle compile
查看truffle.js可以看到webpack默认指定的网络是ganache
module.exports = {
networks: {
ganache: {
host: '127.0.0.1',
port: 7545,
network_id: '*' // Match any network id
}
}
}
下载ganache并启动
Comments