以太坊(Ethereum)实战:编写,部署,运行智能合约

概述

本文讲解如何在以太坊(Ethereum)编写和部署智能合约。

注意:在进行本文的操作前,先按我的前两篇文章搭建好以太坊(go-ethereum)私有链,和添加两个账号并进行转账。

安装solidity编译器

$ 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
  • 通过web3.eth.contract的new方法发起部署合约的交易
> 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"

调用合约

调用get方法

> 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方法,但不会改变区块的数据。

  • 直接调用合约的get方法
> storage.get.call()
0

调用合约的set方法

调用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
}
  • 通过合约的get函数,来获取值
> storage.get.call()
40

可以看到合约执行已经成功了。

总结

本文讲解了如何通过solidity编写一个简单的智能合约,如何编译,部署,并运行该智能合约。

你可能感兴趣的:(区块链)