| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- #!/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"
|