本文讲解如何在以太坊(Ethereum)编写和部署智能合约。
注意:在进行本文的操作前,先按我的前两篇文章搭建好以太坊(go-ethereum)私有链,和添加两个账号并进行转账。
$ brew tap ethereum/ethereum
$ brew install solidity
$ solc --version
solc, the solidity compiler commandline interface
Version: 0.4.24+commit.e67f0147.Darwin.appleclang
$ cat Storage.sol
pragma solidity ^0.4.10
contract Storage {
uint256 storedData;
function set(uint256 data) {
storedData = data;
}
function get() {
return storedData;
}
}
通过以下命令把solidity代码编译到一个js文件中:
echo "var output=`solc --optimize --combined-json abi,bin,interface Storage.sol`" > storage.js
说明:
进入以太坊的Javascript控制台,加载前一步已经生成好的storage.js文件,命令如下:
> loadScript("/geth/smart_contract/storage.js")
true
查看一下output这个输出变量的值:
> output
{
contracts: {
Storage.sol:Storage: {
abi: "[{\"constant\":false,\"inputs\":[{\"name\":\"data\",\"type\":\"uint256\"}],\"name\":\"set\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"get\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]",
bin: "608060405234801561001057600080fd5b5060bf8061001f6000396000f30060806040526004361060485763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166360fe47b18114604d5780636d4ce63c146064575b600080fd5b348015605857600080fd5b5060626004356088565b005b348015606f57600080fd5b506076608d565b60408051918252519081900360200190f35b600055565b600054905600a165627a7a723058206610faab1e6c0a83825fa220731e0a39c12647c2eda3f608e42f961e5fdd07b60029"
}
},
version: "0.4.24+commit.e67f0147.Darwin.appleclang"
}
output对象中存储了一个map对象;在contracts对象中有两个key:abi和bin。分别定义了合约的abi和编译后的二进制代码。我们需要先获取这两个对象的值,然后进行智能合约的部署。
> var storageContractAbi = output.contracts['Storage.sol:Storage'].abi
undefined
> var storageContract = eth.contract(JSON.parse(storageContractAbi))
undefined
> var storageBinCode = "0x"+output.contracts['Storage.sol:Storage'].bin
undefined
> personal.unlockAccount(eth.accounts[0])
Unlock account 0xdae3d4234ad88421bb4dd5033143d25d8760dbfa
Passphrase:
true
> var deployTransationObject = {from: eth.accounts[0], data: storageBinCode, gas: 1000000}
undefined
> var storageInstance = storageContract.new(deployTransationObject)
undefined
> txpool.status
{
pending: 1,
queued: 0
}
> txpool.inspect.pending
{
0xDaE3d4234AD88421Bb4Dd5033143d25d8760DbfA: {
1: "contract creation: 0 wei + 1000000 gas × 18000000000 wei"
}
}
可以看到,有一个交易代处理。现在开始挖矿:
> miner.start(1); admin.sleepBlocks(1); miner.stop()
true
> txpool.status
{
pending: 0,
queued: 0
}
可以看到交易已经被处理。
可以通过变量来查看合约地址:
> storageInstance
{
abi: [{
constant: false,
inputs: [{...}],
name: "set",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function"
}, {
constant: true,
inputs: [],
name: "get",
outputs: [{...}],
payable: false,
stateMutability: "view",
type: "function"
}],
address: "0x4612ff6120e85c5003558284c429247880ea48a0",
transactionHash: "0xad866d5d66bfb9d22f5b86df9dce534ed491a98246b71eb955066829c29bb61f",
allEvents: function(),
get: function(),
set: function()
}
>
> eth.getTransactionReceipt(storageInstance.transactionHash)
{
blockHash: "0x2505cdd65dc6928c859946b1c50879c05e5527c9ab6be41ebc593d2eb7e56c4b",
blockNumber: 121,
contractAddress: "0x4612ff6120e85c5003558284c429247880ea48a0",
cumulativeGasUsed: 103631,
from: "0xdae3d4234ad88421bb4dd5033143d25d8760dbfa",
gasUsed: 103631,
logs: [],
logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
root: "0xabfe591087ac07102af15a82caadecc7fb5d7ad00bb9ca8041e56c46b261200d",
to: null,
transactionHash: "0xad866d5d66bfb9d22f5b86df9dce534ed491a98246b71eb955066829c29bb61f",
transactionIndex: 0
}
> var storageAddress = eth.getTransactionReceipt(storageInstance.transactionHash).contractAddress
undefined
> storageAddress
"0x4612ff6120e85c5003558284c429247880ea48a0"
> var storage = storageContract.at(storageAddress)
undefined
> storage
{
abi: [{
constant: false,
inputs: [{...}],
name: "set",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function"
}, {
constant: true,
inputs: [],
name: "get",
outputs: [{...}],
payable: false,
stateMutability: "view",
type: "function"
}],
address: "0x4612ff6120e85c5003558284c429247880ea48a0",
transactionHash: null,
allEvents: function(),
get: function(),
set: function()
}
以上输出call表示可以直接在本地EVM虚拟机中调用合约的get方法,但不会改变区块的数据。
> storage.get.call()
0
调用set方法,向以太坊发起一条合约调用交易:
> storage.set.sendTransaction(40,{from: eth.accounts[0], gas: 1000000})
"0x5b1288ba671c1fbd0489a1e92818c0e97f2e5e5916874c3ddc81ab88f3328652"
挖矿让交易生效:
# 查看交易状态,有一个交易待处理
> txpool.status
{
pending: 1,
queued: 0
}
> miner.start(1); admin.sleepBlocks(1); miner.stop()
true
> txpool.status
{
pending: 0,
queued: 0
}
> storage.get.call()
40
可以看到合约执行已经成功了。
本文讲解了如何通过solidity编写一个简单的智能合约,如何编译,部署,并运行该智能合约。