quick_consolidate.sh 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. #!/bin/bash
  2. # 快速UTXO归集脚本
  3. # 简化版本,用于快速归集所有notes到指定地址
  4. set -e
  5. # 默认配置
  6. DEFAULT_FEE=1
  7. KEY_FILE="key.txt"
  8. # 读取或创建主公钥
  9. load_master_pubkey() {
  10. if [[ -f "$KEY_FILE" ]]; then
  11. DEFAULT_MASTER_PUBKEY=$(cat "$KEY_FILE" | tr -d '\n\r' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
  12. if [[ -z "$DEFAULT_MASTER_PUBKEY" ]]; then
  13. print_error "key.txt 文件为空,请重新输入主公钥"
  14. ask_and_save_master_pubkey
  15. else
  16. print_info "从 $KEY_FILE 读取主公钥: $DEFAULT_MASTER_PUBKEY"
  17. fi
  18. else
  19. print_info "未找到 $KEY_FILE 文件"
  20. ask_and_save_master_pubkey
  21. fi
  22. }
  23. ask_and_save_master_pubkey() {
  24. echo -n "请输入主公钥地址: "
  25. read -r DEFAULT_MASTER_PUBKEY
  26. DEFAULT_MASTER_PUBKEY=$(echo "$DEFAULT_MASTER_PUBKEY" | tr -d '\n\r' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
  27. if [[ -z "$DEFAULT_MASTER_PUBKEY" ]]; then
  28. print_error "主公钥不能为空"
  29. exit 1
  30. fi
  31. echo "$DEFAULT_MASTER_PUBKEY" > "$KEY_FILE"
  32. print_success "主公钥已保存到 $KEY_FILE"
  33. }
  34. # 颜色输出
  35. GREEN='\033[0;32m'
  36. BLUE='\033[0;34m'
  37. RED='\033[0;31m'
  38. NC='\033[0m'
  39. print_info() {
  40. echo -e "${BLUE}[INFO]${NC} $1"
  41. }
  42. print_success() {
  43. echo -e "${GREEN}[SUCCESS]${NC} $1"
  44. }
  45. print_error() {
  46. echo -e "${RED}[ERROR]${NC} $1"
  47. }
  48. # 显示帮助
  49. show_help() {
  50. echo "快速UTXO归集脚本"
  51. echo ""
  52. echo "用法: $0 [选项]"
  53. echo ""
  54. echo "选项:"
  55. echo " -m, --master-pubkey PUBKEY 主公钥地址 (默认: $DEFAULT_MASTER_PUBKEY)"
  56. echo " -r, --recipient PUBKEY 接收地址 (默认: 使用主公钥)"
  57. echo " -f, --fee FEE 手续费 (默认: $DEFAULT_FEE nicks)"
  58. echo " -h, --help 显示帮助"
  59. echo ""
  60. echo "示例:"
  61. echo " $0 # 归集所有notes到主公钥地址"
  62. echo " $0 --fee 2 # 使用2 nicks手续费"
  63. echo " $0 --recipient PUBKEY # 归集到指定地址"
  64. }
  65. # 解析参数
  66. MASTER_PUBKEY="$DEFAULT_MASTER_PUBKEY"
  67. RECIPIENT=""
  68. FEE="$DEFAULT_FEE"
  69. while [[ $# -gt 0 ]]; do
  70. case $1 in
  71. -m|--master-pubkey)
  72. MASTER_PUBKEY="$2"
  73. shift 2
  74. ;;
  75. -r|--recipient)
  76. RECIPIENT="$2"
  77. shift 2
  78. ;;
  79. -f|--fee)
  80. FEE="$2"
  81. shift 2
  82. ;;
  83. -h|--help)
  84. show_help
  85. exit 0
  86. ;;
  87. *)
  88. echo "未知参数: $1"
  89. show_help
  90. exit 1
  91. ;;
  92. esac
  93. done
  94. # 加载主公钥
  95. load_master_pubkey
  96. if [[ -z "$RECIPIENT" ]]; then
  97. RECIPIENT="$DEFAULT_MASTER_PUBKEY"
  98. fi
  99. print_info "开始快速归集流程..."
  100. # 2. 获取所有notes
  101. print_info "获取notes列表..."
  102. NOTES_OUTPUT=$(nockchain-wallet list-notes 2>&1 | strings | sed 's/\x1b\[[0-9;]*m//g')
  103. # 调试:显示原始输出格式
  104. print_info "原始notes输出格式:"
  105. echo "$NOTES_OUTPUT" | head -20
  106. # 3. 解析notes并构建参数
  107. NAMES=""
  108. RECIPIENTS=""
  109. GIFTS=""
  110. TOTAL_BALANCE=0
  111. NOTE_COUNT=0
  112. # 提取Name和Assets信息
  113. # 先处理跨行的note名称,将换行符替换为空格
  114. 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,}$')
  115. # 重新构建完整的note名称
  116. NAMES_LINES=""
  117. while IFS= read -r line; do
  118. if [[ -n "$line" ]]; then
  119. # 将空格分隔的两部分重新组合成方括号格式
  120. parts=($line)
  121. if [[ ${#parts[@]} -eq 2 ]]; then
  122. full_name="[${parts[0]} ${parts[1]}]"
  123. NAMES_LINES+="$full_name"$'\n'
  124. fi
  125. fi
  126. done <<< "$CLEANED_OUTPUT"
  127. ASSETS_LINES=$(echo "$NOTES_OUTPUT" | grep "Assets:" | sed 's/.*Assets: \([0-9]*\).*/\1/')
  128. print_info "提取的NAMES_LINES:"
  129. echo "$NAMES_LINES" | head -10
  130. print_info "提取的ASSETS_LINES:"
  131. echo "$ASSETS_LINES" | head -10
  132. # 将Name和Assets转换为数组(使用 mapfile 避免 set -e 下的 read -d 错误)
  133. # 过滤掉空行
  134. mapfile -t NAMES_ARRAY < <(printf '%s\n' "$NAMES_LINES" | grep -v '^$')
  135. mapfile -t ASSETS_ARRAY < <(printf '%s\n' "$ASSETS_LINES" | grep -v '^$')
  136. if [[ ${#NAMES_ARRAY[@]} -eq 0 || ${#ASSETS_ARRAY[@]} -eq 0 ]]; then
  137. print_info "没有找到可归集的notes"
  138. exit 0
  139. fi
  140. # 确保Name和Assets数组长度一致;不一致则截断至最短并给出警告
  141. if [[ ${#NAMES_ARRAY[@]} -ne ${#ASSETS_ARRAY[@]} ]]; then
  142. min_len=${#NAMES_ARRAY[@]}
  143. if [[ ${#ASSETS_ARRAY[@]} -lt $min_len ]]; then
  144. min_len=${#ASSETS_ARRAY[@]}
  145. fi
  146. print_info "Name与Assets数量不一致,按最短长度 $min_len 截断继续"
  147. NAMES_ARRAY=("${NAMES_ARRAY[@]:0:$min_len}")
  148. ASSETS_ARRAY=("${ASSETS_ARRAY[@]:0:$min_len}")
  149. fi
  150. # 构建参数
  151. for i in "${!NAMES_ARRAY[@]}"; do
  152. note_name="${NAMES_ARRAY[$i]}"
  153. balance="${ASSETS_ARRAY[$i]}"
  154. # 验证note_name和balance不为空
  155. if [[ -z "$note_name" || -z "$balance" ]]; then
  156. print_error "发现空的note名称或余额,跳过: name='$note_name', balance='$balance'"
  157. continue
  158. fi
  159. # 验证balance是数字
  160. if ! [[ "$balance" =~ ^[0-9]+$ ]]; then
  161. print_error "发现无效的余额格式,跳过: '$balance'"
  162. continue
  163. fi
  164. if [[ -n "$NAMES" ]]; then
  165. NAMES+=","
  166. RECIPIENTS+=","
  167. GIFTS+=","
  168. fi
  169. NAMES+="$note_name"
  170. RECIPIENTS+="[1 $RECIPIENT]"
  171. # 如果是最后一个note,需要扣除手续费
  172. if [[ $i -eq $((${#NAMES_ARRAY[@]} - 1)) ]]; then
  173. if [[ $balance -lt $FEE ]]; then
  174. print_error "最后一个note '$note_name' 的余额 ($balance) 不足以支付手续费 ($FEE)"
  175. exit 1
  176. fi
  177. # 从最后一个note扣除手续费
  178. adjusted_balance=$((balance - FEE))
  179. GIFTS+="$adjusted_balance"
  180. print_info "Note: $note_name, 余额: $balance nicks, 扣除手续费后: $adjusted_balance nicks"
  181. else
  182. GIFTS+="$balance"
  183. print_info "Note: $note_name, 余额: $balance nicks"
  184. fi
  185. TOTAL_BALANCE=$((TOTAL_BALANCE + balance))
  186. NOTE_COUNT=$((NOTE_COUNT + 1))
  187. done
  188. if [[ $NOTE_COUNT -eq 0 ]]; then
  189. print_info "没有找到可归集的notes"
  190. exit 0
  191. fi
  192. print_info "找到 $NOTE_COUNT 个notes,总余额: $TOTAL_BALANCE nicks"
  193. # 验证参数长度一致性
  194. NAMES_COUNT=$(echo "$NAMES" | tr ',' '\n' | grep -v '^$' | wc -l)
  195. RECIPIENTS_COUNT=$(echo "$RECIPIENTS" | tr ',' '\n' | grep -v '^$' | wc -l)
  196. GIFTS_COUNT=$(echo "$GIFTS" | tr ',' '\n' | grep -v '^$' | wc -l)
  197. print_info "调试信息:"
  198. print_info "NAMES 长度: $NAMES_COUNT"
  199. print_info "RECIPIENTS 长度: $RECIPIENTS_COUNT"
  200. print_info "GIFTS 长度: $GIFTS_COUNT"
  201. print_info "NAMES 内容: $NAMES"
  202. print_info "RECIPIENTS 内容: $RECIPIENTS"
  203. print_info "GIFTS 内容: $GIFTS"
  204. # 检查参数长度是否一致
  205. if [[ $NAMES_COUNT -ne $RECIPIENTS_COUNT || $NAMES_COUNT -ne $GIFTS_COUNT || $RECIPIENTS_COUNT -ne $GIFTS_COUNT ]]; then
  206. print_error "参数长度不一致!"
  207. print_error "NAMES: $NAMES"
  208. print_error "RECIPIENTS: $RECIPIENTS"
  209. print_error "GIFTS: $GIFTS"
  210. exit 1
  211. fi
  212. # 4. 创建归集交易
  213. print_info "创建归集交易..."
  214. print_info "归集到地址: $RECIPIENT"
  215. print_info "手续费: $FEE nicks"
  216. nock_cmd="nockchain-wallet create-tx --names \"$NAMES\" --recipients \"$RECIPIENTS\" --gifts \"$GIFTS\" --fee \"$FEE\""
  217. print_info "即将执行命令(可复制粘贴手动运行):"
  218. echo "$nock_cmd"
  219. # 使用 eval 来正确执行包含引号的命令
  220. eval "$nock_cmd"
  221. print_success "归集完成!归集了 $NOTE_COUNT 个notes,总金额: $TOTAL_BALANCE nicks"