Truffle 、Geth、TestRPC 在私有链上搭建智能合约

Truffle 的简介

  • Truffle 是最流行的开发框架,能够在本地编译、部署智能合约,使命是让开发更容易。

  • Truffle 需要以太坊客户端支持,需要支持标准的 JSON RPC API。

  • Truffle 的源代码地址:
    https://github.com/trufflesuite/truffle

  • 适合 Truffle 开发的客户端

    • 有许多的以太坊客户端可以选择。我们推荐在开发和部署时使用不同客户端 。
    • 通用开发的客户端
    • EtherumJS TestRPC
    • 通用正式发布的客户端
    • Geth (go-ethereum)

当开发基于 Truffle 的应用时,推荐使用EthereumJS TestRPC。它是一个完整的在内存中的区块链仅仅存在于你开发的设备上。相对于 Geth,TestRPC 它在执行交易时是实时返回,而不等待默认的出块时间,这样你可以快速验证你新写的代码,当出现错误时,也能即时反馈给你。它同时还是一个支持自动化测试的功能强大的客户端。Truffle 充分利用它的特性,能将测试运行时间提速近90%。

如何安装

  • 接下来的例子,我们会使用 Truffle 分别连接 Geth 和 TestRPC 测试智能合约的部署,首先我们先分别安装Truffle、Geth、TestRPC。

  • 首先安装 Geth。详见前面的文章

  • 安装 Truffle

    • 依赖环境:
    • NodeJS 5.0+
    • 安装很简单
    • npm install -g truffle

如果安装 Truffle 后,需要修改 solidity 编译版本

  • 具体升级solidity版本方法如下:
    • 进入到truffle的安装目录。我的安装目录是在/home/chen/.nvm/versions/node/v8.15.0/lib/node_modules/truffle
    • 修改package.json文件中的solc版本,solc是solidity的编译版本,如下代码:
 "dependencies": {
    "mocha": "^4.1.0",
    "original-require": "1.0.1",
    "solc": "0.5.0"
  },
  • 修改成功后,重新安装truffle
npm uninstall -g truffle
npm install -g truffle

使用 Truffle 进行智能合约的开发

  • 初始化一个 Truffle 项目
    • 通过truffle init命令,可以初始化一个默认的以太坊代币合约项目,后面我们可以通过这个项目来快速学习:
$>mkdir test_truffle
$>ls
mist  test_truffle

$>cd test_truffle/
$>ls
$>truffle init

✔ Preparing to download
✔ Downloading
✔ Cleaning up temporary files
✔ Setting up box 

Unbox successful. Sweet!

Commands:

  Compile:        truffle compile
  Migrate:        truffle migrate
  Test contracts: truffle test            

完成后,会产生下列这些目录:

  • contracts 智能合约目录
  • migrations 发布脚本目录
  • test 存放测试文件
  • truffle.js Truffle的配置文件

编译合约

  • 要编译合约,使用 truffle compile 命令,可以将原始代码编译成以太坊认可的字节码:
$>truffle compile

Compiling your contracts...
==================== 
> Compiling ./contracts/Migrations.sol
> Artifacts written to /home/chen/chen/ethereum-wallet/test_truffle/build/contracts
> Compiled successfully using:
   - solc: 0.5.0+commit.1d4f565a.Emscripten.clang

  • Truffle仅默认编译自上次编译后被修改过的文件,来减少不必要的编译。如果你想编译全部文件,可以使用–compile-all选项
  • truffle compile --compile-all
  • Truffle 需要定义的合约名称和文件名准确匹配,这种匹配是区分大小写的,也就是说大小写也要一致。推荐大写每一个开头字母。
  • 文件之间的相互依赖,可以使用 import 进行合约间的引用,Truffle 将会按正确顺序依次编译合约,并在需要的时候自动关联库。例如:
  • import "./AnotherContract.sol";

创建一个 Hello blake.top 的合约并编译

  • 在 contracts 目录中新建一个Hello_mshk_top.sol 文件,代码如下:
pragma solidity ^0.4.17;

contract Hello_blake_top {

  //say hello blake.top
  function say() public pure returns (string) {
    return "Hello blake.top";
  }

  // print name
  function print(string name) public pure returns (string) {
    return name;
  }
}
  • 代码中有两个方法:say( ) 方法是输出一段文字 Hello blake.top;print(string name)方法是输出传入的内容。
  • 编辑migrations/1_initial_migration.js 部署脚本,将我们刚才创建的Hello_blake_top.sol 文件设置到发布配置文件中,内容如下:
const Migrations = artifacts.require("Migrations");
const Hello_blake_top = artifacts.require("./Hello_blake_top.sol");

module.exports = function(deployer) {
  deployer.deploy(Migrations);
  deploy.deploy(Hello_blake_top);
};

  • 将项目使用truffle compile命令进行编译,编译后的文件都放在了 ./build/contracts 目录下:

  • Hello_blake_top.sol 编译后的文件是 ./build/contracts/Hello_blake_top.json 中,后面在部署到 geth 中,我们会用到。

部署智能合约

  • 编辑 truffle.js 配置文件,设置我们稍后要部署智能合约的位置。
  • 这里要注意的是:Ganache默认运行在7545端口,Ethereumjs-testrpc 默认运行在8545端口,Truffle Develop 默认运行在9545端口。内容如下:
module.exports = {
    networks: {
        development: {
          host: "localhost",
          port: 8545,
          network_id: "*"
        }
    }
};
  • 接下来,我们会使用上面的智能合约,分别在 testRPC 和 geth 中进行部署测试。

  • truffle的智能合约项目部署,使用下面的命令:truffle migrate

  • 这个命令会执行所有 migrations 目录下的 js 文件。如果之前执行过 truffle migrate 命令,再次执行,只会部署新的 js 文件,如果没有新的 js 文件,不会起任何作用。如果使用–reset 参数,则会重新的执行所有脚本的部署。

  • 如果要部署到指定的网络,可以使用–network参数,例如:
    truffle migrate --network live

  • 多个网络的配置格式如下:

networks: {
  development: {
    host: "localhost",
    port: 8545,
    network_id: "*" // match any network
  },
  live: {
    host: "178.25.19.88", // Random IP for example purposes (do not use)
    port: 80,
    network_id: 1,        // Ethereum public network
    // optional config values:
    // gas  Gas limit used for deploys. Default is 4712388
    // gasPrice Gas price used for deploys. Default is 100000000000 (100 Shannon).
    // from - default address to use for any transaction Truffle makes during migrations
    // provider - web3 provider instance Truffle should use to talk to the Ethereum network.
    //          - if specified, host and port are ignored.
  }
}

将智能合约部署到 TestRPC 中测试

  • 启动 TestRPC
    • 直接输入 testrpc 命令,就可以调用起 TestRPC 客户端,启动 testrpc 经后,会默认创建10个帐号,Available Accounts是帐号列表,Private Keys是相对应的帐号密钥:
chen@ubuntu:~$ testrpc
EthereumJS TestRPC v6.0.3 (ganache-core: 2.0.2)

Available Accounts
==================
(0) 0x14b1cf637d214963cd31c923711930aa7b97fd5b
(1) 0x8703ffa009e31e0eebf3aa2dacb32486e06077c0
(2) 0x2237e37056828dfb27e94a3274132a59ae5b4413
(3) 0x1744da91d3eca87fbcf2711740b12e62739965ec
(4) 0x3c35f08665a047f90ddce14d0f82b703a240a56f
(5) 0x2d4fa09b9f71d7b66a9c36d15eeda7311f49b837
(6) 0xc75df94d9a941cbab2a1297bd5770f0c036e4f89
(7) 0x7aaa5ff5f64970268183e06f56110a30571f753f
(8) 0x7c3b0b8193fff996b01bc77154fb91f3b361a646
(9) 0x2a5c667ba684bf0b7db61d7dec17751a6f6e03f8

Private Keys
==================
(0) 237fda9ce07f11cff672e58f633b514575613e96cb2653ea9d08ca69e882f042
(1) 50ea263a896e3b4ec3337ede58131e71dfdd71e7afa879b0957f3a3443b14b85
(2) dd251d5192bb0a73c1a9896f394099d646f6c8e2e96b88e7288f4e83d70a7392
(3) 3409d7a67896bbf2e6a8c9e414bb75284cf87fc66b9bb0e222cb8648a494709e
(4) e1229c76e7c1e5aad682c8b27da5d1a67171c7b3ad560fdb52cb1ae3855cc31d
(5) b627f6f491e8ed270733cd2483287cfa092d0b56b15de0f838ffc06257416c5b
(6) b25209ef82d3a44205d72b418b86476ac710afa1dcc582525954bc31db6c6832
(7) 20b540aa45c394306d38c1d37458d9c46d404311f822fc149fcd523c2e34d84d
(8) 838e1a99cca13ced482d9af2dbba6911a4ee3b676baa395a1462fd29fec070a5
(9) f586c3fe792d63b7459e399454d1798c21eb191db861aff67d9ec771cc22f83a

HD Wallet
==================
Mnemonic:      virus bamboo couch ankle afraid glide family attend empty bulb stadium cricket
Base HD Path:  m/44'/60'/0'/0/{account_index}

Listening on localhost:8545
  • 通过 truffle migrate 命令,对合约进行部署。

    • 使用 truffle migrate 命令,发布项目
  • 测试部署成功的智能合约

    • 输入以下命令打开 truffle 控制台,测试刚才我们部署的 Hello_blake_top 合约:
    • 在当前合约的目录下,执行 truffle console 打开一个控制台和部署的合约进行交互。
truffle(development)> var contract; 

undefined 

truffle(development)> Hello_mshk_top.deployed().then(function(instance){contract= instance;}); 

undefined 

truffle(development)> contract.say() 

'Hello blake' 

truffle(development)> contract.print("hello,blake") 

'hello,blake'
  • var contract 和 javascript 语法一样,表示声明一个contract变量。Hello_blake_top.deployed().then(function(instance){contract= instance;})表示,将Hello_blake_top合约主体,传递给contract变量。后面我们就可以直接使用变量contract分别调用say()方法和print(’’),得到我们想要的结果。

如果需要部署有参数的合约

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

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

// Don't deploy this contract if it has already been deployed
deployer.deploy(A, {overwrite: false});

// Set a maximum amount of gas and `from` address for the deployment
deployer.deploy(A, {gas: 4612388, from: "0x...."});

// 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 single batched request.
deployer.deploy([
  [A, arg1, arg2, ...],
  B,
  [C, arg1]
]);

// External dependency example:
//
// For this example, our dependency provides an address when we're deploying to the
// live network, but not for any other networks like testing and development.
// When we're deploying to the live network we want it to use that address, but in
// testing and development we need to deploy a version of our own. Instead of writing
// a bunch of conditionals, we can simply use the `overwrite` key.
deployer.deploy(SomeDependency, {overwrite: false});

更多的情况可以参见:https://truffleframework.com/docs/truffle/getting-started/running-migrations

以太坊代币空投方案

https://blog.csdn.net/xq723310/article/details/82904178
https://blog.csdn.net/ITleaks/article/details/82218485

在以太坊测试网上发行代币

https://www.jianshu.com/p/b680b9558b22

  • Token.sol
pragma solidity ^0.4.16;
contract Token{
    uint256 public totalSupply;

    function balanceOf(address _owner) public constant returns (uint256 balance);
    function transfer(address _to, uint256 _value) public returns (bool success);
    function transferFrom(address _from, address _to, uint256 _value) public returns   
    (bool success);

    function approve(address _spender, uint256 _value) public returns (bool success);

    function allowance(address _owner, address _spender) public constant returns 
    (uint256 remaining);

    event Transfer(address indexed _from, address indexed _to, uint256 _value);
    event Approval(address indexed _owner, address indexed _spender, uint256 
    _value);
}

contract TokenDemo is Token {

    string public name;                   
    uint8 public decimals;               
    string public symbol; 
    mapping (address => uint256) public balances;
    mapping (address => mapping (address => uint256)) allowed;              

    constructor(uint256 _initialAmount, string _tokenName, uint8 _decimalUnits, string _tokenSymbol) public {
        totalSupply = _initialAmount * 10 ** uint256(_decimalUnits);         
        balances[msg.sender] = totalSupply; 

        name = _tokenName;                   
        decimals = _decimalUnits;          
        symbol = _tokenSymbol;
    }

    function transfer(address _to, uint256 _value) public returns (bool success) {
        require(balances[msg.sender] >= _value && balances[_to] + _value > balances[_to]);
        require(_to != 0x0);
        balances[msg.sender] -= _value;//从消息发送者账户中减去token数量_value
        balances[_to] += _value;//往接收账户增加token数量_value
        emit Transfer(msg.sender, _to, _value);//触发转币交易事件
        return true;
    }


    function transferFrom(address _from, address _to, uint256 _value) public returns 
    (bool success) {
        require(balances[_from] >= _value && allowed[_from][msg.sender] >= _value);
        balances[_to] += _value;//接收账户增加token数量_value
        balances[_from] -= _value; //支出账户_from减去token数量_value
        allowed[_from][msg.sender] -= _value;//消息发送者可以从账户_from中转出的数量减少_value
        emit Transfer(_from, _to, _value);//触发转币交易事件
        return true;
    }
    function balanceOf(address _owner) public constant returns (uint256 balance) {
        return balances[_owner];
    }


    function approve(address _spender, uint256 _value) public returns (bool success)   
    { 
        allowed[msg.sender][_spender] = _value;
        emit Approval(msg.sender, _spender, _value);
        return true;
    }

    function allowance(address _owner, address _spender) public constant returns (uint256 remaining) {
        return allowed[_owner][_spender];//允许_spender从_owner中转出的token数
    }

}
  • AirDrop.sol
pragma solidity ^0.4.24;
 
contract Ownable {
  address public owner;
 
  constructor() public {
    owner = msg.sender;
  }

  modifier onlyOwner() {
    require(msg.sender == owner);
    _;
  }
}

interface Token {
  function balanceOf(address _owner) external constant returns (uint256 ) ;
  function transfer(address _to, uint256 _value) external ;
  event Transfer(address indexed _from, address indexed _to, uint256 _value);
}

contract Airdropper is Ownable {
    
    function AirTransfer(address[] _recipients, uint _values, address _tokenAddress) onlyOwner public returns (bool) {
        require(_recipients.length > 0);

        Token token = Token(_tokenAddress);
        
        for(uint j = 0; j < _recipients.length; j++){
            token.transfer(_recipients[j], _values);
        }
 
        return true;
    }
 
     function withdrawalToken(address _tokenAddress) onlyOwner public { 
        Token token = Token(_tokenAddress);
        token.transfer(owner, token.balanceOf(this));
    }

}

你可能感兴趣的:(#,Ethereum,区块链从,1.0,到,3.0,的技术分享锦集及讲解)