区块链:以太坊私链搭建

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wtdask/article/details/82424476

In me the tiger sniffs the rose.
心有猛虎,细嗅蔷薇


一、为什么用到私有链?

在以太坊的共有链上部署智能合约、发起交易需要花费以太币。而通过修改配置,可以在本机搭建一套以太坊私有链,因为与公有链没关系,既不用同步公有链庞大的数据,也不用花钱购买以太币,很好地满足了智能合约开发和测试的要求,开发好的智能合约也可以很容易地切换接口部署到以太坊公有链上。

二、开源工具和语言

1、brewMacOS包管理器

打开终端执行下面的命令

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
macdeiMac:Downloads mac$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
==> This script will install: /usr/local/bin/brew
/usr/local/share/doc/homebrew
/usr/local/share/man/man1/brew.1
/usr/local/share/zsh/site-functions/_brew
/usr/local/etc/bash_completion.d/brew
/usr/local/Homebrew
==> The Xcode Command Line Tools will be installed. 
Press RETURN to continue or any other key to abort
==> Searching online for the Command Line Tools ==> /usr/bin/sudo /usr/bin/touch /tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress Password:
==> Installing Command Line Tools (macOS High Sierra version 10.13) for Xcode-9.4 ==> /usr/bin/sudo /usr/sbin/softwareupdate -i Command\ Line\ Tools\ (macOS\ High\ Sierra\ version\ 10.13)\ for\ Xcode-9.4 Software Update Tool


Downloading Command Line Tools (macOS High Sierra version 10.13) for Xcode

Downloaded Command Line Tools (macOS High Sierra version 10.13) for Xcode
Installing Command Line Tools (macOS High Sierra version 10.13) for Xcode
Done with Command Line Tools (macOS High Sierra version 10.13) for Xcode
Done.
==> /usr/bin/sudo /bin/rm -f /tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress Password:
Sorry, try again.
Password:
==> /usr/bin/sudo /usr/bin/xcode-select --switch /Library/Developer/CommandLineTools ==> Downloading and installing Homebrew... HEAD is now at 9777b278 Merge pull request #4810 from MikeMcQuaid/mojave-mmacosx-version-min
==> Homebrew is run entirely by unpaid volunteers. Please consider donating:   https://github.com/Homebrew/brew#donations
==> Migrating /Library/Caches/Homebrew to /Users/mac/Library/Caches/Homebrew... ==> Deleting /Library/Caches/Homebrew... Already up-to-date.
==> Installation successful! 
==> Homebrew has enabled anonymous aggregate formulae and cask analytics. Read the analytics documentation (and how to opt-out) here:
  https://docs.brew.sh/Analytics.html

==> Homebrew is run entirely by unpaid volunteers. Please consider donating:   https://github.com/Homebrew/brew#donations
==> Next steps: - Run `brew help` to get started - Further documentation:     https://docs.brew.sh

2、install Go compiler

brew install go
macdeiMac:Downloads mac$ brew install go
Updating Homebrew...
==> Downloading https://homebrew.bintray.com/bottles/go-1.11.high_sierra.bottle.
######################################################################## 100.0%
==> Pouring go-1.11.high_sierra.bottle.tar.gz ==> Caveats
A valid GOPATH is required to use the `go get` command.
If $GOPATH is not specified, $HOME/go will be used by default:
  https://golang.org/doc/code.html#GOPATH

You may wish to add the GOROOT-based install location to your PATH:
  export PATH=$PATH:/usr/local/opt/go/libexec/bin ==> Summary
��  /usr/local/Cellar/go/1.11: 9,273 files, 403.9MB
macdeiMac:Downloads mac$ 

3、geth建立以太坊安装说明geth运行以太坊节点

下载Source code(tar.gz)

macdeiMac:Downloads mac$ cd go-ethereum-1.5.9
macdeiMac:go-ethereum-1.5.9 mac$ pwd
/Users/mac/Downloads/go-ethereum-1.5.9
macdeiMac:go-ethereum-1.5.9 mac$ make geth

4、Solidity以太坊智能合约语言

brew install solidity

三、建立私链

1. 创建一个文件夹来存储你的私链数据

macdeiMac:go-ethereum-1.5.9 mac$ cd /Users/mac/Desktop/GitHub/Solidity/learn 
macdeiMac:learn mac$ mkdir privatechain
macdeiMac:learn mac$ pwd
/Users/mac/Desktop/GitHub/Solidity/learn
macdeiMac:learn mac$ ls
BLC         attributesMethod.sol    method.sol
Person.sol      attributesaccess.sol    method1.sol
SmartContractDemo   inherit.sol     privatechain
macdeiMac:learn mac$ 

2. 使用geth来加载

geth --networkid 123 --dev --datadir data1 --rpc --rpcaddr 192.168.1.5 --rpcport 8989 --port 3000

各选项含义如下:

identity:指定节点 ID;
rpc:表示开启 HTTP-RPC 服务;
rpcaddr:HTTP-RPC 服务ip地址;
rpcport:指定 HTTP-RPC 服务监听端口号(默认为 8545);
datadir:指定区块链数据的存储位置;
port:指定和其他节点连接所用的端口号(默认为 30303);
nodiscover:关闭节点发现机制,防止加入有同样初始配置的陌生节点。
如果你切换到data1文件夹里面,你会看到geth, geth.ipc, 和 keystore

  • 保持节点的运行,不要关闭终端,重新打开一个终端,使用geth attach连接节点,并且打开geth console
geth attach ipc:/Users/mac/Desktop/GitHub/Solidity/learn/privatechain/geth.ipc 
Welcome to the Geth JavaScript console!
instance: Geth/v1.7.1-stable-05101641/darwin-amd64/go1.9.1
 modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 shh:1.0 txpool:1.0 web3:1.0

这是一个交互式的 JavaScript 执行环境,在这里面可以执行 JavaScript 代码,其中 > 是命令提示符。在这个环境里也内置了一些用来操作以太坊的 JavaScript 对象,可以直接使用这些对象。这些对象主要包括:

eth:包含一些跟操作区块链相关的方法;
net:包含一些查看p2p网络状态的方法;
admin:包含一些与管理节点相关的方法;
miner:包含启动&停止挖矿的一些方法;
personal:主要包含一些管理账户的方法;
txpool:包含一些查看交易内存池的方法;
web3:包含了以上对象,还包含一些单位换算的方法。

  • 相关api命令

查看账户

> personal.listAccounts
[]
> 

创建账户

> personal.newAccount('wt') 
"0xb6d7d842e7dc9016fa6900a183b2be26fc90b2d8"
> 

PS:里面的wt是你账户的密码,输入你自己喜欢的密码。

查看账户

> personal.listAccounts 
["0xb6d7d842e7dc9016fa6900a183b2be26fc90b2d8"]
> 
  • web3命令
> web3.eth.coinbase 
"0xb6d7d842e7dc9016fa6900a183b2be26fc90b2d8"
> 
  • 编写智能合约代码
pragma solidity ^0.4.4;

contract test { 

    function multiply(uint a) returns(uint d){

        return a * 7;
    }

}
6060604052341561000f57600080fd5b5b60ab8061001e6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063c6888fa114603d575b600080fd5b3415604757600080fd5b605b60048080359060200190919050506071565b6040518082815260200191505060405180910390f35b60006007820290505b9190505600a165627a7a7230582067d7c851e14e862886b6f53dad6825135557fb3a4b691350c94ea5b80605f6770029
  • ABI
[
  {
    "constant": true,
    "inputs": [ { "name": "a", "type": "uint256" } ],
    "name": "multiply",
    "outputs": [ { "name": "d", "type": "uint256" } ],
    "payable": false,
    "type": "function",
    "stateMutability": "view" }
]
  • 在bejson中转义成字符串

http://www.bejson.com

[{\"constant\":true,\"inputs\":[{\"name\":\"a\",\"type\":\"uint256\"}],\"name\":\"multiply\",\"outputs\":[{\"name\":\"d\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\",\"stateMutability\":\"view\"}]
  • 通过abi创建合约对象
> var abi = JSON.parse('[{\"constant\":true,\"inputs\":[{\"name\":\"a\",\"type\":\"uint256\"}],\"name\":\"multiply\",\"outputs\":[{\"name\":\"d\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\",\"stateMutability\":\"view\"}]')
> myContract = web3.eth.contract(abi)
{
  abi: [{
      constant: false,
      inputs: [{...}],
      name: "multiply",
      outputs: [{...}],
      payable: false,
      type: "function"
  }],
  eth: {
    accounts: ["0x2abf46d8b0d940cdeedd55872bc0648add40227d"],
    blockNumber: 384,
    coinbase: "0x2abf46d8b0d940cdeedd55872bc0648add40227d",
    compile: {
      lll: function(),
      serpent: function(),
      solidity: function()
    },
    defaultAccount: undefined,
    defaultBlock: "latest",
    gasPrice: 0,
    hashrate: 0,
    mining: false,
    pendingTransactions: [],
    protocolVersion: "0x3f",
    syncing: false,
    call: function(),
    contract: function(abi),
    estimateGas: function(),
    filter: function(fil, callback),
    getAccounts: function(callback),
    getBalance: function(),
    getBlock: function(),
    getBlockNumber: function(callback),
    getBlockTransactionCount: function(),
    getBlockUncleCount: function(),
    getCode: function(),
    getCoinbase: function(callback),
    getCompilers: function(),
    getGasPrice: function(callback),
    getHashrate: function(callback),
    getMining: function(callback),
    getPendingTransactions: function(callback),
    getProtocolVersion: function(callback),
    getRawTransaction: function(),
    getRawTransactionFromBlock: function(),
    getStorageAt: function(),
    getSyncing: function(callback),
    getTransaction: function(),
    getTransactionCount: function(),
    getTransactionFromBlock: function(),
    getTransactionReceipt: function(),
    getUncle: function(),
    getWork: function(),
    iban: function(iban),
    icapNamereg: function(),
    isSyncing: function(callback),
    namereg: function(),
    resend: function(),
    sendIBANTransaction: function(),
    sendRawTransaction: function(),
    sendTransaction: function(),
    sign: function(),
    signTransaction: function(),
    submitTransaction: function(),
    submitWork: function()
  },
  at: function(address, callback),
  getData: function(),
  new: function()
}
  • 检查coinbase账号余额
> account1 = web3.eth.coinbase
"0x2abf46d8b0d940cdeedd55872bc0648add40227d"
> web3.eth.getBalance(account1)
0
> 

如果余额大于0,继续,否则,开始挖矿。

> miner.start();
null
> 
  • 停止挖矿。
miner.stop();
  • 停止挖矿,并且查余额
> miner.start();
null
> miner.stop();
true
> web3.eth.getBalance(account1)
1.152e+21
> 
  • 解锁coinbase账号,我们通过coinbase账号来付费部署合约

wt: 换成你的密码。

> personal.unlockAccount(account1, 'wt') 
true
> 
  • 预估手续费
> bytecode = "6060604052341561000f57600080fd5b5b60ab8061001e6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063c6888fa114603d575b600080fd5b3415604757600080fd5b605b60048080359060200190919050506071565b6040518082815260200191505060405180910390f35b60006007820290505b9190505600a165627a7a7230582067d7c851e14e862886b6f53dad6825135557fb3a4b691350c94ea5b80605f6770029"
"6060604052341561000f57600080fd5b5b60ab8061001e6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063c6888fa114603d575b600080fd5b3415604757600080fd5b605b60048080359060200190919050506071565b6040518082815260200191505060405180910390f35b60006007820290505b9190505600a165627a7a7230582067d7c851e14e862886b6f53dad6825135557fb3a4b691350c94ea5b80605f6770029"
> web3.eth.estimateGas({data: bytecode})
Error: invalid argument 0: json: cannot unmarshal hex string without 0x prefix into Go struct field CallArgs.data of type hexutil.Bytes
    at web3.js:3104:20
    at web3.js:6191:15
    at web3.js:5004:36
    at <anonymous>:1:1

> bytecode = "0x6060604052341561000f57600080fd5b5b60ab8061001e6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063c6888fa114603d575b600080fd5b3415604757600080fd5b605b60048080359060200190919050506071565b6040518082815260200191505060405180910390f35b60006007820290505b9190505600a165627a7a7230582067d7c851e14e862886b6f53dad6825135557fb3a4b691350c94ea5b80605f6770029"
"0x6060604052341561000f57600080fd5b5b60ab8061001e6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063c6888fa114603d575b600080fd5b3415604757600080fd5b605b60048080359060200190919050506071565b6040518082815260200191505060405180910390f35b60006007820290505b9190505600a165627a7a7230582067d7c851e14e862886b6f53dad6825135557fb3a4b691350c94ea5b80605f6770029"
> web3.eth.estimateGas({data: bytecode})
98391
> 

备注:字节码前面需要添加0x。手续费大概为98391gas

  • 部署合约,为了方便理解,设置一个回调函数
> contractInstance = myContract.new({data: bytecode gas: 1000000, from: account1}, function(e, contract){
  if(!e){
    if(!contract.address){
      console.log("Contract transaction send: Transaction Hash: "+contract.transactionHash+" waiting to be mined...");
    }else{
      console.log("Contract mined! Address: "+contract.address);
      console.log(contract);
    }
  }else{
    console.log(e)
  }
})
Contract transaction send: Transaction Hash: 0x5e2aebbf400d71a32e807dc3f11f1053b6ee3b2a81435ed8ace2fa54eebb9f3d waiting to be mined...
{
  abi: [{
      constant: false,
      inputs: [{...}],
      name: "multiply",
      outputs: [{...}],
      payable: false,
      type: "function"
  }],
  address: undefined,
  transactionHash: "0x5e2aebbf400d71a32e807dc3f11f1053b6ee3b2a81435ed8ace2fa54eebb9f3d"
}
> 
  • 你的合约等待挖矿,开始挖矿,等一会儿,停止
> miner.start()
null
> Contract mined! Address: 0xbf8b24283f2516360d3a4ba1db0df78ae74689db
[object Object]
> miner.stop()
true
> 
  • 检查合约是否部署成功
> eth.getCode(contractInstance.address)
"0x60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063c6888fa114603d575b600080fd5b3415604757600080fd5b605b60048080359060200190919050506071565b6040518082815260200191505060405180910390f35b60006007820290505b9190505600a165627a7a7230582067d7c851e14e862886b6f53dad6825135557fb3a4b691350c94ea5b80605f6770029"
> 
  • 调用合约方法
> contractInstance.multiply(6)
42
> 
阅读更多 登录后自动展开

更多精彩内容