#!/bin/bash # 快速UTXO归集脚本 # 简化版本,用于快速归集所有notes到指定地址 set -e # 默认配置 DEFAULT_FEE=1 KEY_FILE="key.txt" # 读取或创建主公钥 load_master_pubkey() { if [[ -f "$KEY_FILE" ]]; then DEFAULT_MASTER_PUBKEY=$(cat "$KEY_FILE" | tr -d '\n\r' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') if [[ -z "$DEFAULT_MASTER_PUBKEY" ]]; then print_error "key.txt 文件为空,请重新输入主公钥" ask_and_save_master_pubkey else print_info "从 $KEY_FILE 读取主公钥: $DEFAULT_MASTER_PUBKEY" fi else print_info "未找到 $KEY_FILE 文件" ask_and_save_master_pubkey fi } ask_and_save_master_pubkey() { echo -n "请输入主公钥地址: " read -r DEFAULT_MASTER_PUBKEY DEFAULT_MASTER_PUBKEY=$(echo "$DEFAULT_MASTER_PUBKEY" | tr -d '\n\r' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') if [[ -z "$DEFAULT_MASTER_PUBKEY" ]]; then print_error "主公钥不能为空" exit 1 fi echo "$DEFAULT_MASTER_PUBKEY" > "$KEY_FILE" print_success "主公钥已保存到 $KEY_FILE" } # 颜色输出 GREEN='\033[0;32m' BLUE='\033[0;34m' RED='\033[0;31m' NC='\033[0m' print_info() { echo -e "${BLUE}[INFO]${NC} $1" } print_success() { echo -e "${GREEN}[SUCCESS]${NC} $1" } print_error() { echo -e "${RED}[ERROR]${NC} $1" } # 显示帮助 show_help() { echo "快速UTXO归集脚本" echo "" echo "用法: $0 [选项]" echo "" echo "选项:" echo " -m, --master-pubkey PUBKEY 主公钥地址 (默认: $DEFAULT_MASTER_PUBKEY)" echo " -r, --recipient PUBKEY 接收地址 (默认: 使用主公钥)" echo " -f, --fee FEE 手续费 (默认: $DEFAULT_FEE nicks)" echo " -h, --help 显示帮助" echo "" echo "示例:" echo " $0 # 归集所有notes到主公钥地址" echo " $0 --fee 2 # 使用2 nicks手续费" echo " $0 --recipient PUBKEY # 归集到指定地址" } # 解析参数 MASTER_PUBKEY="$DEFAULT_MASTER_PUBKEY" RECIPIENT="" FEE="$DEFAULT_FEE" while [[ $# -gt 0 ]]; do case $1 in -m|--master-pubkey) MASTER_PUBKEY="$2" shift 2 ;; -r|--recipient) RECIPIENT="$2" shift 2 ;; -f|--fee) FEE="$2" shift 2 ;; -h|--help) show_help exit 0 ;; *) echo "未知参数: $1" show_help exit 1 ;; esac done # 加载主公钥 load_master_pubkey if [[ -z "$RECIPIENT" ]]; then RECIPIENT="$DEFAULT_MASTER_PUBKEY" fi print_info "开始快速归集流程..." # 2. 获取所有notes print_info "获取notes列表..." NOTES_OUTPUT=$(nockchain-wallet list-notes 2>&1 | strings | sed 's/\x1b\[[0-9;]*m//g') # 调试:显示原始输出格式 print_info "原始notes输出格式:" echo "$NOTES_OUTPUT" | head -20 # 3. 解析notes并构建参数 NAMES="" RECIPIENTS="" GIFTS="" TOTAL_BALANCE=0 NOTE_COUNT=0 # 提取Name和Assets信息 # 先处理跨行的note名称,将换行符替换为空格 CLEANED_OUTPUT=$(echo "$NOTES_OUTPUT" | tr '\n' ' ' | sed 's/\[/\n\[/g' | sed 's/\]/\]\n/g' | grep '^\[' | sed 's/^\[\(.*\)\]$/\1/' | grep -E '^[A-Za-z0-9 ]{20,}$') # 重新构建完整的note名称 NAMES_LINES="" while IFS= read -r line; do if [[ -n "$line" ]]; then # 将空格分隔的两部分重新组合成方括号格式 parts=($line) if [[ ${#parts[@]} -eq 2 ]]; then full_name="[${parts[0]} ${parts[1]}]" NAMES_LINES+="$full_name"$'\n' fi fi done <<< "$CLEANED_OUTPUT" ASSETS_LINES=$(echo "$NOTES_OUTPUT" | grep "Assets:" | sed 's/.*Assets: \([0-9]*\).*/\1/') print_info "提取的NAMES_LINES:" echo "$NAMES_LINES" | head -10 print_info "提取的ASSETS_LINES:" echo "$ASSETS_LINES" | head -10 # 将Name和Assets转换为数组(使用 mapfile 避免 set -e 下的 read -d 错误) # 过滤掉空行 mapfile -t NAMES_ARRAY < <(printf '%s\n' "$NAMES_LINES" | grep -v '^$') mapfile -t ASSETS_ARRAY < <(printf '%s\n' "$ASSETS_LINES" | grep -v '^$') if [[ ${#NAMES_ARRAY[@]} -eq 0 || ${#ASSETS_ARRAY[@]} -eq 0 ]]; then print_info "没有找到可归集的notes" exit 0 fi # 确保Name和Assets数组长度一致;不一致则截断至最短并给出警告 if [[ ${#NAMES_ARRAY[@]} -ne ${#ASSETS_ARRAY[@]} ]]; then min_len=${#NAMES_ARRAY[@]} if [[ ${#ASSETS_ARRAY[@]} -lt $min_len ]]; then min_len=${#ASSETS_ARRAY[@]} fi print_info "Name与Assets数量不一致,按最短长度 $min_len 截断继续" NAMES_ARRAY=("${NAMES_ARRAY[@]:0:$min_len}") ASSETS_ARRAY=("${ASSETS_ARRAY[@]:0:$min_len}") fi # 构建参数 for i in "${!NAMES_ARRAY[@]}"; do note_name="${NAMES_ARRAY[$i]}" balance="${ASSETS_ARRAY[$i]}" # 验证note_name和balance不为空 if [[ -z "$note_name" || -z "$balance" ]]; then print_error "发现空的note名称或余额,跳过: name='$note_name', balance='$balance'" continue fi # 验证balance是数字 if ! [[ "$balance" =~ ^[0-9]+$ ]]; then print_error "发现无效的余额格式,跳过: '$balance'" continue fi if [[ -n "$NAMES" ]]; then NAMES+="," RECIPIENTS+="," GIFTS+="," fi NAMES+="$note_name" RECIPIENTS+="[1 $RECIPIENT]" # 如果是最后一个note,需要扣除手续费 if [[ $i -eq $((${#NAMES_ARRAY[@]} - 1)) ]]; then if [[ $balance -lt $FEE ]]; then print_error "最后一个note '$note_name' 的余额 ($balance) 不足以支付手续费 ($FEE)" exit 1 fi # 从最后一个note扣除手续费 adjusted_balance=$((balance - FEE)) GIFTS+="$adjusted_balance" print_info "Note: $note_name, 余额: $balance nicks, 扣除手续费后: $adjusted_balance nicks" else GIFTS+="$balance" print_info "Note: $note_name, 余额: $balance nicks" fi TOTAL_BALANCE=$((TOTAL_BALANCE + balance)) NOTE_COUNT=$((NOTE_COUNT + 1)) done if [[ $NOTE_COUNT -eq 0 ]]; then print_info "没有找到可归集的notes" exit 0 fi print_info "找到 $NOTE_COUNT 个notes,总余额: $TOTAL_BALANCE nicks" # 验证参数长度一致性 NAMES_COUNT=$(echo "$NAMES" | tr ',' '\n' | grep -v '^$' | wc -l) RECIPIENTS_COUNT=$(echo "$RECIPIENTS" | tr ',' '\n' | grep -v '^$' | wc -l) GIFTS_COUNT=$(echo "$GIFTS" | tr ',' '\n' | grep -v '^$' | wc -l) print_info "调试信息:" print_info "NAMES 长度: $NAMES_COUNT" print_info "RECIPIENTS 长度: $RECIPIENTS_COUNT" print_info "GIFTS 长度: $GIFTS_COUNT" print_info "NAMES 内容: $NAMES" print_info "RECIPIENTS 内容: $RECIPIENTS" print_info "GIFTS 内容: $GIFTS" # 检查参数长度是否一致 if [[ $NAMES_COUNT -ne $RECIPIENTS_COUNT || $NAMES_COUNT -ne $GIFTS_COUNT || $RECIPIENTS_COUNT -ne $GIFTS_COUNT ]]; then print_error "参数长度不一致!" print_error "NAMES: $NAMES" print_error "RECIPIENTS: $RECIPIENTS" print_error "GIFTS: $GIFTS" exit 1 fi # 4. 创建归集交易 print_info "创建归集交易..." print_info "归集到地址: $RECIPIENT" print_info "手续费: $FEE nicks" nock_cmd="nockchain-wallet create-tx --names \"$NAMES\" --recipients \"$RECIPIENTS\" --gifts \"$GIFTS\" --fee \"$FEE\"" print_info "即将执行命令(可复制粘贴手动运行):" echo "$nock_cmd" # 使用 eval 来正确执行包含引号的命令 eval "$nock_cmd" print_success "归集完成!归集了 $NOTE_COUNT 个notes,总金额: $TOTAL_BALANCE nicks"