Decentralization? We're still early!

Cardano 测试网 NFT 铸造入门教程

  • Cardano 测试网 NFT 铸造入门教程

    發布人 Brave 2025-10-10 02:48

    目录

    Cardano 是一个第三代区块链平台,以其学术严谨性和可持续性著称。与以太坊不同,Cardano 使用原生代币标准,这意味着 NFT 在协议层面得到支持,而不是通过智能合约实现。这种设计带来了更低的费用、更高的安全性和更好的性能。

    核心概念解释

    策略ID(Policy ID):每个 NFT 系列都有唯一的策略ID,相当于 NFT 的"身份证"。策略ID 由策略脚本的哈希值生成,确保了 NFT 的唯一性和不可伪造性。

    策略脚本(Policy Script):定义了 NFT 铸造规则的脚本,包括谁可以铸造、什么时候可以铸造等条件。一旦部署,这些规则就不可更改。

    时间锁定(Time Lock):在策略脚本中设置的时间限制,过期后该策略就永远无法再用于铸造新的代币,保证了 NFT 的稀缺性。

    资产名称(Asset Name):在同一策略下,每个代币的唯一标识符。完整的代币标识为"策略ID.资产名称"。

    环境准备:搭建 Cardano 节点

    为什么需要运行节点?

    要与 Cardano 网络交互,我们需要一个全节点来:

    • 同步区块链数据
    • 验证交易
    • 提交交易到网络
    • 查询链上状态

    我们使用测试网(Preprod)进行学习,因为:

    • 测试币免费获取
    • 可以安全地进行实验
    • 与主网功能完全相同
    • 不会产生真实费用

    1. Docker Compose 配置详解

    创建 docker-compose.yml 文件:

    version: '3.8'
    
    services:
      # Cardano 节点服务
      cardano-node:
        image: ghcr.io/blinklabs-io/cardano-node:latest
        container_name: cardano-node
        restart: unless-stopped
        ports:
          - "3001:3001"  # 节点 P2P 通信端口
        environment:
          - NETWORK=preprod  # 指定测试网络
        volumes:
          - node-data:/data/db    # 区块链数据存储
          - node-ipc:/ipc         # 进程间通信套接字
        init: true  # 启用 init 进程管理
    
      # Cardano CLI 工具容器
      cardano-cli:
        image: ghcr.io/blinklabs-io/cardano-node:latest
        container_name: cardano-cli
        restart: unless-stopped
        environment:
          - NETWORK=preprod
        volumes:
          - node-ipc:/ipc  # 共享通信套接字
        depends_on:
          - cardano-node   # 依赖节点服务
        command: ["sleep", "infinity"]  # 保持容器运行
    
    volumes:
      node-data:  # 持久化区块链数据
      node-ipc:   # 节点间通信

    配置说明

    • init: true:启用轻量级 init 进程,正确处理信号和僵尸进程
    • ports: 3001:3001:映射节点 P2P 通信端口
    • NETWORK=preprod:指定使用 Preprod 测试网
    • 数据卷node-data 存储区块链数据,node-ipc 用于 CLI 与节点通信
    • depends_on:确保 CLI 容器在节点容器之后启动

    2. 启动和验证服务

    # 启动所有服务
    docker-compose up -d
    
    # 查看服务状态
    docker-compose ps
    
    # 查看节点日志(观察同步进度)
    docker-compose logs -f cardano-node
    
    # 查看最近100行日志
    docker-compose logs --tail=100 cardano-node

    日志解读

    • 看到 "Chain extended" 表示正在同步区块
    • "TraceNodeIsLeader" 表示节点正常参与网络
    • 同步过程可能需要几小时,取决于网络速度

    3. 进入 CLI 工作环境

    # 进入 CLI 容器
    docker exec -ti cardano-cli bash
    
    # 或者使用 Dockge 的 Web 终端(如果使用 Dockge)

    第一步:环境配置和验证

    设置关键环境变量

    # 设置节点套接字路径(必须)
    export CARDANO_NODE_SOCKET_PATH=/ipc/node.socket
    
    # 验证套接字文件存在
    ls -la /ipc/node.socket
    
    # 创建工作目录
    mkdir -p /keys
    cd /keys
    
    # 验证 CLI 工具版本
    cardano-cli --version

    验证节点同步状态

    # 查询节点状态
    cardano-cli query tip --testnet-magic 1

    期望输出示例

    {
        "block": 3992119,
        "epoch": 245,
        "era": "Conway",
        "hash": "25ffab7df708fedb8d14b6bc2998907afa1735445ba132f03cb1f1f6716d57b2",
        "slot": 104340690,
        "slotInEpoch": 142290,
        "slotsToEpochEnd": 289710,
        "syncProgress": "100.00"
    }

    重要指标解释

    • syncProgress: "100.00":必须达到100%才能进行交易
    • era: "Conway":当前网络时代,支持最新功能
    • slot:当前时隙,用于时间锁定计算
    • epoch:当前纪元,每个纪元约5天

    ⚠️ 警告:只有当 syncProgress 显示 "100.00" 时才能继续后续操作!

    第二步:钱包生成和管理

    理解 Cardano 地址体系

    Cardano 使用 Bech32 编码的地址格式:

    • 测试网地址:以 addr_test1 开头
    • 主网地址:以 addr1 开头
    • 地址类型:支付地址、质押地址、脚本地址等

    生成密钥对

    # 生成支付密钥对
    cardano-cli address key-gen \\
        --verification-key-file /keys/payment.vkey \\
        --signing-key-file /keys/payment.skey
    
    # 查看生成的文件
    ls -la /keys/payment.*
    
    # 查看公钥内容(可以安全分享)
    cat /keys/payment.vkey

    文件说明

    • payment.vkey:验证密钥(公钥),用于生成地址,可以公开
    • payment.skey:签名密钥(私钥),用于签名交易,必须保密

    生成钱包地址

    # 生成基础支付地址
    cardano-cli address build \\
        --payment-verification-key-file /keys/payment.vkey \\
        --out-file /keys/payment.addr \\
        --testnet-magic 1
    
    # 保存地址到变量
    ADDRESS=$(cat /keys/payment.addr)
    
    # 显示地址信息
    echo "🏦 你的测试网钱包地址:"
    echo "$ADDRESS"
    echo ""
    echo "📝 地址长度: $(echo -n $ADDRESS | wc -c) 字符"
    echo "🌐 网络类型: Preprod 测试网"

    地址示例

    addr_test1vr35z3scn27mertcrm7qmtdec9373924jtt8f24pe9544dcjk0wsx

    可选:生成质押地址(高级功能)

    # 生成质押密钥对
    cardano-cli stake-address key-gen \\
        --verification-key-file /keys/stake.vkey \\
        --signing-key-file /keys/stake.skey
    
    # 生成包含质押功能的地址
    cardano-cli address build \\
        --payment-verification-key-file /keys/payment.vkey \\
        --stake-verification-key-file /keys/stake.vkey \\
        --out-file /keys/payment_with_stake.addr \\
        --testnet-magic 1
    
    echo "💎 带质押功能的地址:"
    cat /keys/payment_with_stake.addr

    第三步:获取测试币

    理解 Cardano 代币单位

    • ADA:Cardano 的主要代币
    • Lovelace:ADA 的最小单位(1 ADA = 1,000,000 Lovelace)
    • 测试网 ADA:没有真实价值,仅用于测试

    使用官方水龙头

    1. 复制钱包地址
    # 显示地址以便复制
    echo "请复制以下地址:"
    echo "$ADDRESS"
    1. 访问水龙头网站
    2. 申请流程
      • 粘贴你的地址
      • 选择 "Preprod" 网络
      • 点击申请按钮
      • 等待确认信息

    验证资金到账

    # 检查余额(可能需要等待几分钟)
    cardano-cli query utxo --address $ADDRESS --testnet-magic 1
    
    # 如果没有余额,等待一会儿再查询
    sleep 30
    cardano-cli query utxo --address $ADDRESS --testnet-magic 1

    成功输出示例

    {
        "95504c30d402ae2244407a7d7edd1711d88000a20b2101b34c0e80fbf8e5ff4e#0": {
            "address": "addr_test1vr35z3scn27mertcrm7qmtdec9373924jtt8f24pe9544dcjk0wsx",
            "datum": null,
            "datumhash": null,
            "inlineDatum": null,
            "inlineDatumRaw": null,
            "referenceScript": null,
            "value": {
                "lovelace": 10000000000
            }
        }
    }

    输出解释

    • UTXO 标识TxHash#TxIx 格式,唯一标识每个未花费输出
    • lovelace: 10000000000:等于 10,000 ADA
    • datumdatumhash:智能合约相关数据,此处为空

    备用获取方式

    如果官方水龙头不可用:

    1. Discord 水龙头
    2. Telegram 机器人
      • 联系 @CardanoTestnetFaucetBot

    第四步:策略脚本创建

    理解策略脚本的作用

    策略脚本定义了代币铸造的规则:

    • 签名验证:只有特定密钥持有者可以铸造
    • 时间限制:设置铸造的有效时间窗口
    • 数量限制:可以设置最大铸造数量
    • 多重签名:可以要求多个签名

    生成策略密钥

    # 生成策略专用密钥对
    cardano-cli address key-gen \\
        --verification-key-file /keys/policy.vkey \\
        --signing-key-file /keys/policy.skey
    
    echo "✅ 策略密钥生成完成"
    
    # 验证密钥文件
    ls -la /keys/policy.*

    计算密钥哈希

    # 计算公钥哈希(用于策略脚本)
    cardano-cli address key-hash \\
        --payment-verification-key-file /keys/policy.vkey > /keys/policy.keyid
    
    # 显示密钥哈希
    POLICY_KEY_HASH=$(cat /keys/policy.keyid)
    echo "🔑 策略密钥哈希: $POLICY_KEY_HASH"

    设置时间锁定

    # 获取当前区块链状态
    CURRENT_SLOT=$(cardano-cli query tip --testnet-magic 1 | jq .slot)
    echo "⏰ 当前 Slot: $CURRENT_SLOT"
    
    # 计算过期时间(24小时后)
    # 1 slot = 1 秒,24小时 = 86400 秒
    EXPIRE_SLOT=$((CURRENT_SLOT + 86400))
    echo "⏰ 策略过期 Slot: $EXPIRE_SLOT"
    
    # 计算剩余时间
    REMAINING_SECONDS=$((EXPIRE_SLOT - CURRENT_SLOT))
    REMAINING_HOURS=$((REMAINING_SECONDS / 3600))
    echo "⏰ 策略有效期: $REMAINING_HOURS 小时"

    时间选择建议

    • NFT 收藏品:1-7天(保证稀缺性)
    • 游戏道具:30-365天(根据游戏需求)
    • 测试用途:1-24小时(便于快速实验)

    创建策略脚本

    # 创建策略脚本文件
    cat > /keys/policy.script &lt;< EOF
    {
        "type": "all",
        "scripts": [
            {
                "type": "sig",
                "keyHash": "$(cat /keys/policy.keyid)"
            },
            {
                "type": "before",
                "slot": ${EXPIRE_SLOT}
            }
        ]
    }
    EOF
    
    echo "✅ 策略脚本创建完成"
    
    # 显示策略脚本内容
    echo "📜 策略脚本内容:"
    cat /keys/policy.script | jq '.'

    策略脚本解释

    • "type": "all":所有条件都必须满足
    • "type": "sig":需要指定密钥的签名
    • "type": "before":必须在指定 slot 之前执行
    • keyHash:授权签名的密钥哈希
    • slot:过期的绝对时间点

    验证策略脚本

    # 验证脚本格式
    if jq empty /keys/policy.script 2>/dev/null; then
        echo "✅ 策略脚本格式正确"
    else
        echo "❌ 策略脚本格式错误"
    fi
    
    # 检查文件权限
    ls -la /keys/policy.script

    第五步:策略ID生成

    理解策略ID的重要性

    策略ID是策略脚本的加密哈希值,具有以下特性:

    • 唯一性:每个不同的策略脚本都有唯一的策略ID
    • 不可伪造:无法创建具有相同策略ID的不同脚本
    • 永久性:策略ID一旦生成就永远不变

    生成策略ID

    # 从策略脚本生成策略ID
    cardano-cli conway transaction policyid \\
        --script-file /keys/policy.script > /keys/policy.id
    
    # 保存到变量
    POLICY_ID=$(cat /keys/policy.id)
    
    echo "🆔 你的策略ID:"
    echo "$POLICY_ID"
    echo ""
    echo "📏 策略ID长度: $(echo -n $POLICY_ID | wc -c) 字符"
    echo "🔒 策略ID类型: 56位十六进制哈希值"

    策略ID示例

    bfc0c4cef7bfdb90550769f9f10a2f14709312097a5b6560b001c8b8

    验证策略ID

    # 验证策略ID格式(应该是56位十六进制)
    if [[ $POLICY_ID =~ ^[a-f0-9]{56}$ ]]; then
        echo "✅ 策略ID格式正确"
    else
        echo "❌ 策略ID格式错误"
    fi
    
    # 重新生成验证(结果应该相同)
    POLICY_ID_CHECK=$(cardano-cli conway transaction policyid --script-file /keys/policy.script)
    if [ "$POLICY_ID" = "$POLICY_ID_CHECK" ]; then
        echo "✅ 策略ID验证通过"
    else
        echo "❌ 策略ID验证失败"
    fi

    第六步:NFT元数据创建

    理解 CIP-25 元数据标准

    Cardano 使用 CIP-25 标准定义 NFT 元数据:

    • 标准化格式:确保不同平台间的兼容性
    • 链上存储:元数据直接存储在交易中
    • 丰富信息:支持名称、描述、图片、属性等

    基础元数据结构

    # 设置NFT资产名称
    NFT_NAME="MyNFT"
    echo "🎨 NFT名称: $NFT_NAME"
    
    # 将名称转换为十六进制(避免特殊字符问题)
    NFT_NAME_HEX="4d794e4654"  # "MyNFT" 的十六进制编码
    echo "🔢 NFT名称(十六进制): $NFT_NAME_HEX"
    
    # 验证十六进制编码
    echo -n "$NFT_NAME" | xxd -p  # 如果xxd可用

    创建标准元数据

    # 创建符合CIP-25标准的元数据
    cat > /keys/metadata.json &lt;< EOF
    {
        "721": {
            "${POLICY_ID}": {
                "${NFT_NAME}": {
                    "name": "My First Cardano NFT",
                    "description": "This is my first NFT created on Cardano testnet for learning purposes.",
                    "image": "ipfs://QmYourImageHashHere",
                    "mediaType": "image/png",
                    "creator": "Cardano NFT Student",
                    "website": "https://cardano.org",
                    "attributes": {
                        "type": "Digital Art",
                        "rarity": "Common",
                        "color": "Blue",
                        "edition": "1/1",
                        "created": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
                    },
                    "files": [
                        {
                            "name": "My First NFT",
                            "mediaType": "image/png",
                            "src": "ipfs://QmYourImageHashHere"
                        }
                    ]
                }
            }
        }
    }
    EOF
    
    echo "✅ NFT元数据创建完成"

    添加版税信息(可选)

    # 创建包含版税的增强元数据
    cat > /keys/metadata_with_royalty.json &lt;< EOF
    {
        "721": {
            "${POLICY_ID}": {
                "${NFT_NAME}": {
                    "name": "My First Cardano NFT",
                    "description": "This is my first NFT created on Cardano testnet with royalty settings.",
                    "image": "ipfs://QmYourImageHashHere",
                    "mediaType": "image/png",
                    "creator": "Cardano NFT Student",
                    "website": "https://cardano.org",
                    "attributes": {
                        "type": "Digital Art",
                        "rarity": "Common",
                        "color": "Blue",
                        "edition": "1/1",
                        "created": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
                    },
                    "royalty": {
                        "rate": "0.05",
                        "addr": "$ADDRESS"
                    }
                }
            }
        },
        "777": {
            "royalty": {
                "${POLICY_ID}": {
                    "rate": "0.05",
                    "addr": "$ADDRESS"
                }
            }
        }
    }
    EOF
    
    # 选择使用哪个元数据文件
    cp /keys/metadata_with_royalty.json /keys/metadata.json
    echo "✅ 已启用5%版税设置"

    版税设置说明

    • rate: "0.05":5%版税率
    • addr:版税接收地址
    • 777 标签:CIP-27版税标准
    • 市场支持:需要NFT市场主动支持版税执行

    验证元数据格式

    # 验证JSON格式
    if jq empty /keys/metadata.json 2>/dev/null; then
        echo "✅ 元数据JSON格式正确"
        
        # 显示元数据摘要
        echo "📊 元数据摘要:"
        echo "  - NFT名称: $(jq -r ".\\"721\\".\\"$POLICY_ID\\".\\"$NFT_NAME\\".name" /keys/metadata.json)"
        echo "  - 描述: $(jq -r ".\\"721\\".\\"$POLICY_ID\\".\\"$NFT_NAME\\".description" /keys/metadata.json)"
        echo "  - 创建者: $(jq -r ".\\"721\\".\\"$POLICY_ID\\".\\"$NFT_NAME\\".creator" /keys/metadata.json)"
        
        # 检查版税设置
        if jq -e ".\\"777\\"" /keys/metadata.json > /dev/null 2>&1; then
            echo "  - 版税: $(jq -r ".\\"777\\".royalty.\\"$POLICY_ID\\".rate" /keys/metadata.json | awk '{print $1*100}')%"
        else
            echo "  - 版税: 未设置"
        fi
    else
        echo "❌ 元数据JSON格式错误"
        jq . /keys/metadata.json  # 显示错误详情
    fi

    第七步:构建铸造交易

    理解UTXO模型

    Cardano使用UTXO(未花费交易输出)模型:

    • UTXO:类似现金,要么完全花费,要么不花费
    • 找零机制:花费UTXO时,多余的金额作为找零返回
    • 原子性:交易要么完全成功,要么完全失败

    查询可用UTXO

    # 查询当前所有可用UTXO
    echo "🔍 查询可用UTXO..."
    cardano-cli query utxo --address $ADDRESS --testnet-magic 1
    
    # 保存查询结果用于分析
    cardano-cli query utxo --address $ADDRESS --testnet-magic 1 > /keys/utxos.json
    
    # 分析UTXO
    echo ""
    echo "📊 UTXO分析:"
    UTXO_COUNT=$(jq 'length' /keys/utxos.json)
    TOTAL_LOVELACE=$(jq '[.[].value.lovelace] | add' /keys/utxos.json)
    TOTAL_ADA=$((TOTAL_LOVELACE / 1000000))
    
    echo "  - UTXO数量: $UTXO_COUNT"
    echo "  - 总余额: $TOTAL_LOVELACE Lovelace ($TOTAL_ADA ADA)"

    选择合适的UTXO

    # 从查询结果中选择第一个UTXO
    UTXO=$(jq -r 'keys[0]' /keys/utxos.json)
    echo "🎯 选择的UTXO: $UTXO"
    
    # 验证UTXO格式
    if [[ $UTXO =~ ^[a-f0-9]{64}#[0-9]+$ ]]; then
        echo "✅ UTXO格式正确"
    else
        echo "❌ UTXO格式错误,请手动设置"
        echo "请从上面的查询结果中复制一个UTXO:"
        read -p "输入UTXO (格式: txhash#txix): " UTXO
    fi
    
    echo "🔗 使用UTXO: $UTXO"

    设置交易参数

    # 获取当前时间信息
    CURRENT_SLOT=$(cardano-cli query tip --testnet-magic 1 | jq .slot)
    echo "⏰ 当前Slot: $CURRENT_SLOT"
    
    # 设置交易有效期(1小时后)
    VALID_UNTIL=$((CURRENT_SLOT + 3600))
    echo "⏰ 交易有效到Slot: $VALID_UNTIL"
    
    # 计算剩余时间
    REMAINING_TIME=$((VALID_UNTIL - CURRENT_SLOT))
    echo "⏰ 交易有效期: $((REMAINING_TIME / 60))分钟"
    
    # 验证策略是否仍然有效
    POLICY_EXPIRE=$(jq -r '.scripts[1].slot' /keys/policy.script)
    if [ $CURRENT_SLOT -lt $POLICY_EXPIRE ]; then
        echo "✅ 策略仍然有效 (剩余 $(((POLICY_EXPIRE - CURRENT_SLOT) / 3600))小时)"
    else
        echo "❌ 策略已过期,需要重新创建"
        exit 1
    fi

    构建铸造交易

    echo "🔨 构建铸造交易..."
    
    # 构建交易(使用新版cardano-cli语法)
    cardano-cli conway transaction build \\
        --testnet-magic 1 \\
        --tx-in $UTXO \\
        --mint "1 ${POLICY_ID}.${NFT_NAME_HEX}" \\
        --mint-script-file /keys/policy.script \\
        --metadata-json-file /keys/metadata.json \\
        --change-address $ADDRESS \\
        --invalid-hereafter $VALID_UNTIL \\
        --out-file /keys/tx.unsigned
    
    # 检查构建结果
    if [ -f "/keys/tx.unsigned" ]; then
        echo "✅ 交易构建成功"
        
        # 显示交易信息
        echo ""
        echo "📋 交易摘要:"
        echo "  - 输入UTXO: $UTXO"
        echo "  - 铸造资产: 1 ${POLICY_ID}.${NFT_NAME_HEX}"
        echo "  - 找零地址: $ADDRESS"
        echo "  - 有效期至: Slot $VALID_UNTIL"
        
        # 显示文件大小
        TX_SIZE=$(wc -c &lt; /keys/tx.unsigned)
        echo \&quot;  - 交易大小: $TX_SIZE 字节\&quot;
    else
        echo \&quot;❌ 交易构建失败\&quot;
        exit 1
    fi

    构建参数说明

    • --tx-in:指定输入UTXO
    • --mint:指定要铸造的资产数量和名称
    • --mint-script-file:提供策略脚本文件
    • --metadata-json-file:附加元数据
    • --change-address:指定找零地址
    • --invalid-hereafter:设置交易过期时间

    交易费用估算

    # 从构建输出中提取费用信息
    if grep -q \&quot;Estimated transaction fee:\&quot; /dev/stdout; then
        echo \&quot;💰 交易费用已在构建过程中显示\&quot;
    else
        echo \&quot;💰 交易费用: 约184,000 Lovelace (0.184 ADA)\&quot;
    fi
    
    echo \&quot;\&quot;
    echo \&quot;💡 费用说明:\&quot;
    echo \&quot;  - 基础费用: ~155,000 Lovelace\&quot;
    echo \&quot;  - 元数据费用: ~20,000 Lovelace\&quot;
    echo \&quot;  - 脚本执行费: ~10,000 Lovelace\&quot;
    echo \&quot;  - 总计: ~185,000 Lovelace\&quot;

    第八步:签名和提交交易

    理解数字签名

    在Cardano中,交易签名确保:

    • 身份验证:证明交易由密钥持有者发起
    • 完整性:确保交易内容未被篡改
    • 不可否认性:签名者无法否认已签名的交易

    签名交易

    echo \&quot;✍️ 签名交易...\&quot;
    
    # 使用两个密钥签名:支付密钥和策略密钥
    cardano-cli conway transaction sign \\
        --signing-key-file /keys/payment.skey \\
        --signing-key-file /keys/policy.skey \\
        --testnet-magic 1 \\
        --tx-body-file /keys/tx.unsigned \\
        --out-file /keys/tx.signed
    
    # 验证签名结果
    if [ -f \&quot;/keys/tx.signed\&quot; ]; then
        echo \&quot;✅ 交易签名成功\&quot;
        
        # 比较文件大小
        UNSIGNED_SIZE=$(wc -c &lt; /keys/tx.unsigned)
        SIGNED_SIZE=$(wc -c < /keys/tx.signed)
        echo "  - 未签名交易: $UNSIGNED_SIZE 字节"
        echo "  - 已签名交易: $SIGNED_SIZE 字节"
        echo "  - 签名增加: $((SIGNED_SIZE - UNSIGNED_SIZE)) 字节"
    else
        echo "❌ 交易签名失败"
        exit 1
    fi

    签名过程说明

    • 支付密钥签名:证明有权花费UTXO
    • 策略密钥签名:证明有权铸造NFT
    • 网络标识--testnet-magic 1确保签名只在测试网有效

    最终验证

    echo "🔍 最终验证..."
    
    # 检查所有必要文件
    echo "📁 文件检查:"
    for file in payment.addr payment.skey policy.skey policy.script metadata.json tx.signed; do
        if [ -f "/keys/$file" ]; then
            echo "  ✅ $file"
        else
            echo "  ❌ $file (缺失)"
        fi
    done
    
    # 检查环境变量
    echo ""
    echo "🔧 环境检查:"
    echo "  - 节点套接字: $CARDANO_NODE_SOCKET_PATH"
    echo "  - 钱包地址: $ADDRESS"
    echo "  - 策略ID: $POLICY_ID"
    echo "  - 使用UTXO: $UTXO"
    
    # 最后的节点连接测试
    echo ""
    echo "🌐 节点连接测试:"
    if cardano-cli query tip --testnet-magic 1 > /dev/null 2>&1; then
        echo "  ✅ 节点连接正常"
    else
        echo "  ❌ 节点连接失败"
        exit 1
    fi

    提交交易

    echo ""
    echo "🚀 提交交易到Cardano网络..."
    echo "⏳ 请等待..."
    
    # 提交已签名的交易
    cardano-cli conway transaction submit \\
        --testnet-magic 1 \\
        --tx-file /keys/tx.signed
    
    # 检查提交结果
    if [ $? -eq 0 ]; then
        echo ""
        echo "🎉🎉🎉 交易提交成功!🎉🎉🎉"
        echo ""
        echo "🎊 恭喜!你已经成功铸造了你的第一个Cardano NFT!"
    else
        echo ""
        echo "❌ 交易提交失败"
        echo "请检查错误信息并重试"
        exit 1
    fi

    获取交易哈希

    # 计算交易哈希
    TX_HASH=$(cardano-cli hash tx --tx-file /keys/tx.signed)
    echo "🔗 交易哈希: $TX_HASH"
    
    # 保存交易哈希
    echo "$TX_HASH" > /keys/tx.hash
    echo "💾 交易哈希已保存到 /keys/tx.hash"
    
    # 生成交易摘要
    echo ""
    echo "📋 交易摘要报告:"
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
    echo "🆔 策略ID: $POLICY_ID"
    echo "🎨 NFT名称: $NFT_NAME ($NFT_NAME_HEX)"
    echo "🔗 完整资产ID: ${POLICY_ID}.${NFT_NAME_HEX}"
    echo "📧 钱包地址: $ADDRESS"
    echo "🔗 交易哈希: $TX_HASH"
    echo "⏰ 提交时间: $(date)"
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

    第九步:验证和确认结果

    等待交易确认

    echo ""
    echo "⏳ 等待交易确认..."
    echo "💡 Cardano出块时间约20秒,确认通常需要1-3分钟"
    
    # 等待一段时间让交易被包含到区块中
    for i in {1..6}; do
        echo "⏳ 等待中... ($i/6)"
        sleep 30
        
        # 每次等待后检查一次
        echo "🔍 检查交易状态..."
        cardano-cli query utxo --address $ADDRESS --testnet-magic 1 > /tmp/current_utxos.json
        
        # 检查是否包含新的NFT
        if jq -e ".[].value | has(\\"$POLICY_ID\\")" /tmp/current_utxos.json > /dev/null 2>&1; then
            echo "✅ 检测到NFT!交易已确认"
            break
        fi
        
        if [ $i -eq 6 ]; then
            echo "⏰ 等待时间较长,继续检查..."
        fi
    done

    验证NFT铸造结果

    echo ""
    echo "🔍 验证NFT铸造结果..."
    
    # 查询当前钱包状态
    cardano-cli query utxo --address $ADDRESS --testnet-magic 1 > /keys/final_utxos.json
    
    echo "💼 当前钱包状态:"
    cat /keys/final_utxos.json | jq '.'
    
    # 分析结果
    echo ""
    echo "📊 详细分析:"
    
    # 检查NFT是否存在
    if jq -e ".[].value | has(\\"$POLICY_ID\\")" /keys/final_utxos.json > /dev/null; then
        echo "✅ NFT铸造成功!"
        
        # 提取NFT信息
        NFT_AMOUNT=$(jq -r ".[].value.\\"$POLICY_ID\\".\\"$NFT_NAME_HEX\\"" /keys/final_utxos.json | grep -v null | head -1)
        echo "🎨 NFT数量: $NFT_AMOUNT"
        
        # 计算剩余ADA
        REMAINING_LOVELACE=$(jq '[.[].value.lovelace] | add' /keys/final_utxos.json)
        REMAINING_ADA=$((REMAINING_LOVELACE / 1000000))
        echo "💰 剩余ADA: $REMAINING_ADA ADA ($REMAINING_LOVELACE Lovelace)"
        
        # 计算费用
        ORIGINAL_ADA=10000  # 水龙头给的数量
        SPENT_ADA=$((ORIGINAL_ADA - REMAINING_ADA))
        echo "💸 交易费用: ~$SPENT_ADA ADA"
        
    else
        echo "❌ 未检测到NFT,请检查交易状态"
        echo "🔍 你可以通过交易哈希在区块链浏览器中查看详情:"
        echo "   https://preprod.cardanoscan.io/transaction/$TX_HASH"
    fi

    生成成功报告

    # 创建成功报告
    cat > /keys/nft_success_report.txt &lt;&lt; EOF
    🎉 Cardano NFT 铸造成功报告
    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
    
    📅 铸造时间: $(date)
    🌐 网络: Cardano Preprod 测试网
    
    🎨 NFT 详情:
      名称: $NFT_NAME
      完整资产ID: ${POLICY_ID}.${NFT_NAME_HEX}
      策略ID: $POLICY_ID
      资产名称(十六进制): $NFT_NAME_HEX
      数量: 1 (独一无二)
    
    🔗 技术信息:
      交易哈希: $TX_HASH
      钱包地址: $ADDRESS
      使用UTXO: $UTXO
    
    💰 费用信息:
      估算费用: ~0.18 ADA
      剩余余额: ~$REMAINING_ADA ADA
    
    🔍 验证链接:
      Cardano Scan: https://preprod.cardanoscan.io/transaction/$TX_HASH
      
    🎊 恭喜!你已经成功创建了你的第一个Cardano NFT!
    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
    EOF
    
    echo \&quot;\&quot;
    echo \&quot;📄 成功报告已生成: /keys/nft_success_report.txt\&quot;
    cat /keys/nft_success_report.txt

    保存重要文件

    echo \&quot;\&quot;
    echo \&quot;💾 保存重要文件...\&quot;
    
    # 创建备份目录
    mkdir -p /keys/backup
    
    # 备份关键文件
    cp /keys/payment.skey /keys/backup/
    cp /keys/payment.vkey /keys/backup/
    cp /keys/payment.addr /keys/backup/
    cp /keys/policy.skey /keys/backup/
    cp /keys/policy.vkey /keys/backup/
    cp /keys/policy.script /keys/backup/
    cp /keys/policy.id /keys/backup/
    cp /keys/metadata.json /keys/backup/
    cp /keys/tx.signed /keys/backup/
    cp /keys/tx.hash /keys/backup/
    cp /keys/nft_success_report.txt /keys/backup/
    
    echo \&quot;✅ 重要文件已备份到 /keys/backup/\&quot;
    
    # 显示文件清单
    echo \&quot;\&quot;
    echo \&quot;📁 文件清单:\&quot;
    ls -la /keys/backup/
    
    echo \&quot;\&quot;
    echo \&quot;⚠️  重要提醒:\&quot;
    echo \&quot;   - 请妥善保管私钥文件 (.skey)\&quot;
    echo \&quot;   - 私钥丢失将无法控制钱包和NFT\&quot;
    echo \&quot;   - 建议将备份文件下载到安全位置\&quot;

    进阶操作指南

    1. 转移NFT到其他地址

    # 准备转移NFT的函数
    transfer_nft() {
        local RECIPIENT_ADDRESS=\&quot;$1\&quot;
        
        if [ -z \&quot;$RECIPIENT_ADDRESS\&quot; ]; then
            echo \&quot;❌ 请提供接收地址\&quot;
            echo \&quot;用法: transfer_nft "
            return 1
        fi
        
        echo "📤 准备转移NFT到: $RECIPIENT_ADDRESS"
        
        # 查询当前UTXO
        cardano-cli query utxo --address $ADDRESS --testnet-magic 1 > /tmp/transfer_utxos.json
        
        # 选择包含NFT的UTXO
        NFT_UTXO=$(jq -r "to_entries | map(select(.value.value.\\"$POLICY_ID\\")) | .[0].key" /tmp/transfer_utxos.json)
        
        if [ "$NFT_UTXO" = "null" ]; then
            echo "❌ 未找到包含NFT的UTXO"
            return 1
        fi
        
        echo "🎯 使用UTXO: $NFT_UTXO"
        
        # 构建转移交易
        cardano-cli conway transaction build \\
            --testnet-magic 1 \\
            --tx-in $NFT_UTXO \\
            --tx-out "${RECIPIENT_ADDRESS}+2000000+1 ${POLICY_ID}.${NFT_NAME_HEX}" \\
            --change-address $ADDRESS \\
            --out-file /keys/transfer.unsigned
        
        # 签名交易
        cardano-cli conway transaction sign \\
            --signing-key-file /keys/payment.skey \\
            --testnet-magic 1 \\
            --tx-body-file /keys/transfer.unsigned \\
            --out-file /keys/transfer.signed
        
        # 提交交易
        cardano-cli conway transaction submit \\
            --testnet-magic 1 \\
            --tx-file /keys/transfer.signed
        
        echo "✅ NFT转移交易已提交"
    }
    
    echo ""
    echo "💡 NFT转移功能已准备就绪"
    echo "用法: transfer_nft "

    2. 查询策略下的所有NFT

    # 查询策略下所有代币的函数
    query_policy_tokens() {
        echo "🔍 查询策略 $POLICY_ID 下的所有代币..."
        
        # 在整个UTXO集中搜索该策略的代币
        cardano-cli query utxo --whole-utxo --testnet-magic 1 | \\
        jq --arg policy "$POLICY_ID" '
    | to_entries |
    | --- |
    | map(select(.value.value[$policy])) |
            map({
                utxo: .key,
                address: .value.address,
                tokens: .value.value[$policy]
            })
        '
    }
    
    echo ""
    echo "💡 策略代币查询功能已准备就绪"
    echo "用法: query_policy_tokens"

    3. 验证NFT真实性

    # NFT真实性验证函数
    verify_nft() {
        local ASSET_ID="$1"
        
        if [ -z "$ASSET_ID" ]; then
            ASSET_ID="${POLICY_ID}.${NFT_NAME_HEX}"
        fi
        
        echo "🔍 验证NFT真实性: $ASSET_ID"
        
        # 分离策略ID和资产名称
        local VERIFY_POLICY_ID="${ASSET_ID%.*}"
        local VERIFY_ASSET_NAME="${ASSET_ID##*.}"
        
        echo "  策略ID: $VERIFY_POLICY_ID"
        echo "  资产名称: $VERIFY_ASSET_NAME"
        
        # 检查策略脚本是否存在
        if [ -f "/keys/policy.script" ] && [ "$VERIFY_POLICY_ID" = "$POLICY_ID" ]; then
            echo "✅ 策略验证通过 - 这是你创建的NFT"
            
            # 验证策略ID是否匹配脚本
            COMPUTED_POLICY=$(cardano-cli conway transaction policyid --script-file /keys/policy.script)
            if [ "$COMPUTED_POLICY" = "$VERIFY_POLICY_ID" ]; then
                echo "✅ 策略脚本验证通过"
            else
                echo "❌ 策略脚本验证失败"
            fi
        else
            echo "⚠️  这不是你创建的NFT,或策略脚本不可用"
        fi
        
        # 查询该NFT的当前持有者
        echo ""
        echo "🔍 查询当前持有者..."
        cardano-cli query utxo --whole-utxo --testnet-magic 1 | \\
        jq --arg policy "$VERIFY_POLICY_ID" --arg asset "$VERIFY_ASSET_NAME" '
    | to_entries |
    | --- |
    | map(select(.value.value[$policy][$asset])) |
            map({
                holder: .value.address,
                amount: .value.value[$policy][$asset]
            })
        '
    }
    
    echo ""
    echo "💡 NFT验证功能已准备就绪"
    echo "用法: verify_nft [资产ID]"

    故障排除指南

    常见错误及解决方案

    1. "ScriptWitnessNotValidatingUTXOW" 错误

    原因分析

    • 策略时间锁定已过期
    • UTXO已被其他交易消费
    • 策略脚本签名问题

    解决步骤

    # 检查策略是否过期
    CURRENT_SLOT=$(cardano-cli query tip --testnet-magic 1 | jq .slot)
    POLICY_EXPIRE=$(jq -r '.scripts[1].slot' /keys/policy.script)
    
    if [ $CURRENT_SLOT -gt $POLICY_EXPIRE ]; then
        echo "❌ 策略已过期,需要重新创建"
        
        # 重新创建策略
        EXPIRE_SLOT=$((CURRENT_SLOT + 86400))
        
        # 更新策略脚本
        jq --arg slot "$EXPIRE_SLOT" '.scripts[1].slot = ($slot | tonumber)' /keys/policy.script > /keys/policy_new.script
        mv /keys/policy_new.script /keys/policy.script
        
        # 重新生成策略ID
        cardano-cli conway transaction policyid --script-file /keys/policy.script > /keys/policy.id
        POLICY_ID=$(cat /keys/policy.id)
        
        echo "✅ 策略已更新,新策略ID: $POLICY_ID"
    else
        echo "✅ 策略仍然有效"
    fi
    
    # 检查UTXO是否仍然可用
    cardano-cli query utxo --address $ADDRESS --testnet-magic 1

    2. "unexpected '.'" 语法错误

    原因:资产名称包含特殊字符

    解决方案

    # 使用十六进制编码
    echo -n "YourNFTName" | od -A n -t x1 | tr -d ' \\n'
    
    # 或使用预定义的十六进制值
    NFT_NAME_HEX="4d794e4654"  # "MyNFT"

    3. 节点同步问题

    症状:查询命令返回错误或过时数据

    解决方案

    # 检查节点状态
    cardano-cli query tip --testnet-magic 1
    
    # 如果同步进度不是100%,等待同步完成
    while true; do
        SYNC_PROGRESS=$(cardano-cli query tip --testnet-magic 1 | jq -r '.syncProgress')
        echo "同步进度: $SYNC_PROGRESS"
        
        if [ "$SYNC_PROGRESS" = "100.00" ]; then
            echo "✅ 节点同步完成"
            break
        fi
        
        sleep 30
    done

    4. 内存或磁盘空间不足

    检查资源使用

    # 检查磁盘空间
    df -h
    
    # 检查容器资源
    docker stats cardano-node cardano-cli
    
    # 清理不必要的文件
    docker system prune -f

    调试工具和技巧

    1. 交易详细分析

    # 分析未签名交易
    analyze_transaction() {
        local TX_FILE="$1"
        
        echo "🔍 分析交易文件: $TX_FILE"
        
        # 使用cardano-cli解析交易
        cardano-cli conway transaction view --tx-file "$TX_FILE"
        
        echo ""
        echo "📊 交易摘要:"
        
        # 提取关键信息
        local TX_JSON=$(mktemp)
        cardano-cli conway transaction view --tx-file "$TX_FILE" --output-json > "$TX_JSON"
        
        echo "  输入数量: $(jq '.body.inputs | length' "$TX_JSON")"
        echo "  输出数量: $(jq '.body.outputs | length' "$TX_JSON")"
        echo "  费用: $(jq '.body.fee' "$TX_JSON") Lovelace"
        
        if jq -e '.body.mint' "$TX_JSON" > /dev/null; then
            echo "  铸造资产: $(jq '.body.mint' "$TX_JSON")"
        fi
        
        rm "$TX_JSON"
    }
    
    # 使用示例
    # analyze_transaction /keys/tx.unsigned

    2. 网络连接诊断

    # 网络诊断函数
    diagnose_network() {
        echo "🌐 Cardano网络连接诊断"
        echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
        
        # 检查套接字连接
        if [ -S "$CARDANO_NODE_SOCKET_PATH" ]; then
            echo "✅ 节点套接字存在"
        else
            echo "❌ 节点套接字不存在: $CARDANO_NODE_SOCKET_PATH"
        fi
        
        # 检查节点响应
        if cardano-cli query tip --testnet-magic 1 > /dev/null 2>&1; then
            echo "✅ 节点响应正常"
            
            # 获取网络信息
            local TIP=$(cardano-cli query tip --testnet-magic 1)
            echo "  当前区块: $(echo "$TIP" | jq -r '.block')"
            echo "  当前纪元: $(echo "$TIP" | jq -r '.epoch')"
            echo "  同步进度: $(echo "$TIP" | jq -r '.syncProgress')%"
        else
            echo "❌ 节点响应失败"
        fi
        
        # 检查协议参数
        if cardano-cli query protocol-parameters --testnet-magic 1 > /dev/null 2>&1; then
            echo "✅ 协议参数查询正常"
        else
            echo "❌ 协议参数查询失败"
        fi
    }

    3. 环境重置脚本

    # 完整环境重置函数
    reset_environment() {
        echo "🔄 重置Cardano NFT铸造环境"
        
        read -p "⚠️  这将删除所有密钥和数据,确定继续吗?(y/N): " confirm
        if [ "$confirm" != "y" ]; then
            echo "❌ 操作已取消"
            return 1
        fi
        
        # 备份现有数据
        if [ -d "/keys" ]; then
            local BACKUP_DIR="/keys_backup_$(date +%Y%m%d_%H%M%S)"
            mv /keys "$BACKUP_DIR"
            echo "📦 旧数据已备份到: $BACKUP_DIR"
        fi
        
        # 重新创建工作目录
        mkdir -p /keys
        cd /keys
        
        # 重新设置环境变量
        export CARDANO_NODE_SOCKET_PATH=/ipc/node.socket
        
        echo "✅ 环境重置完成"
        echo "💡 现在可以重新开始NFT铸造流程"
    }

    最佳实践和安全建议

    1. 密钥管理

    # 安全的密钥备份脚本
    backup_keys_securely() {
        echo "🔐 安全备份密钥"
        
        # 创建加密备份
        local BACKUP_FILE="/keys/secure_backup_$(date +%Y%m%d_%H%M%S).tar.gz.gpg"
        
        # 压缩关键文件
        tar -czf - payment.skey payment.vkey policy.skey policy.vkey payment.addr policy.script policy.id metadata.json | \\
        gpg --symmetric --cipher-algo AES256 --output "$BACKUP_FILE"
        
        echo "✅ 加密备份已创建: $BACKUP_FILE"
        echo "⚠️  请记住加密密码!"
    }
    
    # 验证备份完整性
    verify_backup() {
        local BACKUP_FILE="$1"
        
        echo "🔍 验证备份完整性: $BACKUP_FILE"
        
        # 测试解密(不实际解压)
        if gpg --decrypt "$BACKUP_FILE" | tar -tzf - > /dev/null 2>&1; then
            echo "✅ 备份文件完整且可解密"
        else
            echo "❌ 备份文件损坏或密码错误"
        fi
    }

    2. 交易安全检查

    # 交易提交前的安全检查
    security_check() {
        echo "🛡️  交易安全检查"
        echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
        
        # 检查网络环境
        local NETWORK_MAGIC=$(cardano-cli query tip --testnet-magic 1 | jq -r '.networkMagic // empty')
        if [ "$NETWORK_MAGIC" = "1" ]; then
            echo "✅ 确认在测试网环境"
        else
            echo "⚠️  网络环境异常"
        fi
        
        # 检查交易目标
        echo "🎯 交易目标检查:"
        echo "  - 铸造策略: $POLICY_ID"
        echo "  - 目标地址: $ADDRESS"
        echo "  - NFT名称: $NFT_NAME ($NFT_NAME_HEX)"
        
        # 检查余额充足性
        local BALANCE=$(cardano-cli query utxo --address $ADDRESS --testnet-magic 1 | jq '[.[].value.lovelace] | add')
        if [ "$BALANCE" -gt 1000000 ]; then
            echo "✅ 余额充足: $((BALANCE / 1000000)) ADA"
        else
            echo "⚠️  余额不足: $((BALANCE / 1000000)) ADA"
        fi
        
        echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
    }

    3. 性能优化建议

    # 性能监控函数
    monitor_performance() {
        echo "📊 性能监控"
        
        # 节点性能
        echo "🖥️  节点性能:"
        docker stats cardano-node --no-stream --format "table {{.CPUPerc}}\\t{{.MemUsage}}\\t{{.NetIO}}\\t{{.BlockIO}}"
        
        # 磁盘使用
        echo ""
        echo "💾 磁盘使用:"
        docker exec cardano-node df -h /data/db
        
        # 同步状态
        echo ""
        echo "🔄 同步状态:"
        local TIP=$(cardano-cli query tip --testnet-magic 1)
        echo "  同步进度: $(echo "$TIP" | jq -r '.syncProgress')%"
        echo "  当前纪元: $(echo "$TIP" | jq -r '.epoch')"
    }

    总结与后续学习

    你已经掌握的技能

    通过完成这个教程,你已经学会了:

    1. 🏗️ 基础设施搭建
      • Docker环境配置
      • Cardano节点部署
      • 测试网络连接
    2. 🔐 密码学基础
      • 公私钥对生成
      • 数字签名原理
      • 地址生成机制
    3. 📜 智能合约基础
      • 策略脚本编写
      • 时间锁定机制
      • 原生代币铸造
    4. 💼 交易处理
      • UTXO模型理解
      • 交易构建和签名
      • 网络提交流程
    5. 🎨 NFT标准
      • CIP-25元数据格式
      • 版税设置(CIP-27)
      • 资产命名规范

    以上这份详细的指南涵盖了从环境搭建到NFT铸造的完整流程,包含了故障排除、最佳实践和进阶学习路径。通过实际操作,你已经掌握了Cardano NFT开发的核心技能。

    Brave 回复 1 week ago 1 成員 · 0 回复
  • 0 回复

歡迎留言回复交流。

Log in to reply.

讨论開始
00 回复 2018 年 6 月
現在