2

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

 2 years ago
source link: https://segmentfault.com/a/1190000040657787
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

以太坊客户端众多,基于各种语言开发的都有,这与区块链崇尚开放的价值观有关。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

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK