手把手教你从源代码开始搭建多节点以太坊私链(五)ubuntu下部署智能合约及代币发行

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sitebus/article/details/83474988

一、Ubuntu安装以太坊合约编译环境

安装solc

智能合约代码的编译可以通过第三方平台或者软件。不过,为了安全起见,还是搭建自己的编译器比较好。(But be aware that if the compiler is compromised, your contract is not safe. )按照下面的步骤来安装编译器solc。

sudo add-apt-repository ppa:ethereum/ethereum
sudo apt update
sudo apt install solc

二、编写第一个智能合约

下面实现了一个非常简单的智能合约。使用solidity语言。

pragma solidity ^0.4.13;

contract Simple {
  function arithmetics(uint _a, uint _b) returns (uint sum, uint product) {
    sum = _a + _b;
    product = _a * _b;
  }

  function multiply(uint _a, uint _b) returns (uint) {
    return _a * _b;
  }
}

三、部署智能合约

3.1 编译智能合约

solc -o . --bin --abi simple.sol

下图是编译结果,可以看到生成了Simple.abi接口文件和Simple.bin二进制编译文件:
在这里插入图片描述
用cat可以看到编译后的文件内容。
在这里插入图片描述

3.2 编辑智能合约编译结果,生成加载脚本

对于编译输出的abi文件和bin文件,需要做如下处理,才能够加载到geth里面并执行。

  1. 对于abi文件,做如下的包围编辑。
var simpleContract = eth.contract([原来abi内容])
var simpleContract = eth.contract([{"constant":false,"inputs":[{"name":"_a","type":"uint256"},{"name":"_b","type":"uint256"}],"name":"multiply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_a","type":"uint256"},{"name":"_b","type":"uint256"}],"name":"arithmetics","outputs":[{"name":"sum","type":"uint256"},{"name":"product","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"}])

下面是编辑结果的拷屏。
在这里插入图片描述

  1. 对于bin文件,做如下的包围编辑。
personal.unlockAccount(eth.accounts[0])
// 不同的合约有自己的实例名字,要注意区分。这里是simple 。
var simple = simpleContract.new(
{ from: eth.accounts[0],
data: "0x(原来的bin内容)",
gas: 500000
})

注意这里的gas汽油费,如果只是发现一个很简单的智能合约,50万wei还可以。
编辑后的结果如下:
在这里插入图片描述

3.3 加载脚本

进入geth控制台,执行如下的命令。

# 进入geth控制台
geth attach /data/00/geth.ipc
# 加载脚本
> loadScript("contract/Simple.abi")
> loadScript("contract/Simple.bin")

在这里插入图片描述

加载bin之后,如果不进行挖矿,会一直处于pending状态,合约不能真正执行。
在这里插入图片描述
可以看到address是undefined。
挖矿成功后,address就会写上实际的合约地址。

4. 执行智能合约

执行结果如下图。
在这里插入图片描述

四、发行代币

1. 写标准ERC代币的智能合约

使用solidity 语言,写一个实现标准ERC20接口的代币发行智能合约。

pragma solidity ^0.4.20;

contract ERC20{
    string public name;
    string public symbol;
    uint8 public decimals;
    uint public totalSupply;

    mapping(address => uint256) internal balances;
    mapping (address => mapping(address => uint256)) internal allowed;

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

    constructor() public{
        name = "MeiToken";
        symbol = "MTK";
        // 本货币使用最大小数位数(定义货币最小单位)
        decimals = 2;
        // 总数为10亿
        totalSupply = 1000000000*10**uint(decimals);
        balances[msg.sender] = totalSupply;
    }

    // 获取指定账户余额
    function balanceOf(address tokenOwner) public constant returns (uint balance) {
        return balances[tokenOwner];
    }

    // 转账到指定账户
    function transfer(address _to, uint256 _value) public returns (bool success){
        success = false;
        // 地址不能为0
        require(_to != address(0));
        // 目标地址不能跟sender相同
        require(msg.sender != _to);
        require(balances[msg.sender] >= _value);
        require(balances[_to] + _value > balances[_to]);

        balances[msg.sender] -= _value;
        balances[_to] += _value;

        emit Transfer(msg.sender, _to, _value);
        success = true;
    }

    // 从一个账户转账到另外一个账户
    function transferFrom(address _from, address _to, uint256 _value) public returns (bool success){

        require(_to != address(0));
        require(balances[_from] >= _value);
        require(allowed[_from][msg.sender] >= _value);
        require(balances[_to] + _value > balances[_to]);

        balances[_from] -= _value;
        balances[_to] += _value;
        emit Transfer(_from, _to, _value);
        success = true;
    }

    // 授权指定账户从本账户可以取现的额度
    function approve(address _spender, uint256 _value) public returns (bool success){

        allowed[msg.sender][_spender] = _value;
        emit Approval(msg.sender, _spender, _value);
        success = true;
    }

    // 查询给某个账户授权取现额度
    function allowance(address _owner, address _spender) view public returns (uint256 remaining){
        return allowed[_owner][_spender];
    }
}

2. 部署代币智能合约

按照第三节的方式完成代币智能合约的编译和结果编辑,并进行加载。
注意ERC20.bin里面需要的gas汽油费,由于智能合约有点复杂,50万wei不够。50万wei连待处理列表都进不去。
如下所示:

> txpool.status
{
  pending: 0,
  queued: 0
}

也没有错误提示。最后设置为200万wei才成功。

# 编译智能合约
solc -o . --bin --abi ERC20.sol
# 按照3.2的介绍,编辑智能合约编译结果,生成加载脚本。
# 进入geth控制台
geth attach /data/00/geth.ipc
# 加载代币智能合约相关文件
> loadScript("contract/ERC20.abi")
> loadScript("contract/ERC20.bin")

3. 代币测试

在这里插入图片描述

挖矿成功后,address内容填上了。
在这里插入图片描述

执行情况:

> erc20.balanceOf.call(eth.accounts[0])
100000000000
> erc20.balanceOf.call(eth.accounts[1])
0
> erc20.symbol.call()
"MTK"
> erc20.name.call()
"MeiToken"
>
> personal.unlockAccount(eth.accounts[0])
Unlock account 0x4d82606518349bcfc1afb4dcf54415f4bd2bed47
Passphrase:
true
> erc20.transfer.sendTransaction(eth.accounts[1],500000,{from:eth.accounts[0]})
"0xde4166620cd352ddcf612a649cb4ac67c530cb710fe064b0d63b34c8ba2bf3ac"
> txpool.status
{
  pending: 1,
  queued: 0
}
> txpool.status
{
  pending: 0,
  queued: 0
}
> erc20.balanceOf.call(eth.accounts[1])
500000
> erc20.balanceOf.call(eth.accounts[0])
99999500000

下面是执行结果拷屏:
在这里插入图片描述

容易掉坑之1:call()与sendTransaction()没有分清。
执行的时候,对于不需要修改内容的调用,可以使用call()函数来调用合约的方法。–其实这种情况也可以直接跳过call()都可以。
对于需要修改内容的调用,需要使用sendTransaction来发起一笔交易,并支付交易燃料费。
我开始使用erc20.transfer.call(eth.accounts[1],500000)来发起转账,调用结果返回true,但是账户余额根本没有变。耗费了我几乎一天的时间。
call与sendTransaction的区别详情可以参考网页What is the difference between a transaction and a call?

容易掉坑之2:sendTransaction操作中没有指定支付gas燃料的账户
跟call不一样,仅仅有本身的调用参数不够,需要知道支付gas燃料的账户。
即最后追加一个参数{from:address}
在这里插入图片描述
容易掉坑之3:sendTransaction操作中忘记解锁账户。
如果不解锁支付燃料的账户,转账也是不能成功的。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/sitebus/article/details/83474988