Windows下以太坊Geth客户端安装使用以及简单合约的部署
操作系统:Windows 10
参考资料:如何搭建以太坊私有链、智能合约开发环境搭建及Hello World合约
以太坊安装
以太坊客户端与Java虚拟机和.NET运行环境类似,能够让你在电脑上运行"以太坊程序"。
以太坊的客户端有很多版本,在这里选择Geth(Go-ethereum)。只需要直接到官网下载Windows对应.exe
可执行文件,并安装即可。
安装完成后,打开cmd命令提示符,输入命令geth --help
显示以下信息,表明安装成功。
PS:如果不加任何参数,只使用geth
命令执行,或双击安装目录下的geth.exe
,会自动连接到以太坊公网,并开始同步区块(你会发现一直卡着不动),此时区块数据存储的路径可以在geth --help
中查看到。
私有链创世区块搭建
以太坊支持自定义创世区块,要运行私有链,我们就需要定义自己的创世区块,创世区块信息写在一个 json 格式的配置文件中。
在磁盘的某个地方创建一个新文件夹,在该文件夹中新建创世区块配置文件genesis.json,内容如下:
{
"config": {
"chainId": 15,
"homesteadBlock": 0,
"eip155Block": 0,
"eip158Block": 0
},
"coinbase" : "0x0000000000000000000000000000000000000000",
"difficulty" : "0x07000",
"extraData" : "",
"gasLimit" : "0xffffffff",
"nonce" : "0x0000000000000042",
"mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"timestamp" : "0x00",
"alloc": { }
}
字段的具体含义在这里就不展开叙述了。之后在cmd窗口进入该文件夹,执行如下命令:
geth --datadir data init genesis.json
上面命令的主体是 geth init
,表示初始化区块链,命令可以带有选项和参数,其中 --datadir
选项后面跟一个目录名,这里为 data
,表示指定数据存放目录为 data
, genesis.json
是 init
命令的参数。执行后结果如下,可以看到,私有链搭建成功。
PS:在data目录下,会有两个文件:geth
和keystore
,前者保存区块数据,后者存放账户信息。
私有链节点的加入
在完成上述步骤后,就已经有了一条自己的私有链了,可以加入自己的私有链节点,使用以下命令启动节点:
geth --datadir data --networkid 2629 console
命令的主体是 geth console
,表示启动节点并进入交互式控制台,--datadir
选项指定使用 data
作为数据目录,--networkid
选项后面跟一个数字,这里是 2629,表示指定这个私有链的网络 id 为 2629。网络 id 在连接到其他节点的时候会用到,以太坊公网的网络 id 是 1,为了不与公有链网络冲突,运行私有链节点的时候要指定自己的网络 id 。执行后,结果如下:
如图,会进入到一个交互式的 Javascript 执行环境,里面可以执行 Javascript 的代码,如输入1+1会输出2。 在这个环境中,可以进行很多操作,例如:查看区块和交易、创建账户、挖矿、发送交易、部署智能合约等。
然后执行如下一系列的操作,进行创建用户,挖矿,就不展开叙述了:
personal.newAccount("123") //创建用户,引号中的是账户密码
eth.accounts //可查看账户信息
miner.start(2) //启动挖矿,参数表示使用的线程数。启动后就会被挖矿信息刷屏
//挖矿所得以太币默认存入第一个账户
miner.start();admin.sleepBlocks(1);miner.stop()//使用这个命令会在挖到一次矿后自动停止
miner.stop() //停止挖矿
eth.getBalance(eth.accounts[0]) //可以查看账户余额,单位是wei
web3.fromWei(eth.getBalance(eth.accounts[0]),'ether') //将余额单位换算成以太币
交易
PS:下文的操作我换用了Windows PowerShell
按照相同方式再创建一个账户,此时先前的账户有余额,而新创建的账户余额为0,可以通过以下命令将10个以太币从账户0转移到账户1:
amount = web3.toWei(10,'ether')
eth.sendTransaction({from:eth.accounts[0],to:eth.accounts[1],value:amount})
此时会报错如下:
原因是账户每隔一段时间就会被锁住,要发送交易,必须先解锁账户,由于我们要从账户 0 发送交易,所以要解锁账户 0,执行图中所示命令(需要输入密码):
解锁后,重新执行之前的命令。
交易后,查看账户1余额,会发现还是0,这是因为交易已经提交到区块链,但还未被处理。正如之前所学到的,需要矿工进行挖矿,处理并记录这个交易。所以我们进行挖矿后,得到如下结果:
可以看到,账户1的余额已经变成10了,说明交易已被处理。
查看区块
使用命令eth.blockNumber
查看当前的区块总数,eth.getBlock(10)
查看具体的区块,执行结果如下:
区块的各字段含义如下:
- difficulty:表示当前区块的难度
- extraData:与此区块相关的附加数据
- gasLimit:当前区块允许使用的最大gas
- gasUsed:当前区块累计使用的gas
- hash:区块的哈希值。如果区块没有被确认,字段值为null
- logsBloom:区块日志的布隆过滤器,区块没被确认时值为null
- miner:取得该区块记账权的矿工
- mixHash:一个Hash值,当与nonce组合时,证明此区块已经执行了足够的计算
- nonce:PoW生成的哈希值
- number:区块号(创世纪块的区块序号为0,对于每个后续区块,区块序号都增加1)
- parentHash:前一个区块的哈希值
- receiptsRoot:收据树的根哈希值
- sha3Uncles:数据块的哈希值
- size:该区块的字节大小
- stateRoot:状态树的根哈希值
- timestamp:区块打包时的unix时间戳
- totalDifficulty:区块链到当前区块的总难度
- transactions:交易的对象
- transactionsRoot:交易树的根哈希值
- uncles:叔哈希的数组
查看交易
使用命令eth.getTransaction(" ")
查看交易,其中引号需加上交易的hash值,在交易执行后会有显示。执行结果如下,该交易是上文所述交易:
交易的各字段含义如下:
- blockHash:这个交易所在的区块的哈希值
- blockNumber:这个交易所在的区块的编号
- from:发起交易的账户
- gas:执行这个交易所需要的gas
- gasPrice:当前gas与以太币换算的汇率
- hash:这个交易的哈希值
- input:合约的16进制代码
- nonce:交易下的nonce值,是账户发起交易所维护的nonce,一个交易对应一个nonce值
- r,s,v:交易签名后的值,它们可以被用来生成签名者的公钥;r,s是ECDSA椭圆加密算法的输出值,v是用于恢复结果的ID
- to:交易接收账户。如果是一个创建智能合约的交易,to为空
- transactionIndex:这个交易在其对应区块的序号
- value:交易的以太币数量。如图所示,正是10个以太币
查看日志
进行到这里才发现有日志功能…
日志功能开启方法:在上文启动以太坊节点时在后面加上2>> test.log
,日志的名称可以自己随意设置。
简单来说,日志的作用就是将我之前在执行命令时会输出的各种提示信息,例如INFO
、WARN
,不直接输出到屏幕上,而是重定向写入到我们指定的日志文件中。日志文件部分内容如下:
上图显示的内容就是启动节点后,geth初始化的一些信息。
对于选择将这些提示信息输入到日志中,我认为有好处也有坏处:好处就是没有这些提示信息后,geth命令行界面和之前相比显得更加简介了,同时有了日志的记录,方便日后的查看;坏处就是似乎少了一些什么的感觉,比如挖矿时刷屏的快感。
编写简单的智能合约
使用Remix - Ethereum IDE直接在浏览器中编写和编译Solidity代码
参考网上博客(见本文开头部分的参考资料),创建helloworld.sol,并编写代码如下:
pragma solidity ^0.4.22;
contract hello {
string greeting;
function hello(string _greeting) public {
greeting = _greeting;
}
function say() constant public returns (string) {
return greeting;
}
}
上述代码创建了一个hello合约。点击页面中的Compile进行编译。对于博客提供的代码似乎给了警告,意思好像是要求写构造函数,不过既然不是报错,暂时先不管。
编译通过后,点击下方的 Compilation Details,找到 WEB3DEPLOY 选项旁的复制按钮进行复制,得到如下结果:
var _greeting = /* var of type string here */ ;
var helloContract = web3.eth.contract([{"constant":true,"inputs":[],"name":"say","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_greeting","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]);
var hello = helloContract.new(
_greeting,
{
from: web3.eth.accounts[0],
data: '0x608060405234801561001057600080fd5b506040516102a83803806102a8833981018060405281019080805182019291905050508060009080519060200190610049929190610050565b50506100f5565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061009157805160ff19168380011785556100bf565b828001600101855582156100bf579182015b828111156100be5782518255916020019190600101906100a3565b5b5090506100cc91906100d0565b5090565b6100f291905b808211156100ee5760008160009055506001016100d6565b5090565b90565b6101a4806101046000396000f300608060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063954ab4b214610046575b600080fd5b34801561005257600080fd5b5061005b6100d6565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561009b578082015181840152602081019050610080565b50505050905090810190601f1680156100c85780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b606060008054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561016e5780601f106101435761010080835404028352916020019161016e565b820191906000526020600020905b81548152906001019060200180831161015157829003601f168201915b50505050509050905600a165627a7a72305820bd9c8f1aad9fb4da7211d57eeaea5ede4555a3d028220142c0fa5bfe05d3874d0029',
gas: '4700000'
}, function (e, contract){
console.log(e, contract);
if (typeof contract.address !== 'undefined') {
console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);
}
})
继续按照博客要求,将第一行的注释部分改为“Hello World”(可以先复制到文本中进行修改)。修改完成后,拷贝到geth控制台中。注意先要解锁合约中from字段对应的账户!
**然后需要进行挖矿!**我起初没有进行挖矿,所以一直以为哪里出错了。查看Log日志可以看到,当我们将上述内容拷贝到控制台后,日志中提示以下信息:
INFO [10-09|21:44:27.864] Submitted contract creation
在开始挖矿一段时间后,可以看到如下所示的提示信息,表示合约已经部署成功了。
此时查看对应账户余额,可以看到,余额变少了。
运行hello合约,得到如下结果:
结果符合预期,即输出了Hello World,合约成功运行!
上一篇: 历史上宝庆公主的一生是什么样的?她最后的结局怎么样?
下一篇: MyBatis的SUM映射问题及解决