RGB 協議開發者完整指南

從環境搭建到智慧合約部署的 RGB 協議開發完整教學,包含 Rust 程式碼範例、錢包整合、Lightning Network 整合與安全性最佳實踐

RGB 協議開發者完整指南:從環境搭建到智慧合約部署

概述

RGB 協議是比特幣生態系統中最具創新性的智慧合約擴展方案之一。與傳統的以太坊智慧合約不同,RGB 採用客戶端驗證(Client-Side Validation)範式,將合約邏輯和狀態驗證移至鏈下進行,比特幣區塊鏈僅用於存放狀態承諾和所有權轉移的歷史記錄。這種設計不僅保留了比特幣的最高安全性,還實現了出色的隱私保護和擴展性。本文將提供 RGB 協議開發的完整指南,從環境搭建、核心概念理解、智慧合約開發到部署最佳實踐,幫助開發者快速掌握這項前沿技術。

RGB 協議核心概念回顧

客戶端驗證範式

RGB 協議的核心創新在於客戶端驗證範式。傳統區塊鏈智慧合約需要在網路中的每個節點上執行和儲存合約狀態,這導致了效率瓶頸和隱私問題。RGB 協議顛覆了这一設計:

傳統智慧合約 vs RGB 智慧合約
═══════════════════════════════════════════════════════════════════════════════

傳統智慧合約(如以太坊):
┌─────────────────────────────────────────────────────────────────────────────┐
│  區塊鏈網路                                                                   │
│  ┌───────────────────────────────────────────────────────────────────────┐  │
│  │  共識節點 A  ──┐                                                      │  │
│  │                ├──► 執行合約邏輯 ──► 儲存合約狀態                       │  │
│  │  共識節點 B  ──┘                                                      │  │
│  │                ├──► 執行合約邏輯 ──► 儲存合約狀態                       │  │
│  │  共識節點 C  ──┘                                                      │  │
│  └───────────────────────────────────────────────────────────────────────┘  │
│                              │                                             │
│                              ▼                                             │
│                    所有節點儲存完整狀態                                       │
│                    所有節點執行相同邏輯                                       │
│                    狀態完全公開                                              │
└─────────────────────────────────────────────────────────────────────────────┘

RGB 智慧合約:
┌─────────────────────────────────────────────────────────────────────────────┐
│  比特幣區塊鏈                                                                 │
│  ┌───────────────────────────────────────────────────────────────────────┐  │
│  │  ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐                 │  │
│  │  │ 承諾 1  │  │ 承諾 2  │  │ 承諾 3  │  │ 承諾 4  │  ...            │  │
│  │  └─────────┘  └─────────┘  └─────────┘  └─────────┘                 │  │
│  └───────────────────────────────────────────────────────────────────────┘  │
│                              │                                             │
│                              ▼                                             │
│                    只儲存狀態承諾(Root Hash)                                │
│                    不執行任何合約邏輯                                         │
└─────────────────────────────────────────────────────────────────────────────┘

  客戶端(鏈下)                                                              
  ┌───────────────────────────────────────────────────────────────────────┐  
  │  用戶 A    用戶 B    用戶 C    用戶 D    驗證者                        │
  │    │         │         │         │         │                           │
  │    ▼         ▼         ▼         ▼         ▼                           │
  │  儲存狀態   儲存狀態  儲存狀態  儲存狀態  驗證所有轉移                   │
  │  執行邏輯   執行邏輯  執行邏輯  執行邏輯  維護承諾樹                    │
  └───────────────────────────────────────────────────────────────────────┘
═══════════════════════════════════════════════════════════════════════════════

RGB 代幣標準

RGB 採用稱為 "Tokens" 的代幣標準,定義了同質化代幣(Fungible Tokens)和非同質化代幣(NFT)的操作介面:

// RGB 代幣合約核心介面(概念性表示)

// 同質化代幣介面
pub trait I RGBToken {
    // 鑄造新代幣
    fn mint(amount: u64);
    
    // 轉移代幣
    fn transfer(to: Address, amount: u64);
    
    // 銷毀代幣
    fn burn(amount: u64);
    
    // 查詢餘額
    fn balanceOf(owner: Address) -> u64;
    
    // 查詢總供應量
    fn totalSupply() -> u64;
}

// 非同質化代幣介面
pub trait IRGBNFT {
    // 鑄造新 NFT
    fn mint(to: Address, metadata: NFTMetadata);
    
    // 轉移 NFT
    fn transfer(to: Address, token_id: u256);
    
    // 銷毀 NFT
    fn burn(token_id: u256);
    
    // 查詢 NFT 持有者
    fn ownerOf(token_id: u256) -> Address;
    
    // 查詢 NFT 元數據
    fn getMetadata(token_id: u256) -> NFTMetadata;
}

開發環境搭建

基礎軟體需求

在開始 RGB 開發之前,需要準備以下軟體環境:

開發環境需求清單
═══════════════════════════════════════════════════════════════════════════════

作業系統支援:
├── Linux(推薦 Ubuntu 22.04 LTS 或更新版本)
│   ├── 完全支援所有 RGB 工具
│   └── 最佳的編譯體驗
├── macOS 12.0 或更新版本
│   ├── 需要 Homebrew 安裝依賴
│   └── M1/M2 晶片原生支援
└── Windows 11(透過 WSL2)
    ├── 建議使用 WSL2 Ubuntu
    └── 原生 Windows 支援正在開發中

核心依賴:
├── Rust 1.70.0 或更新版本
│   └── 用於編譯 RGB 節點和工具
├── Cargo(Rust 包管理器)
│   └── 通常隨 Rust 一起安裝
├── Bitcoin Core 24.0 或更新版本
│   └── 用於與比特幣網路交互
├── Node.js 18.0 或更新版本
│   └── 用於前端開發和測試框架
└── Git
    └── 用於版本控制和程式碼管理
═══════════════════════════════════════════════════════════════════════════════

安裝 RGB 節點

RGB 協議的主要節點實現稱為 rgb-node。以下是完整的安裝步驟:

# 步驟 1:安裝 Rust 環境
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env
rustup update stable

# 步驟 2:安裝系統依賴(Ubuntu/Debian)
sudo apt-get update
sudo apt-get install -y build-essential cmake pkg-config
sudo apt-get install -y libssl-dev libffi-dev

# 步驟 3:克隆 RGB 節點倉庫
git clone https://github.com/RGB-WG/rgb-node.git
cd rgb-node

# 步驟 4:編譯 RGB 節點
cargo build --release

# 步驟 5:驗證安裝
./target/release/rgb-node --version

# 步驟 6:初始化配置
./target/release/rgb-node init

# 步驟 7:配置比特幣節點連接
# 編輯配置文件 ~/.rgb/config.toml
cat >> ~/.rgb/config.toml << 'EOF'

[bitcoin]
network = "testnet"  # 可選:mainnet, testnet, signet
rpc_url = "http://127.0.0.1:8332"
rpc_user = "your_rpc_username"
rpc_password = "your_rpc_password"

[lightning]
enabled = false  # 如需閃電網路支援,設為 true
EOF

安裝 RGB CLI 工具

RGB 提供了命令列工具用於合約管理和錢包操作:

# 安裝 RGB-Wallet(命令行錢包)
git clone https://github.com/RGB-WG/rgb-wallet.git
cd rgb-wallet
cargo build --release
sudo cp target/release/rgb-wallet /usr/local/bin/

# 安裝 RGB 轉換工具
git clone https://github.com/RGB-WG/rgb-std.git
cd rgb-std
cargo build --release

# 驗證所有工具
rgb-node --help
rgb-wallet --help

設定測試網路

對於開發測試,建議使用比特幣測試網路(Testnet)或 Signet:

# 啟動比特幣測試網路節點
# 創建 bitcoin.conf 配置文件
mkdir -p ~/.bitcoin
cat > ~/.bitcoin/bitcoin.conf << 'EOF'
# 網路設置
testnet=1
signet=1
datadir=/home/user/.bitcoin

# RPC 設置
server=1
rpcbind=127.0.0.1
rpcport=18332
rpcuser=devuser
rpcpassword=devpassword

# 錢包設置
disablewallet=0

# 安全性設置
prune=550
maxconnections=10

# 索引設置
txindex=1
addressindex=1
timestampindex=1
spentindex=1
EOF

# 啟動比特幣節點
bitcoind -daemon

# 等待節點同步完成
bitcoin-cli -rpcuser=devuser -rpcpassword=devpassword getblockchaininfo

RGB 智慧合約開發

創建代幣合約

以下是創建一個完整 RGB 代幣合約的完整範例:

// contracts/rgb20_token.rs
// RGB-20 同質化代幣合約範例

use rgb::schema::{
    Contract, 
    GlobalState, 
    OwnedState,
    Assignment,
    StateSchema,
    Genesis,
    Transition
};
use rgb::encoding::{
    Burn,
    Issue,
    Transfer,
};

// 代幣元數據結構
#[derive(SerdeJSON)]
pub struct TokenMetadata {
    pub name: String,           // 代幣名稱,例如 "Bitcoin Private"
    pub ticker: String,         // 代幣代號,例如 "BTCP"
    pub decimals: u8,           // 小數位數,通常為 8
    pub initial_supply: u64,    // 初始供應量
    pub issuance_cap: u64,      // 最大發行量
    pub IssuanceType: IssuanceType, // 發行類型
}

impl TokenMetadata {
    pub fn new(name: String, ticker: String) -> Self {
        Self {
            name,
            ticker,
            decimals: 8,
            initial_supply: 0,
            issuance_cap: 21_000_000_00000000, // 2100萬 * 10^8
            issuance_type: IssuanceType::Finite,
        }
    }
}

// 代幣合約狀態定義
pub struct Rgb20Token {
    pub metadata: TokenMetadata,
    pub total_issued: u64,
    pub balances: HashMap<BlindedUtxo, u64>,
    pub allowances: HashMap<(BlindedUtxo, BlindedUtxo), u64>,
}

impl Rgb20Token {
    // 創世紀(初始化合約)
    pub fn genesis(metadata: TokenMetadata) -> Genesis {
        let state_schema = StateSchema {
            rgb20: Some(StateSchemaEntry {
                tokens: vec![TokenState {
                    id: TokenId::from(0),
                    name: metadata.name.clone(),
                    ticker: metadata.ticker.clone(),
                    decimals: metadata.decimals,
                    supply: metadata.initial_supply,
                }],
            }),
            ..Default::default()
        };
        
        Genesis::with_state(state_schema)
    }
    
    // 鑄造代幣
    pub fn issue(&mut self, to: BlindedUtxo, amount: u64) -> Result<Issue, Error> {
        // 檢查不超過發行上限
        if self.total_issued + amount > self.metadata.issuance_cap {
            return Err(Error::ExceedsCap);
        }
        
        // 更新餘額
        *self.balances.entry(to).or_insert(0) += amount;
        self.total_issued += amount;
        
        // 生成發行轉移
        Ok(Issue {
            recipient: to,
            amount,
            token_id: TokenId::from(0),
        })
    }
    
    // 轉移代幣
    pub fn transfer(
        &mut self, 
        from: BlindedUtxo, 
        to: BlindedUtxo, 
        amount: u64
    ) -> Result<Transfer, Error> {
        // 驗證餘額充足
        let balance = self.balances.get(&from).unwrap_or(&0);
        if *balance < amount {
            return Err(Error::InsufficientBalance);
        }
        
        // 扣減轉出餘額
        self.balances.insert(from, *balance - amount);
        
        // 增加轉入餘額
        *self.balances.entry(to).or_insert(0) += amount;
        
        Ok(Transfer {
            inputs: vec![from],
            outputs: vec![to],
            amount,
        })
    }
    
    // 銷毀代幣
    pub fn burn(&mut self, from: BlindedUtxo, amount: u64) -> Result<Burn, Error> {
        let balance = self.balances.get(&from).unwrap_or(&0);
        if *balance < amount {
            return Err(Error::InsufficientBalance);
        }
        
        self.balances.insert(from, *balance - amount);
        self.total_issued -= amount;
        
        Ok(Burn {
            from,
            amount,
        })
    }
}

創建 NFT 合約

RGB-21 標準用於非同質化代幣:

// contracts/rgb21_nft.rs
// RGB-21 NFT 合約範例

use rgb::schema::{
    NFTId, 
    NFTMetadata, 
    MediaType,
    TokenIndex,
};

// NFT 元數據
#[derive(SerdeJSON)]
pub struct NftMetadata {
    pub name: String,           // NFT 名稱
    pub description: String,   // 描述
    pub media_uri: String,      // 媒體資源 URI
    pub media_type: MediaType,  // 媒體類型
    pub attributes: Vec<NftAttribute>, // 自定義屬性
}

#[derive(SerdeJSON)]
pub struct NftAttribute {
    pub trait_type: String,    // 屬性類型
    pub value: String,         // 屬性值
    pub display_type: Option<String>, // 顯示類型
}

// NFT 合約
pub struct Rgb21Nft {
    pub name: String,
    pub description: String,
    pub items: HashMap<TokenIndex, NftMetadata>,
    pub owners: HashMap<TokenIndex, BlindedUtxo>,
}

impl Rgb21Nft {
    // 鑄造 NFT
    pub fn mint(&mut self, to: BliddenUtxo, metadata: NftMetadata) -> Result<NFTId, Error> {
        let token_index = self.items.len() as TokenIndex;
        let nft_id = NFTId::from(token_index);
        
        self.items.insert(token_index, metadata);
        self.owners.insert(token_index, to);
        
        Ok(nft_id)
    }
    
    // 轉移 NFT
    pub fn transfer(&mut self, nft_id: NFTId, to: BlindedUtxo) -> Result<(), Error> {
        let index = nft_id.to_index();
        
        if !self.owners.contains_key(&index) {
            return Err(Error::NftNotFound);
        }
        
        self.owners.insert(index, to);
        Ok(())
    }
    
    // 查詢持有者
    pub fn owner_of(&self, nft_id: NFTId) -> Option<&BlindedUtxo> {
        self.owners.get(&nft_id.to_index())
    }
}

實現複雜邏輯合約

RGB 還支援更複雜的商業邏輯合約,例如票務系統、供應鏈追蹤等:

// contracts/rgb25_ticket.rs
// RGB-25 票務合約範例

use rgb::schema::{TicketId, TicketType, TicketState};
use chrono::{DateTime, Utc};

// 票務元數據
#[derive(SerdeJSON)]
pub struct TicketMetadata {
    pub event_name: String,
    pub event_date: DateTime<Utc>,
    pub venue: String,
    pub ticket_type: TicketType,
    pub price: u64,
    pub total_quantity: u32,
    pub max_per_person: u32,
}

// 票務合約
pub struct TicketContract {
    pub metadata: TicketMetadata,
    pub issued_tickets: HashMap<TicketId, TicketState>,
    pub purchases: HashMap<BlindedUtxo, Vec<TicketId>>,
    pub used_tickets: HashSet<TicketId>,
}

impl TicketContract {
    // 發行票務
    pub fn issue_ticket(&mut self, to: BlindedUtxo) -> Result<TicketId, Error> {
        let issued_count = self.issued_tickets.len() as u32;
        
        if issued_count >= self.metadata.total_quantity {
            return Err(Error::SoldOut);
        }
        
        // 檢查個人購買限制
        let user_tickets = self.purchases.get(&to).unwrap_or(&vec![]).len() as u32;
        if user_tickets >= self.metadata.max_per_person {
            return Err(Error::PurchaseLimitExceeded);
        }
        
        let ticket_id = TicketId::from(issued_count);
        
        self.issued_tickets.insert(
            ticket_id, 
            TicketState::Valid { owner: to }
        );
        
        self.purchases
            .entry(to)
            .or_insert_with(Vec::new)
            .push(ticket_id);
        
        Ok(ticket_id)
    }
    
    // 使用票務(進場驗證)
    pub fn use_ticket(&mut self, ticket_id: TicketId) -> Result<(), Error> {
        if self.used_tickets.contains(&ticket_id) {
            return Err(Error::TicketAlreadyUsed);
        }
        
        let state = self.issued_tickets.get(&ticket_id)
            .ok_or(Error::TicketNotFound)?;
            
        match state {
            TicketState::Valid { owner: _ } => {
                self.used_tickets.insert(ticket_id);
                Ok(())
            },
            TicketState::Used => Err(Error::TicketAlreadyUsed),
            TicketState::Cancelled => Err(Error::TicketCancelled),
            TicketState::Expired => Err(Error::TicketExpired),
        }
    }
    
    // 退票(退款)
    pub fn refund_ticket(&mut self, ticket_id: TicketId) -> Result<(), Error> {
        let state = self.issued_tickets.get(&ticket_id)
            .ok_or(Error::TicketNotFound)?;
            
        match state {
            TicketState::Valid { owner } => {
                // 檢查是否在退票期限內
                let refund_deadline = self.metadata.event_date - chrono::Duration::days(7);
                if Utc::now() > refund_deadline {
                    return Err(Error::RefundDeadlinePassed);
                }
                
                self.issued_tickets.insert(ticket_id, TicketState::Refunded);
                Ok(())
            },
            _ => Err(Error::CannotRefund),
        }
    }
}

合約部署流程

編譯和發布合約

RGB 合約需要經過編譯、發布到比特幣區塊鏈的流程:

# 步驟 1:準備合約程式碼
# 將合約程式碼放入 contracts/ 目錄

# 步驟 2:編譯合約
cargo build --release --lib

# 步驟 3:生成合約定義檔案(contract.json)
# contract.json 包含合約的狀態架構、過渡規則等

# 步驟 4:發布合約到比特幣區塊鏈
# 使用 RGB CLI 工具發布合約
rgb-node contract publish \
    --contract-path ./target/release/contract.json \
    --signer "your_wallet_seed" \
    --network testnet

# 步驟 5:記錄合約 ID
# 發布成功後會返回合約 ID
# 例如: контракт1qvz9...(請保存此 ID)

# 步驟 6:驗證合約發布
rgb-node contract info <CONTRACT_ID>

使用 Lightning Network 進行轉移

RGB 可以與 Lightning Network 整合,實現快速、低成本的轉移:

// 整合 Lightning Network 的 RGB 轉移範例

use ln_service::{Lightner, Preimage, Route};

pub struct LnRgbBridge {
    ln_client: LightningClient,
    rgb_client: RgbClient,
}

impl LnRgbBridge {
    // 透過 Lightning 發送 RGB 代幣
    pub async fn send_via_lightning(
        &self,
        to: NodeId,
        rgb_amount: u64,
        token_id: TokenId,
    ) -> Result<Preimage, Error> {
        // 1. 建立 Lightning 付款請求
        let invoice = self.ln_client.create_invoice(
            Amount::from_sat(calculate_sats_equivalent(rgb_amount)),
            "RGB Transfer".to_string(),
            3600, // 過期時間:1小時
        ).await?;
        
        // 2. 在 RGB 網路中鎖定代幣
        let rgb_preimage = self.rgb_client.lock_tokens(
            to.clone(),
            rgb_amount,
            token_id,
            invoice.payment_hash(),
        ).await?;
        
        // 3. 等待 Lightning 付款確認
        self.ln_client.wait_for_payment(
            invoice.payment_hash(),
            3600,
        ).await?;
        
        // 4. 完成 RGB 轉移
        self.rgb_client.complete_transfer(
            rgb_preimage,
            invoice.preimage(),
        ).await?;
        
        Ok(invoice.preimage())
    }
    
    // 接收 Lightning 支付的 RGB 代幣
    pub async fn receive_via_lightning(
        &self,
        invoice: &Invoice,
    ) -> Result<BlindedUtxo, Error> {
        // 1. 等待 Lightning 付款
        self.ln_client.wait_for_payment(
            invoice.payment_hash(),
            invoice.expiry_timestamp(),
        ).await?;
        
        // 2. 產生接收 UTXO
        let blinding = self.rgb_client.generate_blinding();
        let blinded_utxo = self.rgb_client.create_blinded_utxo(blinding);
        
        // 3. 解鎖並接收代幣
        self.rgb_client.claim_tokens(
            invoice.preimage(),
            blinded_utxo,
        ).await?;
        
        Ok(blinded_utxo)
    }
}

錢包整合開發

RGB 錢包架構

開發 RGB 應用需要實現錢包功能:

// wallet/rgb_wallet.rs
// RGB 錢包核心實現

use rgb::wallet::{Wallet, WalletDatabase};
use bdk::database::SqliteDatabase;
use bitcoin::{PublicKey, PrivateKey};

pub struct RgbWallet {
    wallet: Wallet<SqliteDatabase>,
    rgb_client: RgbClient,
    signer: RgbSigner,
}

impl RgbWallet {
    // 創建新錢包
    pub fn create(network: Network) -> Result<Self, Error> {
        // 使用 BDK 創建比特幣錢包
        let wallet = Wallet::new(
            SqliteDatabase::new("rgb_wallet.db")?,
            network,
        )?;
        
        // 生成或導入私鑰
        let private_key = PrivateKey::new(
            Secp256k1::new(),
            network,
        );
        
        // 創建 RGB 簽名者
        let signer = RgbSigner::from_private_key(private_key)?;
        
        // 初始化 RGB 客戶端
        let rgb_client = RgbClient::new(
            "https://rgb-node.example.com".to_string(),
            network,
        )?;
        
        Ok(Self {
            wallet,
            rgb_client,
            signer,
        })
    }
    
    // 導入錢包(使用助記詞)
    pub fn restore(mnemonic: &str, network: Network) -> Result<Self, Error> {
        let wallet = Wallet::from_mnemonic(
            mnemonic,
            "",
            SqliteDatabase::new("rgb_wallet.db")?,
            network,
        )?;
        
        let private_key = wallet.get_private_key()?;
        let signer = RgbSigner::from_private_key(private_key)?;
        let rgb_client = RgbClient::new(
            "https://rgb-node.example.com".to_string(),
            network,
        )?;
        
        Ok(Self {
            wallet,
            rgb_client,
            signer,
        })
    }
    
    // 獲取代幣餘額
    pub async fn get_balance(&self, token_id: TokenId) -> Result<u64, Error> {
        let utxos = self.wallet.list_unspent()?;
        let mut total = 0u64;
        
        for utxo in utxos {
            let rgb_utxo = self.rgb_client
                .get_utxo_state(utxo.outpoint())
                .await?;
            
            if let Some(state) = rgb_utxo {
                total += state.balance(token_id);
            }
        }
        
        Ok(total)
    }
    
    // 轉移代幣
    pub async fn transfer(
        &self,
        to: BlindedUtxo,
        amount: u64,
        token_id: TokenId,
        fee_rate: u64, // sat/vB
    ) -> Result<Transaction, Error> {
        // 1. 選擇輸入 UTXO
        let inputs = self.select_inputs(amount, token_id).await?;
        
        // 2. 建立轉移請求
        let transfer = Transfer {
            inputs: inputs.clone(),
            outputs: vec![Output {
                recipient: to,
                amount,
                token_id,
                change: true,
            }],
        };
        
        // 3. 簽署轉移
        let signed_transfer = self.signer.sign(transfer)?;
        
        // 4. 廣播比特幣交易
        let psbt = self.build_psbt(inputs, signed_transfer.clone())?;
        let signed_psbt = self.wallet.sign(psbt)?;
        let tx = signed_psbt.extract_tx()?;
        
        self.wallet.broadcast(&tx).await?;
        
        // 5. 提交 RGB 轉移到節點
        self.rgb_client
            .submit_transfer(signed_transfer, tx.txid())
            .await?;
        
        Ok(tx)
    }
}

前端整合範例

使用 JavaScript/TypeScript 與 RGB 節點交互:

// src/rgb-client.ts
// RGB 節點客戶端

import { Transport, RgbNode } from '@rgb-wallet/lib';

class RgbClient {
  private node: RgbNode;
  private transport: Transport;
  
  constructor(nodeUrl: string) {
    this.transport = new Transport(nodeUrl);
    this.node = new RgbNode(this.transport);
  }
  
  // 連接錢包
  async connectWallet(seed: string): Promise<Wallet> {
    const wallet = await this.node.wallet.create({
      seed,
      network: 'testnet'
    });
    return wallet;
  }
  
  // 查詢餘額
  async getBalance(walletId: string, contractId: string): Promise<Balance> {
    return await this.node.contracts
      .balance(walletId, contractId);
  }
  
  // 創建轉移
  async createTransfer(
    walletId: string,
    to: string,
    amount: bigint,
    contractId: string
  ): Promise<TransferRequest> {
    return await this.node.transfers
      .create({
        walletId,
        recipient: to,
        amount,
        contractId,
        feeRate: 1, // sat/vB
      });
  }
  
  // 簽署並廣播
  async signAndBroadcast(
    transferRequest: TransferRequest,
    seed: string
  ): Promise<string> {
    // 使用錢包簽署
    const signed = await this.node.transfers.sign(
      transferRequest,
      seed
    );
    
    // 廣播比特幣交易
    const txId = await this.node.broadcast(signed.bitcoinTx);
    
    // 提交 RGB 轉移
    await this.node.transfers.submit(
      transferRequest.transferId,
      txId
    );
    
    return txId;
  }
  
  // 監聽新轉移
  subscribeTransfers(
    walletId: string,
    callback: (transfer: Transfer) => void
  ): void {
    this.node.transfers
      .listen(walletId, (event) => {
        callback(event.transfer);
      });
  }
}

// 使用範例
const client = new RgbClient('https://rgb.example.com');
const wallet = await client.connectWallet('your-seed-phrase');

const balance = await client.getBalance(
  wallet.id,
  'contract1qvz9...'
);

console.log(`餘額: ${balance.amount} tokens`);

測試與調試

單元測試

// tests/rgb20_token.rs

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_token_issuance() {
        let metadata = TokenMetadata::new(
            "Test Token".to_string(),
            "TEST".to_string()
        );
        
        let mut token = Rgb20Token::genesis(metadata);
        
        let to = BlindedUtxo::new(); // 測試用 UTXO
        
        let issue = token.issue(to.clone(), 1000).unwrap();
        
        assert_eq!(token.total_issued, 1000);
        assert_eq!(token.balance_of(&to), 1000);
    }
    
    #[test]
    fn test_token_transfer() {
        let metadata = TokenMetadata::new(
            "Test Token".to_string(),
            "TEST".to_string()
        );
        
        let mut token = Rgb20Token::genesis(metadata);
        
        let from = BlindedUtxo::new();
        let to = BlindedUtxo::new();
        
        // 發行代幣
        token.issue(from.clone(), 1000).unwrap();
        
        // 轉移
        token.transfer(from.clone(), to.clone(), 500).unwrap();
        
        assert_eq!(token.balance_of(&from), 500);
        assert_eq!(token.balance_of(&to), 500);
    }
    
    #[test]
    fn test_insufficient_balance() {
        let metadata = TokenMetadata::new(
            "Test Token".to_string(),
            "TEST".to_string()
        );
        
        let mut token = Rgb20Token::genesis(metadata);
        
        let from = BlindedUtxo::new();
        let to = BlindedUtxo::new();
        
        // 嘗試轉移超過餘額
        let result = token.transfer(from.clone(), to.clone(), 1000);
        
        assert!(result.is_err());
        assert!(matches!(result.unwrap_err(), Error::InsufficientBalance));
    }
    
    #[test]
    fn test_burn() {
        let metadata = TokenMetadata::new(
            "Test Token".to_string(),
            "TEST".to_string()
        );
        
        let mut token = Rgb20Token::genesis(metadata);
        
        let holder = BlindedUtxo::new();
        
        token.issue(holder.clone(), 1000).unwrap();
        token.burn(holder.clone(), 500).unwrap();
        
        assert_eq!(token.balance_of(&holder), 500);
        assert_eq!(token.total_issued, 500);
    }
}

整合測試

# 執行測試
cargo test --test integration

# 測試 RGB 節點交互
rgb-node testnet-faucet <ADDRESS>

# 監控測試網路狀態
rgb-node status

# 調試模式
RUST_LOG=debug cargo run --bin rgb-node

安全性考量

合約安全最佳實踐

// 安全最佳實踐範例

// 1. 防止重入攻擊
fn transfer_with_reentry_protection(
    &mut self,
    from: BlindedUtxo,
    to: BlindedUtxo,
    amount: u64
) -> Result<Transfer, Error> {
    // 先更新狀態
    let from_balance = self.balances.get(&from).unwrap_or(&0);
    
    if *from_balance < amount {
        return Err(Error::InsufficientBalance);
    }
    
    // 重要:先扣減餘額,再執行轉移
    self.balances.insert(from, *from_balance - amount);
    
    // 最後才增加目標餘額
    *self.balances.entry(to).or_insert(0) += amount;
    
    // 這樣可以防止重入攻擊
}

// 2. 溢出保護
fn safe_add(a: u64, b: u64) -> Result<u64, Error> {
    a.checked_add(b)
        .ok_or(Error::Overflow)
}

fn safe_sub(a: u64, b: u64) -> Result<u64, Error> {
    a.checked_sub(b)
        .ok_or(Error::Underflow)
}

// 3. 權限控制
pub fn only_owner(&self, caller: &BlindedUtxo) -> Result<(), Error> {
    if caller != &self.owner {
        return Err(Error::Unauthorized);
    }
    Ok(())
}

錢包安全

// 錢包安全功能

pub struct SecureWallet {
    inner: RgbWallet,
    encryption_key: [u8; 32],
}

impl SecureWallet {
    // 使用密碼加密私鑰
    pub fn encrypt_private_key(
        private_key: &PrivateKey,
        password: &str
    ) -> EncryptedKey {
        let key = derive_key_from_password(password);
        let encrypted = encrypt(private_key.as_bytes(), &key);
        EncryptedKey { encrypted, salt: key.salt }
    }
    
    // 檢查地址是否在黑名單中
    pub fn is_blacklisted(&self, address: &str) -> bool {
        // 實現黑名單檢查邏輯
        // 包括已知的犯罪地址、制裁名單等
        BLACKLIST.contains(address)
    }
    
    // 交易金額限制
    pub fn check_transaction_limits(
        &self,
        amount: u64,
        token_id: TokenId
    ) -> Result<(), Error> {
        let limits = self.get_limits(token_id);
        
        if amount > limits.daily_max {
            return Err(Error::ExceedsDailyLimit);
        }
        
        if amount > limits.single_max {
            return Err(Error::ExceedsSingleLimit);
        }
        
        Ok(())
    }
}

部署最佳實踐

主網部署檢查清單

部署前檢查清單
═══════════════════════════════════════════════════════════════════════════════

□ 程式碼審計
  ├── 第三方安全審計完成
  ├── 已知漏洞修復確認
  └── 程式碼品質檢查通過

□ 測試覆蓋
  ├── 單元測試覆蓋率 > 80%
  ├── 整合測試全部通過
  └── 壓力測試完成

□ 安全檢查
  ├── 智慧合約權限控制審查
  ├── 輸入驗證完善
  └── 溢出保護實現

□ 營運準備
  ├── 監控系統部署
  ├── 日誌系統配置
  ├── 備份策略制定
  └── 災難復原計畫

□ 法規合規
  ├── KYC/AML 政策實施
  ├── 證券法規評估(如適用)
  └── 稅務報告功能

□ 文檔
  ├── API 文檔完成
  ├── 使用者指南
  └── 開發者文檔
═══════════════════════════════════════════════════════════════════════════════

節點運維

# 監控 RGB 節點
watch -n 5 'rgb-node status'

# 查看日誌
journalctl -u rgb-node -f

# 備份錢包
rgb-node wallet backup --wallet-id <ID> --output ./backup.dat

# 升級節點
# 1. 停止節點
systemctl stop rgb-node

# 2. 更新代碼
git pull origin master

# 3. 重新編譯
cargo build --release

# 4. 啟動節點
systemctl start rgb-node

# 5. 驗證升級
rgb-node --version

結論

RGB 協議代表了比特幣智慧合約的重大進步,其客戶端驗證範式在保持比特幣安全性的同時,實現了智慧合約的靈活性。本指南涵蓋了 RGB 開發的各個方面,從環境搭建、智慧合約開發、錢包整合到部署最佳實踐。隨著 RGB 生態系統的持續發展,開發者應密切關注協議的更新和最佳實踐的演進,以構建安全、高效的比特幣應用。

延伸閱讀

延伸閱讀與來源

這篇文章對您有幫助嗎?

評論

發表評論

注意:由於這是靜態網站,您的評論將儲存在本地瀏覽器中,不會公開顯示。

目前尚無評論,成為第一個發表評論的人吧!