×

第二章 以太坊私有环境搭建

96
gloriow
2017.10.29 17:43* 字数 6123

从word粘贴过来的时候,日常少了部分表格,代码就很难看。

再重申一下,本文写于16年年底,如有不同请以现在官方文档为准。官方文档是最胖的么么哒~

第二章 以太坊私有环境搭建

1.综述

以太坊(Ethereum)是目前市值仅次于比特币的区块链平台,但二者有很大的区别。比特币(BTC)是一种通用的交易数字货币,而以太坊却是一个部署、执行智能合约的平台。以太币(ETH)在更大的程度上,是一种用于购买智能合约计算资源的数字货币,而不是用于实物的购买。如果要进行智能合约的编写和开发,以太坊是目前最合适的平台。

以太坊有基于python、go、C++、java等多种语言的版本。但是殊途同归,在总体上,以太坊区块链架构都是由核心库、通讯库和客户端三大部分组成:

1.核心库:核心库的主要功能是组成区块链、EVM虚拟机运行环境以及共识(在公有链中的共识即为挖矿)。区块链的主要功能是形成区块链来保存数据,还有很多与区块链相关的功能也包含在其中,比如简单支付验证SPV、判断交易是否属于某区块的bloom filter、快速验证交易的patricia树等等。EVM即以太坊虚拟机,是智能合约的执行环境,类似于JVM是JAVA程序的执行环境。共识是组成区块链的方法,以太坊的公有链是采用与比特币类似的一种pow机制。python版本对应程序为pyethereum,go语言程序为go-ethereum,C++语言程序为cpp-ethereum,java语言程序为ethereumj。

2.通讯库:通讯库是一个p2p的网络库,主要功能是实现节点间的发现和互联,实现多种基于多信道、加密连接的传输服务。我个人的理解是这个库的作用类似于互联网中的网关协议,在p2p网络中的每个节点都保存了一个所有节点地址的路由表。当一个新节点连接进入以太网公有网络时,它会向p2p模块中默认的四个节点发送discorvery消息,在这四个节点接收到新节点加入的消息时,则将新节点加入以太网节点名单并进行广播。此外,该模块还实现了节点间的广播和通讯。python版本对应通讯库程序为pydevp2p,而go语言中已经将其打包入go-ethereum的p2p模块。

3.客户端(应用库):客户端的作用是作为在外部对区块链进行操作的工具,包括获取区块链内的数据,对区块链发送交易、部署合约甚至编译智能合约等等各种操作。客户端通过JSONRPC对外开放端口保持监听,在收到消息之后对底层进行操作。python版本对应程序为pyethapp,默认监听4000端口;go语言相应程序为geth,默认监听8545端口。大部分程序都监听8545端口。

在进行智能合约的开发之前,需要搭建一个开发环境。当然也可以使用以太坊的公有链来进行合约的部署,但那可是要花大量的时间同步区块链,还要花费实实在在的以太币:)。

搭建智能合约的开发环境,一般有几种方法搭建一个测试用区块链:

1.第一种方法是使用testrpc。

Testrpc是基于node.js开发的一个单点以太坊客户端。

2.第二种方法是改造python版公有链的配置,形成私有链。

对于python版以太坊的pyethapp部分,可以通过配置其中的config.yaml文件,将公有链改造成私有链。

3.第三种方法是对geth公有链进行配置,形成私有链。

go语言的geth客户端,可以配合使用ethereum-bootstrap项目,快速搭建一个本地开发环境。项目地址:https://github.com/janx/ethereum-bootstrap

随后的几节会分别介绍在Ubuntu16.04,各个开发环境的搭建方法。

2.testrpc测试链搭建

2.1 testrpc介绍

项目地址:https://github.com/ethereumjs/testrpc

Testrpc是基于node.js开发的一个单点以太坊客户端,整个blockchain的数据驻留在内存空间,testrpc可以模拟一个geth客户端的行为,包括所有的RPC api,发送给testrpc的交易会被马上处理而不需要等待挖矿时间,让基于以太坊的开发测试工作更加方便快速。

参考testrpc项目文档。

2.2前置需求及安装方法

(1)node.js

项目地址:https://github.com/nodejs/node

node.js是javascript的运行环境,类似于JVM之于JAVA。目前最新版是7.2.1,但是官方推荐安装的版本是6.9.2,这里使用nodejs-6.9。node.js版本升级很快,具体的安装方法随着版本不同,可能会出现变化,请参看官方网站(nodejs.org)的安装说明文档。这里是当前版本的安装方式:

curl -sL https://deb.nodesource.com/setup_6.x| sudo -E bash -

sudo apt-get install -y nodejs

稍微注意一下,如果直接使用apt-get

install命令安装nodejs,可能会导致版本较低的问题,建议使用上面的安装方法。

(2)npm

npm是Node.js的包管理器,号称是全球最大的开源库生态系统。很多依赖库,都需要通过npm进行下载。

apt-get install npm

(3)build-essential

build-essential是在debian环境下的一个开发工具集成包,内容包括了gcc、g++、make等等,是开发和运行很多项目的必备工具。

apt-get install build-essentail

(4)apt

安装apt

apt-get install apt

2.3 testrpc的安装

项目地址:https://github.com/ethereumjs/testrpc

通过下载的node.js包管理器npm安装。

npm install -g ethereumjs-testrpc

3. python版私有链搭建

3.1 pyethereum的安装

项目地址:https://github.com/ethereum/pyethereum

3.1.1 pyethereum前置需求以及方法安装

pyethereum依赖的库文件较多,如下所示:

(1)setuptools

项目地址:https://pypi.python.org/pypi/setuptools

github地址:https://github.com/pypa/setuptools

setuptools是一个python的安装工具。现在很多程序的安装都需要执行python xxxx install命令,就是使用了setuptools。

//安装方法1

wget https://bootstrap.pypa.io/ez_setup.py-O - | sudo python

//安装方法2

curl https://bootstrap.pypa.io/ez_setup.py-o - | python

上面两个方法本质上都是下载ez_setup.py文件并执行。但是有时候,wget命令无响应,却可以使用curl命令。另外可以自行下载源码安装。不要使用pip安装,版本不固定,我当时下载到的版本是0.6c11,已经落后当前版本一个世纪。另外,这个项目版本更新很快,现在已更新至30.2.0。

tips:使用27.0.0版本时,这个bug折磨了我将近一天,后来我下载了源码,安装还是失败,后来找到了这个原因是在python程序中的项目介绍里的author后面是一个无法识别的编码。解决方法是修改作者的名字

(2)python-dev

我自己的理解是python-dev是能够让python调用系统底层库的工具。

sudo apt-get installpython-dev

(3)pip

pip是python的包管理器,很多依赖库都需要通过pip安装。

https://pip.pypa.io/en/stable/installing/

(4)libssl-dev libffi-dev

libssl-dev是openssl的底层库,因为以太坊需要使用openssl中的椭圆加密secp-256k1算法;libffi-dev是外部函数接口。

sudo apt-get installlibssl-dev libffi-dev

3.1.2安装pyethereum

(1)下载pyethereum

git clone https://github.com/ethereum/pyethereum.git

(2)使用之前安装的pip工具,安装pyethereum所需要的另外一些依赖文件

sudo pip install-r requirements.txt

(3)安装pyethereum

sudo python setup.pyinstall

3.2 pydevp2p的安装

项目地址:https://github.com/ethereum/pydevp2p

(1)安装依赖

sudo apt-get installlibssl-dev build-essential automake pkg-config libtool libffi-dev libgmp-dev

(2)下载pydevp2p

git clone https://github.com/ethereum/pydevp2p.git

(3)安装pydevp2p

cd pydevp2p

sudo python setup.pyinstall

3.3 pyethapp

项目地址:https://github.com/ethereum/pyethapp

(1)安装依赖

(这一步应该在之前进行过了,可以省略)

sudo apt-get installlibssl-dev build-essential automake pkg-config libtool libffi-dev libgmp-dev

(2)下载pydevp2p

git clone https://github.com/ethereum/pyethapp.git

(3)安装pydevp2p

cd pyethapp

//sudo python setup.py develop

sudo python setup.pyinstall

要启动节点,直接调用命令pyethapp就可以了

3.4 solc的安装

项目地址:git clone https://github.com/ethereum/solidity

文档地址:git clone https://solidity.readthedocs.io/en/develop/

智能合约有几种编写语言,目前使用最普遍而且最好用的就是solidity。solc是solidity语言的编译器。solc安装方法如下:

(1)Ubuntu-14.04有可能需要更新至最新的cmake版本,请参考FAQ中的Q1

(2)加入其他的包

sudo apt-get -y update

sudo apt-get -y install language-pack-en-base

sudo dpkg-reconfigure locales

sudo apt-get -y install software-properties-common

sudo add-apt-repository -y ppa:ethereum/ethereum

sudo add-apt-repository -y ppa:ethereum/ethereum-dev

sudo apt-get -y update

sudo apt-get -y upgrade

(3)安装开发相关的包,包括build-essential,git,cmake等等等等。

①Ubuntu15.04(Vivid Vervet)之前的版本,执行

sudo apt-get -y installbuild-essential git cmake libboost-all-dev libgmp-dev libleveldb-dev libminiupnpc-devlibreadline-dev libncurses5-dev libcurl4-openssl-dev libcryptopp-dev libjson-rpc-cpp-devlibmicrohttpd-dev libjsoncpp-dev libedit-dev libz-dev

②Ubuntu 15.10(Wily Werewolf)或者更新版本,执行

sudo apt-get -y installbuild-essential git cmake libboost-all-dev libgmp-dev libleveldb-dev libminiupnpc-devlibreadline-dev libncurses5-dev libcurl4-openssl-dev libcryptopp-dev libjsonrpccpp-devlibmicrohttpd-dev libjsoncpp-dev libedit-dev libz-dev

不同版本使用不同获取命令的原因是,libjsonrpccpp-dev已经在最新版的Ubuntu的通用代码仓库中。

(4)安装最新版的solidity,请参考solidity文档。

①Ubuntu安装稳定版本。

sudo add-apt-repositoryppa:ethereum/ethereum

sudo apt-get update

sudo apt-get install solc

②Ubuntu安装最新版本。

sudo add-apt-repositoryppa:ethereum/ethereum

sudo add-apt-repository ppa:ethereum/ethereum-dev

sudo apt-get update

sudo apt-get install solc

③Homebrew安装

brew update

brew upgrade

brew tap ethereum/ethereum

brew install solidity

brew linkapps solidity

④源码安装最新版(目前来看最稳定的安装方法)

//下载solidity项目

git clone --recursive https://github.com/ethereum/solidity.git

cd solidity

//安装外部依赖

./scripts/install_deps.sh

//安装项目,

mkdir build

cd build

cmake .. && make

make

sudo make install

tips:快速FAQ

Q1:如果没有cmake怎么安装?需要安装3.0.0以上版本的cmake

A1:(1)去https://cmake.org/download/下载新版cmake

(2)./configure

(3)make

(4)make install

Q2:安装solidity之后,执行solc,如果发现没有该命令怎么办?

A2:进入build路径下,依次执行make与make install,这样就将solc文件与相关支持文件写入了/usr/bin/等等路径下。

Q3:运行solc报错在路径/usr/bin/下找不到solc怎么办?

A3:首先要看在/usr/local/bin/下有没有这个文件(我没写错路径哈),如果没有,去执行make install

如果有,那么问题是环境变量中$PATH没有/usr/local/bin/这个路径。需要将该路径填入$PATH。方法有多种,简单的是修改/etc/environment或者/etc/profile(影响所有程序)或者修改/etc/bash.bashrc(只影响本用户,所以对于terminal执行程序有效,对于其他程序可能无效)

在后面添加export PATH=$PATH:/xxx/xxx/xxx

保存退出后,执行source/etc/bash.bashrc

Q4:运行solc报错,libsolidity.so

no such file or directory,而经过检查,发现在/usr/local/lib/里已经存在这个文件怎么办?

A4:首先要看在$PATH中(比如/usr/local/lib/)有没有这个文件。如果没有,去执行make install;如果有,那这是64位系统与32位系统不兼容导致的。需要安装支持文件,执行命令

apt-get install libc6-i386

据说还可以使用ia32-libs,我没有亲自使用过,慎之慎之。

3.5私有链的配置

3.5.1私钥与地址的获取

私钥的获取有两种方法,一个是自己在外部通过openssl随机数生成一个32位数字作为私钥;也可以直接在控制台调用建立账户的命令。下面节点1和节点2分别采用了这两种不同的方法建立私钥。

(1)给自己弄一个32位数字作为私钥,把它保存到文件中~/.config/pyethapp/prikey:

openssl rand -hex 32

vim ~/.config/pyethapp/prikey

(2)然后根据这个私钥生成地址:b857059451cbced23021a1a97426d5b678d37c8529dc5b2c894c7d8ef7bad56e

pyethapp account import~/.config/pyethapp/prikey

导入私钥之后,会自动生成~/.config/pyethapp/keystore文件,记录相关信息。

导入私钥的时候,需要输入一个密码来对私钥进行加密,这个要记住。

(3)上控制台把公钥搞到手:

pyethapp run --console

In[1]: myaccount = eth.services.accounts[0]

In[2]: myaccount.unlock("123456")<-----解锁,输入建立私钥时的密码,时间巨长

In [3]: myaccount.pubkey.encode('hex')

Out[3]: '0429afaaea55eed9f6166af956863dbbdc4d5066be006f0909095eeb0806b465e9b3228ca898f70531d939a3d65811d11951c9181d69b9cd3857b030362501c409'

终于得到了公钥,把前面04去掉,剩下64位作为公钥备用。

3.5.2本机节点配置

当我们输入pyethapp config的时候,就可以看到以太坊app的当前配置。如果想对某一项进行修改,只需要在相关的配置文件中增加相应的字段即可。pyethapp默认的路径是~/.config/pyethapp/config.yaml。

(1)配置节点1

调整配置文件~/.config/pyethapp/config.yaml。

tips:不能用tab键\t,还是要用空格。用\t会报错。

下面举四个例子:

①调整默认的监听端口和地址

p2p:

bootstrap_nodes:[]

listen_host:127.0.0.1

listen_port:18181

max_peers:10

min_peers:5

discovery:

bootstrap_nodes:['enode://29afaaea55eed9f6166af956863dbbdc4d5066be006f0909095eeb0806b465e9b3228ca898f70531d939a3d65811d11951c9181d69b9cd3857b030362501c409@127.0.0.2:18181']<-----------这里前面的29afaaea...c409就是之前获得的公钥

listen_host:127.0.0.1

listen_port:18181

②修改rpc调用远程虚拟机监听来自任意ip的地址(0.0.0.0)和监听端口,在config.yaml文件中修改下列语句。

jsonrpc: {corsdomain: '', listen_host:127.0.0.1, listen_port: 4000}

改成

jsonrpc: {corsdomain: '', listen_host:0.0.0.0, listen_port: 4000}

③区块链不挖空块,在配置文件config.yaml中增加信息:

pow: {activated: true,

coinbase_hex: null,

cpu_pct: 20,

mine_empty_blocks: false}

④要降低挖矿难度以提升挖矿速度,在配置文件config.yaml中增加一下信息:

eth:

block:

MIN_DIFF:1024

genesis:mygenesis.json<----在此文件中,可将创世块难度降低"difficulty":"0x0400"

tips:需要mygenesis.json需要和本文件在同一路径下。

tips:mygenesis.json是创世块文件,即第一个块的属性。

{

"nonce": "0x0000000000000042",

"difficulty": "0x4000",// (这里可以调整创世区块构建的难度,可以修改为0400)

"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",

"coinbase": "0x0000000000000000000000000000000000000000",

"timestamp": "0x00",

"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",

"extraData": "0x424f43",

"gasLimit": "0xffffffff",

"alloc": {//(这里是在创世区块中创建账户,设定账户余额,使用之前建立的账户地址)

"7e89f3c1d7c96704f89dc0417f2f2ea71818674b":{

"balance":"1000000000000000000"

}

}

}

启动节点1

pyethapp -m 25 run --console

(-m 25的作用是使用25%的CPU算力进行挖矿。相当于修改config.yaml中的pow: {cpu_pct: 25)如果正常,待一会儿有:

INFO:pow.subprocessnonce found

INFO:pow.subprocesssending nonce

说明挖矿成功。

tips:亲测,如果使用的CPU算力过低,可能会出现挖不出块的情况。

(2)本机配置节点2

设置一个目录,比如在~/.config/pyethapp下再建立一个目录:~/.config/pyethapp/18182

在这个目录下启动节点,建立新账号,这里account new与之前生成随机数再导入功能完全一样:

sudo pyethapp -d ~/.config/pyethapp/18182 accountnew

自动多了一个config.yaml配置文件、keystore文件夹

把config.yaml增加一下配置,把监听端口改一改,比如18182;静态目标节点还是18181那个实例,这样就能互联了。

同步节点数据

pyethapp export databak

pyethapp --data-dir ~/.config/pyethapp/18182import databak

启动节点

pyethapp --data-dir ~/.config/pyethapp/18182-m 25 run --console

然后启动这个节点,用控制台看看:

In [21]: eth.services.peermanager.peers

Out[21]: []

看到目标地址后面的pyethapp/v1.5.0,说明两个节点已经连接成功。

4.geth版私有链搭建

ethereum bootstrap项目地址:https://github.com/janx/ethereum-bootstrap(现在会自动跳转至https://github.com/cryptape/ethereum-bootstrap)

4.1准备工作

(1)在本地安装好go-ethereum和solc,可以执行geth和solc命令。如果操作系统是ubuntu,安装官方的ethereum安装包即可。

(2)将本仓库通过git clone命令下载到本地。

(3)安装expect,工具脚本用它来自动化一些过程。例如在ubuntu上: sudo apt-get install expect

4.2启动geth

(1)进入本仓库目录

cd ethereum-bootstrap

(2)导入测试账户私钥

./bin/import_keys.sh

(3)初始化blockchain

./bin/private_blockchain_init.sh

输出的结果类似如下:I082216:28:29.767646 ethdb/database.go:82] Alloted 16MB cache and 16 file handles todata/chaindata I0822 16:28:29.773596 cmd/geth/main.go:299] successfully wrote genesisblock and/or chain rule set: 19425866b7d3298a15ad79accf302ba9d21859174e7ae99ce552e05f13f0efa3

(4)启动私有链节点: ./bin/private_blockchain.sh.启动成功后可以看到类似如下输出:


(5)此时以太坊交互式控制台已经启动,我们可以开始测试和开发了。

tips:工具脚本假设你的geth安装在默认位置,可以直接通过geth执行。如果geth命令安装在非标准的位置,可以设置GETH环境变量指定geth可执行文件的路径。例如:

GETH=/some/weird/dir/geth./bin/import_keys.sh

tips:私有链的所有数据都会放在仓库根目录下的data目录中,删除这个目录可以清除所有数据,重新启动新环境。

4.3 geth客户端操作

所有操作方式都是通过web3的api进行操作。

下面介绍两个基本操作,获得ether和部署合约。

4.3.1通过挖矿来为account发放ether

(1)查看账号余额

> web3.eth.getBalance(web3.eth.accounts[0])

0

(2)可以通过挖矿的方式给第一个账号发行ether

> miner.start(1)

I0822 17:17:43.496826 miner/miner.go:119] Starting mining operation (CPU=1 TOT=3)

I0822 17:17:43.497379 miner/worker.go:573] commit new work on block 30 with 0 txs& 1 uncles. Took 527.407µs

(3)需要调用miner.stop来停止挖矿

> miner.stop()

true

> web3.eth.getBalance(web3.eth.accounts[0])

309531250000000000000

(4)使用账号前先解锁

> personal.unlockAccount(web3.eth.accounts[0])

4.3.2编译和部署智能合约

在contracts目录下有一个智能合约样例文件Token.sol,通过Solidity语言实现了基本的代币功能,合约持有者可以发行代币,使用者可以互相转账.

我们可以使用以太坊控制台来编译部署这个合约。以太坊控制台是最基本的工具,使用会比较繁琐。社区也提供了其他更加方便的部署工具,此处不做讨论。

(1)我们先把合约代码压缩为一行。新建一个ssh session,切换到geth用户环境su - geth,然后输入:cat contracts/Token.sol | tr'\n' ' '.

(2)切换到以太坊控制台,把合约代码保存为一个变量:

var tokenSource = 'contract Token {address issuer;mapping (address => uint) balances;event Issue(address account, uint amount);event Transfer(address from, address to, uintamount);function Token() {issuer = msg.sender;}function issue(address account, uint amount) {if (msg.sender != issuer) throw;balances[account]+= amount;}function transfer(address to, uint amount){if (balances[msg.sender] < amount)throw;balances[msg.sender] -= amount;balances[to] += amount;Transfer(msg.sender, to, amount);}functiongetBalance(address account) constant returns (uint) {return balances[account];} }';

(3)然后编译合约代码:

var tokenCompiled = web3.eth.compile.solidity(tokenSource);

通过tokenCompiled.Token.code可以看到编译好的二进制代码,通过tokenCompiled.Token.info.abiDefinition可以看到合约的ABI。

(4)接下来我们要把编译好的合约部署到网络上去。

①用ABI来创建一个javascript环境中的合约对象:

var contract = web3.eth.contract(tokenCompiled.Token.info.abiDefinition);

②通过合约对象来部署合约

var initializer ={from: web3.eth.accounts[0], data: tokenCompiled.Token.code, gas: 300000};

var callback = function(e, contract){

if(!e) {

if(!contract.address) {

console.log("Contract transactionsend: TransactionHash: " + contract.transactionHash + " waiting to bemined...");

} else {

console.log("Contract mined!");

console.log(contract);

}

}

};

var token = contract.new(initializer, callback);

contract.new方法的第一个参数设置了这个新合约的创建者地址from,这个新合约的代码data,和用于创建新合约的费用gas。gas是一个估计值,只要比所需要的gas多就可以,合约创建完成后剩下的gas会退还给合约创建者。

contract.new方法的第二个参数设置了一个回调函数,可以告诉我们部署是否成功.

contract.new执行时会提示输入钱包密码。执行成功后,我们的合约Token就已经广播到网络上了。

③等待矿工把我们的合约打包保存到以太坊区块链上,部署就完成了。

在公有链上,矿工打包平均需要15秒,在私有链上,我们需要自己来做这件事情。首先开启挖矿:

miner.start(1)

此时需要等待一段时间,以太坊节点会生成挖矿必须的数据,这些数据都会放到内存里面。在数据生成好之后,挖矿就会开始,稍后就能在控制台输出中看到类似:

:hammer:Mined block

这样的信息,这说明挖到了一个块,合约已经部署到以太坊网络上了!此时我们可以把挖矿关闭:

miner.stop()

④之后就可以调用合约了。先通过token.address获得合约部署到的地址,以后新建合约对象时可以使用。这里我们直接使用原来的contract对象

//本地钱包的第一个地址所持有的token数量

> web3.eth.getBalance(web3.eth.accounts[0])

0

//发行100个token给本地钱包的第一个地址

> token.issue.sendTransaction(web3.eth.accounts[0], 100, {from: web3.eth.accounts[0]});

I1221 11:48:30.51229611155 xeth.go:1055]Tx(0xc0712460a826bfea67d58a30f584e4bebdbb6138e7e6bc1dbd6880d2fce3a8ef) to: 0x37dc85ae239ec39556ae7cc35a129698152afe3c

"0xc0712460a826bfea67d58a30f584e4bebdbb6138e7e6bc1dbd6880d2fce3a8ef"

//发行token是一个transaction,因此需要挖矿使之生效

> miner.start(1)

:hammer:Mined block

> miner.stop()

//再次查询本地钱包第一个地址的token数量

> token.getBalance(web3.eth.accounts[0])

100

//从第一个地址转30个token给本地钱包的第二个地址

> token.transfer.sendTransaction(web3.eth.accounts[1], 30, {from: web3.eth.accounts[0]})

I1221 11:53:31.85254111155 xeth.go:1055]Tx(0x1d209cef921dea5592d8604ac0da680348987b131235943e372f8df35fd43d1b) to: 0x37dc85ae239ec39556ae7cc35a129698152afe3c

"0x1d209cef921dea5592d8604ac0da680348987b131235943e372f8df35fd43d1b"

> miner.start(1)

> miner.stop()

> token.getBalance(web3.eth.accounts[0])

70

> token.getBalance(web3.eth.accounts[1])

30

参考资料:

以上各项目的github项目地址及其文档

http://ethcast.com/v3汪晓明的区块链视频

geth --datadir "~/ethdev" --dev console2> geth.log

fail to importscrypt

pip uninstall scrypt

pip install scrypt

以太坊从入门到实战
Web note ad 1