以太坊 DApp 开发-Geth 私链环境搭建-Windows 平台
一、安装 DApp 开发环境
1.1 安装 Node.js
看我这篇node.js版本问题 | 逐梦 (0xdadream.github.io)
node --version
v22.0.0
1.2 安装 Geth
下载 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
: 该区块使用的 gastimestamp
: 区块的时间戳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. 多节点连接的总结步骤
- 创建
genesis.json
文件,并初始化所有节点。 - 启动每个节点,确保使用相同的
networkid
和不同的port
。 - 使用
admin.addPeer
手动连接节点,确保它们可以互相通信。 - 启动挖矿,并通过
miner.start()
命令进行区块生成。 - 使用
admin.peers
和net.peerCount
查看连接状态。
通过这些步骤,你可以在私链上搭建多个节点的网络,节点之间可以互相同步区块、挖矿和发送交易。
三、构建示例项目
创建 Truffle 项目
创建项目目录:
mkdir my-dapp cd my-dapp
初始化 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 控制台与合约进行交互:
启动 Truffle 控制台:
truffle console --network development
在控制台中,获取合约实例并与其交互:
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在日常使用中的常见需求,包括节点管理、账户管理、挖矿、网络连接、交易等。你可以根据自己的需求组合使用。