以太坊 DApp 开发-Geth 私链环境搭建-Windows 平台


以太坊 DApp 开发-Geth 私链环境搭建-Windows 平台

一、安装 DApp 开发环境

1.1 安装 Node.js

看我这篇node.js版本问题 | 逐梦 (0xdadream.github.io)

node --version
v22.0.0

1.2 安装 Geth

Downloads | go-ethereum

下载 64 位https://gethstore.blob.core.windows.net/builds/geth-windows-amd64-1.8.3-329ac18e.exe

或者 32 位https://gethstore.blob.core.windows.net/builds/geth-windows-386-1.8.3-329ac18e.exe

Geth 安装程序,然后进行安装。 安装完毕后打开一个控制台,执行命令验证安装成功:

geth version
Geth
Version: 1.8.3-stable 

1.3 安装 solidity 编译器

npm install solc -g

安装完毕后,执行命令验证安装成功

solcjs -V
0.8.28+commit.7893614a.Emscripten.clang

1.4 安装 web3

Web3 的安装过程使用了 git,因此需要先安装 windows 版的 git 命令行git使用 | 逐梦 (0xdadream.github.io)

npm install web3 -g

验证,创建一个文件index.js,写入以下内容

const { Web3 } = require('web3');

console.log("Version:",Web3.version);

运行

node index.js
Version: 4.13.0

1.5 安装 truffle 框架

执行以下命令安装 truffle 开发框架:

npm install -g truffle

验证安装:

truffle.cmd version
Truffle v5.11.5 (core: 5.11.5)

1.6 安装 webpack

执行以下命令安装 webpack:

npm install webpack –g 

验证安装

webpack -h
Usage: webpack [entries...] [options]
Alternative usage to run commands: webpack [command] [options]

二、运行私链节点

2.1 创世块配置

创建一个节点目录 node1,并在其中创建私链的创世块配置文件:

mkdir node1
cd node1
notepad gensis.json

然后编辑内容如下:

{
    "config": {
        "chainId": 987,
        "homesteadBlock": 0,
        "eip155Block": 0,
        "eip158Block": 0
    },
    "difficulty": "200",
    "gasLimit": "2100000",
    "alloc": {
        "7df9a875a174b3bc565e6424a0050ebc1b2d1d82": {
            "balance": "300000"
        },
        "f41c74c9ae680c1aa78f42e5647a62f353b7bdde": {
            "balance": "400000"
        }
    }
}

config.chainId用来声明以太坊网络编号,选择一个大于 10 的数字即可。 difficulty 用来声明挖矿难度,越小的值难度越低,也就能更快速地出块。

完整版的 gensis.json 如下所示

{
  "config": {
      "chainId": 987,
      "homesteadBlock": 0,
      "eip155Block": 0,
      "eip158Block": 0
  },
  "difficulty": "0x400",
  "gasLimit": "0xffffffff",
  // 可选填的参数
  "coinbase": "0x0000000000000000000000000000000000000000",
  "extraData": "0x00",
  "nonce": "0x0000000000000001",
  "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
  "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
  "timestamp": "0x00",
  "alloc": {
    "430e986e0cca10a174baad96871ec9cb308c6d05": {"balance": "111111"}
  }
}

各个字段解释如下:

必填
chainId 自定义私链的网络ID,不同的网络ID无法互联通讯,以太坊公链ID 为1,我们设置为987以防止与网络中其他私链冲突。
homesteadBlock 是否为HomeStead版本的区块,设置为0表明是。
eip155Block EIP155 [1] 是一个以太坊分叉提议,为了和以前的以太坊经典ETC 链条分叉而存在,我们私链不需要它,设为0。
eip158Block EIP158 [2] 是一个以太坊分叉提议,为了解决之前以太坊空账户造成效率低下的协议漏洞而分叉,我们私链不需要它,设为0。
difficulty 设置当前区块难度,若难度过大挖矿就很慢,我们设置较小值。
gasLimit 单一区块最大 gas 消耗上限,用来限制能够打包进入块的交易数量与信息总和,我们在学习中可以设置为最大。
选填
coinbase 打包该块的矿工的奖励地址,因为是创世块,可设为0地址。
extraData 打包该块时矿工记录的笔记。
nonce 打包该块时矿工挖矿所用到的Ethash输入参数nonce。
mixHash 与nonce配合用于挖矿,创世区无前一个区块,可不填。
parentHash 前一个区块头的哈希值,创世区块无前一个区块,设为0。
timestamp 打包该块的时间戳,符合Unix Timestamp标准,设为0。
alloc 创世时各账户分配以太币的数量,不想分配则忽略。

2.2 初始化私链节点

执行 geth 的 init 命令初始化私链节点:

geth --datadir .\data init gensis.json

# Successfully wrote genesis state

这会在当前目录下创建 data 目录,用来保存区块数据及账户信息

可以上述命令写到一个脚本 init.cmd 里,文件内容如下:

geth --datadir .\data init gensis.json

在部署下一个节点时,就可以直接执行这个脚本进行初始化了。例如,在另一台机器上:

init.cmd

2.3 启动私链节点

在我们现在的环境下,需要启动一个 Geth 节点来接入私链网络(实际上也是这个私链网络的唯一一个节点),负责在创世块后挖出第一个块。该节点也是我们与以太坊私链通信的节点服务器。下面我们来启动这样一个节点。同样,你可以用一个脚本 console.cmd 来简化启动节点时的输入,文件内容如下:

geth --datadir ./data ^
--rpc --rpcaddr 127.0.0.1 --rpcport 8545 --rpccorsdomain "*" ^
--rpcapi "eth,net,web3,personal,admin,shh,txpool,debug,miner" ^
--nodiscover --maxpeers 30 --networkid 987 --port 30303 ^
--mine --minerthreads 1 ^
--etherbase "0x7df9a875a174b3bc565e6424a0050ebc1b2d1d82" console

geth启动时命令行参数解释如下表:

参数 解释
–rpc 开启JSON-RPC 服务,可供调用/调试访问。
–rpcaddr 本地监听JSON-RPC的地址。
–rpcport 本地监听JSON-RPC的端口。
–prccorsdomain 本地监听JSON-RPC允许的域名访问。
–rpcapi 允许提供的RPC服务模块,在示例中选择了数个模块加载。
–nodiscover 关闭自动发现节点,私有链开发时防止他人意外接入,可选择关闭该选项避免他人加入网络。
–maxpeers 允许最大节点链接数目。
–networkid 指定以太坊网络ID。
–port 监听以太坊节点之间P2P消息的TCP/UDP端口,默认30303。
–mine 节点启动挖矿功能,参与挖矿。
–minerthreads 挖矿的多线程配置,例子中配置为1个线程。
–etherbase 若启动挖矿功能,挖矿奖励的接受地址,例子中我们随便填了一个。
console (可选) 启动后进入命令行模式,直接输入命令互动操作。

输入回车,启动成功!此时控制台会输出一组日志信息并有欢迎信息

以后启动节点,只要直接执行这个脚本即可:

console.cmd

进入控制台

geth attach http://127.0.0.1:8545

Geth 启动结果解释

  • 数据目录:你指定了 --datadir ./data,Geth 会将所有区块链数据和配置文件存储在该目录中。
  • RPC 服务:你启动了 HTTP-RPC 接口,监听地址为 127.0.0.1,端口为 8545。这意味着你可以通过 HTTP 方式与节点交互。
  • 挖矿:你启用了 --mine 参数,使用一个线程(--minerthreads 1)进行挖矿,挖矿奖励会发送到地址 0x7df9a875a174b3bc565e6424a0050ebc1b2d1d82
  • 禁止节点发现:你启用了 --nodiscover 参数,这意味着节点不会自动发现和连接其他节点(适用于私有链)。

后续如何使用这些配置

进入控制台后:

你可以直接在 Geth 控制台中输入命令与节点交互。以下是一些常见的操作:

  • 查看挖矿状态

    miner.hashrate

    这会返回当前的挖矿算力。

  • 查看当前区块高度

    eth.blockNumber

    这会显示当前节点已同步的最新区块号。

  • 查看账户余额

    eth.getBalance("0x7df9a875a174b3bc565e6424a0050ebc1b2d1d82")

    这会显示指定账户的余额。

  • 停止挖矿

    miner.stop()

    这会停止挖矿进程。

  • 发送交易: 你可以使用 eth.sendTransaction 发送交易。例如:

    eth.sendTransaction({from: "0x7df9a875a174b3bc565e6424a0050ebc1b2d1d82", to: "0xRecipientAddress", value: web3.toWei(1, "ether")})
  • 解锁账户: 如果要发送交易,首先需要解锁账户:

    personal.unlockAccount("0x7df9a875a174b3bc565e6424a0050ebc1b2d1d82", "password", 600)
使用 RPC 接口:

你启动了 RPC 服务,这意味着你可以通过 HTTP 与节点交互,而不必进入控制台。可以使用 curl、Web3.js、Python 等工具来与节点通信。下面是几个例子:

  • 使用 curl 查看区块高度

    curl -X POST --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' http://127.0.0.1:8545

    这会返回当前的区块高度。

  • 使用 Web3.js 与节点交互: 你可以使用 Web3.js 编写一个简单的脚本来获取节点信息。例如,安装 Web3.js:

    npm install web3

    然后使用以下 JavaScript 代码获取节点的区块高度:

    const Web3 = require('web3');
    const web3 = new Web3('http://127.0.0.1:8545');
    
    web3.eth.getBlockNumber().then(console.log);
手动添加其他节点:

如果你想手动连接其他节点,可以使用 admin.addPeer 命令。例如:

admin.addPeer("enode://publickey@ip:port")

这将允许你将其他节点加入到网络中。

2.4 账户管理

2.4.1 查看账户列表

在 geth 控制台,使用 eth 对象的 accounts 属性查看目前的账户列表:

> eth.accounts  or personal.listAccounts
[] 

2.4.2 创建新账户

在 geth 控制台,使用 personal 对象的 newAccount()方法创建一个新账户,参数为你自己选择的密码:

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

输出就是新创建的账户地址(公钥),你的输出不会和上面的示例相同。geth 会保存到数据目录下的 keystore 文件中。

2.4.3 查询账户余额

在 geth 控制台,使用 personal 对象的 getBalance()方法获取挃定账户的余额,参数为账户地址:

> eth.getBalance(eth.accounts[0])  
0

或者直接输入账户地址:

> eth.getBalance('0xd8bcf1324d566cbec5d3b67e6e14485b06a41d49')  
0  
web3.fromWei(eth.getBalance(eth.accounts[0])) # 将wei换算成eth
web3.toWei(10)

2.4.4 挖矿

私有链不用其他节点也能挖矿

1. 启动和停止挖矿
启动挖矿

要开始挖矿,你可以使用以下命令:

miner.start(threads)

threads参数表示使用多少个线程进行挖矿。比如要用 1 个线程:

miner.start(1)
停止挖矿

要停止挖矿,可以使用以下命令:

miner.stop()
2. 查看挖矿状态
查看挖矿是否正在进行

你可以通过以下命令检查节点当前是否正在挖矿:

eth.mining
  • 这个命令返回 true 表示节点正在挖矿,返回 false 表示节点没有在挖矿。
查看当前的哈希算力

使用以下命令查看节点的哈希算力:

miner.hashrate
  • 返回的数值表示每秒计算的哈希数,以 H/s(哈希每秒)为单位。
查看矿工账户

使用以下命令查看当前挖矿收益的账户(即矿工地址):

eth.coinbase
查看当前区块高度

你可以通过以下命令查看节点同步到的最新区块高度:

eth.blockNumber
3. 设置矿工账户

在挖矿前,通常需要设置 coinbase(即矿工账户地址),挖矿的奖励会发送到这个地址。你可以使用以下命令设置 coinbase

设置矿工账户
miner.setEtherbase("0xYourAccountAddress")
  • 这个命令将指定的账户设置为矿工账户。
4. 挖矿奖励和账户
查看当前账户的余额

要查看矿工账户的余额,可以使用以下命令:

eth.getBalance(eth.coinbase)
解锁矿工账户

如果你希望自动将矿工奖励发送到某个账户,你可能需要解锁该账户,尤其是在你打算用这个账户发送交易时:

personal.unlockAccount(eth.coinbase, "yourpassword", duration)
  • duration 是账户解锁的时间,单位是秒。
5. 与挖矿相关的高级操作
设置挖矿目标难度

在私有链上,你可以通过修改 genesis.json 文件中的 difficulty 值来控制挖矿难度。在 Geth 运行期间,无法直接修改目标难度。

查看挖到的区块

使用以下命令可以查看最新的区块信息:

eth.getBlock('latest')

eth.getBlock() 返回的区块信息包含以下常见字段:

  • number: 区块号
  • hash: 区块的哈希值
  • parentHash: 父区块的哈希
  • nonce: 区块中工作量证明的 nonce 值
  • miner: 挖出该区块的矿工地址
  • difficulty: 区块的挖矿难度
  • totalDifficulty: 该区块链上到当前区块为止的总难度
  • size: 区块大小(以字节为单位)
  • gasLimit: 该区块的 gas 上限
  • gasUsed: 该区块使用的 gas
  • timestamp: 区块的时间戳
  • transactions: 包含在该区块中的交易数组
  • uncles: 包含该区块的叔块数组
自动挖矿新交易

Geth 提供一个选项可以在有新交易进入交易池时自动开始挖矿:

miner.setAuto(1)
  • 设置为 1 后,每当交易池中有新交易时,节点将自动开始挖矿。
6. 挖矿的调试工具
查看交易池中的交易

如果你想查看待处理的交易,可以使用以下命令:

txpool.status
  • 返回的信息会告诉你有多少笔交易在交易池中等待执行。
查看挖矿日志

挖矿日志可以帮助你跟踪挖矿状态和进度。在 Geth 启动时,你可以通过设置日志等级来查看挖矿详细信息。例如,使用 --verbosity 参数:

geth --verbosity 3

这样,挖矿时的日志会显示更多详细的挖矿信息。

7. 挖矿常见问题
  • 无法开始挖矿? 如果你无法开始挖矿,可能是由于账户未解锁。确保你已解锁矿工账户:

    personal.unlockAccount(eth.coinbase, "yourpassword", 600)
  • 矿工奖励没有到账? 矿工奖励发放需要时间,尤其是在主网上挖矿时,奖励只有在挖到的区块被确认后才能到账。

8. 挖矿命令汇总
命令 作用
miner.start(threads) 启动挖矿,threads 为使用的线程数
miner.stop() 停止挖矿
miner.hashrate 查看当前的挖矿算力
eth.mining 查看是否正在挖矿
miner.setEtherbase("address") 设置矿工账户
eth.getBalance(eth.coinbase) 查看矿工账户的余额
eth.blockNumber 查看当前区块高度
eth.getBlock('latest') 查看最新的区块信息
txpool.status 查看交易池中的待处理交易
personal.unlockAccount(eth.coinbase, "password", 600) 解锁矿工账户

2.4.5 解锁账户

在部署合约时需要一个解锁的账户。

在 geth 控制台使用 personal 对象的 unlockAccount() 方法来解锁挃定的账户,参数为账户地址和账户密码(在创建账户时挃定的那个密码):

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

2.5 交易

1. 发送交易

使用 eth.sendTransaction 发送交易

发送交易时,你可以使用 eth.sendTransaction 命令。示例如下:

eth.sendTransaction({
  from: "0xSenderAddress",
  to: "0xRecipientAddress",
  value: web3.toWei(1, "ether")
})
  • from: 发送者账户的地址,必须先解锁账户。
  • to: 接收者账户的地址。
  • value: 发送的金额,单位是 wei(可以使用 web3.toWei 转换成 ether)。
  • gas: (可选)指定交易的 gas 上限,默认为 21000,适用于普通转账。
  • gasPrice: (可选)指定每单位 gas 的价格,默认为当前网络的 gas 价格。
  • data: (可选)包含用于合约调用的数据(智能合约交易时用到)。
解锁账户

在发送交易前,如果账户被锁定,你需要解锁账户:

personal.unlockAccount("0xSenderAddress", "password", 600)
  • 600 表示账户将保持解锁 600 秒。

2. 查询交易

根据交易哈希查询交易信息

发送交易后,你可以使用交易哈希查询交易的详细信息:

eth.getTransaction("0xTransactionHash")

这将返回交易的详细信息,包括发送者、接收者、金额、gas 费用等。

eth.getTransaction() 会返回一个包含该交易详细信息的对象,常见的字段包括:

  • blockHash: 该交易所属区块的哈希。
  • blockNumber: 该交易所属区块的区块号。
  • from: 发起该交易的地址。
  • gas: 该交易消耗的 gas 限额。
  • gasPrice: 该交易的 gas 价格。
  • hash: 该交易的哈希值。
  • input: 交易的数据字段,通常是合约调用时传递的数据。
  • nonce: 发送方账户的 nonce 值,表示该账户发起的交易计数。
  • to: 交易接收者的地址,如果是合约创建交易则为 null
  • transactionIndex: 该交易在区块中的索引。
  • value: 该交易中发送的以太币数量(以 wei 为单位)。
交易未打包

blockHash: null 当交易尚未被打包时,blockHash 的值为 null,因为该交易还没有被包含在任何区块中。

blockNumber: null 同样,由于交易未被打包,blockNumber 也会是 null,表示该交易还未属于任何区块。

transactionIndex: null 因为交易还没有被打包到区块,因此也没有在区块中的位置,transactionIndexnull

其他字段 其他交易的基本信息仍然可以被查询到,包括:

  • from: 发送者的地址。
  • to: 接收者的地址(或者 null 如果是合约创建交易)。
  • value: 发送的以太币数量(以 wei 为单位)。
  • gas: 该交易所消耗的 Gas 限额。
  • gasPrice: Gas 价格。
  • nonce: 发送者账户的交易计数。
  • input: 交易中包含的数据(如合约调用时的参数)。
根据交易哈希查询交易收据

要查看交易是否成功被打包到区块中,可以使用以下命令查询交易收据:

eth.getTransactionReceipt("0xTransactionHash")

返回的交易收据包含以下信息:

  • status: 交易的执行状态,1 表示成功,0 表示失败。
  • blockHash: 包含该交易的区块的哈希。
  • blockNumber: 区块号。
  • transactionHash: 交易哈希。
  • gasUsed: 此交易消耗的 gas 量。
  • logs: 该交易的事件日志列表

3. 创建自定义交易

你可以使用 eth.sendTransaction 创建自定义交易,除了 fromto 之外,还可以指定 gasgasPricedata 等字段:

eth.sendTransaction({
  from: "0xSenderAddress",
  to: "0xRecipientAddress",
  value: web3.toWei(1, "ether"),
  gas: 21000,
  gasPrice: web3.toWei(20, "gwei"),
  data: "0xSomeData"
})
  • gas: 设置交易的 gas 限制,确保足够高。
  • gasPrice: 设置每单位 gas 的价格,单位是 wei。
自定义 gas 价格
eth.sendTransaction({
  from: "0xSenderAddress",
  to: "0xRecipientAddress",
  value: web3.toWei(0.5, "ether"),
  gas: 21000,
  gasPrice: web3.toWei(50, "gwei")
})

这将设置一个较高的 gas 价格,以加快交易打包。

4. 离线签名交易

在某些场景下,你可能希望在离线状态下生成交易并签名,然后再发送。以下是离线签名交易的步骤:

构建未签名交易
var rawTx = {
  nonce: web3.eth.getTransactionCount("0xSenderAddress"),
  gasPrice: web3.toHex(web3.toWei('20', 'gwei')),
  gasLimit: web3.toHex(21000),
  to: "0xRecipientAddress",
  value: web3.toHex(web3.toWei(1, 'ether'))
};
签名交易

使用 eth.accounts.signTransaction 函数来签名交易:

var signedTx = eth.accounts.signTransaction(rawTx, "0xPrivateKey");
发送已签名交易

发送已签名的交易,可以使用 eth.sendSignedTransaction

eth.sendSignedTransaction(signedTx.rawTransaction)
  .on('receipt', console.log);

5. 批量发送交易

如果你需要一次发送多笔交易,你可以在脚本中批量调用 eth.sendTransaction

for (let i = 0; i < 10; i++) {
  eth.sendTransaction({
    from: "0xSenderAddress",
    to: "0xRecipientAddress",
    value: web3.toWei(0.1, "ether")
  });
}

6. 检查账户余额

你可以随时使用以下命令检查账户的余额:

eth.getBalance("0xAddress")
  • 返回值为账户的余额,单位是 wei。如果你想以以太币(ether)显示,可以使用以下命令:

    web3.fromWei(eth.getBalance("0xAddress"), "ether")

7. 检查交易池中的交易

交易被发送后,可能会在交易池中等待确认。你可以使用以下命令查看交易池中的状态:

检查挂起的交易
txpool.status

这会返回交易池中的待处理和挂起的交易数量。

查看具体的挂起交易
txpool.inspect

这会显示详细的待处理和挂起交易的信息。

8. 检查当前的 gas 价格

你可以使用以下命令来获取当前网络建议的 gas 价格:

eth.gasPrice
  • 返回的值是以 wei 为单位的当前 gas 价格。

9. 监听区块和交易事件

你可以通过监听事件来实时监控交易状态。例如,当新区块生成时,可以收到通知:

web3.eth.subscribe('newBlockHeaders', function(error, result) {
    if (!error)
        console.log(result);
})
.on("connected", function(subscriptionId){
    console.log(subscriptionId);
})
.on("data", function(blockHeader){
    console.log(blockHeader);
});

10. 交易命令汇总

命令 说明
eth.sendTransaction({...}) 发送普通或自定义交易
personal.unlockAccount(...) 解锁账户以便发送交易
eth.getTransaction("txHash") 根据哈希查询交易详情
eth.getTransactionReceipt("txHash") 根据哈希查询交易收据
eth.getBalance("0xAddress") 查询账户余额
txpool.status 查看交易池的状态
eth.gasPrice 获取当前的 gas 价格
eth.accounts.signTransaction({...}, "privateKey") 离线签名交易
eth.sendSignedTransaction("signedTx") 发送已签名交易

2.6 私链多个节点连接的详细命令

在以太坊私链中,多个节点可以通过网络连接形成一个共识网络。在这种情况下,节点之间需要能够发现彼此,并保持同步。以下是详细的步骤和命令,用于设置并连接多个 Geth 节点到同一个私链网络。

1. 准备工作

创建 genesis.json 文件

在私链中,所有节点需要共享同一个创世区块 (genesis block)。首先,创建一个 genesis.json 文件,它定义了私链的创世区块和区块链的初始配置。

以下是一个示例 genesis.json 文件:

{
  "config": {
    "chainId": 987, 
    "homesteadBlock": 0, 
    "eip150Block": 0,
    "eip155Block": 0,
    "eip158Block": 0,
    "byzantiumBlock": 0,
    "constantinopleBlock": 0,
    "petersburgBlock": 0
  },
  "difficulty": "200000",
  "gasLimit": "8000000",
  "alloc": {
    "0xYourAccountAddress": { "balance": "1000000000000000000000" }
  }
}
  • chainId: 为私链设置一个独特的网络 ID(不同于主网和测试网)。
  • difficulty: 设置初始挖矿难度,数值越低,挖矿越容易。
  • gasLimit: 设置每个区块的 gas 上限。
  • alloc: 为指定账户分配初始余额。

2. 初始化 Geth 节点

每个节点在第一次启动时都需要初始化私链。假设你已经有了 Geth,并将数据目录设置为 ./data,可以使用以下命令初始化节点:

geth --datadir ./data init genesis.json
  • --datadir ./data: 指定节点的数据目录。
  • init genesis.json: 使用 genesis.json 文件初始化链。

每个私链节点都需要运行此命令。

3. 启动节点

使用以下命令启动每个节点,并确保每个节点都指定相同的 --networkid 和使用 --port 参数设置不同的端口。

启动第一个节点(节点A)
geth --datadir ./data --networkid 987 --port 30303 --nodiscover --http --http.addr "127.0.0.1" --http.port 8545 --http.corsdomain "*" --http.api "eth,net,web3,personal,admin" console
  • --datadir ./data: 指定节点的数据目录。
  • --networkid 987: 设置网络 ID,与 genesis.json 中的 chainId 保持一致。
  • --port 30303: 设置 P2P 连接的监听端口。
  • --nodiscover: 禁用节点发现功能(如果你想手动连接节点)。
  • --http: 启用 HTTP-RPC 服务,便于远程访问节点。
  • console: 启动 Geth 控制台以便执行命令。
启动第二个节点(节点B)

节点B可以在另一台机器上,也可以是本机上的另一个实例,只需要不同的数据目录和端口:

geth --datadir ./node2data --networkid 987 --port 30304 --nodiscover --http --http.addr "127.0.0.1" --http.port 8546 --http.corsdomain "*" --http.api "eth,net,web3,personal,admin" console
  • --datadir ./node2data: 为节点B设置不同的数据目录。
  • --port 30304: 节点B的 P2P 端口必须与节点A不同。
  • --http.port 8546: HTTP-RPC 服务端口与节点A不同。

4. 手动连接节点

因为 --nodiscover 禁用了自动发现,必须手动连接节点。

获取节点的 enode 信息

在节点A的控制台中,输入以下命令获取节点A的 enode 地址:

admin.nodeInfo.enode

输出类似于:

"enode://1234567890abcdef@127.0.0.1:30303?discport=0"

这个地址是节点A的唯一标识,后面的 @127.0.0.1:30303 表示节点A运行的 IP 和端口。

连接节点B到节点A

在节点B的控制台中,使用 admin.addPeer 命令连接到节点A:

admin.addPeer("enode://1234567890abcdef@127.0.0.1:30303")
  • "enode://1234567890abcdef@127.0.0.1:30303" 替换为节点A的 enode 地址。
验证节点是否连接

在任何一个节点的控制台中,使用以下命令检查当前连接的对等节点数量:

net.peerCount
  • 如果节点成功连接,该命令将返回大于 0 的值。

你还可以使用以下命令查看当前的连接节点列表:

admin.peers

5. 同步和挖矿

在节点A或节点B中,可以启动挖矿:

miner.start(1)  // 使用 1 个线程进行挖矿
  • 挖到的区块会自动同步到其他节点。你可以通过以下命令查看区块高度:

    eth.blockNumber

6. 账户和交易管理

创建新账户

在 Geth 控制台中,你可以为每个节点创建新账户:

personal.newAccount("password")
解锁账户

发送交易前需要解锁账户:

personal.unlockAccount("0xYourAccountAddress", "password", 600)
发送交易

在节点之间发送交易,可以使用 eth.sendTransaction

eth.sendTransaction({
  from: "0xSenderAddress",
  to: "0xRecipientAddress",
  value: web3.toWei(1, "ether")
})
  • from: 发送者账户地址。
  • to: 接收者账户地址。
  • value: 转账金额,单位为 wei。

7. 设置启动脚本

为了方便,你可以将节点启动命令放入 .bat.cmd 文件中,在 Windows 上直接双击执行。例如,创建一个 start_nodeA.bat 文件,内容为:

geth --datadir ./data --networkid 987 --port 30303 --nodiscover --http --http.addr "127.0.0.1" --http.port 8545 --http.corsdomain "*" --http.api "eth,net,web3,personal,admin" console

然后创建 start_nodeB.bat

geth --datadir ./node2data --networkid 987 --port 30304 --nodiscover --http --http.addr "127.0.0.1" --http.port 8546 --http.corsdomain "*" --http.api "eth,net,web3,personal,admin" console

8. 多节点连接的总结步骤

  1. 创建 genesis.json 文件,并初始化所有节点。
  2. 启动每个节点,确保使用相同的 networkid 和不同的 port
  3. 使用 admin.addPeer 手动连接节点,确保它们可以互相通信。
  4. 启动挖矿,并通过 miner.start() 命令进行区块生成。
  5. 使用 admin.peersnet.peerCount 查看连接状态。

通过这些步骤,你可以在私链上搭建多个节点的网络,节点之间可以互相同步区块、挖矿和发送交易。

三、构建示例项目

创建 Truffle 项目

  1. 创建项目目录

    mkdir my-dapp
    cd my-dapp
  2. 初始化 Truffle 项目

    truffle init or truffle.cmd unbox webpack #后者用 webpack 模版初始化项目骨架结构

    安装项目依赖的 NPM 包 ,执行以下命令安装 npm 包:

    cd app
    npm install  

修改 Truffle 配置

在项目根目录下找到 truffle-config.js 文件,并修改如下:

module.exports = {
  networks: {
    development: {
      host: "localhost",  // 根据实际情况设置
      port: 8545,         // 根据实际情况设置
      network_id: "*",    // 匹配任何网络 ID
      gas: 3000000        // 设置最大 gas 限制
    }
  },
  compilers: {
    solc: {
      version: "0.8.0"   // 指定 Solidity 编译器版本
    }
  }
};

编写智能合约

contracts 目录下创建一个新的合约文件,例如 MyContract.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract MyContract {
    string public message;

    constructor(string memory initialMessage) {
        message = initialMessage;
    }

    function setMessage(string memory newMessage) public {
        message = newMessage;
    }
}

创建迁移文件

migrations 目录下创建一个新的迁移文件,例如 2_deploy_my_contract.js

const MyContract = artifacts.require("MyContract");

module.exports = function(deployer) {
  deployer.deploy(MyContract, "Hello, World!");
};

1. 默认迁移行为

当你运行以下命令时:

truffle migrate

Truffle 会按照 migrations 目录中脚本的顺序自动部署所有合约。每个脚本的文件名通常以数字开头,以确定部署顺序。例如:

  • 1_initial_migration.js
  • 2_deploy_contracts.js

2. 部署特定合约

如果你只想部署特定的合约,可以按照以下步骤进行:

2.1 创建特定迁移文件

你可以在 migrations 目录中创建新的迁移文件,只包含要部署的合约。例如,假设你只想部署 MyContract 合约,你可以创建一个新的迁移文件,例如 3_deploy_my_contract.js,内容如下:

const MyContract = artifacts.require("MyContract");

module.exports = function(deployer) {
  deployer.deploy(MyContract, "Hello, World!");
};
2.2 使用 --f 参数

如果你想只运行特定的迁移文件,可以使用 --f 参数(--f 后接文件的序号):

truffle migrate --f 3

这将只运行 3_deploy_my_contract.js 文件中的迁移脚本。

3. 回滚并重新部署

如果你需要重新部署某个合约,可以使用 --reset 参数,这将重新运行所有的迁移:

truffle migrate --reset

如果只想回滚到某个特定迁移并重新部署,你可以使用 --to 参数:

truffle migrate --to 2

这将回滚到并重新部署到第二个迁移(2_deploy_contracts.js)为止的所有迁移。

4. 部署的选择性控制

如果你的合约之间存在依赖关系(例如,一个合约依赖于另一个合约),你应该确保按照适当的顺序部署。可以在迁移脚本中引用之前已经部署的合约:

const MyContractA = artifacts.require("MyContractA");
const MyContractB = artifacts.require("MyContractB");

module.exports = async function(deployer) {
  await deployer.deploy(MyContractA, "Hello");
  const instanceA = await MyContractA.deployed();
  
  // 部署时传递 MyContractA 的地址
  await deployer.deploy(MyContractB, instanceA.address);
};

编译合约

在部署合约之前,您需要确保智能合约已被编译。您可以通过以下命令来编译合约:

truffle compile

自动编译与部署

在执行 truffle migrate 时,Truffle 会自动检查合约是否已经编译。如果合约文件有更改或未编译,Truffle 会自动执行编译。因此,在以下情况下,您可以不手动执行编译命令:

  • 合约未编译:当您修改了合约后,执行 truffle migrate 时会自动编译。
  • 合约已经是最新:如果合约未更改并且已经编译,则可以直接执行迁移命令。

如何确认编译状态

如果您不确定合约是否已经编译,可以检查 build/contracts 目录。编译后,该目录下应该会生成合约的 JSON 文件(包含字节码和 ABI)。例如,MyContract.json 文件中应该有相关信息。

如果您希望在每次部署时都重新编译合约,可以在 migrate 命令中使用 --compile-all 参数,如下所示:

truffle migrate --compile-all

这将强制 Truffle 重新编译所有合约。

启动 Geth 节点

在 Geth 的命令行窗口中,启动您的节点,使用以下命令:

geth --datadir ./data --networkid 987 --nodiscover --port 30303 --rpc --rpcaddr "127.0.0.1" --rpcport "8545" --rpccorsdomain "*" --rpcapi "eth,net,web3,personal,admin,shh,txpool,debug,miner" --mine --minerthreads 1

解锁账户

在 Geth 控制台中,解锁您的账户(假设 user1 是您的账户地址):

personal.unlockAccount("user1", "your_password", 0)

如果已经正确地解锁了账户,你会看到部署过程停止在某个状态

这是因为 truffle 在等待部署交易提交,但是我们在私链中还没有启动挖矿。 现在切换回 geth 终端窗口,查看交易池的状态:

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

有一个挂起的交易

启动挖矿

在 Geth 控制台中,启动挖矿:

miner.start()

稍等小会儿,再查看交易池的状态:

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

交易已经成功提交了

部署合约

在项目目录中,使用以下命令部署合约:

truffle migrate --network development

检查部署结果

部署完成后,您可以查看终端输出中的合约地址和交易哈希,确认合约是否成功部署。

与合约交互

在合约部署后,您可以使用 Truffle 控制台与合约进行交互:

  1. 启动 Truffle 控制台:

    truffle console --network development
  2. 在控制台中,获取合约实例并与其交互:

    const instance = await MyContract.deployed();
    const currentMessage = await instance.message();
    console.log(currentMessage);  // 输出当前消息
    
    await instance.setMessage("New Message");  // 更新消息
    const updatedMessage = await instance.message();
    console.log(updatedMessage);  // 输出更新后的消息

启动 DApp

执行以下命令来启动 DApp:

npm run dev  

在浏览器里访问 http://localhost:8080 即可

如果你希望从别的机器也可以访问你的 DApp 应用,修改一下 package.json:

{  
	scripts:{
    	"dev": "webpack-dev-server –-host 0.0.0.0" 
    	}  
    }

四、命令详解

1. Geth 命令行选项

这些命令是在启动 Geth 时使用的,用来配置节点行为。

  • –datadir:指定数据目录,所有区块链和配置数据将保存在此目录中。

    geth --datadir ./data
  • –networkid:选择要加入的网络 ID,避免连接到错误的链。

    geth --networkid 987
  • –port:指定节点的P2P通信端口(默认是 30303)。

    geth --port 30303
  • –nodiscover:禁用节点自动发现,防止自动连接其他节点(常用于私有网络)。

    geth --nodiscover
  • –maxpeers:设置最多连接的节点数(默认 25)。

    geth --maxpeers 30
  • –mine:启动节点时立即开始挖矿。

    geth --mine
  • –minerthreads:设置挖矿使用的线程数量(默认为 1)。

    geth --minerthreads 1
  • –etherbase:指定挖矿奖励接收的以太坊地址。

    geth --etherbase 0x7df9a875a174b3bc565e6424a0050ebc1b2d1d82
  • –rpc:启用 HTTP-RPC 接口,允许通过 HTTP 与节点交互。

    geth --rpc
  • –rpcaddr:指定 HTTP-RPC 接口监听的地址。

    geth --rpcaddr 127.0.0.1
  • –rpcport:指定 HTTP-RPC 端口。

    geth --rpcport 8545
  • –rpccorsdomain:指定允许通过 CORS(跨域资源共享)访问的域,通常设置为 * 允许所有域。

    geth --rpccorsdomain "*"
  • –rpcapi:指定允许通过 RPC 接口调用的 API 模块(例如 eth, net, web3)。

    geth --rpcapi "eth,net,web3"
  • –syncmode:设置节点的同步模式,常用的有:

    • fast(默认,快速同步)
    • full(完全同步)
    • light(轻节点,只下载区块头)
    geth --syncmode fast
  • –ipcdisable:禁用 Geth 的 IPC-RPC 通信接口(默认启用 IPC)。

    geth --ipcdisable

2. Geth 控制台命令

这些命令是在 Geth 启动并进入控制台时使用的,或者通过 geth attach 来附加到已运行的节点。

  • eth.syncing:查看节点是否在同步,并返回同步状态。

    eth.syncing
  • eth.blockNumber:获取当前节点的区块高度。

    eth.blockNumber
  • **eth.getBlock(blockNumber)**:查看某个区块的信息(例如查看最新区块)。

    eth.getBlock(eth.blockNumber)
  • **eth.getBalance(address)**:查看某个地址的余额。

    eth.getBalance("0xYourAddressHere")
  • **miner.start(threads)**:启动挖矿,并指定使用的线程数(例如 1 个线程)。

    miner.start(1)
  • **miner.stop()**:停止挖矿。

    miner.stop()
  • **personal.newAccount(password)**:创建新账户,并设置密码。

    personal.newAccount("YourPassword")
  • **personal.unlockAccount(address, password)**:解锁指定账户,允许在一段时间内使用账户进行交易。

    personal.unlockAccount("0xYourAddressHere", "YourPassword", 600)
  • admin.peers:查看当前连接的节点。

    admin.peers
  • **admin.addPeer(enodeURL)**:手动添加一个节点。

    admin.addPeer("enode://...")
  • net.peerCount:查看连接的节点数量。

    net.peerCount
  • txpool.status:查看交易池的状态,包含等待和挂起的交易数量。

    txpool.status
  • **debug.verbosity(level)**:设置日志的详细程度(0 为最少,5 为最多)。

    debug.verbosity(3)
  • **web3.sha3(string)**:计算字符串的 Keccak-256 哈希。

    web3.sha3("hello")
  • **eth.sendTransaction({from: sender, to: receiver, value: amount})**:发送交易(注意单位为 Wei)。

    eth.sendTransaction({from: "0xYourAddress", to: "0xReceiverAddress", value: web3.toWei(1, "ether")})

这些命令涵盖了Geth在日常使用中的常见需求,包括节点管理、账户管理、挖矿、网络连接、交易等。你可以根据自己的需求组合使用。


文章作者: 0xdadream
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 0xdadream !
评论
  目录