区块链初探
内容摘要
- Geth的安装配置
- 搭建私有链以及加入私有节点
- 智能合约编写、部署与测试
- 日志分析
0. 实验环境
- 虚拟机: ubuntu-17.10.1-desktop-amd64
- go的版本: go1.11
1. 以太坊的安装,安装geth客户端
-
geth是用go语言写的,编译geth源码需要go语言和C语言编译器,因此需要先安装go语言,go语言版本推荐用go1.7及以上,go语言的安装配置教程。
-
将github仓库克隆源文件:
git clone https://github.com/ethereum/go-ethereum
-
构建geth需要安装Go和C编译器:
sudo apt-get install -y build-essential golang
-
最后,使用以下命令构建程序生成
geth
。cd go-ethereum make geth
-
在
go-ethereum
文件夹下可以运行build/bin/geth
以启动节点,当然可以在/etc/profile
配置一下geth
的环境变量,然后就可以直接使用geth
而不用具体指定路径了啦。vi /etc/profile
在后面加入
geth
的文件具体路径,例如:export PATH=$PATH:/home/liuyt49/Desktop/go-ethereum/build/bin
2. 私有链创世区块搭建
-
自定义创世区块,
genesis.json
{ "config": { "chainId": 666, "homesteadBlock": 0, "eip155Block": 0, "eip158Block": 0 }, "alloc" : {}, "coinbase" : "0x0000000000000000000000000000000000000000", "difficulty" : "0x00000001", "extraData" : "", "gasLimit" : "0xffffff", "nonce" : "0x0000000000000042", "mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000", "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", "timestamp" : "0x00" }
参数解释:
参数 解释 alloc 用来预置账号以及账号的eth数量。私链挖矿比较容易,不需要预置有币的账号 coinbase 矿工的账号 difficulty 设置当前区块的难度 extraData 附加信息 gasLimit 该值设置对GAS的消耗总量限制,用来限制区块能包含的交易信息总和,因为我们是私有链,所以填最大 nonce nonce值是一个64随机数,用于挖矿 mixHash 与nonce配合用于挖矿,由上一个区块的一部分生成的hash parentHash 上一个区块的hash值,因为是创世区块,因此该值为0 -
初始化区块链
geth -datadir myData/00 init genesis.json
参数解释:-
datadir
- 设置当前区块链网络数据存放的位置,区块链网络存储在myData/00
文件夹下 -
init
- 指定创世块文件的位置,并创建初始块
-
-
启动私有链节点
geth -datadir myData/00 -networkid 2018 -rpc --rpcport "8545" --port "30303" -rpcaddr 你的IP -rpccorsdomain "*" console 2>output.log
参数解释:-
rpc
- 启动rpc
通信,可以进行智能合约的部署和调试 -
rpcaddr
– rpc接口的地址 -
networkid
- 设置当前区块链的网络ID,用于区分不同的网络,是一个数字 -
console
- 启动命令行模式,可以在Geth中执行命令 - 输出重定向到
output.log
-
-
启动节点成功后,会进入Geth的命令行模式
-
创建一个新用户,用户名为空
personal.newAccount(password, [callback])
-
解锁用户
personal.unlockAccount(accountname, password);
-
挖矿
- 开始挖矿
其中start
的参数表示挖矿使用的线程数。第一次启动挖矿会先生成挖矿所需的DAG文件,这个过程有点慢,等进度达到100%后,就会开始挖矿,此时屏幕会被挖矿信息刷屏。miner.start(1)
- 挖到第一个区块即停止挖矿
admin.sleepBlocks(1)
- 停止挖矿
miner.stop()
- 返回null不代表没有开始挖矿,实际正在进行中,在日志文件中有输出,详细解释why did it returned null after call miner.start()
- 开始挖矿
-
查看余额
eth.getBalance(eth.accounts[0])
-
账号转账
web3.eth.sendTransaction(transactionObject [, callback])
-
新手的一些小总结
- 关于如何中断一个挖矿过程的具体操作
输入miner.start()
即启动挖矿,挖矿后,会不停刷屏,输入miner.stop()
即停止,不用管刷屏导致的命令不全,命令会正常执行。 - 正常退出
Geth
输入exit
即可 - 退出以后如何再次进入
geth --datadir myData/00 console
当然其中dtadir
需要对应自己区块链网络数据存放的位置
- 关于如何中断一个挖矿过程的具体操作
-
-
创建并加入新的私有节点
- 创建新节点与之前流程相似,切记使用同一个创世区块的配置
genesis.json
- 同一台主机创建两个节点需要注意使用的端口避免冲突
- 设置的
networkid
需要相同 - 新的终端输入
geth -datadir myData/01 init genesis.json geth -datadir myData/01 -networkid 2018 -rpc --rpcport "8546" --port "30304" -rpcaddr 你的IP -rpccorsdomain "*" console 2>output.log
- 建立节点连接
-
节点
00
,获取其节点信息 -
节点
01
连接节点00
,使用到方法admin.addPeer(节点00的enode)
-
查看连接情况。可能一开始没有显示,需要等一会。
admin.peers
-
- 创建新节点与之前流程相似,切记使用同一个创世区块的配置
3. 对getBlock中所得区块的各个字段进行解释
-
web3.eth.getBlock()
方法返回指定块编号或块哈希对应的块。 - 调用:
eth.getBlock(blockHashOrBlockNumber [, returnTransactionObjects] [, callback])
- 参数:
-
blockHashOrBlockNumber
:String|Number - 块编号或块哈希值,或者使用以下字符串:“genesis”、“latest” 或 “pending” 。 -
returnTransactionObjects
:Boolean - 可选,默认值为false。当设置为true时,返回块中将包括所有交易详情,否则仅返回交易哈希。 -
callback
:Function - 可选的回调函数,其第一个参数为错误对象,第二个参数为结果。
-
-
eth.getBlock(0)
- 返回值字段解释
-
difficulty
: 该块的难度值 -
extraData
: 块中的"extra data"字段 -
gasLimit
: 该块允许的最大的gas值 -
gasUsed
: 该块中所有交易使用的gas的总量 -
hash
: 块的哈希值,处于pending
状态的块的hash值为null -
logsBloom
: 块中日志的bloom filter,处于pending
状态的块为null -
miner
: 接收奖励的矿工地址 -
mixHash
: 与nonce配合用于挖矿,由上一个区块的一部分生成的hash -
nonce
: nonce就是一个64位随机数,用于挖矿,用于计算生成的proof-of-work的哈希,处于pending状态的块为null -
number
: 块编号,处于pending状态的块为null -
parentHash
: 父块的哈希值(hash) -
receiptsRoot
: 收款字典树的根节点哈希值 -
sha3Uncles
: 块中叔伯数据的SHA3值 -
size
: 字节为单位的块大小 -
stateRoot
: 块中的最终状态树根节点,存储整个系统状态的专用Merkle树的根哈希 -
timestamp
: 块创建的Unix时间戳 -
totalDifficulty
: 截至该块的全链总难度值 -
transactions
: 该块交易集合 -
transactionsRoot
: 交易树根节点 -
uncles
: 叔伯块哈希值数组
-
4. 编写简单的智能合约,在remix下进行调试,并部署在链上进行调用
- 编写简单的智能合约,并在remix下进行调试
- 智能合约
pragma solidity ^0.4.0; contract SimpleContract { string message; function setMessage(string x) public{ message = x; } function getMessage() constant public returns (string) { return message; } }
- 编译合约
- 选择合适的编译器版本
- 点击右侧栏的Compile模块下的Start to compile按钮进行编译,同时编译成功可以在
Run
中模拟运行。
- 部署合约
部署合约就是将编译好的合约字节码通过外部账号发送交易的形式部署到以太坊区块链上。
步骤:-
compile
页面编译成功后点击Details
按钮,出现浮层页面在浮层中找到WEB3DEPLOY
, 将WEB3DEPLOY
中的内容复制。 -
将智能合约直接粘贴到命令行(
console
)var simplecontractContract = web3.eth.contract([{"constant":false,"inputs":[{"name":"x","type":"string"}],"name":"setMessage","outputs":[],"payable":false,"type":"function","stateMutability":"nonpayable"},{"constant":true,"inputs":[],"name":"getMessage","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function","stateMutability":"view"}]); var simplecontract = simplecontractContract.new( { from: web3.eth.accounts[0], data: '0x606060405261028f806100126000396000f360606040526000357c010000000000000000000000000000000000000000000000000000000090048063368b877214610047578063ce6d41de146100a257610042565b610002565b34610002576100a06004808035906020019082018035906020019191908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050909091905050610122565b005b34610002576100b460048050506101d3565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156101145780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b8060006000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017157805160ff19168380011785556101a2565b828001600101855582156101a2579182015b828111156101a1578251826000505591602001919060010190610183565b5b5090506101cd91906101af565b808211156101c957600081815060009055506001016101af565b5090565b50505b50565b602060405190810160405280600081526020015060006000508054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156102805780601f1061025557610100808354040283529160200191610280565b820191906000526020600020905b81548152906001019060200180831161026357829003601f168201915b5050505050905061028c565b9056', gas: '4700000' }, function (e, contract){ console.log(e, contract); if (typeof contract.address !== 'undefined') { console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash); } })
-
出现报错,进行部署之前,先将交易的账号进行解锁,不然会出现错误
personal.unlockAccount(eth.accounts[0])
-
-
再次进行粘贴,没有报错了
-
检查一下交易池,查看当前交易的待处理状况:
txpool.status
看到当前的交易池中有一个交易正在等待确认。然后,我们查看待确认交易的详细内容:eth.getBlock("pending",true).transactions
查看区块信息与我们之前所设置的相同。-
from
数据项就是我们发送交易的地址 -
input
就是合约编译完成的字节码 - 新的交易创建在第3号区块中
-
-
查看日志
INFO [11-03|02:52:58.543] Submitted contract creation fullhash=0x16acefe5b61cff177b3cc65d21237594913bdd7e0c71d97200a42db22147f55d contract=0xE87619e16Aa3d2fd7BeA8A18Bb770b8B1Fd808b8
说明交易已经发送到区块链中了,正在等待矿工的确认,确认后才真正完成智能合约的部署
- 注意:接下来要开始挖矿(
miner.start(1)
)
- 注意:接下来要开始挖矿(
-
- 调用智能合约
- 关于 web3.eth.contract
- 使用一个在某个地址上已经存在的合约
-
simplecontract
是合约对象,通过创建合约的交易来部署合约,是第一个合约 -
simplecontract.address
得到之前部署了的合约的合约地址 -
testcontract
为合约对象,通过交易修改合约的数据。
// instantiate by address testcontract = simplecontractcontract.at(simplecontract.address)
-
- 调用
如果调用的智能合约函数需要合约中的数据, 则需要消耗以太坊gas,否则不需要消耗。由于需要修改合约中的数据, 调用setMessage()
时需要使用sendTransaction()发起交易, 并附加发起者的账号. 以上操作只是发起了交易, 但交易并不一定会被处理. 交易被处理还必须要有节点处于挖矿模式。想要获取到修改后的数据,必须等到交易完成后,getMessage
才可以获取到修改后的数据。-
调用
setMessage
方法testcontract.setMessage.sendTransaction("sysu", {from:eth.accounts[0]})
解决方法与之前一样,只需要解锁一下账号就可以了
返回的值发送的交易的hash值
查看交易池信息以及详细的交易信息
然后开始挖矿 -miner.start(1)
-
调用
getMessage
方法
-
- 智能合约
5. 对交易的字段进行解释
获取一个交易信息,使用到eth.getTransaction(hash)
方法,返回具有指定哈希值hash
的交易对象。
- 已确认交易
- 未确认交易
- 字段解释
-
blockHash
: 交易所在块的哈希值,如果交易处于pending
状态,该值为null
-
blockNumber
: 交易所在块的编号,如果交易处于pending
状态,该值为null
-
from
: 交易发起者的地址 -
gas
: 发送方提供的gas
用量 -
gasPrice
: 发送方承诺的gas
的价格,以wei
为单位 -
hash
: 交易的哈希值 -
input
: 随交易发送的数据 -
nonce
: 交易发起者(from账户)发出交易的次数 -
r
:ECDSA
签名值,交易的签名数据 -
s
:ECDSA
签名值,交易的签名数据 -
to
: 交易接收方的地址。对于创建合约的交易,该值为null
。 -
transactionIndex
: 交易在块中的索引位置 -
v
:ECDSA
签名值,交易的签名数据 -
value
: 以wei
为单位的转账金额
-
6. 对日志输出进行解释
-
启动本地节点
self=enode://XXX
INFO [10-31|19:57:55.038] Started P2P networking self=enode://63b1fc80da8edeaa22bc873c6263e4a6f3c4b5dbd73b66042aed71691671daab5[email protected]127.0.0.1:30303
-
从本地
database=/home/liuyt49/Desktop/Blockchain/data/00/geth/chaindata
读取区块数据INFO [10-31|19:57:54.880] Allocated cache and file handles database=/home/liuyt49/Desktop/Blockchain/data/00/geth/chaindata cache=492 handles=512
-
初始化区块链配置
INFO [10-31|19:57:54.904] Initialised chain configuration config="{ChainID: 666 Homestead: 0 DAO: <nil> DAOSupport: false EIP150: <nil> EIP155: 0 EIP158: 0 Byzantium: <nil> Constantinople: <nil> Engine: unknown}"
-
允许硬盘存储 ethash 相关
caches、DAGs
INFO [10-31|19:57:54.904] Disk storage enabled for ethash caches dir=/home/liuyt49/Desktop/Blockchain/data/00/geth/ethash count=3 INFO [10-31|19:57:54.904] Disk storage enabled for ethash DAGs dir=/home/liuyt49/.ethash count=2
-
初始化以太坊协议
INFO [10-31|19:57:54.904] Initialising Ethereum protocol versions="[63 62]" network=2018
-
第一次启动挖矿会先生成挖矿所需的 DAG 文件,这个过程有点慢,等进度达到 100% 后,就会开始挖矿
INFO [10-31|19:59:04.873] Generating DAG in progress epoch=0 percentage=0 elapsed=10.269s INFO [10-31|19:59:13.702] Generating DAG in progress epoch=0 percentage=1 elapsed=19.099s INFO [10-31|19:59:22.337] Generating DAG in progress epoch=0 percentage=2 elapsed=27.734s INFO [10-31|19:59:32.276] Generating DAG in progress epoch=0 percentage=3 elapsed=37.672s INFO [10-31|19:59:40.475] Generating DAG in progress epoch=0 percentage=4 elapsed=45.871s INFO [10-31|19:59:48.898] Generating DAG in progress epoch=0 percentage=5 elapsed=54.294s INFO [10-31|19:59:57.013] Generating DAG in progress epoch=0 percentage=6 elapsed=1m2.410s INFO [10-31|20:00:05.233] Generating DAG in progress epoch=0 percentage=7 elapsed=1m10.630s INFO [10-31|20:00:13.440] Generating DAG in progress epoch=0 percentage=8 elapsed=1m18.837s INFO [10-31|20:00:21.584] Generating DAG in progress epoch=0 percentage=9 elapsed=1m26.980s INFO [10-31|20:00:30.118] Generating DAG in progress epoch=0 percentage=10 elapsed=1m35.514s INFO [10-31|20:00:38.132] Generating DAG in progress epoch=0 percentage=11 elapsed=1m43.529s INFO [10-31|20:00:46.207] Generating DAG in progress epoch=0 percentage=12 elapsed=1m51.603s INFO [10-31|20:00:54.200] Generating DAG in progress epoch=0 percentage=13 elapsed=1m59.597s INFO [10-31|20:01:02.389] Generating DAG in progress epoch=0 percentage=14 elapsed=2m7.785s INFO [10-31|20:01:10.538] Generating DAG in progress epoch=0 percentage=15 elapsed=2m15.934s INFO [10-31|20:01:18.506] Generating DAG in progress epoch=0 percentage=16 elapsed=2m23.902s ....... INFO [10-31|20:13:42.967] Generating DAG in progress epoch=1 percentage=2 elapsed=1m6.364s
-
挖到新的区块
INFO [10-31|20:13:43.441] Successfully sealed new block number=1 sealhash=737470…ef7f65 hash=ed8040…934883 elapsed=14m50.215s INFO [10-31|20:13:43.441] ? mined potential block number=1 hash=ed8040…934883
-
提交交易,但是交易还未被确认,处于
pending
,其中fullhash
是该交易的哈希值,recipient
是该交易接收者的地址INFO [11-03|05:43:22.270] Submitted transaction fullhash=0x361469a6b9e6cddf8e9c69ca9ab256c2a7be730b9a97ebc3b1e36dd580a7eddc recipient=0xE87619e16Aa3d2fd7BeA8A18Bb770b8B1Fd808b8
-
添加新的节点
01
,并与之建立连接DEBUG[11-03|04:33:03.972] Adding p2p peer name=Geth/01/v1.8.18-unst... addr=127.0.0.1:33534 peers=1 DEBUG[11-03|04:33:03.972] Ethereum peer connected id=663bef20db32f8c5 conn=inbound name=Geth/01/v1.8.18-unstable-3e1cfbae/linux-amd64/go1.11.1
-
部署合约,其中
fullhash
是部署合约的交易的哈希值,contract
是合约地址INFO [11-03|02:52:58.543] Submitted contract creation fullhash=0x16acefe5b61cff177b3cc65d21237594913bdd7e0c71d97200a42db22147f55d contract=0xE87619e16Aa3d2fd7BeA8A18Bb770b8B1Fd808b8
-
退出
INFO [11-04:14:22.449] HTTP endpoint closed url=http://127.0.0.1:8545 INFO [11-04:14:22.952] IPC endpoint closed endpoint=/home/liuyt49/Desktop/Blockchain/data/00/geth.ipc INFO [11-04:14:23.416] Writing cached state to disk block=1 hash=ed8040…934883 root=f09abd…6b0e95 INFO [11-04:14:23.959] Persisted trie from memory database nodes=1 size=148.00B time=542.494034ms gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B INFO [11-04:14:24.449] Blockchain manager stopped INFO [11-04:14:24.449] Stopping Ethereum protocol INFO [11-04:14:24.449] Ethereum protocol stopped INFO [11-04:14:24.449] Transaction pool stopped INFO [11-04:14:26.085] Database closed database=/home/liuyt49/Desktop/Blockchain/data/00/geth/chaindata