ubuntu系统搭建以太坊开发环境

node、npm安装(apt-get安装的版本不对)

通过 https://nodejs.org 下载最新/稳定的nodejs、npm版本(需要nodejs 5.0+);

Truffle、ganache-cli安装

Truffle 是目前最流行的以太坊开发框架,采用JavaScript编写,支持智能合约的编译、部署和测试。通过以下指令安装Truffle:

1
$ npm install -g truffle

ganache是truffle推出的一个可视化私有链客户端,方便开发,与testrpc类似。

可以下载命令行的ganache-cli

1
npm install -g ganache-cli

也可以下载图形化的ganache

1
2
3
wget https://github.com/trufflesuite/ganache/releases/download/v1.1.0/ganache-1.1.0-x86_64.AppImage
chmod +x ganache-1.1.0-x86_64.AppImage //修改权限
sudo ./ganache-1.1.0-x86_64.AppImage //启动ganache

启动后操作界面如下图如示,其中ganache默认生成了10个Account账户,我们可以清晰的看到钱包地址及账户余额(右侧钥匙为私钥):

可以通过修改hostname和port改变私有私访问地址(端口默认为7545):

基本使用

1、随意建立代码文件夹
mkdir -p ~/blockchain/test_truffle

2、进入刚刚建立的文件夹并执行初始化命令
cd ~/blockchain/test_truffle && truffle init

3、在contracts目录中新建一个HelloWorld.sol文件,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
pragma solidity ^0.4.17;

contract HelloWorld {

//say hello world
function say() public pure returns (string) {
return "Hello world";
}

//print name
function print(string name) public pure returns (string) {
return name;
}
}

4、在migrations目录下新建部署脚本,文件名自定义,比如2_deploy_contracts.js,将我们刚才创建的HelloWorld.sol文件添加到发布配置文件中,内容如下:

1
2
3
4
5
var HelloWorld = artifacts.require("./HelloWorld.sol");

module.exports = function(deployer) {
deployer.deploy(HelloWorld);
};

5、编译合约
truffle compile

6、编辑truffle.js
编辑truffle.js(注ganache-cli默认使用8545,ganache图型化客户端默认使用7545),内容如下:

1
2
3
4
5
6
7
8
9
module.exports = {
networks: {
development: {
host: "localhost",
port: 8545,
network_id: "*"
}
}
};

7、部署智能合约
truffle migrate

8、功能验证
输入truffle console命令打开truffle控制台,测试刚才我们部署的HelloWorld合约

1
2
3
4
5
6
7
8
9
10
11
truffle(development)> var contract;
undefined

truffle(development)> HelloWorld.deployed().then(function(instance){contract= instance;});
undefined

truffle(development)> contract.say()
'Hello world'

truffle(development)> contract.print("hi")
'hi'

安装geth客户端

Geth又名Go Ethereum,由Go语言开发,完全开源的项目。它是一个命令行工具,提供很多命令和选项,可以运行以太坊节点、创建和管理账户、发送交易、挖矿、部署智能合约等。

1
2
3
4
sudo apt-get install software-properties-common
sudo add-apt-repository -y ppa:ethereum/ethereum
sudo apt-get update
sudo apt-get install ethereum

搭建以太坊私有链

1、准备创世区块配置文件

以太坊支持自定义创世区块,要运行私有链,我们就需要定义自己的创世区块,创世区块信息写在一个json格式的配置文件中。首先将下面的内容保存到一个json文件中,例如genesis.json。创建blockchain文件夹,并将genesis.json放入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{  
"nonce": "0xdeedbeafdeadbeef",
"timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData": "0x00",
"gasLimit": "0x80000000",
"difficulty": "0x400",
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"alloc": {},
"config": {
"chainId": 54001,
"homesteadBlock": 0,
"eip155Block": 0,
"eip158Block": 0
}
}

各参数的作用如下:

1
2
3
4
5
6
7
8
9
mixhash:与nonce配合用于挖矿,由上一个区块的一部分生成的hash。
nonce:nonce就是一个64位随机数,用于挖矿
difficulty:设置当前区块的难度,如果难度过大,cpu挖矿就很难,这里设置较小难度
alloc:用来预置账号以及账号的以太币数量
coinbase:矿工的账号,随便填
timestamp:设置创世块的时间戳
parentHash:上一个区块的hash值,因为是创世块,所以这个值是0
extraData:附加信息,随便填,可以填你的个性信息
gasLimit:该值设置对GAS的消耗总量限制,用来限制区块能包含的交易信息总和

2、写入创世区块

此时genesis.json保存在~/blockchain中,准备好创世区块配置文件后,需要初始化区块链,将上面的创世区块信息写入到区块链中。创建node目录,然后执行下列指令:

1
geth --datadir './node' init genesis.json

命令的主体是geth init,表示初始化区块链,命令可以带有选项和参数,其中–datadir选项后面跟一个目录名,这里为node,表示指定数据存放目录为node,genesis.json是init命令的参数。运行上面的命令,会读取genesis.json文件,根据其中的内容,将创世区块写入到区块链中。此时目录结构如下:

blockchain
├── node
│ ├── geth
│ │ ├── chaindata
│ │ └── …
│ └── keystore
└── genesis.json

其中geth/chaindata中存放的是区块数据,keystore中存放的是账户数据。

3、启动私有链节点

初始化完成后,就有了一条自己的私有链,之后就可以启动自己的私有链节点并做一些操作,在终端中输入以下命令即可启动节点。

1
nohup geth --rpc --rpcaddr="0.0.0.0" --rpccorsdomain="*" --rpcapi "db,eth,net,web3" --networkid '54001' --datadir '~/blockchain/node' &

–datadir选项指定使用node作为数据目录,–networkid选项后面跟一个数字,这里是54001,表示指定这个私有链的网络id为54001。网络id在连接到其他节点的时候会用到,以太坊公网的网络id是1,为了不与公有链网络冲突,运行私有链节点的时候要指定自己的网络id。

执行以下命令,即可进入区块链的console

1
geth attach ~/blockchain/node/geth.ipc

运行上面的命令后,就启动了区块链节点并进入了Javascript Console。这是一个交互式的Javascript执行环境,在这里面可以执行Javascript代码,其中>是命令提示符。在这个环境里也内置了一些用来操作以太坊的Javascript对象,可以直接使用这些对象。

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

4、探索Javascript Console

进入以太坊Javascript Console后,就可以使用里面的内置对象做一些操作,这些内置对象提供的功能很丰富,比如查看区块和交易、创建账户、挖矿、发送交易、部署智能合约等。
(注:命令都可以按Tab键自动补全。)

1、创建账户
前面只是搭建了私有链,并没有自己的账户,可以在js console中输入eth.accounts来验证:

1
2
> eth.accounts
[]

通过使用personal对象来创建账户:

personal.newAccount()
1
2
3
Passphrase: 
Repeat passphrase:
"0xbe38bf86bb9837cd60c86f70cb344c59c91f9b7f"

接下来就可以查看到刚才创建的账户了:

1
2
> eth.accounts
["0xbe38bf86bb9837cd60c86f70cb344c59c91f9b7f"]

2、查看余额

1
2
3
eth对象提供了查看账户余额的方法:
> eth.getBalance(eth.accounts[0])
0

3、启动&停止挖矿

通过miner.start()来启动挖矿:

1
> miner.start(1)

其中start的参数表示挖矿使用的线程数。第一次启动挖矿会先生成挖矿所需的DAG文件,这个过程有点慢,等进度达到100%后,就会开始挖矿,此时屏幕会被挖矿信息刷屏。

如果想停止挖矿,在js console中输入miner.stop():

1
> miner.stop()

挖到一个区块会奖励5个以太币,挖矿所得的奖励会进入矿工的账户,这个账户叫做coinbase,默认情况下coinbase是本地账户中的第一个账户:

1
2
> eth.coinbase
"0xbe38bf86bb9837cd60c86f70cb344c59c91f9b7f"

要想使挖矿奖励进入其他账户,通过miner.setEtherbase()将其他账户设置成coinbase即可。
getBalance()返回值的单位是wei,wei是以太币的最小单位,1个以太币=10的18次方个wei。要查看有多少个以太币,可以用web3.fromWei()将返回值换算成以太币:

1
> web3.fromWei(eth.getBalance(eth.accounts[0]),'ether')

4、发送交易
通过eth.sendTransaction可以通过发送一笔交易,从账户0转移5个以太币到账户1

1
2
3
4
5
6
> eth.sendTransaction({from:eth.accounts[0],to:eth.accounts[1],value:web3.toWei(3,'ether')})
Error: authentication needed: password or unlock
at web3.js:3119:20
at web3.js:6023:15
at web3.js:4995:36
at <anonymous>:1:1

这里报错是因为账户每隔一段时间就会被锁住,要发送交易需要先解锁账户,由于我们要从账户0发送交易,所以要解锁账户0:

1
2
3
4
> personal.unlockAccount(eth.accounts[0])
Unlock account 0xbe38bf86bb9837cd60c86f70cb344c59c91f9b7f
Passphrase:
true

输入创建账户时设置的密码,就可以成功解锁账户。然后再发送交易:

1
2
> eth.sendTransaction({from:eth.accounts[0],to:eth.accounts[1],value:web3.toWei(3, 'ether')})
"0xd2947d185056a3952d1f1641cae0eb4f89347f1f88770f5e2a75f978414351d0"

此时交易已经提交到区块链,返回了交易的hash,但还未被处理,这可以通过查看txpool来验证:

1
2
3
4
5
> txpool.status
{
pending: 1,
queued: 0
}

其中有一条pending的交易,pending表示已提交但还未被处理的交易。要使交易被处理,必须要挖矿。这里我们启动挖矿,然后等待挖到一个区块之后就停止挖矿。当txpool中pending的交易数量应该为0了,说明交易已经被处理了:

1
2
3
4
5
6
7
8
> txpool.status
{
pending: 0,
queued: 0
}

> eth.getBalance(eth.accounts[1])
3000000000000000000

5、查看交易和区块
eth对象封装了查看交易和区块信息的方法。

查看当前区块总数:

1
2
> eth.blockNumber
33

通过交易hash查看交易:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
> eth.getTransaction("0xd2947d185056a3952d1f1641cae0eb4f89347f1f88770f5e2a75f978414351d0")
{
blockHash: "0x0f00b3a4f8599ebe5867bf793a4ba463dc47211815e20f8d7a1593ac18ff872c",
blockNumber: 634359,
from: "0xf16bae16adb39a886e04c728863812fa23141b8b",
gas: 90000,
gasPrice: 1000000000,
hash: "0xd2947d185056a3952d1f1641cae0eb4f89347f1f88770f5e2a75f978414351d0",
input: "0x",
nonce: 22,
r: "0x64204d18a947be6bb7033248166f06c6b97129077d9b895ed67f3e9610718a07",
s: "0x21e2a1c101c72d36d5b450dc44fb41d56c095306ff48ac666b2e7a14a035223b",
to: "0xbe38bf86bb9837cd60c86f70cb344c59c91f9b7f",
transactionIndex: 0,
v: "0x1a606",
value: 3000000000000000000
}

通过区块号查看区块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
> eth.getBlock(634359)
{
difficulty: 1278282,
extraData: "0xd783010703846765746887676f312e392e32856c696e7578",
gasLimit: 4712388,
gasUsed: 21000,
hash: "0x0f00b3a4f8599ebe5867bf793a4ba463dc47211815e20f8d7a1593ac18ff872c",
logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
miner: "0xf16bae16adb39a886e04c728863812fa23141b8b",
mixHash: "0x9ad2c2c50c3c223078d17853860a1e60dbf1ce4d773ccf22c92db92dee91d866",
nonce: "0x6f1fc6c4e0f51c12",
number: 634359,
parentHash: "0xafe36f1d414fc95100fff815149246c3a449bbfafda897732b235e548f4b513c",
receiptsRoot: "0x299e65dd0d084faf2ae29b3abcc14ab7b99029e3629bbcaeef3a69c14a9e39f5",
sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
size: 654,
stateRoot: "0x832f5fde59c6de9304a50831d8b9d1caa0107b9ec495484d52af6106ee0de856",
timestamp: 1523329711,
totalDifficulty: 1042016387960,
transactions: ["0xd2947d185056a3952d1f1641cae0eb4f89347f1f88770f5e2a75f978414351d0"],
transactionsRoot: "0x668edf6f5eadce730ac49224553a9f0cf761fe518d7a66d89cba0398e6d23b08",
uncles: []
}

6、连接到其他节点
可以通过admin.addPeer()方法连接到其他节点,两个节点要想联通,必须保证网络是相通的,并且要指定相同的networkid。
假设有两个节点:节点一和节点二,networkid都是54001,通过下面的步骤就可以从节点二连接到节点一。
首先要知道节点一的enode信息,在节点一的js console中执行下面的命令查看enode信息:

1
2
> admin.nodeInfo.enode
"enode://07f36d1c6ee18aa231dfac161913f0a3d533108505883418735b838b9453a7e5321f69bbc08a526695313554e9e325ca3eec12242b1b079c246a07d20a4e33e5@[::]:30303"

然后在节点二的js console中执行admin.addPeer(),就可以连接到节点一:

1
> admin.addPeer("enode://07f36d1c6ee18aa231dfac161913f0a3d533108505883418735b838b9453a7e5321f69bbc08a526695313554e9e325ca3eec12242b1b079c246a07d20a4e33e5@ip:30303")

addPeer()的参数就是节点一的enode信息,注意要把enode中的[::]替换成节点二的IP地址。连接成功后,节点二就会开始同步节点一的区块,同步完成后,任意一个节点开始挖矿,另一个节点会自动同步区块,向任意一个节点发送交易,另一个节点也会收到该笔交易。
通过admin.peers可以查看连接到的其他节点信息,通过net.peerCount可以查看已连接到的节点数量。
除了上面的方法,也可以在启动节点的时候指定–bootnodes选项连接到其他节点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
> net.peerCount
1
> admin.peers
[{
caps: ["eth/62", "eth/63"],
id: "07f36d1c6ee18aa231dfac161913f0a3d533108505883418735b838b9453a7e5321f69bbc08a526695313554e9e325ca3eec12242b1b079c246a07d20a4e33e5",
name: "Geth/v1.7.3-stable-4bb3c89d/linux-amd64/go1.9.2",
network: {
localAddress: "10.104.28.16:58870",
remoteAddress: "111.230.144.116:30303"
},
protocols: {
eth: {
difficulty: 1042104536363,
head: "0xb7a8c1e47797a06760959935f3d72076398f2244cf12ef2de63e13ac1bd4cce9",
version: 63
}
}
}]

安装MetaMask

MetaMask是一款在谷歌浏览器Chrome上使用的插件类型的以太坊钱包,该钱包不需要下载,只需要在谷歌浏览器添加对应的扩展程序即可,非常轻量级,使用起来也非常方便。

MetaMask安装

这款插件的官方地址为 https://metamask.io/ ,Chrome网上应用商店的下载地址是: https://chrome.google.com/webstore/detail/nkbihfbeogaeaoehlefnkodbefgpgknn ,如果无法访问,可以直接点击下载

MetaMask使用

根据提示同意条款并输入钱包密码:

保存好这些助记词之后,点击下面的按钮,进入钱包页面,如下图所示:

点击左上角的Private Network->Custom RPC即可添加自己的以太坊私链地址:

点击右上角图标可以切换或导入新Account:

参考资料

CRYPTOZOMBIES
Truffle Framework
ETHEREUM PET SHOP
BUILDING ROBUST SMART CONTRACTS WITH OPENZEPPELIN
以太坊学习笔记:私有链搭建操作指南
zeppelin-solidity
GO EthEREUM

Thank you for your support!