├── README.md
├── nock-install-mac.sh
└── nock-install.sh
/README.md:
--------------------------------------------------------------------------------
1 | # 配置要求
2 | 最低要求 64 G RAM 没有到这个要求的可以省省了。。。
3 |
4 | ## 一键命令 Linux 版
5 | ```bash
6 | wget -O nock-install.sh https://raw.githubusercontent.com/GzGod/nock/refs/heads/main/nock-install.sh && sed -i 's/\r$//' nock-install.sh && chmod +x nock-install.sh && ./nock-install.sh
7 | ```
8 | ## 一键命令 Mac 版
9 | ```bash
10 | wget -O nock-install-mac.sh https://raw.githubusercontent.com/GzGod/nock/refs/heads/main/nock-install-mac.sh && sed -i 's/\r$//' nock-install-mac.sh && chmod +x nock-install-mac.sh && ./nock-install-mac.sh
11 | ```
12 | 其中生成钱包这一步会生成 wallet.txt 相关内容看图
13 |
14 |
15 |
16 |
17 | 
18 |
19 |
--------------------------------------------------------------------------------
/nock-install-mac.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # ========= 色彩定义 =========
4 | RESET='\033[0m'
5 | BOLD='\033[1m'
6 | GREEN='\033[0;32m'
7 | BLUE='\033[0;34m'
8 | YELLOW='\033[1;33m'
9 | RED='\033[0;31m'
10 |
11 | # ========= 项目路径 =========
12 | NCK_DIR="$HOME/nockchain"
13 |
14 | # ========= 横幅与署名 =========
15 | function show_banner() {
16 | clear
17 | echo -e "${BOLD}${BLUE}"
18 | echo " ╔═╗╔═╦╗─╔╦═══╦═══╦═══╦═══╗"
19 | echo " ╚╗╚╝╔╣║─║║╔══╣╔═╗║╔═╗║╔═╗║"
20 | echo " ─╚╗╔╝║║─║║╚══╣║─╚╣║─║║║─║║"
21 | echo " ─╔╝╚╗║║─║║╔══╣║╔═╣╚═╝║║─║║"
22 | echo " ╔╝╔╗╚╣╚═╝║╚══╣╚╩═║╔═╗║╚═╝║"
23 | echo " ╚═╝╚═╩═══╩═══╩═══╩╝─╚╩═══╝"
24 | echo -e "${RESET}"
25 | echo " 关注TG频道:t.me/xuegaoz"
26 | echo " 我的GitHub:github.com/Gzgod"
27 | echo " 我的推特:推特雪糕战神@Xuegaogx"
28 | echo "-----------------------------------------------"
29 | echo ""
30 | }
31 |
32 | # ========= 等待任意键继续 =========
33 | function pause_and_return() {
34 | echo ""
35 | read -n1 -r -p "按任意键返回主菜单..." key
36 | main_menu
37 | }
38 |
39 | # ========= 安装系统依赖 =========
40 | function install_dependencies() {
41 | echo -e "[*] 检查 Homebrew..."
42 | if ! command -v brew &> /dev/null; then
43 | echo -e "${RED}[-] Homebrew 未安装,正在安装...${RESET}"
44 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
45 | # 配置 Homebrew 环境变量
46 | echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> "$HOME/.zshrc"
47 | eval "$(/opt/homebrew/bin/brew shellenv)"
48 | fi
49 | echo -e "[*] 更新 Homebrew 并安装依赖..."
50 | brew update && brew upgrade
51 | brew install curl git wget lz4 jq make gcc nano tmux htop llvm pkg-config openssl@3 leveldb tar clang ncdu unzip gnu-sed screen || {
52 | echo -e "${RED}[-] 依赖安装失败,请检查 Homebrew 或网络!${RESET}"
53 | pause_and_return
54 | return
55 | }
56 | echo -e "${GREEN}[+] 依赖安装完成。${RESET}"
57 | pause_and_return
58 | }
59 |
60 | # ========= 安装 Rust =========
61 | function install_rust() {
62 | if command -v rustc &> /dev/null; then
63 | echo -e "${YELLOW}[!] Rust 已安装,跳过安装。${RESET}"
64 | pause_and_return
65 | return
66 | fi
67 | echo -e "[*] 安装 Rust..."
68 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
69 | source "$HOME/.cargo/env" || { echo -e "${RED}[-] 无法配置 Rust 环境变量!${RESET}"; pause_and_return; return; }
70 | rustup default stable
71 | echo -e "${GREEN}[+] Rust 安装完成。${RESET}"
72 | pause_and_return
73 | }
74 |
75 | # ========= 设置仓库 =========
76 | function setup_repository() {
77 | echo -e "[*] 检查 nockchain 仓库..."
78 | if [ -d "$NCK_DIR" ]; then
79 | echo -e "${YELLOW}[?] 已存在 nockchain 目录,是否删除重新克隆?(y/n)${RESET}"
80 | read -r confirm
81 | if [[ "$confirm" == "y" || "$confirm" == "Y" ]]; then
82 | rm -rf "$NCK_DIR" "$HOME/.nockapp"
83 | git clone https://github.com/zorp-corp/nockchain "$NCK_DIR"
84 | else
85 | cd "$NCK_DIR" && git pull
86 | fi
87 | else
88 | git clone https://github.com/zorp-corp/nockchain "$NCK_DIR"
89 | fi
90 | if [ $? -ne 0 ]; then
91 | echo -e "${RED}[-] 克隆仓库失败,请检查网络或权限!${RESET}"
92 | pause_and_return
93 | return
94 | fi
95 | cd "$NCK_DIR" || { echo -e "${RED}[-] 无法进入 nockchain 目录!${RESET}"; pause_and_return; return; }
96 | if [ -f ".env" ]; then
97 | cp .env .env.bak
98 | echo -e "[*] .env 已备份为 .env.bak"
99 | fi
100 | if [ -f ".env_example" ]; then
101 | cp .env_example .env
102 | echo -e "${GREEN}[+] 环境文件 .env 已创建。${RESET}"
103 | else
104 | echo -e "${RED}[-] 未找到 .env_example 文件,请检查仓库!${RESET}"
105 | fi
106 | echo -e "${GREEN}[+] 仓库设置完成。${RESET}"
107 | pause_and_return
108 | }
109 |
110 | # ========= 编译项目和配置环境变量 =========
111 | function build_and_configure() {
112 | if [ ! -d "$NCK_DIR" ]; then
113 | echo -e "${RED}[-] nockchain 目录不存在,请先运行选项 3 设置仓库!${RESET}"
114 | pause_and_return
115 | return
116 | fi
117 | cd "$NCK_DIR" || { echo -e "${RED}[-] 无法进入 nockchain 目录!${RESET}"; pause_and_return; return; }
118 | echo -e "[*] 编译核心组件..."
119 | make install-hoonc || { echo -e "${RED}[-] 执行 make install-hoonc 失败,请检查 Makefile 或依赖!${RESET}"; pause_and_return; return; }
120 | if command -v hoonc &> /dev/null; then
121 | echo -e "[*] hoonc 安装成功,可用命令:hoonc"
122 | else
123 | echo -e "${YELLOW}[!] 警告:hoonc 命令不可用,安装可能不完整。${RESET}"
124 | fi
125 | make build || { echo -e "${RED}[-] 执行 make build 失败,请检查 Makefile 或依赖!${RESET}"; pause_and_return; return; }
126 | make install-nockchain-wallet || { echo -e "${RED}[-] 执行 make install-nockchain-wallet 失败,请检查 Makefile 或依赖!${RESET}"; pause_and_return; return; }
127 | make install-nockchain || { echo -e "${RED}[-] 执行 make install-nockchain 失败,请检查 Makefile 或依赖!${RESET}"; pause_and_return; return; }
128 | echo -e "[*] 配置环境变量..."
129 | RC_FILE="$HOME/.zshrc"
130 | if ! grep -q "$NCK_DIR/target/release" "$RC_FILE"; then
131 | echo "export PATH=\"\$PATH:$NCK_DIR/target/release\"" >> "$RC_FILE"
132 | source "$RC_FILE" || echo -e "${YELLOW}[!] 无法立即应用环境变量,请手动 source $RC_FILE 或重新打开终端。${RESET}"
133 | else
134 | source "$RC_FILE" || echo -e "${YELLOW}[!] 无法立即应用环境变量,请手动 source $RC_FILE 或重新打开终端。${RESET}"
135 | fi
136 | echo -e "${GREEN}[+] 编译和环境变量配置完成。${RESET}"
137 | pause_and_return
138 | }
139 |
140 | # ========= 生成钱包 =========
141 | function generate_wallet() {
142 | if [ ! -d "$NCK_DIR" ] || [ ! -f "$NCK_DIR/target/release/nockchain-wallet" ]; then
143 | echo -e "${RED}[-] 未找到钱包命令或 nockchain 目录,请先运行选项 3 和 4!${RESET}"
144 | pause_and_return
145 | return
146 | fi
147 | cd "$NCK_DIR" || { echo -e "${RED}[-] 无法进入 nockchain 目录!${RESET}"; pause_and_return; return; }
148 | echo -e "[*] 生成钱包密钥对..."
149 | read -p "[?] 是否创建钱包?[Y/n]: " create_wallet
150 | create_wallet=${create_wallet:-y}
151 | if [[ ! "$create_wallet" =~ ^[Yy]$ ]]; then
152 | echo -e "[*] 已跳过钱包创建。"
153 | pause_and_return
154 | return
155 | fi
156 | if ! command -v nockchain-wallet &> /dev/null; then
157 | echo -e "${RED}[-] nockchain-wallet 命令不可用,请检查 target/release 目录或构建过程!${RESET}"
158 | pause_and_return
159 | return
160 | fi
161 | nockchain-wallet keygen > wallet_keys.txt 2>&1 || { echo -e "${RED}[-] nockchain-wallet keygen 执行失败!${RESET}"; pause_and_return; return; }
162 | echo -e "${GREEN}[+] 钱包密钥已保存到 $NCK_DIR/wallet_keys.txt,请妥善保管!${RESET}"
163 | PUBLIC_KEY=$(grep -i "public key" wallet_keys.txt | awk '{print $NF}' | tail -1)
164 | if [ -n "$PUBLIC_KEY" ]; then
165 | echo -e "${YELLOW}公钥:${RESET}\n$PUBLIC_KEY"
166 | echo -e "${YELLOW}[!] 请使用选项 6 设置挖矿公钥或手动将以下公钥添加到 $NCK_DIR/.env 文件中:${RESET}"
167 | echo -e "MINING_PUBKEY=$PUBLIC_KEY"
168 | else
169 | echo -e "${RED}[-] 无法提取公钥,请检查 wallet_keys.txt!${RESET}"
170 | fi
171 | echo -e "${GREEN}[+] 钱包生成完成。${RESET}"
172 | pause_and_return
173 | }
174 |
175 | # ========= 设置挖矿公钥 =========
176 | function configure_mining_key() {
177 | if [ ! -d "$NCK_DIR" ]; then
178 | echo -e "${RED}[-] nockchain 目录不存在,请先运行选项 3!${RESET}"
179 | pause_and_return
180 | return
181 | fi
182 | cd "$NCK_DIR" || { echo -e "${RED}[-] 无法进入 nockchain 目录!${RESET}"; pause_and_return; return; }
183 | echo -e "[*] 设置挖矿公钥(MINING_PUBKEY)..."
184 | read -p "[?] 请输入您的 MINING_PUBKEY(可从选项 5 获取): " public_key
185 | if [ -z "$public_key" ]; then
186 | echo -e "${RED}[-] 未提供 MINING_PUBKEY,请输入有效的公钥!${RESET}"
187 | pause_and_return
188 | return
189 | fi
190 | if [ ! -f ".env" ]; then
191 | echo -e "${RED}[-] .env 文件不存在,请先运行选项 3 设置仓库!${RESET}"
192 | pause_and_return
193 | return
194 | fi
195 | if ! grep -q "^MINING_PUBKEY=" .env; then
196 | echo "MINING_PUBKEY=$public_key" >> .env
197 | else
198 | gsed -i "s|^MINING_PUBKEY=.*|MINING_PUBKEY=$public_key|" .env || {
199 | echo -e "${RED}[-] 无法更新 .env 文件中的 MINING_PUBKEY!${RESET}"
200 | pause_and_return
201 | return
202 | }
203 | fi
204 | if grep -q "^MINING_PUBKEY=$public_key$" .env; then
205 | echo -e "${GREEN}[+] .env 文件中的 MINING_PUBKEY 更新成功!${RESET}"
206 | else
207 | echo -e "${RED}[-] .env 文件更新失败,请检查文件内容!${RESET}"
208 | fi
209 | echo -e "${GREEN}[+] 挖矿公钥设置完成。${RESET}"
210 | pause_and_return
211 | }
212 |
213 | # ========= 启动 Miner 节点 =========
214 | function start_miner_node() {
215 | if [ ! -d "$NCK_DIR" ]; then
216 | echo -e "${RED}[-] nockchain 目录不存在,请先运行选项 3!${RESET}"
217 | pause_and_return
218 | return
219 | fi
220 | cd "$NCK_DIR" || { echo -e "${RED}[-] 无法进入 nockchain 目录!${RESET}"; pause_and_return; return; }
221 |
222 | # 验证 nockchain 命令是否可用
223 | echo -e "[*] 正在验证 nockchain 命令..."
224 | if ! command -v nockchain &> /dev/null; then
225 | echo -e "${RED}[-] nockchain 命令不可用,请检查选项 4 是否成功!${RESET}"
226 | echo -e "${YELLOW}[!] 请确保 \$PATH 包含 $NCK_DIR/target/release,可运行 'source ~/.zshrc' 或重新打开终端${RESET}"
227 | pause_and_return
228 | return
229 | fi
230 |
231 | # 验证 screen 和 tee 命令是否可用
232 | echo -e "[*] 正在验证 screen 和 tee 命令..."
233 | if ! command -v screen &> /dev/null; then
234 | echo -e "${RED}[-] screen 命令不可用,请确保已安装 screen(运行选项 1 或手动安装)!${RESET}"
235 | pause_and_return
236 | return
237 | fi
238 | if ! command -v tee &> /dev/null; then
239 | echo -e "${RED}[-] tee 命令不可用,请确保已安装 tee!${RESET}"
240 | pause_and_return
241 | return
242 | fi
243 |
244 | # 从 .env 文件中读取公钥
245 | if [ -f ".env" ]; then
246 | public_key=$(grep "^MINING_PUBKEY=" .env | cut -d'=' -f2)
247 | if [ -z "$public_key" ]; then
248 | echo -e "${YELLOW}[!] .env 文件中未找到 MINING_PUBKEY,请使用选项 6 设置或手动输入。${RESET}"
249 | read -p "[?] 请输入您的 MINING_PUBKEY(可从选项 5 获取): " public_key
250 | if [ -z "$public_key" ]; then
251 | echo -e "${RED}[-] 未提供 MINING_PUBKEY,请输入有效的公钥!${RESET}"
252 | pause_and_return
253 | return
254 | fi
255 | else
256 | echo -e "[*] 使用 .env 文件中的 MINING_PUBKEY:$public_key"
257 | fi
258 | else
259 | echo -e "${YELLOW}[!] .env 文件不存在,请使用选项 6 设置或手动输入 MINING_PUBKEY。${RESET}"
260 | read -p "[?] 请输入您的 MINING_PUBKEY(可从选项 5 获取): " public_key
261 | if [ -z "$public_key" ]; then
262 | echo -e "${RED}[-] 未提供 MINING_PUBKEY,请输入有效的公钥!${RESET}"
263 | pause_and_return
264 | return
265 | fi
266 | fi
267 |
268 | # 提示清理数据目录
269 | if [ -d ".data.nockchain" ]; then
270 | echo -e "${YELLOW}[?] 检测到数据目录 .data.nockchain,是否清理以重新初始化?(y/n)${RESET}"
271 | read -r confirm_clean
272 | if [[ "$confirm_clean" == "y" || "$confirm_clean" == "Y" ]]; then
273 | echo -e "[*] 备份并清理数据目录..."
274 | mv .data.nockchain .data.nockchain.bak-$(date +%F-%H%M%S) 2>/dev/null
275 | echo -e "${GREEN}[+] 数据目录已清理,备份至 .data.nockchain.bak-*${RESET}"
276 | fi
277 | fi
278 |
279 | # 默认端口
280 | LEADER_PORT=3005
281 | FOLLOWER_PORT=3006
282 | PORTS_TO_CHECK=("$LEADER_PORT" "$FOLLOWER_PORT")
283 | PORTS_OCCUPIED=false
284 | declare -A PID_PORT_MAP
285 |
286 | # 检查端口占用(优先使用 lsof)
287 | echo -e "[*] 检查端口 $LEADER_PORT 和 $FOLLOWER_PORT 是否被占用..."
288 | if command -v lsof &> /dev/null; then
289 | for PORT in "${PORTS_TO_CHECK[@]}"; do
290 | PIDS=$(lsof -i :$PORT -t | sort -u)
291 | if [ -n "$PIDS" ]; then
292 | echo -e "${YELLOW}[!] 端口 $PORT 已被占用。${RESET}"
293 | for PID in $PIDS; do
294 | echo -e "${YELLOW}[!] 占用端口 $PORT 的进程 PID: $PID${RESET}"
295 | PID_PORT_MAP[$PID]+="$PORT "
296 | PORTS_OCCUPIED=true
297 | done
298 | fi
299 | done
300 | else
301 | echo -e "${RED}[-] 未找到 lsof 命令,无法检查端口!请安装 lsof(运行选项 1 或手动安装)。${RESET}"
302 | pause_and_return
303 | return
304 | fi
305 |
306 | # 处理端口占用
307 | if [ "$PORTS_OCCUPIED" = true ]; then
308 | echo -e "${YELLOW}[?] 检测到端口被占用,是否杀死占用进程以释放端口?(y/n)${RESET}"
309 | read -r confirm_kill
310 | if [[ "$confirm_kill" == "y" || "$confirm_kill" == "Y" ]]; then
311 | for PID in "${!PID_PORT_MAP[@]}"; do
312 | PORTS=${PID_PORT_MAP[$PID]}
313 | echo -e "[*] 正在杀死占用端口 $PORTS 的进程 (PID: $PID)..."
314 | kill -9 "$PID" 2>/dev/null
315 | if [ $? -eq 0 ]; then
316 | echo -e "${GREEN}[+] 成功杀死 PID $PID,端口 $PORTS 应已释放。${RESET}"
317 | else
318 | echo -e "${RED}[-] 无法杀死 PID $PID,请手动检查!${RESET}"
319 | pause_and_return
320 | return
321 | fi
322 | done
323 | echo -e "[*] 验证端口是否已释放..."
324 | for PORT in "${PORTS_TO_CHECK[@]}"; do
325 | if lsof -i :$PORT -t >/dev/null 2>&1; then
326 | echo -e "${RED}[-] 端口 $PORT 仍被占用,请手动检查!${RESET}"
327 | pause_and_return
328 | return
329 | fi
330 | done
331 | else
332 | echo -e "${RED}[-] 用户取消杀死进程,无法启动 Miner 节点!${RESET}"
333 | pause_and_return
334 | return
335 | fi
336 | else
337 | echo -e "${GREEN}[+] 端口 $LEADER_PORT 和 $FOLLOWER_PORT 未被占用。${RESET}"
338 | fi
339 |
340 | # 清理现有的 miner screen 会话
341 | echo -e "[*] 正在清理现有的 miner screen 会话..."
342 | screen -ls | grep -q "miner" && screen -X -S miner quit
343 |
344 | # 启动 Miner 节点,使用公钥和 peer 参数
345 | echo -e "[*] 正在启动 Miner 节点..."
346 | NOCKCHAIN_CMD="RUST_LOG=trace ./target/release/nockchain --mining-pubkey \"$public_key\" --mine --peer /ip4/95.216.102.60/udp/3006/quic-v1 --peer /ip4/65.108.123.225/udp/3006/quic-v1 --peer /ip4/65.109.156.108/udp/3006/quic-v1 --peer /ip4/65.21.67.175/udp/3006/quic-v1 --peer /ip4/65.109.156.172/udp/3006/quic-v1 --peer /ip4/34.174.22.166/udp/3006/quic-v1 --peer /ip4/34.95.155.151/udp/30000/quic-v1 --peer /ip4/34.18.98.38/udp/30000/quic-v1"
347 |
348 | echo -e "${GREEN}[+] 启动 nockchain 节点在 screen 会话 'miner' 中,日志同时输出到 $NCK_DIR/miner.log${RESET}"
349 | echo -e "${YELLOW}[!] 使用 'screen -r miner' 查看节点实时输出,Ctrl+A 然后 D 脱离 screen(节点继续运行)${RESET}"
350 | screen -dmS miner -L -Logfile "$NCK_DIR/screen_miner.log" bash -c "source $HOME/.zshrc; $NOCKCHAIN_CMD 2>&1 | tee -a miner.log; echo 'nockchain 已退出,查看日志:$NCK_DIR/miner.log'; sleep 30"
351 |
352 | sleep 5
353 | if screen -ls | grep -q "miner"; then
354 | echo -e "${GREEN}[+] Miner 节点已在 screen 会话 'miner' 中运行,可使用 'screen -r miner' 查看${RESET}"
355 | echo -e "${GREEN}[+] 所有步骤已成功完成!${RESET}"
356 | echo -e "当前目录:$(pwd)"
357 | echo -e "MINING_PUBKEY 已设置为:$public_key"
358 | echo -e "Leader 端口:$LEADER_PORT"
359 | echo -e "Follower 端口:$FOLLOWER_PORT"
360 | if [ -f "wallet_keys.txt" ]; then
361 | echo -e "钱包密钥已生成,保存在 $NCK_DIR/wallet_keys.txt,请妥善保存!"
362 | fi
363 | if [ -f "miner.log" ] && [ -s "miner.log" ]; then
364 | echo -e "${YELLOW}[!] miner.log 内容:${RESET}"
365 | tail -n 10 miner.log
366 | else
367 | echo -e "${YELLOW}[!] miner.log 文件尚未生成或为空,请稍后检查或使用 'screen -r miner' 查看实时输出${RESET}"
368 | fi
369 | if [ -f "$NCK_DIR/screen_miner.log" ] && [ -s "$NCK_DIR/screen_miner.log" ]; then
370 | echo -e "${YELLOW}[!] screen_miner.log 内容(最后 10 行):${RESET}"
371 | tail -n 10 "$NCK_DIR/screen_miner.log"
372 | else
373 | echo -e "${YELLOW}[!] screen_miner.log 文件尚未生成或为空,可能是 screen 输出问题${RESET}"
374 | fi
375 | else
376 | echo -e "${RED}[-] 无法启动 Miner 节点!请检查 $NCK_DIR/miner.log 和 $NCK_DIR/screen_miner.log${RESET}"
377 | echo -e "${YELLOW}[!] 最后 10 行 miner.log:${RESET}"
378 | tail -n 10 "$NCK_DIR/miner.log" 2>/dev/null || echo -e "${YELLOW}[!] 未找到 miner.log${RESET}"
379 | echo -e "${YELLOW}[!] 最后 10 行 screen_miner.log:${RESET}"
380 | tail -n 10 "$NCK_DIR/screen_miner.log" 2>/dev/null || echo -e "${YELLOW}[!] 未找到 screen_miner.log${RESET}"
381 | fi
382 | pause_and_return
383 | }
384 |
385 | # ========= 备份密钥 =========
386 | function backup_keys() {
387 | if [ ! -d "$NCK_DIR" ]; then
388 | echo -e "${RED}[-] nockchain 目录不存在,请先运行选项 3!${RESET}"
389 | pause_and_return
390 | return
391 | fi
392 | if ! command -v nockchain-wallet &> /dev/null; then
393 | echo -e "${RED}[-] nockchain-wallet 命令不可用,请先运行选项 4!${RESET}"
394 | pause_and_return
395 | return
396 | fi
397 | cd "$NCK_DIR" || { echo -e "${RED}[-] 无法进入 nockchain 目录!${RESET}"; pause_and_return; return; }
398 | echo -e "[*] 备份密钥..."
399 | nockchain-wallet export-keys > nockchain_keys_backup.txt 2>&1
400 | if [ $? -eq 0 ]; then
401 | echo -e "${GREEN}[+] 密钥备份成功!已保存到 $NCK_DIR/nockchain_keys_backup.txt${RESET}"
402 | echo -e "${YELLOW}[!] 请妥善保管该文件,切勿泄露!${RESET}"
403 | else
404 | echo -e "${RED}[-] 密钥备份失败,请检查 nockchain-wallet export-keys 命令输出!${RESET}"
405 | echo -e "${YELLOW}[!] 详细信息见 $NCK_DIR/nockchain_keys_backup.txt${RESET}"
406 | fi
407 | pause_and_return
408 | }
409 |
410 | # ========= 查看节点日志 =========
411 | function view_logs() {
412 | LOG_FILE="$NCK_DIR/miner.log"
413 | if [ -f "$LOG_FILE" ]; then
414 | echo -e "${GREEN}[+] 正在显示日志文件:$LOG_FILE${RESET}"
415 | tail -f "$LOG_FILE"
416 | else
417 | echo -e "${RED}[-] 日志文件 $LOG_FILE 不存在,请确认是否已运行选项 7 启动 Miner 节点!${RESET}"
418 | fi
419 | pause_and_return
420 | }
421 |
422 | # ========= 查询余额 =========
423 | function check_balance() {
424 | if [ ! -d "$NCK_DIR" ]; then
425 | echo -e "${RED}[-] nockchain 目录不存在,请先运行选项 3!${RESET}"
426 | pause_and_return
427 | return
428 | fi
429 | if ! command -v nockchain-wallet &> /dev/null; then
430 | echo -e "${RED}[-] nockchain-wallet 命令不可用,请先运行选项 4!${RESET}"
431 | pause_and_return
432 | return
433 | fi
434 | cd "$NCK_DIR" || { echo -e "${RED}[-] 无法进入 nockchain 目录!${RESET}"; pause_and_return; return; }
435 |
436 | # 检查 socket 文件(macOS 路径可能不同,需用户确认)
437 | SOCKET_PATH="$NCK_DIR/.socket/nockchain_npc.sock"
438 | if [ ! -S "$SOCKET_PATH" ]; then
439 | echo -e "${RED}[-] socket 文件 $SOCKET_PATH 不存在,请确保 nockchain 节点正在运行(可尝试选项 7)!${RESET}"
440 | echo -e "${YELLOW}[!] 如果 socket 路径不同,请手动查找(例如:find ~ -name nockchain_npc.sock)并更新脚本${RESET}"
441 | pause_and_return
442 | return
443 | fi
444 |
445 | # 执行余额查询
446 | echo -e "[*] 正在查询余额..."
447 | nockchain-wallet --nockchain-socket "$SOCKET_PATH" update-balance > balance_output.txt 2>&1
448 | if [ $? -eq 0 ]; then
449 | echo -e "${GREEN}[+] 余额查询成功!以下是查询结果:${RESET}"
450 | echo -e "----------------------------------------"
451 | cat balance_output.txt
452 | echo -e "----------------------------------------"
453 | else
454 | echo -e "${RED}[-] 余额查询失败,请检查 nockchain-wallet 命令或节点状态!${RESET}"
455 | echo -e "${YELLOW}[!] 详细信息见 $NCK_DIR/balance_output.txt${RESET}"
456 | fi
457 | pause_and_return
458 | }
459 |
460 | # ========= 主菜单 =========
461 | function main_menu() {
462 | show_banner
463 | echo "请选择操作:"
464 | echo " 1) 安装系统依赖"
465 | echo " 2) 安装 Rust"
466 | echo " 3) 设置仓库"
467 | echo " 4) 编译项目和配置环境变量"
468 | echo " 5) 生成钱包"
469 | echo " 6) 设置挖矿公钥"
470 | echo " 7) 启动 Miner 节点"
471 | echo " 8) 备份密钥"
472 | echo " 9) 查看节点日志"
473 | echo " 10) 查询余额"
474 | echo " 0) 退出"
475 | echo ""
476 | read -p "请输入编号: " choice
477 | case "$choice" in
478 | 1) install_dependencies ;;
479 | 2) install_rust ;;
480 | 3) setup_repository ;;
481 | 4) build_and_configure ;;
482 | 5) generate_wallet ;;
483 | 6) configure_mining_key ;;
484 | 7) start_miner_node ;;
485 | 8) backup_keys ;;
486 | 9) view_logs ;;
487 | 10) check_balance ;;
488 | 0) echo -e "${GREEN}已退出。${RESET}"; exit 0 ;;
489 | *) echo -e "${RED}[-] 无效选项!${RESET}"; pause_and_return ;;
490 | esac
491 | }
492 |
493 | # ========= 启动主程序 =========
494 | main_menu
495 |
--------------------------------------------------------------------------------
/nock-install.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # ========= 色彩定义 =========
4 | RESET='\033[0m'
5 | BOLD='\033[1m'
6 | GREEN='\033[0;32m'
7 | BLUE='\033[0;34m'
8 | YELLOW='\033[1;33m'
9 | RED='\033[0;31m'
10 |
11 | # ========= 项目路径 =========
12 | NCK_DIR="$HOME/nockchain"
13 |
14 | # ========= 横幅与署名 =========
15 | function show_banner() {
16 | clear
17 | echo -e "${BOLD}${BLUE}"
18 | echo " ╔═╗╔═╦╗─╔╦═══╦═══╦═══╦═══╗"
19 | echo " ╚╗╚╝╔╣║─║║╔══╣╔═╗║╔═╗║╔═╗║"
20 | echo " ─╚╗╔╝║║─║║╚══╣║─╚╣║─║║║─║║"
21 | echo " ─╔╝╚╗║║─║║╔══╣║╔═╣╚═╝║║─║║"
22 | echo " ╔╝╔╗╚╣╚═╝║╚══╣╚╩═║╔═╗║╚═╝║"
23 | echo " ╚═╝╚═╩═══╩═══╩═══╩╝─╚╩═══╝"
24 | echo -e "${RESET}"
25 | echo " 关注TG频道:t.me/xuegaoz"
26 | echo " 我的GitHub:github.com/Gzgod"
27 | echo " 我的推特:推特雪糕战神@Xuegaogx"
28 | echo "-----------------------------------------------"
29 | echo ""
30 | }
31 |
32 | # ========= 等待任意键继续 =========
33 | function pause_and_return() {
34 | echo ""
35 | read -n1 -r -p "按任意键返回主菜单..." key
36 | main_menu
37 | }
38 |
39 | # ========= 安装系统依赖 =========
40 | function install_dependencies() {
41 | if ! command -v apt-get &> /dev/null; then
42 | echo -e "${RED}[-] 此脚本假设使用 Debian/Ubuntu 系统 (apt)。请手动安装依赖!${RESET}"
43 | pause_and_return
44 | return
45 | fi
46 | echo -e "[*] 更新系统并安装依赖..."
47 | apt-get update && apt-get upgrade -y
48 | sudo apt install -y curl git make clang llvm-dev libclang-dev screen
49 | echo -e "${GREEN}[+] 依赖安装完成。${RESET}"
50 | pause_and_return
51 | }
52 |
53 | # ========= 安装 Rust =========
54 | function install_rust() {
55 | if command -v rustc &> /dev/null; then
56 | echo -e "${YELLOW}[!] Rust 已安装,跳过安装。${RESET}"
57 | pause_and_return
58 | return
59 | fi
60 | echo -e "[*] 安装 Rust..."
61 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
62 | source "$HOME/.cargo/env" || { echo -e "${RED}[-] 无法配置 Rust 环境变量!${RESET}"; pause_and_return; return; }
63 | rustup default stable
64 | echo -e "${GREEN}[+] Rust 安装完成。${RESET}"
65 | pause_and_return
66 | }
67 |
68 | # ========= 设置仓库 =========
69 | function setup_repository() {
70 | echo -e "[*] 检查 nockchain 仓库..."
71 | if [ -d "$NCK_DIR" ]; then
72 | echo -e "${YELLOW}[?] 已存在 nockchain 目录,是否删除重新克隆?(y/n)${RESET}"
73 | read -r confirm
74 | if [[ "$confirm" == "y" || "$confirm" == "Y" ]]; then
75 | rm -rf "$NCK_DIR" "$HOME/.nockapp"
76 | git clone https://github.com/zorp-corp/nockchain "$NCK_DIR"
77 | else
78 | cd "$NCK_DIR" && git pull
79 | fi
80 | else
81 | git clone https://github.com/zorp-corp/nockchain "$NCK_DIR"
82 | fi
83 | if [ $? -ne 0 ]; then
84 | echo -e "${RED}[-] 克隆仓库失败,请检查网络或权限!${RESET}"
85 | pause_and_return
86 | return
87 | fi
88 | cd "$NCK_DIR" || { echo -e "${RED}[-] 无法进入 nockchain 目录!${RESET}"; pause_and_return; return; }
89 | if [ -f ".env" ]; then
90 | cp .env .env.bak
91 | echo -e "[*] .env 已备份为 .env.bak"
92 | fi
93 | if [ -f ".env_example" ]; then
94 | cp .env_example .env
95 | echo -e "${GREEN}[+] 环境文件 .env 已创建。${RESET}"
96 | else
97 | echo -e "${RED}[-] 未找到 .env_example 文件,请检查仓库!${RESET}"
98 | fi
99 | echo -e "${GREEN}[+] 仓库设置完成。${RESET}"
100 | pause_and_return
101 | }
102 |
103 | # ========= 编译项目和配置环境变量 =========
104 | function build_and_configure() {
105 | if [ ! -d "$NCK_DIR" ]; then
106 | echo -e "${RED}[-] nockchain 目录不存在,请先运行选项 3 设置仓库!${RESET}"
107 | pause_and_return
108 | return
109 | fi
110 | cd "$NCK_DIR" || { echo -e "${RED}[-] 无法进入 nockchain 目录!${RESET}"; pause_and_return; return; }
111 | echo -e "[*] 编译核心组件..."
112 | make install-hoonc || { echo -e "${RED}[-] 执行 make install-hoonc 失败,请检查 Makefile 或依赖!${RESET}"; pause_and_return; return; }
113 | if command -v hoonc &> /dev/null; then
114 | echo -e "[*] hoonc 安装成功,可用命令:hoonc"
115 | else
116 | echo -e "${YELLOW}[!] 警告:hoonc 命令不可用,安装可能不完整。${RESET}"
117 | fi
118 | make build || { echo -e "${RED}[-] 执行 make build 失败,请检查 Makefile 或依赖!${RESET}"; pause_and_return; return; }
119 | make install-nockchain-wallet || { echo -e "${RED}[-] 执行 make install-nockchain-wallet 失败,请检查 Makefile 或依赖!${RESET}"; pause_and_return; return; }
120 | make install-nockchain || { echo -e "${RED}[-] 执行 make install-nockchain 失败,请检查 Makefile 或依赖!${RESET}"; pause_and_return; return; }
121 | echo -e "[*] 配置环境变量..."
122 | RC_FILE="$HOME/.bashrc"
123 | [[ "$SHELL" == *"zsh"* ]] && RC_FILE="$HOME/.zshrc"
124 | if ! grep -q "$HOME/.cargo/bin" "$RC_FILE"; then
125 | echo "export PATH=\"\$PATH:$HOME/.cargo/bin\"" >> "$RC_FILE"
126 | source "$RC_FILE" || echo -e "${YELLOW}[!] 无法立即应用环境变量,请手动 source $RC_FILE 或重新打开终端。${RESET}"
127 | else
128 | source "$RC_FILE" || echo -e "${YELLOW}[!] 无法立即应用环境变量,请手动 source $RC_FILE 或重新打开终端。${RESET}"
129 | fi
130 | echo -e "${GREEN}[+] 编译和环境变量配置完成。${RESET}"
131 | pause_and_return
132 | }
133 |
134 | # ========= 生成钱包 =========
135 | function generate_wallet() {
136 | if [ ! -d "$NCK_DIR" ] || [ ! -f "$NCK_DIR/target/release/nockchain-wallet" ]; then
137 | echo -e "${RED}[-] 未找到钱包命令或 nockchain 目录,请先运行选项 3 和 4!${RESET}"
138 | pause_and_return
139 | return
140 | fi
141 | cd "$NCK_DIR" || { echo -e "${RED}[-] 无法进入 nockchain 目录!${RESET}"; pause_and_return; return; }
142 | echo -e "[*] 生成钱包密钥对..."
143 | read -p "[?] 是否创建钱包?[Y/n]: " create_wallet
144 | create_wallet=${create_wallet:-y}
145 | if [[ ! "$create_wallet" =~ ^[Yy]$ ]]; then
146 | echo -e "[*] 已跳过钱包创建。"
147 | pause_and_return
148 | return
149 | fi
150 | if ! command -v nockchain-wallet &> /dev/null; then
151 | echo -e "${RED}[-] nockchain-wallet 命令不可用,请检查 target/release 目录或构建过程!${RESET}"
152 | pause_and_return
153 | return
154 | fi
155 | nockchain-wallet keygen > wallet_keys.txt 2>&1 || { echo -e "${RED}[-] nockchain-wallet keygen 执行失败!${RESET}"; pause_and_return; return; }
156 | echo -e "${GREEN}[+] 钱包密钥已保存到 $NCK_DIR/wallet_keys.txt,请妥善保管!${RESET}"
157 | PUBLIC_KEY=$(grep -i "public key" wallet_keys.txt | awk '{print $NF}' | tail -1)
158 | if [ -n "$PUBLIC_KEY" ]; then
159 | echo -e "${YELLOW}公钥:${RESET}\n$PUBLIC_KEY"
160 | echo -e "${YELLOW}[!] 请使用选项 6 设置挖矿公钥或手动将以下公钥添加到 $NCK_DIR/.env 文件中:${RESET}"
161 | echo -e "MINING_PUBKEY=$PUBLIC_KEY"
162 | else
163 | echo -e "${RED}[-] 无法提取公钥,请检查 wallet_keys.txt!${RESET}"
164 | fi
165 | echo -e "${GREEN}[+] 钱包生成完成。${RESET}"
166 | pause_and_return
167 | }
168 |
169 | # ========= 设置挖矿公钥 =========
170 | function configure_mining_key() {
171 | if [ ! -d "$NCK_DIR" ]; then
172 | echo -e "${RED}[-] nockchain 目录不存在,请先运行选项 3!${RESET}"
173 | pause_and_return
174 | return
175 | fi
176 | cd "$NCK_DIR" || { echo -e "${RED}[-] 无法进入 nockchain 目录!${RESET}"; pause_and_return; return; }
177 | echo -e "[*] 设置挖矿公钥(MINING_PUBKEY)..."
178 | read -p "[?] 请输入您的 MINING_PUBKEY(可从选项 5 获取): " public_key
179 | if [ -z "$public_key" ]; then
180 | echo -e "${RED}[-] 未提供 MINING_PUBKEY,请输入有效的公钥!${RESET}"
181 | pause_and_return
182 | return
183 | fi
184 | if [ ! -f ".env" ]; then
185 | echo -e "${RED}[-] .env 文件不存在,请先运行选项 3 设置仓库!${RESET}"
186 | pause_and_return
187 | return
188 | fi
189 | if ! grep -q "^MINING_PUBKEY=" .env; then
190 | echo "MINING_PUBKEY=$public_key" >> .env
191 | else
192 | sed -i "s|^MINING_PUBKEY=.*|MINING_PUBKEY=$public_key|" .env || {
193 | echo -e "${RED}[-] 无法更新 .env 文件中的 MINING_PUBKEY!${RESET}"
194 | pause_and_return
195 | return
196 | }
197 | fi
198 | if grep -q "^MINING_PUBKEY=$public_key$" .env; then
199 | echo -e "${GREEN}[+] .env 文件中的 MINING_PUBKEY 更新成功!${RESET}"
200 | else
201 | echo -e "${RED}[-] .env 文件更新失败,请检查文件内容!${RESET}"
202 | fi
203 | echo -e "${GREEN}[+] 挖矿公钥设置完成。${RESET}"
204 | pause_and_return
205 | }
206 |
207 | # ========= 启动节点(Miner 或非 Miner) =========
208 | function start_node() {
209 | if [ ! -d "$NCK_DIR" ]; then
210 | echo -e "${RED}[-] nockchain 目录不存在,请先运行选项 3!${RESET}"
211 | pause_and_return
212 | return
213 | fi
214 | cd "$NCK_DIR" || { echo -e "${RED}[-] 无法进入 nockchain 目录!${RESET}"; pause_and_return; return; }
215 |
216 | # 验证 nockchain 命令是否可用
217 | echo -e "[*] 正在验证 nockchain 命令..."
218 | if ! command -v nockchain &> /dev/null; then
219 | echo -e "${RED}[-] nockchain 命令不可用,请检查选项 4 是否成功!${RESET}"
220 | echo -e "${YELLOW}[!] 请确保 \$PATH 包含 $HOME/.cargo/bin,可运行 'source ~/.bashrc' 或重新打开终端${RESET}"
221 | pause_and_return
222 | return
223 | fi
224 |
225 | # 验证 screen 命令是否可用
226 | echo -e "[*] 正在验证 screen 命令..."
227 | if ! command -v screen &> /dev/null; then
228 | echo -e "${RED}[-] screen 命令不可用,请确保已安装 screen(运行选项 1 或手动安装)!${RESET}"
229 | pause_and_return
230 | return
231 | fi
232 |
233 | # 选择节点类型
234 | echo -e "[*] 请选择节点类型:"
235 | echo -e " 1) Miner 节点(挖矿)"
236 | echo -e " 2) 非 Miner 节点(仅运行节点)"
237 | read -p "[?] 请输入编号 (1/2): " node_type
238 | if [[ "$node_type" != "1" && "$node_type" != "2" ]]; then
239 | echo -e "${RED}[-] 无效选项,请选择 1 或 2!${RESET}"
240 | pause_and_return
241 | return
242 | fi
243 |
244 | # 设置工作目录
245 | echo -e "[*] 输入节点工作目录(默认:$NCK_DIR)..."
246 | read -p "[?] 工作目录 [$NCK_DIR]: " work_dir
247 | work_dir=${work_dir:-$NCK_DIR}
248 | if [ ! -d "$work_dir" ]; then
249 | echo -e "[*] 创建工作目录 $work_dir..."
250 | mkdir -p "$work_dir" || { echo -e "${RED}[-] 无法创建工作目录!${RESET}"; pause_and_return; return; }
251 | cp "$NCK_DIR/.env" "$work_dir/.env" 2>/dev/null || echo -e "${YELLOW}[!] 未找到 .env 文件,请确保已运行选项 3!${RESET}"
252 | fi
253 | cd "$work_dir" || { echo -e "${RED}[-] 无法进入工作目录 $work_dir!${RESET}"; pause_and_return; return; }
254 |
255 | # 如果是 Miner 节点,获取公钥
256 | public_key=""
257 | if [ "$node_type" = "1" ]; then
258 | if [ -f ".env" ]; then
259 | public_key=$(grep "^MINING_PUBKEY=" .env | cut -d'=' -f2)
260 | if [ -z "$public_key" ]; then
261 | echo -e "${YELLOW}[!] .env 文件中未找到 MINING_PUBKEY,请使用选项 6 设置或手动输入。${RESET}"
262 | read -p "[?] 请输入您的 MINING_PUBKEY(可从选项 5 获取): " public_key
263 | if [ -z "$public_key" ]; then
264 | echo -e "${RED}[-] 未提供 MINING_PUBKEY,请输入有效的公钥!${RESET}"
265 | pause_and_return
266 | return
267 | fi
268 | else
269 | echo -e "[*] 使用 .env 文件中的 MINING_PUBKEY:$public_key"
270 | fi
271 | else
272 | echo -e "${YELLOW}[!] .env 文件不存在,请使用选项 6 设置或手动输入 MINING_PUBKEY。${RESET}"
273 | read -p "[?] 请输入您的 MINING_PUBKEY(可从选项 5 获取): " public_key
274 | if [ -z "$public_key" ]; then
275 | echo -e "${RED}[-] 未提供 MINING_PUBKEY,请输入有效的公钥!${RESET}"
276 | pause_and_return
277 | return
278 | fi
279 | fi
280 | fi
281 |
282 | # 提示清理数据目录
283 | if [ -d ".data.nockchain" ]; then
284 | echo -e "${YELLOW}[?] 检测到数据目录 .data.nockchain,是否清理以重新初始化?(y/n)${RESET}"
285 | read -r confirm_clean
286 | if [[ "$confirm_clean" == "y" || "$confirm_clean" == "Y" ]]; then
287 | echo -e "[*] 备份并清理数据目录..."
288 | mv .data.nockchain .data.nockchain.bak-$(date +%F-%H%M%S) 2>/dev/null
289 | echo -e "${GREEN}[+] 数据目录已清理,备份至 .data.nockchain.bak-*${RESET}"
290 | fi
291 | fi
292 |
293 | # 设置端口
294 | echo -e "[*] 输入节点端口(默认 Leader: 3005, Follower: 3006)..."
295 | read -p "[?] Leader 端口 [3005]: " LEADER_PORT
296 | LEADER_PORT=${LEADER_PORT:-3005}
297 | read -p "[?] Follower 端口 [3006]: " FOLLOWER_PORT
298 | FOLLOWER_PORT=${FOLLOWER_PORT:-3006}
299 | PORTS_TO_CHECK=("$LEADER_PORT" "$FOLLOWER_PORT")
300 | PORTS_OCCUPIED=false
301 | declare -A PID_PORT_MAP
302 |
303 | # 设置绑定地址
304 | echo -e "[*] 输入 P2P 绑定地址(例如 /ip4/0.0.0.0/udp/$FOLLOWER_PORT/quic-v1,若 NAT 后请输入公网 IP)..."
305 | read -p "[?] 绑定地址 [/ip4/0.0.0.0/udp/$FOLLOWER_PORT/quic-v1]: " bind_addr
306 | bind_addr=${bind_addr:-/ip4/0.0.0.0/udp/$FOLLOWER_PORT/quic-v1}
307 |
308 | # 检查端口占用
309 | echo -e "[*] 检查端口 $LEADER_PORT 和 $FOLLOWER_PORT 是否被占用..."
310 | if command -v lsof &> /dev/null; then
311 | for PORT in "${PORTS_TO_CHECK[@]}"; do
312 | PIDS=$(lsof -i :$PORT -t | sort -u)
313 | if [ -n "$PIDS" ]; then
314 | echo -e "${YELLOW}[!] 端口 $PORT 已被占用。${RESET}"
315 | for PID in $PIDS; do
316 | echo -e "${YELLOW}[!] 占用端口 $PORT 的进程 PID: $PID${RESET}"
317 | PID_PORT_MAP[$PID]+="$PORT "
318 | PORTS_OCCUPIED=true
319 | done
320 | fi
321 | done
322 | elif command -v netstat &> /dev/null; then
323 | for PORT in "${PORTS_TO_CHECK[@]}"; do
324 | PIDS=$(netstat -tulnp 2>/dev/null | grep ":$PORT " | awk '{print $7}' | cut -d'/' -f1 | sort -u)
325 | if [ -n "$PIDS" ]; then
326 | echo -e "${YELLOW}[!] 端口 $PORT 已被占用。${RESET}"
327 | for PID in $PIDS; do
328 | echo -e "${YELLOW}[!] 占用端口 $PORT 的进程 PID: $PID${RESET}"
329 | PID_PORT_MAP[$PID]+="$PORT "
330 | PORTS_OCCUPIED=true
331 | done
332 | fi
333 | done
334 | else
335 | echo -e "${RED}[-] 未找到 lsof 或 netstat 命令,无法检查端口!${RESET}"
336 | pause_and_return
337 | return
338 | fi
339 |
340 | # 处理端口占用
341 | if [ "$PORTS_OCCUPIED" = true ]; then
342 | echo -e "${YELLOW}[?] 检测到端口被占用,是否杀死占用进程以释放端口?(y/n)${RESET}"
343 | read -r confirm_kill
344 | if [[ "$confirm_kill" == "y" || "$confirm_kill" == "Y" ]]; then
345 | for PID in "${!PID_PORT_MAP[@]}"; do
346 | PORTS=${PID_PORT_MAP[$PID]}
347 | echo -e "[*] 正在杀死占用端口 $PORTS 的进程 (PID: $PID)..."
348 | if ! ps -p "$PID" -o user= | grep -q "^$USER$"; then
349 | echo -e "${YELLOW}[!] 进程 PID $PID 由其他用户拥有,尝试使用 sudo 杀死...${RESET}"
350 | sudo kill -9 "$PID" 2>/dev/null
351 | else
352 | kill -9 "$PID" 2>/dev/null
353 | fi
354 | if [ $? -eq 0 ]; then
355 | echo -e "${GREEN}[+] 成功杀死 PID $PID,端口 $PORTS 应已释放。${RESET}"
356 | else
357 | echo -e "${RED}[-] 无法杀死 PID $PID,请手动检查!${RESET}"
358 | pause_and_return
359 | return
360 | fi
361 | done
362 | echo -e "[*] 验证端口是否已释放..."
363 | for PORT in "${PORTS_TO_CHECK[@]}"; do
364 | if command -v lsof &> /dev/null && lsof -i :$PORT -t >/dev/null 2>&1; then
365 | echo -e "${RED}[-] 端口 $PORT 仍被占用,请手动检查!${RESET}"
366 | pause_and_return
367 | return
368 | elif command -v netstat &> /dev/null && netstat -tuln | grep -q ":$PORT "; then
369 | echo -e "${RED}[-] 端口 $PORT 仍被占用,请手动检查!${RESET}"
370 | pause_and_return
371 | return
372 | fi
373 | done
374 | else
375 | echo -e "${RED}[-] 用户取消杀死进程,无法启动节点!${RESET}"
376 | pause_and_return
377 | return
378 | fi
379 | else
380 | echo -e "${GREEN}[+] 端口 $LEADER_PORT 和 $FOLLOWER_PORT 未被占用。${RESET}"
381 | fi
382 |
383 | # 设置日志级别
384 | echo -e "[*] 请选择日志级别:"
385 | echo -e " 1) info(推荐,常规信息)"
386 | echo -e " 2) debug(调试信息)"
387 | echo -e " 3) trace(详细调试信息)"
388 | read -p "[?] 请输入编号 (1/2/3) [1]: " log_level_choice
389 | case "$log_level_choice" in
390 | 2) log_level="debug" ;;
391 | 3) log_level="trace" ;;
392 | *) log_level="info" ;;
393 | esac
394 | if [ -f ".env" ]; then
395 | if ! grep -q "^RUST_LOG=" .env; then
396 | echo "RUST_LOG=$log_level" >> .env
397 | else
398 | sed -i "s|^RUST_LOG=.*|RUST_LOG=$log_level|" .env
399 | fi
400 | fi
401 | echo -e "[*] 日志级别设置为:$log_level"
402 |
403 | # 询问是否输出 miner.log
404 | echo -e "${YELLOW}[?] 是否输出 miner.log 日志文件?(警告:日志文件可能占用较大磁盘空间,请谨慎选择!)[y/N]${RESET}"
405 | read -r log_to_file
406 | log_to_file=${log_to_file:-n}
407 | if [[ "$log_to_file" =~ ^[Yy]$ ]]; then
408 | echo -e "${GREEN}[+] 将输出日志到 $work_dir/miner.log,请定期清理以避免磁盘空间不足!${RESET}"
409 | log_output="2>&1 | tee -a miner.log"
410 | else
411 | echo -e "${YELLOW}[!] 已选择不输出 miner.log,日志仅保存在 screen 日志 $work_dir/screen_$session_name.log 中${RESET}"
412 | log_output="2>&1"
413 | fi
414 |
415 | # 清理现有的 screen 会话
416 | if [ "$node_type" = "1" ]; then
417 | session_name="miner"
418 | else
419 | session_name="node"
420 | fi
421 | echo -e "[*] 正在清理现有的 $session_name screen 会话..."
422 | screen -ls | grep -q "$session_name" && screen -X -S "$session_name" quit
423 |
424 | # 启动节点
425 | echo -e "[*] 正在启动节点..."
426 | if [ "$node_type" = "1" ]; then
427 | script="$NCK_DIR/scripts/run_nockchain_miner.sh"
428 | else
429 | script="$NCK_DIR/scripts/run_nockchain_node.sh"
430 | fi
431 | if [ ! -f "$script" ]; then
432 | echo -e "${RED}[-] 未找到 $script,请检查 nockchain 仓库!${RESET}"
433 | pause_and_return
434 | return
435 | fi
436 | chmod +x "$script"
437 | echo -e "${GREEN}[+] 启动节点在 screen 会话 '$session_name' 中,screen 日志输出到 $work_dir/screen_$session_name.log${RESET}"
438 | echo -e "${YELLOW}[!] 使用 'screen -r $session_name' 查看节点实时输出,Ctrl+A 然后 D 脱离 screen(节点继续运行)${RESET}"
439 | screen -dmS "$session_name" -L -Logfile "$work_dir/screen_$session_name.log" bash -c "source $HOME/.bashrc; sh $script --bind \"$bind_addr\" $log_output; echo '节点已退出,查看 screen 日志:$work_dir/screen_$session_name.log'; sleep 30"
440 |
441 | # 等待足够时间,确保 screen 会话初始化
442 | sleep 5
443 |
444 | # 检查 screen 会话是否运行
445 | if screen -ls | grep -q "$session_name"; then
446 | echo -e "${GREEN}[+] 节点已在 screen 会话 '$session_name' 中运行,可使用 'screen -r $session_name' 查看${RESET}"
447 | echo -e "${GREEN}[+] 所有步骤已成功完成!${RESET}"
448 | echo -e "工作目录:$work_dir"
449 | [ -n "$public_key" ] && echo -e "MINING_PUBKEY:$public_key"
450 | echo -e "Leader 端口:$LEADER_PORT"
451 | echo -e "Follower 端口:$FOLLOWER_PORT"
452 | echo -e "绑定地址:$bind_addr"
453 | if [ -f "wallet_keys.txt" ]; then
454 | echo -e "钱包密钥已生成,保存在 $work_dir/wallet_keys.txt,请妥善保存!"
455 | fi
456 | # 检查 miner.log 是否有内容(如果启用)
457 | if [[ "$log_to_file" =~ ^[Yy]$ ]] && [ -f "miner.log" ] && [ -s "miner.log" ]; then
458 | echo -e "${YELLOW}[!] miner.log 内容(最后 10 行):${RESET}"
459 | tail -n 10 miner.log
460 | elif [[ "$log_to_file" =~ ^[Yy]$ ]]; then
461 | echo -e "${YELLOW}[!] miner.log 文件尚未生成或为空,请稍后检查或使用 'screen -r $session_name' 查看实时输出${RESET}"
462 | fi
463 | # 检查 screen 日志
464 | if [ -f "$work_dir/screen_$session_name.log" ] && [ -s "$work_dir/screen_$session_name.log" ]; then
465 | echo -e "${YELLOW}[!] screen_$session_name.log 内容(最后 10 行):${RESET}"
466 | tail -n 10 "$work_dir/screen_$session_name.log"
467 | else
468 | echo -e "${YELLOW}[!] screen_$session_name.log 文件尚未生成或为空,可能是 screen 输出问题${RESET}"
469 | fi
470 | else
471 | echo -e "${RED}[-] 无法启动节点!请检查 $work_dir/screen_$session_name.log${RESET}"
472 | if [[ "$log_to_file" =~ ^[Yy]$ ]]; then
473 | echo -e "${YELLOW}[!] 最后 10 行 miner.log:${RESET}"
474 | tail -n 10 "$work_dir/miner.log" 2>/dev/null || echo -e "${YELLOW}[!] 未找到 miner.log${RESET}"
475 | fi
476 | echo -e "${YELLOW}[!] 最后 10 行 screen_$session_name.log:${RESET}"
477 | tail -n 10 "$work_dir/screen_$session_name.log" 2>/dev/null || echo -e "${YELLOW}[!] 未找到 screen_$session_name.log${RESET}"
478 | fi
479 | pause_and_return
480 | }
481 |
482 | # ========= 备份密钥 =========
483 | function backup_keys() {
484 | if [ ! -d "$NCK_DIR" ]; then
485 | echo -e "${RED}[-] nockchain 目录不存在,请先运行选项 3!${RESET}"
486 | pause_and_return
487 | return
488 | fi
489 | if ! command -v nockchain-wallet &> /dev/null; then
490 | echo -e "${RED}[-] nockchain-wallet 命令不可用,请先运行选项 4!${RESET}"
491 | pause_and_return
492 | return
493 | fi
494 | cd "$NCK_DIR" || { echo -e "${RED}[-] 无法进入 nockchain 目录!${RESET}"; pause_and_return; return; }
495 | echo -e "[*] 备份密钥..."
496 | nockchain-wallet export-keys > keys.export 2>&1
497 | if [ $? -eq 0 ]; then
498 | echo -e "${GREEN}[+] 密钥备份成功!已保存到 $NCK_DIR/keys.export${RESET}"
499 | echo -e "${YELLOW}[!] 请妥善保管该文件,切勿泄露!${RESET}"
500 | else
501 | echo -e "${RED}[-] 密钥备份失败,请检查 nockchain-wallet export-keys 命令输出!${RESET}"
502 | echo -e "${YELLOW}[!] 详细信息见 $NCK_DIR/keys.export${RESET}"
503 | fi
504 | pause_and_return
505 | }
506 |
507 | # ========= 查看节点日志 =========
508 | function view_logs() {
509 | LOG_FILE="$NCK_DIR/miner.log"
510 | if [ -f "$LOG_FILE" ]; then
511 | echo -e "${GREEN}[+] 正在显示日志文件:$LOG_FILE${RESET}"
512 | tail -f "$LOG_FILE"
513 | else
514 | echo -e "${RED}[-] 日志文件 $LOG_FILE 不存在,请确认是否已运行选项 7 启动节点并启用 miner.log 输出!${RESET}"
515 | echo -e "${YELLOW}[!] 您可以检查 screen 日志 $NCK_DIR/screen_miner.log 或 $NCK_DIR/screen_node.log${RESET}"
516 | fi
517 | pause_and_return
518 | }
519 |
520 | # ========= 查询余额 =========
521 | function check_balance() {
522 | if [ ! -d "$NCK_DIR" ]; then
523 | echo -e "${RED}[-] nockchain 目录不存在,请先运行选项 3!${RESET}"
524 | pause_and_return
525 | return
526 | fi
527 | if ! command -v nockchain-wallet &> /dev/null; then
528 | echo -e "${RED}[-] nockchain-wallet 命令不可用,请先运行选项 4!${RESET}"
529 | pause_and_return
530 | return
531 | fi
532 | cd "$NCK_DIR" || { echo -e "${RED}[-] 无法进入 nockchain 目录!${RESET}"; pause_and_return; return; }
533 |
534 | # 检查 socket 文件
535 | SOCKET_PATH="$NCK_DIR/nockchain.sock"
536 | if [ ! -S "$SOCKET_PATH" ]; then
537 | echo -e "${RED}[-] socket 文件 $SOCKET_PATH 不存在,请确保 nockchain 节点正在运行(可尝试选项 7)!${RESET}"
538 | pause_and_return
539 | return
540 | fi
541 |
542 | # 获取公钥
543 | public_key=""
544 | if [ -f ".env" ]; then
545 | public_key=$(grep "^MINING_PUBKEY=" .env | cut -d'=' -f2)
546 | fi
547 |
548 | # 执行余额查询
549 | echo -e "[*] 正在查询余额..."
550 | echo -e "${YELLOW}[!] 查询所有 UTXOs...${RESET}"
551 | nockchain-wallet --nockchain-socket "$SOCKET_PATH" list-notes > balance_output.txt 2>&1
552 | if [ $? -eq 0 ]; then
553 | echo -e "${GREEN}[+] 所有 UTXOs 查询成功!${RESET}"
554 | echo -e "----------------------------------------"
555 | cat balance_output.txt
556 | echo -e "----------------------------------------"
557 | else
558 | echo -e "${RED}[-] 所有 UTXOs 查询失败,请检查 nockchain-wallet 命令或节点状态!${RESET}"
559 | echo -e "${YELLOW}[!] 详细信息见 $NCK_DIR/balance_output.txt${RESET}"
560 | fi
561 |
562 | if [ -n "$public_key" ]; then
563 | echo -e "${YELLOW}[!] 查询公钥 $public_key 的 UTXOs...${RESET}"
564 | nockchain-wallet --nockchain-socket "$SOCKET_PATH" list-notes-by-pubkey "$public_key" > balance_output_pubkey.txt 2>&1
565 | if [ $? -eq 0 ]; then
566 | echo -e "${GREEN}[+] 公钥 UTXOs 查询成功!${RESET}"
567 | echo -e "----------------------------------------"
568 | cat balance_output_pubkey.txt
569 | echo -e "----------------------------------------"
570 | else
571 | echo -e "${RED}[-] 公钥 UTXOs 查询失败,请检查公钥或节点状态!${RESET}"
572 | echo -e "${YELLOW}[!] 详细信息见 $NCK_DIR/balance_output_pubkey.txt${RESET}"
573 | fi
574 | else
575 | echo -e "${YELLOW}[!] 未找到 MINING_PUBKEY,无法查询特定公钥的 UTXOs!${RESET}"
576 | fi
577 | pause_and_return
578 | }
579 |
580 | # ========= 主菜单 =========
581 | function main_menu() {
582 | show_banner
583 | echo "请选择操作:"
584 | echo " 1) 安装系统依赖"
585 | echo " 2) 安装 Rust"
586 | echo " 3) 设置仓库"
587 | echo " 4) 编译项目和配置环境变量"
588 | echo " 5) 生成钱包"
589 | echo " 6) 设置挖矿公钥"
590 | echo " 7) 启动节点(Miner 或非 Miner)"
591 | echo " 8) 备份密钥"
592 | echo " 9) 查看节点日志"
593 | echo " 10) 查询余额"
594 | echo " 0) 退出"
595 | echo ""
596 | read -p "请输入编号: " choice
597 | case "$choice" in
598 | 1) install_dependencies ;;
599 | 2) install_rust ;;
600 | 3) setup_repository ;;
601 | 4) build_and_configure ;;
602 | 5) generate_wallet ;;
603 | 6) configure_mining_key ;;
604 | 7) start_node ;;
605 | 8) backup_keys ;;
606 | 9) view_logs ;;
607 | 10) check_balance ;;
608 | 0) echo -e "${GREEN}已退出。${RESET}"; exit 0 ;;
609 | *) echo -e "${RED}[-] 无效选项!${RESET}"; pause_and_return ;;
610 | esac
611 | }
612 |
613 | # ========= 启动主程序 =========
614 | main_menu
615 |
--------------------------------------------------------------------------------