以太坊实践整理(二)Geth客户端

Geth

以太坊客户端众多,基于各种语言开发的都有,这与区块链崇尚开放的价值观有关。Geth(全称go-ethereum)是以太坊官方社区开发的客户端,是客户端里的领头羊。我们可以使用Geth命令搭建以太坊私链,也可以基于Geth提供的交互式命令控制台,与以太坊网络环境进行交互。

Geth安装

Geth是基于GO语言编写的客户端,需要先安装goland。

安装Go语言环境

下载二进制包:go1.4.linux-amd64.tar.gz;

将下载的二进制包解压至 /usr/local目录:

$ tar -C /usr/local -zxvf go1.17.linux-amd64.tar.gz

设置环境变量:

$ export PATH=$PATH:/usr/local/go/bin

生效环境变量:

$ source /etc/profile

验证:

$ go env

源码编译安装geth

下载源码:

$ cd /usr/local
$ git clone https://github.com/ethereum/go-ethereum.git

编译源码:

$ cd go-ethereum
$ make geth

国内用户编译过程可能无法访问proxy.golang.org,可以设置代理后再执行:

$ go env -w GOPROXY=https://goproxy.cn
$ make geth

设置环境变量:

export PATH=$PATH:/usr/local/go-ethereum/build/bin

生效环境变量:

$ source /etc/profile

验证:

$ geth version

Geth启动节点

连接主链网络

启动一个节点连接到以太坊主链,并进入JavaScript交互式控制台:

$ geth console

执行命令后,输入如下:

Welcome to the Geth JavaScript console!

instance: Geth/v1.10.8-unstable-dfeb2f7e-20210823/darwin-amd64/go1.17
at block: 0 (Thu Jan 01 1970 08:00:00 GMT+0800 (CST))
 datadir: /Users/zhutx/Library/Ethereum
 modules: admin:1.0 debug:1.0 eth:1.0 ethash:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0

To exit, press ctrl-d

过一会儿后,控制台开始不断地输出信息,表示节点正在以快速同步(Fast-Sync)模式,下载最新区块链数据与状态。从刚开始执行命令时的输出里可以看到,数据默认存储在/Users/zhutx/Library/Ethereum目录下。

在主链上进行开发测试是不切实际的,现在你可以停掉节点了。节点所下载的区块链数据会迅速占据好些存储空间,可以删除该目录

连接测试网络

程序在开发阶段总是需要进行测试的,我们可以启动节点并连接至以太坊测试网络:

$ mkdir testdir
// 连接Ropsten测试网络,指定存储目录为testdir
$ geth --ropsten --datadir "./testdir" console

如果你连接测试网络进行开发,相关操作还是需要支付燃料(Gas)费的,除了挖矿让账户获得以太币外,我们也可以直接使用提供水龙头服务的网站(如:https://faucet.ropsten.be/),输入自己测试链的账户地址,直接获取以太币(测试链的以太币不具备实际价值)。


geth有很多参数,常用的后续还会介绍到几个,这里就不一一举例了,可以自行了解(geth --help)。


Geth搭建私链

利用Geth我们还可以搭建自己的以太坊私链。

创建数据存储目录

$ mkdir blockchain
$ cd blockchain

编写创世区块配置

$ vim gensis.json
{
    "config": {
        "chainId": 666,
        "homesteadBlock": 0,
        "eip150Block": 0,
        "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
        "eip155Block": 0,
        "eip158Block": 0,
        "byzantiumBlock": 0,
        "constantinopleBlock": 0,
        "petersburgBlock": 0,
        "istanbulBlock": 0,
        "ethash": {}
    },
    "nonce": "0x0",
    "timestamp": "0x5ddf8f3e",
    "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000",
    "gasLimit": "0x47b760",
    "difficulty": "0x00002",
    "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
    "coinbase": "0x0000000000000000000000000000000000000000",
    "alloc": {},
    "number": "0x0",
    "gasUsed": "0x0",
    "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
}

创世区块参数说明【TODO】:

参数 描述

初始化区块链

// 新建一个目录用来存放区块链数据
$ mkdir db
// 初始化区块链
$ geth --datadir "./db" init gensis.json

初始化成功后,会在db目录下生产geth和keystore目录,此时目录结构如下:

blockchain
├── db
│   ├── geth
│   │   ├── chaindata
│   │   │   ├── 000001.ldb
│   │   │   ├── CURRENT
│   │   │   ├── LOCK
│   │   │   ├── LOG
│   │   │   └── MANIFEST-000000
│   │   ├── lightchaindata
│   │   │   ├── 000001.log
│   │   │   ├── CURRENT
│   │   │   ├── LOCK
│   │   │   ├── LOG
│   │   │   └── MANIFEST-000000
│   └── keystore
└── gensis.jso

启动私有链

$ geth --rpc --rpcport 8545 --rpccorsdomain "*" --datadir "./db" --port 30303 --rpcapi "eth,net,web3,personal,admin,shh,txpool,debug,miner" --networkid 100000 --rpcaddr=0.0.0.0 --nodiscover --allow-insecure-unlock console 2>> eth.log

相关启动参数可以自行了解下


Geth的JavaScript控制台

在Geth的JavaScript控制台内置了一些对象,通过这些对象我们可以很方便地与以太坊交互:

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

我们在上面搭建的私链上,以转账业务去演示下JavaScript控制台的部分操作,做个了解即可:

0.进入JavaScript Console环境

如果启动节点时未输入console,你可以单独通过attach命令,进入节点的JavaScript命令环境:

cd blockchain
$ geth --datadir "./db" attach ipc:./db/geth.ipc

1. 新建账户

传入账户密码,执行成功返回公钥:

> personal.newAccount("123456")
"0xf05ed6c1bab6800d94ae3af4471b77caf07860f0"

生成的账户文件在keystore文件夹下。我们执行两次,生成2个账号用于转账备用。

2. 查看账户

> eth.accounts
["0xf05ed6c1bab6800d94ae3af4471b77caf07860f0", "0x8efa17c5646c60d4b67d118445f2b9614d9ea3e7"]

3. 查看余额

> balance = web3.fromWei(eth.getBalance(eth.accounts[0]), "ether")
0

eth.getBalance(账户公钥),返回账户的余额,单位为wei,web3.fromWei将wei转换成ether。

4. 挖矿

为了获取以太币,需要先开启挖矿,我们把第一个账号设置为挖矿账户:

> miner.setEtherbase(eth.accounts[0])
true

查看是否设置成功:

> eth.coinbase
"0xf05ed6c1bab6800d94ae3af4471b77caf07860f0"

开启挖矿:

> miner.start(1)
null

先挖一会儿,确保账户有余额后先暂停挖矿,等交易需要打包区块的时候再打开。

在区块链领域,转账、部署智能合约、调用智能合约等操作都是交易。并非狭义的交易概念

关闭挖矿:

> miner.stop()

再次查看余额:

> balance = web3.fromWei(eth.getBalance(eth.accounts[0]), "ether")
234

现在账户上有234以太币了,转账交易之前需要先解锁账户,否则会报错。

5. 解锁账户

> personal.unlockAccount(eth.accounts[0], "123456")
true

6. 交易

把零头34转给另一个账户:

> eth.sendTransaction({from: eth.accounts[0], to: eth.accounts[1], value: web3.toWei(34, "ether")})
"0x47872d357a318f0213e63d40311a990cb0d467ad2bb79a6d8c82a9b667a079b9"

向区块链网络中发送一笔转账交易,返回交易hash,此时的交易正在交易池中等待被打包。

查看交易池状态:

> txpool.status
{
  pending: 1,
  queued: 0
}

pending表示已提交但还未被处理的交易,显示有1个交易等待打包。

查看pending交易详情:

> txpool.inspect.pending
{
  0xf05ED6C1baB6800D94Ae3af4471B77caf07860f0: {
    0: "0x8eFA17c5646c60D4B67D118445f2B9614D9Ea3e7: 34000000000000000000 wei + 21000 gas × 1000000000 wei"
  }
}

为了使交易被处理,必须开启挖矿。为了方便分析,让挖到一个区块之后就停止挖矿:

> miner.start(1);admin.sleepBlocks(1);miner.stop();
INFO [08-24|02:29:13.504] Updated mining threads                   threads=1
INFO [08-24|02:29:13.504] Transaction pool price threshold updated price=1,000,000,000
INFO [08-24|02:29:13.505] Commit new mining work                   number=118 sealhash=a408a5..244a99 uncles=0 txs=0 gas=0 fees=0 elapsed="146.423µs"
INFO [08-24|02:29:13.505] Commit new mining work                   number=118 sealhash=ed24ca..e08e23 uncles=0 txs=1 gas=21000 fees=2.1e-05 elapsed="350.824µs"
INFO [08-24|02:29:15.927] Successfully sealed new block            number=118 sealhash=ed24ca..e08e23 hash=c0b43c..28c921 elapsed=2.422s
INFO [08-24|02:29:15.927]  mined potential block                  number=118 hash=c0b43c..28c921
INFO [08-24|02:29:15.927] Commit new mining work                   number=119 sealhash=e9df41..7397c0 uncles=0 txs=0 gas=0     fees=0       elapsed="187.308µs"

分别查看转出方和转让方的余额:

> balance = web3.fromWei(eth.getBalance(eth.accounts[0]), "ether")
202
> balance = web3.fromWei(eth.getBalance(eth.accounts[1]), "ether")
34

可以看到转入账户eth.accounts[1]有34个以太币了,而转出账户eth.accounts[0]账户余额并非200,因为该账号有挖矿收益;

7. 区块

查看交易详情:

> eth.getTransaction("0x47872d357a318f0213e63d40311a990cb0d467ad2bb79a6d8c82a9b667a079b9")
{
  blockHash: "0xc0b43c476fc4406b4db342beed0ed0b0d4bad02330b2b353a354b558bc28c921",
  blockNumber: 118,
  from: "0xf05ed6c1bab6800d94ae3af4471b77caf07860f0",
  gas: 21000,
  gasPrice: 1000000000,
  hash: "0x47872d357a318f0213e63d40311a990cb0d467ad2bb79a6d8c82a9b667a079b9",
  input: "0x",
  nonce: 0,
  r: "0xe1d9e0678c2a67c0fcec81cab407f20caca4ff617fbc697249409d3d78a74ef0",
  s: "0x6db176315eebaafc07c695549fccbfed0dbe1317a3ffbbf398d9c3aa6b8bb250",
  to: "0x8efa17c5646c60d4b67d118445f2b9614d9ea3e7",
  transactionIndex: 0,
  type: "0x0",
  v: "0x557",
  value: 34000000000000000000
}

上面的命令是查看交易发起时的详情,如果要查看交易被打包进区块时的详细信息,用以下命令:

> eth.getTransactionReceipt("0x47872d357a318f0213e63d40311a990cb0d467ad2bb79a6d8c82a9b667a079b9")
{
  blockHash: "0xc0b43c476fc4406b4db342beed0ed0b0d4bad02330b2b353a354b558bc28c921",
  blockNumber: 118,
  contractAddress: null, // 如果是合约创建交易,则返回合约地址,其他情况返回null
  cumulativeGasUsed: 21000, // 累计话费的gas总值
  effectiveGasPrice: 1000000000,
  from: "0xf05ed6c1bab6800d94ae3af4471b77caf07860f0",
  gasUsed: 21000, // 执行当前这个交易单独话费的gas
  logs: [],
  logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  status: "0x1",
  to: "0x8efa17c5646c60d4b67d118445f2b9614d9ea3e7", // 如果是合约创建交易,返回null
  transactionHash: "0x47872d357a318f0213e63d40311a990cb0d467ad2bb79a6d8c82a9b667a079b9",
  transactionIndex: 0, // 交易在区块里的序号
  type: "0x0"
}

以下是其他查询区块的命令:

查看当前区块总数:

> eth.blockNumber
118

查看最新区块:

> eth.getBlock('latest')
{
  difficulty: 131886,
  extraData: "0xd683010a08846765746886676f312e3137856c696e7578",
  gasLimit: 5273553,
  gasUsed: 21000,
  hash: "0xc0b43c476fc4406b4db342beed0ed0b0d4bad02330b2b353a354b558bc28c921",
  logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  miner: "0xf05ed6c1bab6800d94ae3af4471b77caf07860f0",
  mixHash: "0x009c9034e94034344fc03dd6914d3753a8b98b8a3b0e3b5d11d473d9ad5a1402",
  nonce: "0x66f00bbbcd357270",
  number: 118,
  parentHash: "0xa475ef4ac3b9c1d9b8390e5044a1d11186c57b6c369756b642420f345b7ec82f",
  receiptsRoot: "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
  sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
  size: 649,
  stateRoot: "0x798af7bcb3bd31b18c955c05e694ae342d1db654681b1703b817ff6e02639d69",
  timestamp: 1629743353,
  totalDifficulty: 15897610,
  transactions: ["0x47872d357a318f0213e63d40311a990cb0d467ad2bb79a6d8c82a9b667a079b9"],
  transactionsRoot: "0x979e07128c057ff81e2eaaba45b3704be371ffcb794876f570f88e88e00cd313",
  uncles: []
}

eth.getBlock(blockNumber|blockHash))可以获取具体区块的信息。


Geth多节点组网

当前私链是单一节点(节点1),下面我们再弄一个节点(节点2)连接进来,组成一个最简以太坊私链网络。

节点2安装geth客户端后,进入到JavaScript控制台,输入以下命令获取节点2的信息:

> admin.nodeInfo
{
  enode: "enode://5873af9deabd8f7e40878ce0e3caec0bb86c79cf9bd99781be2aaab8a61e3c24aded28102971a38a7cb0ff2749b3a1b3624232cc96ac4f76dc9f035232629652@127.0.0.1:30303?discport=0",
  enr: "enr:-Ja4QCuP0rodmA2imicnqbuZ14kPiMqPrX-Dp_5z8FU1l1PvcU0IKz4cynQppjEqVguSlU_Tn-UlVkaw_ehxcCsM0XoEg2V0aMfGhJPR68WAgmlkgnY0gmlwhH8AAAGJc2VjcDI1NmsxoQJYc6-d6r2PfkCHjODjyuwLuGx5z5vZl4G-Kqq4ph48JIRzbmFwwIN0Y3CCdl8",
  id: "37829b9d6cdb1610dca7ba713a1c0647d6474957c686a3b4967b7cd77f0d47a5",
  ip: "127.0.0.1",
  listenAddr: "[::]:30303",
  name: "Geth/v1.10.8-unstable-dfeb2f7e-20210823/linux-amd64/go1.17",
  ports: {
    discovery: 0,
    listener: 30303
  },
  protocols: {
    eth: {
      config: {
        byzantiumBlock: 0,
        chainId: 666,
        constantinopleBlock: 0,
        eip150Block: 0,
        eip150Hash: "0x0000000000000000000000000000000000000000000000000000000000000000",
        eip155Block: 0,
        eip158Block: 0,
        ethash: {},
        homesteadBlock: 0,
        istanbulBlock: 0,
        petersburgBlock: 0
      },
      difficulty: 15897610,
      genesis: "0xd3d6bb893a6e274cab241245d5df1274c58d664fbb1bfd6e59141c2e0bc5304a",
      head: "0xc0b43c476fc4406b4db342beed0ed0b0d4bad02330b2b353a354b558bc28c921",
      network: 100000
    },
    snap: {}
  }
}

实际上只用到节点2里的enode信息,可以直接获取enode:

> admin.nodeInfo.enode
"enode://5873af9deabd8f7e40878ce0e3caec0bb86c79cf9bd99781be2aaab8a61e3c24aded28102971a38a7cb0ff2749b3a1b3624232cc96ac4f76dc9f035232629652@127.0.0.1:30303?discport=0"

在节点1处执行命令连接节点2:

127.0.0.1改成节点2的公网地址
> admin.addPeer("enode://5873af9deabd8f7e40878ce0e3caec0bb86c79cf9bd99781be2aaab8a61e3c24aded28102971a38a7cb0ff2749b3a1b3624232cc96ac4f76dc9f035232629652@127.0.0.1:30303?discport=0")
true

连接成功后,节点2就会开始同步节点1的区块,同步完成后,任意一个节点开始挖矿,另一个节点会同步区块,向一个节点发送交易,另一个节点也会收到该笔交易。

以下命令可以查看已连接的远程节点:

> admin.peers

你可能感兴趣的