作者:邹祁峰
邮箱:[email protected]
博客:http://blog.csdn.net/qifengzou
日期:2018.03.04 01:18
转载请注明来自”祁峰”的CSDN博客
智能合约就像我们业务的后台逻辑, 其运行在以太坊平台上. 以太坊就像操作系统, 其天然的提供了区块链的特性. 绝大多数开发人员只需关注使用Solidity编写智能合约实现业务逻辑, 而无需去修改以太坊的特性. 因此, 此文中将重点讲述如何在以太坊上部署和调用智能合约.
为方便大家理解智能合约和以太坊之间的关系,可以用一下几个概念做类比.
序号 | 通用概念 | 以太坊 |
---|---|---|
01 | 操作系统 | 以太坊 |
02 | C/C++/Java/Go | Solidity |
03 | 类+函数 | 智能合约 |
为了让大家对智能合约的编写/部署/调用有一个全面的了解.以下将以一个实例串联整个过程.
智能合约的编写,编译以及部署可以通过网页版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;
}
}
完成智能合约的编辑后,则需编译智能合约.在项目根目录输入如下指令:
# 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
文件中.
完成智能合约的编译后,便可进行智能合约的部署了.部署智能合约Adoption.sol的大体步骤如下:
1.启动以太坊私链
既然要将智能合约部署到以太坊网络,在部署之前需要搭建好以太坊网络.搭建以太坊私链的过程可参考博文: <<区块链 之 搭建以太坊私有链>>
以太坊私有链--rpcport
为8545.
2.指定以太坊平台
首先需要指明将智能合约部署到哪个以太坊网络,只需指明该以太坊网络的某一个节点便可. 在根目录下的truffle-config.js
添加如下配置信息:
module.exports = {
// See
// 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
, 该地址将会在智能合约的调用中被使用.
为了保证后续智能合约的调用能够正常执行, 以太坊私有链中至少有一个节点正在挖矿.
要调用刚才部署的智能合约, 需要找到被部署智能合约的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中如何调用智能合约, 在此未能讲述. 后续章节将会陆续补充.