专栏首页字节流动在以太坊私有链上部署智能合约

在以太坊私有链上部署智能合约

上节简单介绍了基于以太坊搭建私有链以及挖矿和交易,在部署智能合约之前请确保私有链上的账户有余额,因为部署智能合约需要消耗 Gas ,而 Gas 需要 ether 币来兑换。

1. 智能合约

什么是智能合约?智能合约是存储在以太坊网络特定地址的一组代码和数据集。在以太坊网络中智能合约以以太坊虚拟机(EVM)字节码的形式存在,由以太坊虚拟机解释执行。用于编写智能合约常用的语言有 Solidity 、Serpent 以及 LLL ,其中最著名的就是 Solidity 。智能合约的部署和执行都需要燃料(Gas),一旦部署便不能修改。

2. 部署智能合约

部署智能合约可以使用以太坊命令行客户端(Geth Console)和 Mist 。 选用 Solidity 官网的例子 Coin 。

pragma solidity ^0.4.0;
contract Coin {
    // The keyword "public" makes those variables
    // easily readable from outside.
    address public minter;
    mapping (address => uint) public balances;

    // Events allow light clients to react to
    // changes efficiently.
    event Sent(address from, address to, uint amount);

    // This is the constructor whose code is
    // run only when the contract is created.
    constructor() public {
        minter = msg.sender;
    }

    function mint(address receiver, uint amount) public {
        require(msg.sender == minter);
        require(amount < 1e60);
        balances[receiver] += amount;
    }

    function send(address receiver, uint amount) public {
        require(amount <= balances[msg.sender], "Insufficient balance.");
        balances[msg.sender] -= amount;
        balances[receiver] += amount;
        emit Sent(msg.sender, receiver, amount);
    }
}

以太坊命令行客户端部署

打开网站

https://remix.ethereum.org,

将上面的智能合约代码替换为 Coin 的例子。

然后选择 Compile ,点击 Start to compile 生成 EVM 的字节码,复制生成的 ABI 和 ByteCode 。

ABI:

[
    {
        "constant": false,
        "inputs": [
            {
                "name": "receiver",
                "type": "address"
            },
            {
                "name": "amount",
                "type": "uint256"
            }
        ],
        "name": "mint",
        "outputs": [],
        "payable": false,
        "stateMutability": "nonpayable",
        "type": "function"
    },
    {
        "constant": false,
        "inputs": [
            {
                "name": "receiver",
                "type": "address"
            },
            {
                "name": "amount",
                "type": "uint256"
            }
        ],
        "name": "send",
        "outputs": [],
        "payable": false,
        "stateMutability": "nonpayable",
        "type": "function"
    },
    {
        "anonymous": false,
        "inputs": [
            {
                "indexed": false,
                "name": "from",
                "type": "address"
            },
            {
                "indexed": false,
                "name": "to",
                "type": "address"
            },
            {
                "indexed": false,
                "name": "amount",
                "type": "uint256"
            }
        ],
        "name": "Sent",
        "type": "event"
    },
    {
        "inputs": [],
        "payable": false,
        "stateMutability": "nonpayable",
        "type": "constructor"
    },
    {
        "constant": true,
        "inputs": [
            {
                "name": "",
                "type": "address"
            }
        ],
        "name": "balances",
        "outputs": [
            {
                "name": "",
                "type": "uint256"
            }
        ],
        "payable": false,
        "stateMutability": "view",
        "type": "function"
    },
    {
        "constant": true,
        "inputs": [],
        "name": "minter",
        "outputs": [
            {
                "name": "",
                "type": "address"
            }
        ],
        "payable": false,
        "stateMutability": "view",
        "type": "function"
    }
]

ByteCode, 其中 object 字段对应的就是就是我们需要的 EVM 字节码(下面只贴出来部分代码):

{
    "linkReferences": {},
    "object": "608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506104df806100606000396000f300608060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806307546172146...",
    "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH2 0x10 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP CALLER PUSH1 0x0 DUP1 PUSH2 0x100 EXP DUP2 SLOAD DUP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF MUL NOT AND SWAP1 DUP4 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND MUL OR SWAP1 SSTORE POP PUSH2 0x4DF DUP1 PUSH2 0x60 PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN STOP PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x4 CALLDATASIZE LT PUSH2 0x62 JUMPI
    ...",
    "sourceMap": "24:902:0:-;;;427:57;8:9:-1;5:2;;;30:1;27;20:12;5:2;427:57:0;467:10;458:6;;:19;;;;;;;;;;;;;;;;;;24:902;;;;;;"
}

另外 ABI 需要转义成字符串,使用网站

http://www.bejson.com,选择【删除空格并转义】。

[{\"constant\":false,\"inputs\":[{\"name\":\"receiver\",\"type\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"mint\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"receiver\",\"type\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"send\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Sent\",\"type\":\"event\"},{\"inputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"address\"}],\"name\":\"balances\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"minter\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]

通过 Geth Console 创建合约对象。

// 声明合约 coin
> var abi = JSON.parse('[{\"constant\":false,\"inputs\":[{\"name\":\"receiver\",\"type\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"mint\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"receiver\",\"type\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"send\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Sent\",\"type\":\"event\"},{\"inputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"address\"}],\"name\":\"balances\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"minter\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]')
> coin = web3.eth.contract(abi)

// 需要为字节码添加,'0x' 前缀。
> byteCode = "0x608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506104df80610060600039
6000f300608060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063075461721461006757806327e235e3146100be57806340c10f1914610115578063d0679d34146101
..."

"0x608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506104df806100606000396000f30060806
0405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063075461721461006757806327e235e3146100be57806340c10f19146101..."
// 就是部署智能合约需要的燃料(Gas)值。
> eth.estimateGas({data: byteCode})
406661
// 我们输入的燃料值需要大于 406661 
>
// 解锁 coinbase 账户
> personal.unlockAccount(eth.coinbase)
Unlock account 0x33213084015dab454fee16eb369fa2a8e6e65eee
Passphrase:
true

// 创建 coin 合约对象,部署合约
> coinContractIns = coin.new({data: byteCode gas: 410000, from: eth.coinbase}, function(e, contract){
  if(!e){
    if(!contract.address){
      console.log("Contract transaction send: Transaction Hash: "+contract.transactionHash+" waiting to be mined...");
    }else{
      console.log("Contract mined! Address: " + contract.address);
      console.log(contract);
    }
  }else{
    console.log(e)
  }
})
INFO [10-07|12:19:00.077] Submitted contract creation              fullhash=0x114e241d2a62064b0d41ec143c595768f648306595a931563a3add4cee3c2d77 contract=0x7227255a42b167061B4260A0b1827e3E6F737905
Contract transaction send: Transaction Hash: 0x114e241d2a62064b0d41ec143c595768f648306595a931563a3add4cee3c2d77 waiting to be mined...
{
  abi: [{
      constant: false,
      inputs: [{...}, {...}],
      name: "mint",
      outputs: [],
      payable: false,
      stateMutability: "nonpayable",
      type: "function"
  }, {
      constant: false,
      inputs: [{...}, {...}],
      name: "send",
      outputs: [],
      payable: false,
      stateMutability: "nonpayable",
      type: "function"
  }, {
      anonymous: false,
      inputs: [{...}, {...}, {...}],
      name: "Sent",
      type: "event"
  }, {
      inputs: [],
      payable: false,
      stateMutability: "nonpayable",
      type: "constructor"
  }, {
      constant: true,
      inputs: [{...}],
      name: "balances",

// coin 合约并没有部署成功,需要启动挖矿
> miner.start();
> // 等待约 12 以上区块生成,停止挖矿
> miner.stop();

// 查看合约是否部署成功
> eth.getCode(coinContractIns.address)
"0x608060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063075461721461006757806327e235e31461
b600080fd5b34801561007357600080fd5b5061007c6101af565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff
00080fd5b506100ff600480360381019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506101d4565b6040518082815260200191505060405180
..."
// 合约部署成功


//解锁账户
> eth.defaultAccount=eth.coinbase
"0x33213084015dab454fee16eb369fa2a8e6e65eee" // coinbase 账户
> personal.unlockAccount(eth.coinbase)
Unlock account 0x33213084015dab454fee16eb369fa2a8e6e65eee
Passphrase:
true

// 查看账户在合约中的余额
> coinContractIns.balances("0x33213084015DAB454fEe16eb369FA2a8E6E65eEE") //替换成自己的 coinbase 账户
0
// 由于合约没有被执行,合约中的余额为 0

// 通过合约中的 mint 接口设置 coinbase 账户在合约中的 coin 数目为 30000 。
> coinContractIns.mint("0x33213084015DAB454fEe16eb369FA2a8E6E65eEE", 30000);
INFO [10-07|12:37:31.191] Submitted transaction                    fullhash=0x788857480e7c33d59f5893e7287c697d33e5ad29a52817e19930ec26178e62b3 recipient=0x7227255a42b1
"0x788857480e7c33d59f5893e7287c697d33e5ad29a52817e19930ec26178e62b3"

// 上述操作并未执行,需要启动挖矿
> miner.start();
> // 等待约 12 以上区块生成,停止挖矿
> miner.stop();

// 挖矿结束,查看 coinbase 账户 coin 数量
> coinContractIns.balances("0x33213084015DAB454fEe16eb369FA2a8E6E65eEE")
30000

//解锁 coinbase 账户,进行交易
> personal.unlockAccount(eth.coinbase)
Unlock account 0x33213084015dab454fee16eb369fa2a8e6e65eee
Passphrase:
true
// 发送给账户 0x55213084015DAB454fEe16eb369FA2a8E6E65ebb(这个账户可以随意填写,保证 20 Byte)10000 个 coin
> coinContractIns.send("0x55213084015DAB454fEe16eb369FA2a8E6E65ebb", 10000);
INFO [10-07|12:47:17.161] Submitted transaction                    fullhash=0xf0ec8dc4daf6d85cf55be8289a37fbd271437658b2a02fc330272403192d863e recipient=0x7227255a42
"0xf0ec8dc4daf6d85cf55be8289a37fbd271437658b2a02fc330272403192d863e"
// 交易还未执行,这个账户余额还是 0 
> coinContractIns.balances("0x55213084015DAB454fEe16eb369FA2a8E6E65ebb")
0
// 上述操作并未执行,需要启动挖矿
> miner.start();
> // 等待约 12 以上区块生成,停止挖矿
> miner.stop();

// 挖矿结束,查看账户的 coin 余额,合约执行成功。
> coinContractIns.balances("0x55213084015DAB454fEe16eb369FA2a8E6E65ebb")
10000
> coinContractIns.balances("0x33213084015DAB454fEe16eb369FA2a8E6E65eEE")
20000
>

Mist 部署

通过 Mist 部署智能合约非常简单方便。打开 Mist ,选择【CONTRACT】=>【DEPLOY NEW CONTRACT】,

在 【SOLIDITY CONTRACT SOURCE CODE】 中,将 coin 例子代码粘贴进来,将合约改为 LTCCoin 。

点击【DEPLOY】输入账户密码创建合约(这里创建账户选择 Account1)。

点击【WALLETS】在下面的【Latest Transaction】可以看到合约正在创建中,这里需要在 Geth Console 启动挖矿,合约才能创建成功,当然进行任何交易操作都需要挖矿来完成。

挖矿后,选择【CONTRACT】查看合约部署成功。

然后在右侧【Select function】选择相应操作,注意执行 mint 函数执行账户需要选择为创建账户 Account1 ,交易成功需要挖矿。

3. 参考

https://github.com/EthFans/wiki/wiki/%E6%99%BA%E8%83%BD%E5%90%88%E7%BA%A6 https://solidity.readthedocs.io/en/develop/introduction-to-smart-contracts.html

本文分享自微信公众号 - 字节流动(google_developer),作者:haohao

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-10-09

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 推荐一款强大的 Android OpenGL ES 调试工具

    GAPID (Graphics API Debugger)是 Google 的一款开源且跨平台的图形开发调试工具,用于记录和检查应用程序对图形驱动程序的调用,支...

    字节流动
  • 你真的了解 Java volatile 关键字吗?

    happens-before 规则中有一条是 volatile 变量规则:对一个 volatile 域的写,happens-before 于任意后续对这个 vo...

    字节流动
  • NDK OpenGL ES 3.0 开发(二十二):PBO

    OpenGL PBO(Pixel Buffer Object),被称为像素缓冲区对象,主要被用于异步像素传输操作。PBO 仅用于执行像素传输,不连接到纹理,且与...

    字节流动
  • SILVA、GREENGENES、RDP三大数据库的序列探索统计

    最近对16s的三大数据库的序列的具体序列情况挺好奇的,决定统计一下各个序列的长度分布情况,以及这些序列具体分布在哪几个V区,有助于我解决后面16So数据的问题。...

    用户1075469
  • 使用ELK分析腾讯云CLB日志

    最近在使用腾讯云,想对访问日志进行收集与分析,发现CLB(负责均衡)日志只能保存到COS上面,而且是每个CLB没小时压发送个gz压缩包到COS。

    三杯水Plus
  • dbGaP加密数据权限申请和数据解密

    我们在NCBI、TCGA、GEO等数据库下载数据时,经常遇到controlled access(限制下载)的数据,不知道怎么弄,有时选择其他可以下载的数据代替...

    企鹅号小编
  • three.js鼠标控制物体旋转

    当我们需要固定场景背景,固定摄像机的时候。移动旋转物体可以使用Three.js提供的OrbitControls.js,也可以手动写控制器。

    tianyawhl
  • Matlab R2017b快速入门

    版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/...

    乐百川
  • Elasticsearch SQL介绍及实例

    Elasticsearch 是一个全文搜索引擎,具有您期望的所有优点,例如相关性评分,词干,同义词等。而且,由于它是具有水平可扩展的分布式文档存储,因此它可以处...

    zhisheng
  • 关于前端使用SiteMesh的一些介绍 转

    在网站开发的过程中,通常一个网站会有一个整体的风格,页面都有很多共同的菜单,横栏的底部信息。以前我们会采用include标签在每个jsp页面中来不断的包含各种h...

    wuweixiang

扫码关注云+社区

领取腾讯云代金券