区块链project热身报告
安装以太坊
Geth是以太坊智能合约开发中最常用的工具,执行在GO上运行的完整以太坊节点,通过Geth,我们可以实现以太坊的各种功能,如账户的新建、编辑、删除,开启挖矿,以太币的转移,智能合约的部署和执行等等。
windows下安装
在官网下载安装包即可(需翻墙),安装过程会自动配置环境变量,在cmd中使用geth version
可以查看是否安装成功
ubuntu下安装
同样去官网下载对应版本,这是一个tar.gz包,下载后解压即可得到geth可执行文件,配置好环境变量就可以使用了
私有链搭建
进入到为搭建私有链创建的文件夹privatechain
后,执行以下操作
配置文件
要搭建私有链,首先需要编写创始区块配置文件,命名为genesis.json
,内容如下:
1 | { |
要注意几点:
chainId不能为0,否则部署智能合约时会出错:
1
invalid sender undefined
gaslimit不能太小,否则部署智能合约时会报错:
1
Error: exceeds block gas limit undefined
difficulty会影响到你节点的挖矿速度
初始化私有链
1 | geth --datadir data0 init genesis.json |
这条命令表示初始化私有链到文件夹data0中,这条链的数据存放在data0里,并根据genesis.json
中的内容把创世区块写入区块链,通过log信息中的Successfully wrote genesis state
我们知道初始化成功。
初始化后的初始化成功后的目录如下:
其中geth/chaindata中存放的是区块数据,keystore中存放的是账户数据。
启动私有链节点
1 | geth --datadir data0 --networkid 1108 console |
geth console
,表示启动节点并进入交互式控制台,控制台是一个交互式的Javascript执行环境
–-datadir选项指定使用data0作为数据目录,即运行我们前面初始化的那条私有链
–-networkid选项指定这个私有链的网络id为1108。网络id在连接到其他节点的时候会用到,以太坊公网的网络id是1,为了不与公有链网络冲突,运行私有链节点的时候要指定自己的网络id
一些简单操作
查看账户
1 | >eth.accounts |
创建账户
1 | >personal.newAccount() |
查看账户余额
1 | > eth.getBalance(eth.accounts[0]) |
启动&停止挖矿
1 | > miner.start(10)#参数10表示挖矿使用的线程数 |
解锁账户
1 | > personal.unlockAccount(eth.accounts[0]) |
发送交易
1 | > amount = web3.toWei(10,'ether') |
私有链节点加入
要向私有链中加入节点,我们需要同时运行两个节点,同时启动节点的命令是有要求的,我启动两个节点(同一台电脑)的命令分别为:
1 | geth --networkid 1108 --nodiscover --datadir data0 --port 30303 --rpc --rpcapi net,eth,web3,personal --rpcport 8545 --rpcaddr localhost --verbosity 6 console 2>>geth0.log --ipcpath "geth0.rpc" |
1 | geth --networkid 1108 --nodiscover --datadir data1 --port 55554 --rpc --rpcapi net,eth,web3,personal --rpcport 8101 --rpcaddr localhost --verbosity 6 console 2>>geth1.log --ipcpath "geth1.rpc" |
当然,首先得搭建两个节点data0和data1.
参数解释:
1 | --nodiscover 关闭p2p网络的自动发现,需要手动添加节点,这样有利于我们隐藏私有网络 |
这之中要注意的几个地方是:
- 端口号必须不同
- rpc端口号必须不同
- ipcpath必须不同
进入了Javascript Console之后,首先在第一个节点data0那执行命令查看enode:
1 | admin.nodeInfo.enode |
然后在第二个节点data1那执行命令
1 | admin.addPeer("xxx") |
括号内填查看到的data0的enode信息,返回true即可成功加入节点,加入后data1就会自动开始同步data0的所有区块。
之后可以使用以下命令来查看节点是否添加成功:
1 | net.peerCount |
在data1使用eth.blockNumber
来查看区块是否同步成功。
注意:
- windows下节点同步可能会失败,所以最好在Linux下进行加入节点的操作
- 要保证两个节点在同一个区块链上工作的话首先要保证genesis创世区块链是一样的,所以注意使用同样的genesis.json文件来创建.
解释getBlock字段
使用getBlock
来得到区块相关信息:
1 | eth.getBlock(0) |
参数为0,查看的是创世区块,也可以把参数设为”latest”来查看最新区块。
- difficulty:当前块的难度。
- extraData:包含这个区块的任意字节相关数据。
- gasLimit:当前区块允许使用的最大gas,这个值是我们
genesis.json
中的gasLimit值。 - gasUsed:当前区块累计使用的总的gas。
- hash:区块的哈希串。当这个区块处于pending将会返回null。
- logsBloom:由日志信息组成的一个Bloom过滤器 (数据结构)。当这个区块处于pending将会返回null。
- miner:这个区块获得奖励的矿工。
- mixHash:一个Hash值,当与nonce组合时,证明此区块已经执行了足够的计算
- nonce:在挖矿过程中起作用,POW生成的哈希。当这个区块处于pending将会返回null。
- number:区块号。当这个区块处于pending将会返回null。
- parentHash:父区块的哈希值。由于这是创始区块,因此该值为0。
- receiptsRoot:包含此区块所列的所有交易收据的树的根节点Hash值
- sha3Uncles:叔区块的哈希值。
- size:当前这个块的字节大小。
- stateRoot:区块的最终状态前缀树的根。
- timestamp:区块打包时的unix时间戳。
- totalDifficulty:区块链到当前块的总难度。
- transactions:交易对象。或者是32字节的交易哈希。
- transactionsRoot:包含此区块所列的所有交易的树的根节点Hash值
- uncles:叔哈希的数组。
叔块是什么?
在上面的区块中有叔块的信息,那么叔块是什么呢?
我们都知道在挖矿过程中,只有最长的那条链才会被所有节点所承认,挖到在最长链的那些区块的矿工才能获得奖励。如果一个块不是最长链的一部分,那么它被称为是“孤块”。在比特币中,孤块没有意义,随后将被抛弃,发现这个孤块的矿工也拿不到采矿相关的奖励。
根据Ethereum的GHOST协议,不认为孤块没有价值,而是会给与发现孤块的矿工以回报。在以太坊中,孤块被称为“叔块”(uncle block),它们可以为主链的安全作出贡献。
由于GHOST协议支付报酬给叔块,这激励了矿工在新发现的块中去引用叔块。引用叔块使主链更重。在比特币,最长的链是主链。在以太坊中,主链是指最重的链
解释日志输出
通过在启动私有链节点时在命令最后加上2 >>xxx.log
即可把日志输出到log文件中,通过设置选项--verbosity n
可以设置日志的程度。
我选取了一些日志进行解释:
日志1
这个日志是我在给私有链添加节点时的日志输出,可以看到,执行命令后的第一个日志输出就是Adding p2p peer
,表示加入节点。connnection set up
表示建立连接,之后直到Ethereum peer connected
,连接成功。然后就要开始为节点上区块链的同步做准备了,Block synchronisation started
表示区块同步开始。此后要经过一系列的准备工作,比如获取链高度、下载区块体、获取区块头…最后可以看到一连串的Inserted new block
,这就表示同步区块正式开始啦,可以从右边的number得知同步进度。
日志2
这个日志是挖矿的日志。一开始的Updated mining threads
表示更新挖矿的线程,右边可以看到线程数量为10,这是在miner.start(10)
中设置的。Etherbase automatically configured
表示使用默认的coinbase,默认的是本地账户中的第一个账户,即eth.accounts[0]
,挖矿所得到的所有奖励都会进入这个账户中。之后Commit new mining work
表示挖矿正式开始,那个🔨开头的日志说明成功挖到了矿,可以从右边的number看到挖到区块的数目。
日志3
这个简短的日志是进行一笔交易的日志,不知道为啥设置verbosity为6也还是看不到更具体的信息。Pooled new future transaction
表示提交到交易缓冲池,Promoting queued transaction
表示选择一部分交易进入pending队列进行处理,Submitted transaction
表示交易被成功处理并提交,最后的Broadcast transaction
表示把这笔交易广播出去。
部署智能条约
编写合约
首先编写智能合约,用solidity语言实现:
1 | contract Mortal { |
由于Solidity同样具有继承的特性,通过只声明Greeter是Mortal的,Greeter就可以继承”Mortal”合约的所有特征。
这段代码的主函数是greet()
,我们之后将调用这个函数来调用智能合约。
编译合约
使用在线编译器remix来编译(需要翻墙):
首先确定右窗格中选择的是Greeter而不是Mortal,然后点击Details来获取编译后的代码,复制WEB3DEPLOY中的代码:
保存到js文件中,命名为contract.js,并修改第一行为:
1 | var _greeting = "Hello World!" |
部署合约
执行loadScript("contract.js")
导入文件,报错如下:
这是因为我们必须要先解锁账户,因为我们需要支付部署合约的GAS费用,解锁后继续执行:
执行eth.getCode(greeter.address)
来验证代码是否部署成功:
报错是因为部署合约需要挖矿,启动挖矿后再次执行:
部署成功!
调用合约
执行greeter.greet()
,我们可以看到输出:
由于这个调用没引起在区块链上的任何变化,因此它会立即返回并且无需任何Gas费用
使用其他节点调用合约
这首先要求添加节点到私有链中,因此转到ubuntu操作:
上图是在data0进行部署合约的操作
然后,我们要知道通过其他节点调用这个合约需要两个信息:
- 合同所在地址
- ABI(应用程序二进制接口),这是一种用户手册,描述合同功能的名称以及如何将它们调用到您的JavaScript控制台
获取合同所在地址很简单,greeter.address;
即可
要获取ABI,就需要去Remix官网,同样在Detail中获得,复制ABI文本框即可,然后暂时保存到一个abi.txt文件。如果我们想直接把它复制到终端是不行的,因为它含有换行符,因此需要去掉换行符(替换为空格):
1 | cat abi.txt | tr '\n' ' ' |
然后就可以把abi.txt中内容复制到以太坊交互式控制台中,把这段内容赋值给abi,查看赋值后abi的值:
在data1中执行以下命令:
1 | myContract = web3.eth.contract(abi).at(address) |
address为合约地址
查看myContract以及调用合约:
在节点data1中调用合约好像需要先解锁账户,但是这里我没有解锁也成功了,我想可能是因为这个调用不要消耗Gas值吧
解释交易字段
首先解锁账户0:
1 | > personal.unlockAccount(eth.accounts[0]) |
发送交易(从账户0向账户1发送10个以太币):
1 | > amout = web3.toWei(10, 'ether') |
最后返回的那个字符串即是交易的地址,可以通过getTransaction
来查看交易信息,需要注意,此时交易还处于Pending状态(已提交但还未被处理的交易),需要启动挖矿:
1 | miner.start(1);admin.sleepBlocks(1);miner.stop(); |
这样交易就得到处理,使用getTransaction
来查看交易:
1 | > eth.getTransaction("0x5eeae4989feab2ef1d3a3c68f3cba9a5a9adafcaef4c4af79ba3088f01df0e76") |
- blockHash: 交易所在区块的哈希值。当这个区块处于pending将会返回null。
- blockNumber:交易所在区块的块号。当这个区块处于pending将会返回null。
- from: 交易发起者的地址。在这里为accouts[0]。
- gas:交易发起者提供的gas。
- gasPrice:交易发起者配置的gas价格,单位是wei。
- hash:交易的哈希值。
- input:交易附带的数据。
- nonce:交易的发起者在之前进行过的交易数量。
- r:用于产生标识交易发生者的签名(ECDSA签名值)
- s:用于产生标识交易发生者的签名(ECDSA签名值)
- to:交易接收者的地址。当这个区块处于pending将会返回null,如果是部署智能合约的交易,这个值也是null。在这里显示为accouts[1]。
- transactionIndex:交易在区块中的序号。当这个区块处于pending将会返回null。
- v:用于产生标识交易发生者的签名(ECDSA签名值)
- value:交易附带的货币量,单位为Wei。在这里即是转账的值