首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >动手编写一个以太坊智能合约

动手编写一个以太坊智能合约

作者头像
区块链大本营
发布2018-05-10 10:37:04
6230
发布2018-05-10 10:37:04
举报
文章被收录于专栏:区块链大本营区块链大本营

如何部署、调用智能合约

1RPC

之前的章节中讲到了怎么写、部署合约以及与合约互动(点击阅读上一章节)。现在该讲讲与以太坊网络和智能合约沟通的细节了。

一个以太坊节点提供一个RPC界面。这个界面给Ðapp(去中心化应用)访问以太坊区块链的权限和节点提供的功能,比如编译智能合约代码,它用JSON-RPC 2.0规范(不支持提醒和命名的参数) 的子集作为序列化协议,在HTTP和IPC (linux/OSX上的unix域接口,在Windows上叫pipe’s)上可用。

2惯例

RPC界面会使用一些惯例,但它们不是JSON-RPC 2.0规范的一部分,这些惯例如下:

  • 数字是十六进制编码。做这个决定是因为有些语言对运行极大的数字没有或有很少的限制。为了防止这些错误数字类型是十六进制编码,由开发者来分析这些数字并正确处理它们。在维基页百科查看十六进制编码章节查看案例。
  • 默认区块数字。几个RPC 方法接受区块数字。在一些情况下,给出区块数字是不可能的或者不太方便。在那样的情况下,默认区块数字可以是以下字符串中的一个[”earliest”, “latest”, “pending”]。在维基页面可查看使用默认区块参数的RPC方法列表。

3部署合约

我们会通过不同的步骤来部署下面的合约,但只用到RPC界面。

contract Multiply7 {

event Print(uint);

function multiply(uint input) returns (uint) {

Print(input

*

7);

return input

*

7;

}

}

要做的第一件事是确保HTTP RPC界面可用。这意味着我们在开始为geth供应—rpc标志,为eth提供-j标志。在这个例子中,用的是私有开发链上的geth节点。通过这种方法,我们就不需要真实网络上的以太币了。

> geth --rpc --dev --mine --minerthreads 1 --unlock 0 console 2>>geth.log

这会在http://localhost:8545上启动HTTP RPC界面。

注意:geth支持CORS查看—rpccorsdomain标志了解更多。

我们可以通过用curl检索coinbase地址和余额来证明界面正在运行。请注意这些例子中的数据在你本地的节点上会有所不同。如果你想要试试这些参数,视情况替换需要的参数。

> curl --data '{"jsonrpc":"2.0","method":"eth_coinbase", "id":1}' localhost:8545

{"id":1,"jsonrpc":"2.0","result":["0xeb85a5557e5bdc18ee1934a89d8bb402398ee26a"]}

> curl --data '{"jsonrpc":"2.0","method":"eth_getBalance", "params": ["0xeb85a5557e5bdc18ee1934a89d8bb402398ee26a"], "id":2}' localhost:8545

{"id":2,"jsonrpc":"2.0","result":"0x1639e49bba16280000"}

记不记得前面说过数字是十六进制编码?在这个情况下,余额作为十六进制字符串以Wei的形式返还。如果希望余额作为数字以太币为单位,可以从控制台用web3,示例如下:

> web3.fromWei("0x1639e49bba16280000", "ether")

"410"

现在我们在私有开发链上有一些以太币,就可以部署合约了。第一步是验证solidity编译器可用,可以用eth_getCompilers RPC method方法来检索可用的编译器,示例如下:

> curl --data '{"jsonrpc":"2.0","method": "eth_getCompilers", "id": 3}' localhost:8545

{"id":3,"jsonrpc":"2.0","result":["Solidity"]}

我们可以看到solidity编译器可用。

下一步是把Multiply7合约编译到可以发送给以太坊虚拟机的字节代码中,示例如下:

> curl --data '{"jsonrpc":"2.0","method": "eth_compileSolidity", "params": ["contract Multiply7 { event Print(uint); function multiply(uint input) returns (uint) { Print(input

{"id":4,"jsonrpc":"2.0","result":{"Multiply7":{"code":"0x6060604052605f8060106000396000f360606040

现在我们有了编译代码,需要决定花多少gas去部署它。RPC界面有eth_estimateGas方法,会给我们一个预估数量,如下:

> curl --data '{"jsonrpc":"2.0","method": "eth_estimateGas", "params": [{"from": "0xeb85a5557e5bdc18ee1934a89d8bb402398ee26a", "data": "0x6060604052605f8060106000396000f3606060405260e060020a6000350463c6888fa18114601a575b005b60586004356007810260609081526000907f24abdb5865df5079dcc5ac590ff6f01d5c16edbc5fab4e195d9febd1114503da90602090a15060070290565b5060206060f3"}], "id": 5}' localhost:8545

{"id":5,"jsonrpc":"2.0","result":"0xb8a9"}

最后部署合约。

> curl --data '{"jsonrpc":"2.0","method": "eth_sendTransaction", "params": [{"from": "0xeb85a5557e5bdc18ee1934a89d8bb402398ee26a", "gas": "0xb8a9", "data": "0x6060604052605f8060106000396000f3606060405260e060020a6000350463c6888fa18114601a575b005b60586004356007810260609081526000907f24abdb5865df5079dcc5ac590ff6f01d5c16edbc5fab4e195d9febd1114503da90602090a15060070290565b5060206060f3"}], "id": 6}' localhost:8545

{"id":6,"jsonrpc":"2.0","result":"0x3a90b5face52c4c5f30d507ccf51b0209ca628c6824d0532bcd6283df7c08

交易由节点接受,交易散表被返还。我们可以用这个散表来跟踪交易。

下一步是决定部署合约的地址。每个执行的交易都会创建一个接收。这个接收包含交易的各种信息,比如交易被包含在哪个区块,以太坊虚拟机用掉多少gas。如果交易创建了一个合约,它也会包含合约地址。我们可以用eth_getTransactionReceipt RPC方法检索接收,示例如下:

> curl --data '{"jsonrpc":"2.0","method": "eth_getTransactionReceipt", "params": ["0x3a90b5face52c4c5f30d507ccf51b0209ca628c6824d0532bcd6283df7c08a7c"], "id": 7}' localhost:8545

{"id":7,"jsonrpc":"2.0","result":{"transactionHash":"0x3a90b5face52c4c5f30d507ccf51b0209ca628c682

可以看到,合约在0x6ff93b4b46b41c0c3c9baee01c255d3b4675963d上被创建。如果你得到了零而不是接收,说明还没有被纳入区块。这时,要检查看看你的矿工是否在运行,然后重新试一遍。

4和智能合约互动

现在已经部署了合约,我们可以和它互动了。有两种方法进行互动,即发送交易或像5.7.6节说明的那样调用。在本节的例子中,将会发送交易到合约的multiply方法里。

在我们的实例中,需要具体说明from、to 和data参数。From是我们账户的公共地址,to是合约地址,Data参数有一点复杂,它包括了规定调用哪个方法和哪个参数的负载量。这就需要ABI发挥作用了,ABI规定了如何为以太坊虚拟机规定和编码数据。

负载量的字节是功能选择符,规定了调用哪个方法。它取Keccak散表的头4个字节,涵盖功能名称参数类型,并进行十六进制编码。multiply功能接受一个参数。示例如下:

> web3.sha3("multiply(uint256)").substring(0, 8)

"c6888fa1"

下一步是编码参数。我们只有一个unit256,假定提供了值6。ABI有一个章节规定了编码uint字节的方法,如下:

int<M>: enc(X) is the big-endian two’s complement encoding of X, padded on the higher-oder (left) side with 0xff for negative X and with zero 字节s for positive X such that the length is a multiple of 32 bytes.

它会编码到0000000000000000000000000000000000000000000000000000000000000006。

将功能选择符和编码参数结合起来,数据就会变成0xc6888fa10000000000000000000000000000000000000000000000000000000000000006。

我们来试一下:

> curl --data '{"jsonrpc":"2.0","method": "eth_sendTransaction", "params": [{"from": "0xeb85a5557e5bdc18ee1934a89d8bb402398ee26a", "to": "0x6ff93b4b46b41c0c3c9baee01c255d3b4675963d", "data": "0xc6888fa10000000000000000000000000000000000000000000000000000000000000006"}], "id": 8}' localhost:8545

{"id":8,"jsonrpc":"2.0","result":"0x759cf065cbc22e9d779748dc53763854e5376eea07409e590c990eafc0869

由于我们发送了交易,于是有交易散表返回。如果检索接收,可以看到一些新内容,如下:

{

blockHash: "0xbf0a347307b8c63dd8c1d3d7cbdc0b463e6e7c9bf0a35be40393588242f01d55",

blockNumber: 268,

contractAddress: null,

cumulativeGasUsed: 22631,

gasUsed: 22631,

logs: [{

address: "0x6ff93b4b46b41c0c3c9baee01c255d3b4675963d",

blockHash: "0xbf0a347307b8c63dd8c1d3d7cbdc0b463e6e7c9bf0a35be40393588242f01d55",

blockNumber: 268,

data: "0x000000000000000000000000000000000000000000000000000000000000002a",

logIndex: 0,

topics: ["0x24abdb5865df5079dcc5ac590ff6f01d5c16edbc5fab4e195d9febd1114503da"],

transactionHash: "0x759cf065cbc22e9d779748dc53763854e5376eea07409e590c990eafc0869d74",

transactionIndex: 0

}],

transactionHash: "0x759cf065cbc22e9d779748dc53763854e5376eea07409e590c990eafc0869d74",

transactionIndex: 0

}

接收包含一个日志。日志由以太坊虚拟机在交易执行时生成,包含接收。如果我们看Multiply功能,可以看到打印事件和输入次数7一起被提出。由于打印事件的参数是uint256,因此可以根据ABI规则对它进行编码,这样就会得到预期的十进制42。

> web3.sha3("Print(uint256)")

"24abdb5865df5079dcc5ac590ff6f01d5c16edbc5fab4e195d9febd1114503da"

这只是对一些最常见任务的简单介绍。在RPC维基页面查看可用RPC方法的完整列表。

5Web3.js

正如在之前的案例所见,使用JSON-RPC界面相当单调乏味且容易出错,尤其是在处理ABI的时候。Web3.js是Javascript库,它的目标是提供更友好的界面,减少出错机会。

用web3部署Multiply7合约看起来是这样:

var source = 'contract Multiply7 { event Print(uint); function multiply(uint input) returns (uint) { Print(input

var compiled = web3.eth.compile.solidity(source);

var code = compiled.Multiply7.code;

var abi = compiled.Multiply7.info.abiDefinition;

web3.eth.contract(abi).new({from: "0xeb85a5557e5bdc18ee1934a89d8bb402398ee26a", data: code}, function (err, contract) {

if (!err && contract.address)

console.log("deployed on:", contract.address);

}

);

deployed on: 0x0ab60714033847ad7f0677cc7514db48313976e2

装载一个部署的合约,发送交易:

var source = 'contract Multiply7 { event Print(uint); function multiply(uint input) returns (uint) { Print(input

var compiled = web3.eth.compile.solidity(source);

var Multiply7 = web3.eth.contract(compiled.Multiply7.info.abiDefinition);

var multi = Multiply7.at("0x0ab60714033847ad7f0677cc7514db48313976e2")

multi.multiply.sendTransaction(6, {from: "0xeb85a5557e5bdc18ee1934a89d8bb402398ee26a"})

注册一个回调,打印事件创建日志的时候会被调用。

multi.Print(function(err, data) { console.log(JSON.stringify(data)) })

{"address":"0x0ab60714033847ad7f0677cc7514db48313976e2","args": {"":"21"},"blockHash":"0x259c7dc0

在web3.js维基页面可查看更多信息。

6控制台

geth控制台提供命令行界面和Javascript执行时间。它可以连接到本地或远程的geth或eth节点。它会装载用户能使用的web3.js库,从而方便用户从控制台通过web3.js部署智能合约,并和智能合约互动。实际上Web3.js章节的例子可以被复制进控制台并且调用。

7查看合约与交易

有几个可用的在线区块链浏览器,能让你查询以太坊区块链,它们分别是:

  • EtherChain
  • EtherCamp
  • EtherScan

其他可查看节点或交易的资源

  • EtherNodes :节点的地理分配,由客户端区分。
  • EtherListen:实时以太坊交易可视器和可听器。

智能合约案例实战

以太坊是区块链开发领域最好的编程平台,而truffle是以太坊(Ethereum)最受欢迎的一个开发框架,这也是介绍truffle的原因。实战是最重要的事情,这篇文章不讲原理,只搭建环境,运行第一个区块链程序(Dapp)。

1. 安装truffle

安装truffle的命令如下:

$ npm install -g truffle

2. 依赖环境

可用的系统包括:Windows、Linux和Mac OS X,推荐Mac OS X,不建议使用Windows,会碰到各种各样的问题,很可能导致放弃。首先,访问https://nodejs.org 官方网站下载安装NodeJS。

此外,需要安装Ethereum客户端,来支持JSON RPC API调用。

至于开发环境,推荐使用EthereumJS TestRPC,地址为: https://github.com/ethereumjs/testrpc。

安装命令如下:

$ npm install -g ethereumjs-testrpc

3. 新建第一个项目

通过以下命令新建一个项目:

$ mkdir zhaoxi

$ cd zhaoxi

$ truffle init

默认会生成一个MetaCoin的demo,可以从这个demo中学习truffle的架构。

项目的目录结构如图5-3所示。

图5-3 项目的目录结构

项目所有文件的目录如图5-4所示。

图5-4 项目文件目录目录结构

现在,通过以下命令编译项目。

$ truffle compile

图5-5是运行以上命令后的结果。

图5-5 Truffle compile执行结果图

下面介绍部署项目的方式。

部署之前先启动TestRPC,命令如下:

$ testrpc

$ truffle deploy(在Truffle 2.0以上版本中,命令变成了:truffle migrate)

图5-6是运行truffle deploy后的结果。

图5-6 truffle deploy执行结果图

$ truffle migrate migrate的执行结果见图5-7。

图5-7 truffle migrate migrate执行结果图

现在,可以启动服务了,命令如下:

$ truffle serve

图5-8是truffle serve执行结果图

图5-8 truffle serve执行结果图

启动服务后,可以在浏览器访问项目了,地址是:http://localhost:8080/ ,网页界面如图5-9所示。

图5-9智能合约运行界面

好了,第一个区块链程序跑起来了,后面可以不断地实践深入学习了。

本文节选自图书《区块链开发指南》,本书由 申屠青春 主编,宋波、张鹏、汪晓明、季宙栋、左川民 联合编著,中国三大区块链联盟的大伽联袂推荐。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2017-09-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 区块链大本营 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档