以太坊 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 因为交易还没有被打包到区块,因此也没有在区块中的位置,transactionIndex 为 null。

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

  • 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 创建自定义交易,除了 from 和 to 之外,还可以指定 gas、gasPrice 和 data 等字段:

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.peers 和 net.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 !
赏
评论
  目录