以太坊系列 - 智能合约开发与调试

本文默认智能合约所需要的相关环境Geth、Truffle、Solidity、Ganache等都是完备的!
主要介绍:

  1. Remix IDE环境如何与指定的链进行连接;
  2. 编译 部署与调试智能合约
  3. 使用 zeppelin-solidity

1.安装 Remix IDE

    npm install remix-ide -g

网络资料相关内容都没有作特别强调,故在此特别说明一下:
开发调试过程中,使用本地的环境相对而言,可以更加方便直观的与本地代码进行关联
安装好remix-ide后,可以在任意路径下运行命令行命令 remix-ide
然后打开网页 http://localhost:8080
左侧的menu按钮最后一个“连接到本地”, 点击之后,确认连接,左侧会多一个localhost工作目录
此目录即命令行运行 remix-ide 的路径, 也是智能合约代码路径(也就是DAPP开发的框架环境)

2.Geth启动节点命令及相关参数

   geth --datadir=data --networkid 55661 --port 2000 --rpc --rpcaddr 0.0.0.0 --rpcapi “db,eth,net,web3,personal” --rpcport 8102 --rpccorsdomain "*" --bootnodes "enode://0a2c7ee420570b3f6d59f214731d0d84aa5297f8b704939fd95ad643a752e34d7623a138e22b7849229b913c2b75b54ac3042dd008f0a3500a1ff2715cd9b4f1@192.168.80.30:30301" console

如果没有 --rpccorsdomain “*” 在remix-ide网页环境中无法连接到链!

*相关控制台命令参数说明:参见 https://learnblockchain.cn/2017/11/29/geth_cmd_options/

3.一个简单的存储智能合约

a.在SimpleStorage项目的contract路径下添加SimpleStorage.sol文件:

    pragma solidity ^0.4.23;
    contract SimpleStorage {
        uint myVariable;

        constructor() public {
        }

        //将值存储在该变量中
        function set(uint x) public {
            myVariable = x;
        }

        //查询存储的值
        function get() constant public returns (uint) {
            return myVariable;
        }
    }

b.在SimpleStorage项目的migrations路径下添加S2_deployed_simplestorage.js文件:

    var SimpleStorage = artifacts.require("SimpleStorage");

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

c.在命令行下编译智能合约:

    truffle compile

d.打开truffle控制台

    truffle develop (启动了一个基于Ganache的控制台,内置于Truffle的本地开发区块链)
    Accounts:
    (0) 0x627306090abab3a6e1400e9345bc60c78a8bef57
    (1) 0xf17f52151ebef6c7334fad080c5704d77216b732
    (2) 0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef
    (3) 0x821aea9a577a9b44299b9c15c88cf3087f3b5544
    (4) 0x0d1d4e623d10f9fba5db95830f7d3839406c6af2
    (5) 0x2932b7a2355d6fecc4b5c0b6bd44cc31df247a2e
    (6) 0x2191ef87e392377ec08e7c08eb105ef5448eced5
    (7) 0x0f4f2ac550a1b4e2280d04c21cea7ebd822934b5
    (8) 0x6330a553fc93768f612722bb8c2ec78ac90b3bbc
    (9) 0x5aeda56215b167893e80b4fe645ba6d5bab767de

    Private Keys:
    (0) c87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3
    (1) ae6ae8e5ccbfb04590405997ee2d52d2b330726137b875053c36d94e974d162f
    (2) 0dbbe8e4ae425a6d2687f1a7e3ba17bc98c673636790f1b8ad91193c05875ef1
    (3) c88b703fb08cbea894b6aeff5a544fb92e78a18e19814cd85da83b71f772aa6c
    (4) 388c684f0ba1ef5017716adb5d21a053ea8e90277d0868337519f97bede61418
    (5) 659cbb0e2411a44db63778987b1e22153c086a95eb6b18bdf89de078917abc63
    (6) 82d052c865f5763aad42add438569276c00d3d88a2d062d36b2bae914d58b8c8
    (7) aa3680d5d48a8283413f7a108367c7299ca73f553735860a87b08f39395618b7
    (8) 0f62d96d6675f32685bbdb8ac13cda7c23436f63efbb9d07700d8669ff12b7c4
    (9) 8d5366123cb560bb606379f90a0bfd4769eecc0557f1b362dcae9012b548b1e5

e.部署智能合约

    migrate

    Using network 'develop'.

    Running migration: 1_initial_migration.js
    Deploying Migrations...
    ... 0xb70a114fd49735ef6c56ae86e329c38544c70729924e4f8f895e495d38ec3877
    Migrations: 0x8cdaf0cd259887258bc13a92c0a6da92698644c0
    Saving successful migration to network...
    ... 0xd7bc86d31bee32fa3988f1c1eabce403a1b5d570340a3a9cdba53a472ee8c956
    Saving artifacts...
    Running migration: 2_deployed_simplestorage.js
    Deploying SimpleStorage...
    ... 0x1b0294e4b04df57c8ff79001943c913465dc687162553e2a4a2aa0e57dc9bd0c
    SimpleStorage: 0x345ca3e014aaf5dca488057592ee47305d9b3e10
    Saving successful migration to network...
    ... 0xf36163615f41ef7ed8f4a8f192149a0bf633fe1a2398ce001bf44c43dc7bdda0
    Saving artifacts...

    此时执行get()函数:
        SimpleStorage.deployed().then(function(instance){
     return instance.get.call();}).then(function(value){
     return value.toNumber()});
    结果是 0 

    然后调用set()函数:
        SimpleStorage.deployed().then(function(instance){
     return instance.set(6);});
        存储的值就变为6了
    
    再次调用get()函数:
        SimpleStorage.deployed().then(function(instance){
     return instance.get.call();}).then(function(value){
     return value.toNumber()});
    结果是 6

    *如果需要

4. truffle 命令行下出现错误时如何debug ?

    正常情况下,logs里面是空的内容
    {
      tx: '0x070e447c68b4337264ccfd83376e40d48f9c580a9a6e54694c7eb420326369ab',
    receipt: 
        {
      transactionHash: '0x070e447c68b4337264ccfd83376e40d48f9c580a9a6e54694c7eb420326369ab',
        transactionIndex: 0,
        blockHash: '0xc7a1b8ee4bd6364d482576adae45c84caca8796b3cce59451eacf8038a097d2c',
        blockNumber: 16,
        gasUsed: 41669,
        cumulativeGasUsed: 41669,
        contractAddress: null,
        logs: [],
        status: '0x01',
        logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' },
    logs: [] 
    }

    修改合约中的set函数:
    function set(uint x) public {
     
        myVariable = x;
        if (x%2 == 0) {
     
        emit Odd();
        } else {
     
        emit Even();
        }
    }
    重新编译部署 compile / migrate --reset
    再次执行set()函数:
        SimpleStorage.deployed().then(function(instance){
     return instance.set(4);});

    {
      tx: '0x2cc0d39fc0bec51835df91343e64577b34ae335f7d998143349d5ab8b3d63181',
    receipt: 
    {
      transactionHash: '0x2cc0d39fc0bec51835df91343e64577b34ae335f7d998143349d5ab8b3d63181',
        transactionIndex: 0,
        blockHash: '0x1ec12a731aff546eb84d87402cfc36f285c7e14faa9f6d14de22ea94f9434bfe',
        blockNumber: 11,
        gasUsed: 42514,
        cumulativeGasUsed: 42514,
        contractAddress: null,
        logs: [ [Object] ],
        status: '0x01',
        logsBloom: '0x00000000000000000000000000000000000000000000000002000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000800000000000000' },
    logs: 
    [ {
      logIndex: 0,
        transactionIndex: 0,
        transactionHash: '0x2cc0d39fc0bec51835df91343e64577b34ae335f7d998143349d5ab8b3d63181',
        blockHash: '0x1ec12a731aff546eb84d87402cfc36f285c7e14faa9f6d14de22ea94f9434bfe',
        blockNumber: 11,
        address: '0xfb88de099e13c3ed21f80a7a1e49f8caecf10df6',
        type: 'mined',
        event: 'Odd',
        args: {
     } } ] }

此时logs中记录的是异常情况相关信息【具体原理尚未弄清~】
此时最重要的是:transactionHash: ‘0x2cc0d39fc0bec51835df91343e64577b34ae335f7d998143349d5ab8b3d63181’,
在truffle控制台下, debug 0x2cc0d39fc0bec51835df91343e64577b34ae335f7d998143349d5ab8b3d63181 连续回车
可以跟踪到相关代码

    !!! Truffle includes an integrated debugger so that you can debug transactions made against your contracts. This debugger looks and feels like existing command line debuggers available for traditional development environments.
    !!! Debugging a transaction on the blockchain is different than debugging traditional applications (for instance, applications written in C++ or Javascript). When debugging a transaction on the blockchain, you're not running the code in real-time; instead, you're stepping over the historical execution of that transaction, and mapping that execution onto its associated code. This gives us many liberties in debugging, in that we can debug any transaction, any time, so long as we have the code and artifacts for the contracts the transaction interacted with. Think of these code and artifacts as akin to the debugging symbols needed by traditional debuggers.

5.使用 zeppelin-solidity 发布 ERC-20标准Token

按照之前的步骤创建智能合约项目目录,并在初始化,然后安装 zeppelin-solidity

    sudo npm install zeppelin-solidity
    
    命令行下调用Token合约:
    let contract = ZTJCoin.deployed().then(instance => contract = instance);
    contract.name();
    contract.symbol();
    contract.decimals();
    contract.INITIAL_SUPPLY();
    contract.balanceOf("0x627306090abab3a6e1400e9345bc60c78a8bef57");
    contract.balanceOf("0xf17f52151ebef6c7334fad080c5704d77216b732");
    contract.transfer("0xf17f52151ebef6c7334fad080c5704d77216b732",8888);
    contract.balanceOf("0x627306090abab3a6e1400e9345bc60c78a8bef57");
    contract.balanceOf("0xf17f52151ebef6c7334fad080c5704d77216b732");

6.使用OpenZeppelin的StandardToken来创建自己的TutorialToken【ERC20】

    添加TutorialToken.sol文件
    pragma solidity ^0.4.24;
    import "zeppelin-solidity/contracts/token/ERC20/StandardToken.sol";

    contract TutorialToken is StandardToken {
     
        string public name = 'TutorialToken';
        string public symbol = 'TT';
        uint public decimals = 2;
        uint public INITIAL_SUPPLY = 12000;
        
        constructor() public {
     
            totalSupply_ = INITIAL_SUPPLY;
            balances[msg.sender] = INITIAL_SUPPLY;
        }
    }

在上面的代码中,import了StandardToken.sol,并声明TutorialToken继承自StandardToken。这样就继承了StandardToken合约中所有变量和函数。继承的合约可以被覆盖,只要在子类重定义对应的变量与函数就行了。

接下来,设置Token的参数,需要定义自己的name,symbol,decimals和INITIAL_SUPPLY。

添加2_deploy_contracts.js文件

    var TutorialToken = artifacts.require("./TutorialToken.sol");

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

启动Ganache 配置app.js的端口与Ganache端口一致
truffle console 启动并连接上Ganache内部的链
项目初始化时已经安装了lite-server
npm run dev 运行dapp程序, 即可以执行账户转账相关的操作了


往期精彩回顾:
区块链知识系列
密码学系列
零知识证明系列
共识系列
公链调研系列
比特币系列
以太坊系列
EOS系列
联盟链系列
Fabric系列
智能合约系列
Token系列

你可能感兴趣的:(以太坊系列,智能合约系列,以太坊,remix,truffle,Solidity,Ganache)