07月01, 2019

Truffle

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并启动

本文链接:https://harry.ren/post/truffle.html

-- EOF --

Comments