区块链 之 部署和调用以太坊智能合约

Posted 祁峰

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了区块链 之 部署和调用以太坊智能合约相关的知识,希望对你有一定的参考价值。

区块链 之 部署和调用以太坊智能合约

作者:邹祁峰
邮箱:Qifeng.zou.job@hotmail.com
博客:http://blog.csdn.net/qifengzou
日期:2018.03.04 01:18
转载请注明来自”祁峰”的CSDN博客

1 引言

智能合约就像我们业务的后台逻辑, 其运行在以太坊平台上. 以太坊就像操作系统, 其天然的提供了区块链的特性. 绝大多数开发人员只需关注使用Solidity编写智能合约实现业务逻辑, 而无需去修改以太坊的特性. 因此, 此文中将重点讲述如何在以太坊上部署和调用智能合约.
为方便大家理解智能合约和以太坊之间的关系,可以用一下几个概念做类比.

序号通用概念以太坊
01操作系统以太坊
02C/C++/Java/GoSolidity
03类+函数智能合约

2 智能合约

为了让大家对智能合约的编写/部署/调用有一个全面的了解.以下将以一个实例串联整个过程.

2.1 编写智能合约

智能合约的编写,编译以及部署可以通过网页版Remix,也可通过本地truffle框架进行操作.以下以truffle框架下进行操作.[注:truffle的使用请自查资料]
新建Adoption.sol文件:

# mkdir pet-adopt // 新建项目目录
# truffle init // 初始化truffle框架, 将会自动生成如下目录结构
# tree // 查看目录结构
.
├── contracts // 智能合约存放目录
│ └── Migrations.sol
├── migrations // 智能合约部署脚本
│ └── 1_initial_migration.js
├── test
└── truffle-config.js

进入智能合约存放目录contracts,并创建智能合约Adoption.sol文件,输入如下内容:

pragma solidity ^0.4.17;

contract Adoption 
    address[16] public adopters;  // 保存领养者的地址

    // 领养宠物
    function adopt(uint petId) public returns (uint) 
        require(petId >= 0 && petId <= 15);  // 确保id在数组长度内

        adopters[petId] = msg.sender;        // 保存调用这地址
        return petId;
    

    // 返回领养者
    function getAdopters() public view returns (address[16]) 
        return adopters;
    

2.2 编译智能合约

完成智能合约的编辑后,则需编译智能合约.在项目根目录输入如下指令:

# truffle compile // 编译智能合约
Compiling ./contracts/Adoption.sol…
Compiling ./contracts/Migrations.sol…
Writing artifacts to ./build/contracts
# tree // 查看目录结构
.
├── build
│ └── contracts
│ ├── Adoption.json // 编译结果
│ └── Migrations.json
├── contracts
│ ├── Adoption.sol
│ └── Migrations.sol
├── migrations
│ └── 1_initial_migration.js
├── test
└── truffle-config.js

完成编译后, 编译结果将会输出到./build/contracts/Adoption.json文件中.

2.3 部署智能合约

完成智能合约的编译后,便可进行智能合约的部署了.部署智能合约Adoption.sol的大体步骤如下:
1.启动以太坊私链
既然要将智能合约部署到以太坊网络,在部署之前需要搭建好以太坊网络.搭建以太坊私链的过程可参考博文: <<区块链 之 搭建以太坊私有链>>
以太坊私有链--rpcport为8545.
2.指定以太坊平台
首先需要指明将智能合约部署到哪个以太坊网络,只需指明该以太坊网络的某一个节点便可. 在根目录下的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
    
  
;

3.添加部署脚本
需要在migrations中添加部署脚本:2_deploy_adoption.js. 其格式如下:

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

module.exports = function(deployer) 
      deployer.deploy(Adoption);
;

4.执行部署操作
完成以上的配置后,可通过执行以下命令部署智能合约到指定的以太坊私有链中:

# truffle migrate

如过执行以上命令后出现以下信息时, 表示智能合约部署失败:

Using network ‘development’.

Running migration: 1_initial_migration.js
Deploying Migrations…
… undefined
Error encountered, bailing. Network state unknown. Review successful transactions manually.
Error: authentication needed: password or unlock
at Object.InvalidResponse (/usr/local/lib/node_modules/truffle/build/webpack:/~/web3/lib/web3/errors.js:38:1)

信息中存在Error: authentication needed: password or unlock的提示,则需要在以太坊私链中执行如下操作:

# geth --datadir ./data/00 --networkid 15 --port 61910 --rpc --rpcport 8545 --rpcapi 'db,net,eth,web3' --rpccorsdomain '*' console
> personal.unlock(eth.accounts[0], '123456') // 解锁
true
> miner.start() // 开始挖矿.

完成以上操作后, 便可再次向以太坊私有链部署智能合约.

# truffle migrate / /部署智能合约
Using network ‘development’.

Running migration: 1_initial_migration.js
Deploying Migrations…
… 0x0d55306dc39c028eed2867c819bd9c73d8bfbfeaac071613ef43f4456cdf72cf
Migrations: 0xe23b05e83fbbc5e57d8fe5e1bf0bd460b03f1541
Saving successful migration to network…
… 0x1e646661887966b0cfeda56ce317995a9e9189126828a8b56f6627e5a20207f1
Saving artifacts…
Running migration: 2_deploy_adopt.js
Deploying Adoption…
… 0xed2ea576bc349d6688f66758a19cb7d889da459df615a7440ed33ec8d816e486
Adoption: 0xff8f47f643827fc324ac9575b8b4981d042ce5e7
Saving successful migration to network…
… 0x99db86745721494b955e9d4f3099d91d180435a792f75f2dc085c143337e7aa2
Saving artifacts…

以上日志提示智能合约已经部署成功, 同时以上日志Adoption: 0xff8f47f643827fc324ac9575b8b4981d042ce5e7中也指明了智能合约Adoption地址为:0xff8f47f643827fc324ac9575b8b4981d042ce5e7, 该地址将会在智能合约的调用中被使用.

2.4 调用智能合约

为了保证后续智能合约的调用能够正常执行, 以太坊私有链中至少有一个节点正在挖矿.
要调用刚才部署的智能合约, 需要找到被部署智能合约的abi信息和地址.
1.智能合约abi信息
智能合约的abi信息在编译后的文件中, 如:Adoption.sol被编译后, 其abi信息存储在Adoption.json文件中.

# tree
.
├── build
│ └── contracts
│ ├── Adoption.json // 智能合约abi信息存储在该文件中
│ └── Migrations.json
├── contracts
│ ├── Adoption.sol // 智能合约
│ └── Migrations.sol
├── migrations
│ ├── 1_initial_migration.js
│ └── 2_deploy_adopt.js
├── test
└── truffle-config.js

智能合约abi信息为一个数组, 如下所示:

"abi": [
    
      "constant": true,
      "inputs": [
        
          "name": "",
          "type": "uint256"
        
      ],
      "name": "adopters",
      "outputs": [
        
          "name": "",
          "type": "address"
        
      ],
      "payable": false,
      "stateMutability": "view",
      "type": "function"
    ,
    
      "constant": false,
      "inputs": [
        
          "name": "petId",
          "type": "uint256"
        
      ],
      "name": "adopt",
      "outputs": [
        
          "name": "",
          "type": "uint256"
        
      ],
      "payable": false,
      "stateMutability": "nonpayable",
      "type": "function"
    ,
    
      "constant": true,
      "inputs": [],
      "name": "getAdopters",
      "outputs": [
        
          "name": "",
          "type": "address[16]"
        
      ],
      "payable": false,
      "stateMutability": "view",
      "type": "function"
    
  ]

拷贝abi数组, 并将以上json转换成一行子串, 再切换到以太坊的geth命令中, 输入如下指令:

> var abi = [ "constant": true, "inputs": [ "name": "", "type": "uint256" ], "name": "adopters", "outputs": [ "name": "", "type": "address" ], "payable": false, "stateMutability": "view", "type": "function" , "constant": false, "inputs": [ "name": "petId", "type": "uint256" ], "name": "adopt", "outputs": [ "name": "", "type": "uint256" ], "payable": false, "stateMutability": "nonpayable", "type": "function" , "constant": true, "inputs": [], "name": "getAdopters", "outputs": [ "name": "", "type": "address[16]" ], "payable": false, "stateMutability": "view", "type": "function" ]

2.智能合约地址信息
在2.3节中提到智能合约的地址为:0xff8f47f643827fc324ac9575b8b4981d042ce5e7, 可将该地址存储到geth的变量中.

> var addr = "0xff8f47f643827fc324ac9575b8b4981d042ce5e7" // 注意:必须使用双引号

有了智能合约的abi和地址信息后, 可通过以下语句创建新建智能合约对象.

> var adoption = eth.contract(abi).at(addr) // 创建合约对象

可在geth命令行中输入adoption变量名, 将会打印该变量的数据信息. 如下所示:

  abi: [
      constant: true,
      inputs: [...],
      name: "adopters",
      outputs: [...],
      payable: false,
      stateMutability: "view",
      type: "function"
  , 
      constant: false,
      inputs: [...],
      name: "adopt",
      outputs: [...],
      payable: false,
      stateMutability: "nonpayable",
      type: "function"
  , 
      constant: true,
      inputs: [],
      name: "getAdopters",
      outputs: [...],
      payable: false,
      stateMutability: "view",
      type: "function"
  ],
  address: "0xff8f47f643827fc324ac9575b8b4981d042ce5e7",
  transactionHash: null,
  adopt: function(),
  adopters: function(),
  allEvents: function(),
  getAdopters: function()

在最末尾可以看到我们在Adoption.sol中编辑的函数名, 说明已获取到智能合约对象.
3.调用智能合约
完成以上操作后, 则可以通过adoption对象调用智能合约对象了.
如果调用的智能合约函数未改变合约中的数据, 则不会消耗以太坊gas. 以Adoption中的getAdopters()为例, 获取16只宠物被哪些账号收养.

>adoption.getAdopters.call()
[“0x0000000000000000000000000000000000000000”,
“0x0000000000000000000000000000000000000000”,
“0x0000000000000000000000000000000000000000”,
“0x0000000000000000000000000000000000000000”,
“0x0000000000000000000000000000000000000000”,
“0x0000000000000000000000000000000000000000”,
“0x0000000000000000000000000000000000000000”,
“0x0000000000000000000000000000000000000000”,
“0x0000000000000000000000000000000000000000”,
“0x0000000000000000000000000000000000000000”,
“0x0000000000000000000000000000000000000000”,
“0x0000000000000000000000000000000000000000”,
“0x0000000000000000000000000000000000000000”,
“0x0000000000000000000000000000000000000000”,
“0x0000000000000000000000000000000000000000”]

以上地址均为0x0000000000000000000000000000000000000000, 表示暂无人收养.
如果调用的智能合约函数需要合约中的数据, 则需要消耗以太坊gas. 以Adoption中的adopt()为例, 收养第2只宠物.

>adoption.adopt.sendTransaction(2, from: eth.accounts[0])
“0x0d1a6079e878b82870eafbbd66c311c9acad893c7389bd43c9b003a5a5416ef0”

由于需要修改合约中的数据, 调用adopt()时需要使用sendTransaction()发起交易, 并附加发起者的账号.
以上操作只是发起了交易, 但交易并不一定会被处理. 交易被处理还必须要有节点处于挖矿模式.

> miner.start()

过了一段时间后, 再次查看宠物是否被收养成功.

> adoption.getAdopters()
[“0x0000000000000000000000000000000000000000”,
“0x0000000000000000000000000000000000000000”,
“0xa0cf841d14ba336ffd6c1f617eb4663223790cc3”,
“0x0000000000000000000000000000000000000000”,
“0x0000000000000000000000000000000000000000”,
“0x0000000000000000000000000000000000000000”,
“0x0000000000000000000000000000000000000000”,
“0x0000000000000000000000000000000000000000”,
“0x0000000000000000000000000000000000000000”,
“0x0000000000000000000000000000000000000000”,
“0x0000000000000000000000000000000000000000”,
“0x0000000000000000000000000000000000000000”,
“0x0000000000000000000000000000000000000000”,
“0x0000000000000000000000000000000000000000”,
“0x0000000000000000000000000000000000000000”,
“0x0000000000000000000000000000000000000000”]

可以发现索引为2的数组内容的值为0xa0cf841d14ba336ffd6c1f617eb4663223790cc3, 说明宠物已经收养成功.即: 发起的交易已经被执行.

至此, 智能合约的编辑/编译/部署以及最后的调用过程已经讲解完成. 对于智能合约的调用, 还可通过其他方式调用. 如:在js中如何调用智能合约, 在此未能讲述. 后续章节将会陆续补充.

以上是关于区块链 之 部署和调用以太坊智能合约的主要内容,如果未能解决你的问题,请参考以下文章

11O 以太坊 ethereum OpenZeppelin : 部署智能合约并与之交互

以太坊私链账户下智能合约的部署与调用——使用RemixGolangGeth

以太坊虚拟机 EVMEVMC interpreter

区块链2.0以太坊智能合约solidity之helloworld

区块链以太坊 web3j for java 使用 - 部署和调用合约 <3;

创建自己的区块链合约java版web3接口——以太坊代币