在以太坊(1):在CentOS 6.5上搭建以太坊私有链的步骤 中我们搭建了以太坊的私有链,在 以太坊(2):以太坊私有链环境下的账户管理、挖矿与转账 中演示了普通账户的创建、挖矿与转账操作,但实际上,私有链环境下以太币的转账意义似乎并不是很大,很多情况下,我们搭建私有链环境的目的是使用以太坊的智能合约功能。这次我们就通过一个非常简单的智能合约来演示智能合约的编写、编译、创建与执行。大概步骤如下:
1. 通过在线IDE并使用Solidity语言进行一个简单智能合约的编写与编译
2. 在我们之前创建的私有链环境中创建智能合约
3. 通过智能合约中的方法进行合约的执行或调用
注意:下面的内容当中我们有的地方会直接称智能合约为合约
1. 通过在线IDE并使用Solidity语言进行一个简单智能合约的编写与编译
我们使用在线IDE browser-solidity的URL是:https://ethereum.github.io/browser-solidity/ ,刚打开时,界面如下:
pragma solidity ^0.4.0;
contract SimpleStorage {
uint storedData;
function set(uint x) {
storedData = x;
}
function get() constant returns (uint) {
return storedData;
}
}
结果如下:
如果右边的Auto Compile勾选上的话,将会Solidity代码自动编译,否则,可以手动点击Compile按钮进行编译。到目前为止,第一步就完成了。
2. 创建智能合约
我们回到命令行模式,如同 以太坊(2):以太坊私有链环境下的账户管理、挖矿与转账 的开头所说的,控制台已关闭需要重新启动。
如果默认账户未解锁,我们首先需要解锁。具体操作参考:以太坊(2):以太坊私有链环境下的账户管理、挖矿与转账
将第1步中IDE右侧的Web3 deploy后面文本框的内容粘贴到控制台中,然后回车:
> var simplestorageContract = web3.eth.contract([{"constant":false,"inputs":[{"name":"x","type":"uint256"}],"name":"set","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"}]);
undefined
> var simplestorage = simplestorageContract.new(
... {
...... from: web3.eth.accounts[0],
...... {20019091905050607a565b005b3460005760646085565b6040518082815260200191505060405180910390f35b806000819055505b50565b600060005490505b9056',
...... gas: '4700000'
...... }, function (e, contract){
...... console.log(e, contract);
...... if (typeof contract.address !== 'undefined') {
......... console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);
......... }
...... })
I1130 19:16:22.440524 eth/api.go:1191] Tx(0x7fc6174180c8f79c3589917f34fc8ac6c6b62f6f4804efce77dd700fa2c28870) created: 0xb9e50c80ea238f47a006b3f3c2c8548a615d2b57
null [object Object]
undefined
>
其实从IDE复制出来的是2句JavaScript代码,而控制台实际上是JavaScript的控制台,这两句JavaScript代码分别创建了2个JavaScript变量,simplestorageContract和simplestorage,尤其是simplestorage这个变量,待会我们会用到。2句JavaScript执行完成后,以太坊会给我们返回Tx和合约地址。这这里0x7fc6174180c8f79c3589917f34fc8ac6c6b62f6f4804efce77dd700fa2c28870是交易哈希,0xb9e50c80ea238f47a006b3f3c2c8548a615d2b57是合约地址,这个地址非常重要,我们以后会用到。
下面开启挖矿,将合约写进以太坊的区块链,挖矿成功后关闭挖矿:
> miner.start(1)
I1130 19:37:53.678580 miner/miner.go:119] Starting mining operation (CPU=1 TOT=2)
true
> I1130 19:37:53.679821 miner/worker.go:573] commit new work on block 12 with 1 txs & 0 uncles. Took 1.150381ms
I1130 19:37:54.522348 miner/worker.go:339] Mined block (#12 / 0cf9cd21). Wait 5 blocks for confirmation
I1130 19:37:54.522574 miner/worker.go:573] commit new work on block 13 with 0 txs & 0 uncles. Took 189.496µs
I1130 19:37:54.522711 miner/worker.go:573] commit new work on block 13 with 0 txs & 0 uncles. Took 106.293µs
null [object Object]
Contract mined! address: 0xb9e50c80ea238f47a006b3f3c2c8548a615d2b57 transactionHash: 0x7fc6174180c8f79c3589917f34fc8ac6c6b62f6f4804efce77dd700fa2c28870
I1130 19:37:55.125083 miner/worker.go:339] Mined block (#13 / 0d800749). Wait 5 blocks for confirmation
I1130 19:37:55.125236 miner/worker.go:573] commit new work on block 14 with 0 txs & 0 uncles. Took 115.642µs
I1130 19:37:55.125355 miner/worker.go:573] commit new work on block 14 with 0 txs & 0 uncles. Took 76.929µs
miner.stop()
true
到此为止,合约的创建就完成了。
3. 智能合约的执行或调用
在第2步中我们创建了智能合约,合约地址是0xb9e50c80ea238f47a006b3f3c2c8548a615d2b57。这一步我们将调用这一智能合约。
首先我们看保持控制台不关闭的情况下智能合约的调用。我们说过,第2步的2句JavaScript实际上创建了2个变量,我们将通过这个变量来进行合约的调用。
合约中有一个set方法,这个方法接收一个整数型作为参数。
跟合约的创建一样,合约的调用也是需要花费的,因此,需要在set方法中有另外一个JSON Object类型的参数用于说明这笔花费。这里我们仅仅先介绍JSON Object类型的参数中的from这个属性。from这个属性说明了花费应该从哪个账号支出,所以对于这个账号有2个要求:有足够的以太币和账号是解锁的。
> simplestorage.set(12,{"from":"0xb96b46cea29da90891df995c4fd7f142963d1826"})
I1201 11:15:58.676038 eth/api.go:1193] Tx(0xe53d686de62d13642684595e8eebcd81021a46f131d4cb7ed15e81082d001313) to: 0xb9e50c80ea238f47a006b3f3c2c8548a615d2b57
"0xe53d686de62d13642684595e8eebcd81021a46f131d4cb7ed15e81082d001313"
>
我们通过上面的操作将storedData的值通过set方法设置为12,这个操作的花费从0xb96b46cea29da90891df995c4fd7f142963d1826这个账号支出。以太坊给我们生成的交易哈希是0xe53d686de62d13642684595e8eebcd81021a46f131d4cb7ed15e81082d001313。
然后,开启挖矿,挖矿成功后关闭挖矿。由于此操作在之前演示过多次,由于篇幅原因,在这里我们省略掉。但实际操作中不可省略。
我们通过合约的get方法来验证设值是否成功:
> simplestorage.get()
12
通过get方法得到12,说明我们设值成功。这里需要特别说明一下,我们前面说过,合约的调用需要有花费,需要指定支出的账户,这里为什么没有指定呢?我们看下合约的源码会发现get和set方法的区别:在get方法中有一个constant。如果一个方法带有constant说明此方法不会改变任何值、任何状态,因此也不会有任何花费。
我们创建的simplestorage这个JavaScript变量仅仅在当前context中有效,如果我们将控制台关闭,再次打开控制台,这个变量将不复存在。那如何从新的控制台中创建出simplestorage这个变量呢?
我们回到第1步,编译那步。我们将使用编译结果中的Interface。我们这个示例中 Interface是:[{"constant":false,"inputs":[{"name":"x","type":"uint256"}],"name":"set","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"}]
我们将这个字符串赋给一个JavaScript变量abi:
> var abi = [{"constant":false,"inputs":[{"name":"x","type":"uint256"}],"name":"set","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"}]
undefined
>
我们命名了一个变量abi,然后使用abi来创建simplestorage:
> var simplestorage = eth.contract(abi).at("0xb9e50c80ea238f47a006b3f3c2c8548a615d2b57")
undefined
>
上面这句代码中我们创建出了simplestorage。其中,eth、contract()和at()都是以太坊提供的API。abi是我们之前创建的JavaScript变量, 0xb9e50c80ea238f47a006b3f3c2c8548a615d2b57是我们之前记住的智能合约的地址。
由于simplestorage仅仅是当前context中的一个变量,并不是需要写进区块链的数据,因此这个变量的创建不需要挖矿。
剩下的智能合约里的方法调用就像我们没关闭控制台时的调用方法一样了。这里不再叙述。
我们发现,每次需要往区块链写入数据时,都需要开启挖矿,成功后关闭。生成环境需要一直开启挖矿,下一次我们将会讲述如何开启自动挖矿,如何让以太坊后台运行,以及如何搭建以太坊的网络。