Rust Bitcoin 開發入門(四):進階應用與整合
這是 Rust Bitcoin 開發入門系列的最後一篇。本篇探討如何構建完整的 Bitcoin 應用,包括與節點互動、錢包開發和實際部署考量。
系列文章導航:
- 第一篇:環境設置與基礎概念
- 第二篇:地址生成與交易構建
- 第三篇:腳本與簽名
- 第四篇:進階應用與整合(本篇)
1. 區塊鏈數據存取策略
1.1 理解不同的存取方式
要構建實際的 Bitcoin 應用,你需要與區塊鏈數據互動。有幾種主要的方式可以做到這一點,每種都有其優缺點。
Bitcoin Core RPC 是最直接的方式。你運行一個完整節點,透過 JSON-RPC 介面查詢數據。這種方式提供了最高的安全性和隱私性——你不依賴任何第三方。缺點是需要存儲完整的區塊鏈(數百 GB),且初始同步需要數天。
Electrum 協議 使用客戶端-伺服器模式。輕量級客戶端連接到 Electrum 伺服器,伺服器維護一個索引的區塊鏈數據庫。客戶端可以快速查詢地址餘額和交易歷史,而不需要下載完整區塊鏈。缺點是你需要信任伺服器提供正確的數據(除非你自己運行伺服器)。
Esplora API 是 Blockstream 開發的另一種輕量級解決方案,透過 REST API 提供區塊鏈數據。它類似於 Electrum,但使用標準的 HTTP 介面,更容易整合。
選擇哪種方式取決於你的應用需求。對於個人錢包,Electrum 或 Esplora 通常足夠。對於需要最高安全性的應用(如交易所),你應該運行自己的完整節點。
1.2 Bitcoin Core RPC
Bitcoin Core 提供了一個豐富的 JSON-RPC 介面,可以查詢區塊鏈狀態、管理錢包和廣播交易。使用 bitcoincore-rpc crate 可以方便地從 Rust 存取這個介面。
首先需要配置 Bitcoin Core 啟用 RPC:
1
2
3
4
5
# bitcoin.conf
server=1
rpcuser=your_username
rpcpassword=your_password
rpcport=8332 # mainnet
然後在 Rust 中連接:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
use bitcoincore_rpc::{Auth, Client, RpcApi};
fn connect_to_node() -> anyhow::Result<Client> {
let rpc = Client::new(
"http://127.0.0.1:8332",
Auth::UserPass("your_username".into(), "your_password".into()),
)?;
// 驗證連接
let info = rpc.get_blockchain_info()?;
println!("已連接到 Bitcoin Core");
println!("區塊數: {}", info.blocks);
println!("同步進度: {:.2}%", info.verification_progress * 100.0);
Ok(rpc)
}
RPC 提供了廣泛的功能。你可以查詢區塊鏈信息、獲取特定區塊和交易、管理錢包、估算費率、廣播交易等。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
fn rpc_examples(rpc: &Client) -> anyhow::Result<()> {
// 獲取最新區塊
let best_hash = rpc.get_best_block_hash()?;
let block = rpc.get_block(&best_hash)?;
println!("最新區塊包含 {} 筆交易", block.txdata.len());
// 估算費率(6 區塊確認目標)
let fee = rpc.estimate_smart_fee(6, None)?;
if let Some(rate) = fee.fee_rate {
println!("建議費率: {} sat/vB", rate.to_sat() / 1000);
}
// 獲取 mempool 狀態
let mempool = rpc.get_mempool_info()?;
println!("Mempool 中有 {} 筆待確認交易", mempool.size);
Ok(())
}
1.3 Electrum 協議
Electrum 協議設計用於輕量級客戶端。它讓你可以快速查詢特定地址的餘額和歷史,而不需要掃描整個區塊鏈。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
use electrum_client::{Client, ElectrumApi};
fn electrum_example() -> anyhow::Result<()> {
// 連接到 Electrum 伺服器
let client = Client::new("ssl://electrum.blockstream.info:60002")?;
// 查詢地址餘額
let address = Address::from_str("bc1q...")?;
let script = address.script_pubkey();
let balance = client.script_get_balance(&script)?;
println!("已確認餘額: {} sat", balance.confirmed);
println!("未確認餘額: {} sat", balance.unconfirmed);
// 獲取交易歷史
let history = client.script_get_history(&script)?;
println!("共有 {} 筆相關交易", history.len());
// 獲取未花費輸出
let utxos = client.script_list_unspent(&script)?;
for utxo in utxos {
println!("UTXO: {}:{} - {} sat",
utxo.tx_hash, utxo.tx_pos, utxo.value);
}
Ok(())
}
Electrum 的一個重要特性是訂閱功能。你可以訂閱特定地址的變化通知,這對於支付處理器等需要監控入帳的應用很有用。
1
2
3
4
5
6
7
8
9
10
fn monitor_address(client: &Client, script: &Script) -> anyhow::Result<()> {
// 訂閱地址狀態變化
let status = client.script_subscribe(script)?;
println!("初始狀態: {:?}", status);
// 在實際應用中,你會在後台執行緒中監聽變化
// 當地址收到新交易時,會收到通知
Ok(())
}
2. 使用 BDK 構建錢包
2.1 什麼是 BDK
BDK(Bitcoin Development Kit)是一個現代化的錢包開發框架。它抽象了錢包開發的複雜性,讓開發者可以專注於應用邏輯,而不是底層的區塊鏈細節。
BDK 的設計理念是基於「描述符」(descriptors)。描述符是一種標準化的方式來描述如何生成地址和花費資金。這種方法比傳統的「地址列表」方式更靈活,可以輕鬆支持各種腳本類型(P2PKH、P2WPKH、多簽等)。
BDK 的架構分為幾層:
錢包層 處理地址生成、交易構建和簽名。它不關心如何獲取區塊鏈數據或如何存儲狀態。
區塊鏈後端 提供區塊鏈數據。BDK 支持多種後端:Electrum、Esplora、Bitcoin Core RPC 等。你可以根據需求選擇。
數據庫後端 存儲錢包狀態(已知交易、UTXO 等)。BDK 支持內存數據庫、SQLite 等。
2.2 創建 BDK 錢包
讓我們從創建一個簡單的錢包開始。我們將使用助記詞生成密鑰,創建原生 SegWit(BIP84)錢包。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
use bdk::{
Wallet,
database::MemoryDatabase,
bitcoin::Network,
};
use bip39::Mnemonic;
fn create_wallet() -> anyhow::Result<Wallet<MemoryDatabase>> {
// 生成新的助記詞(實際應用中應該讓用戶備份)
let mnemonic = Mnemonic::generate(12)?;
println!("請備份您的助記詞: {}", mnemonic);
// 從助記詞派生主密鑰
let seed = mnemonic.to_seed("");
let xprv = bitcoin::bip32::Xpriv::new_master(Network::Testnet, &seed)?;
// 創建 BIP84 描述符
// 外部鏈(接收地址):m/84'/1'/0'/0/*
// 內部鏈(找零地址):m/84'/1'/0'/1/*
let external = format!("wpkh({}/84'/1'/0'/0/*)", xprv);
let internal = format!("wpkh({}/84'/1'/0'/1/*)", xprv);
// 創建錢包
let wallet = Wallet::new(
&external,
Some(&internal),
Network::Testnet,
MemoryDatabase::default(),
)?;
// 生成幾個地址
println!("\n生成的地址:");
for i in 0..3 {
let addr = wallet.get_address(bdk::wallet::AddressIndex::New)?;
println!(" {}: {}", i, addr);
}
Ok(wallet)
}
描述符的威力在於其靈活性。你可以輕鬆創建不同類型的錢包:
1
2
3
4
5
6
7
8
// 單簽 P2WPKH(原生 SegWit)
let single_sig = "wpkh([fingerprint/84'/0'/0']xpub.../0/*)";
// 2-of-3 多簽
let multisig = "wsh(multi(2,[fp1]xpub1.../0/*,[fp2]xpub2.../0/*,[fp3]xpub3.../0/*))";
// Taproot
let taproot = "tr([fingerprint/86'/0'/0']xpub.../0/*)";
2.3 同步和查詢
創建錢包後,需要與區塊鏈同步以獲取餘額和交易歷史。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
use bdk::{
blockchain::{ElectrumBlockchain, GetHeight},
SyncOptions,
};
fn sync_and_query(wallet: &Wallet<MemoryDatabase>) -> anyhow::Result<()> {
// 創建區塊鏈後端
let blockchain = ElectrumBlockchain::from(
electrum_client::Client::new("ssl://electrum.blockstream.info:60002")?
);
println!("當前區塊高度: {}", blockchain.get_height()?);
// 同步錢包
println!("正在同步...");
wallet.sync(&blockchain, SyncOptions::default())?;
println!("同步完成");
// 查詢餘額
let balance = wallet.get_balance()?;
println!("\n餘額:");
println!(" 已確認: {} sat", balance.confirmed);
println!(" 待確認入帳: {} sat", balance.untrusted_pending);
println!(" 待確認出帳: {} sat", balance.trusted_pending);
// 列出 UTXO
let utxos = wallet.list_unspent()?;
if !utxos.is_empty() {
println!("\nUTXO:");
for utxo in utxos {
println!(" {}:{} - {} sat",
utxo.outpoint.txid,
utxo.outpoint.vout,
utxo.txout.value);
}
}
Ok(())
}
2.4 構建和發送交易
BDK 的交易構建器提供了直觀的 API 來創建交易。它自動處理選幣、費用計算和找零。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
use bdk::{SignOptions, FeeRate};
use bdk::blockchain::Blockchain;
fn send_transaction(
wallet: &Wallet<MemoryDatabase>,
blockchain: &ElectrumBlockchain,
recipient: &Address,
amount_sat: u64,
) -> anyhow::Result<Txid> {
// 構建交易
let mut builder = wallet.build_tx();
builder
// 添加接收者
.add_recipient(recipient.script_pubkey(), amount_sat)
// 設置費率(sat/vB)
.fee_rate(FeeRate::from_sat_per_vb(10.0))
// 啟用 RBF(允許稍後提高費率)
.enable_rbf();
// 完成構建
let (mut psbt, tx_details) = builder.finish()?;
println!("交易詳情:");
println!(" 發送金額: {} sat", amount_sat);
println!(" 手續費: {} sat", tx_details.fee.unwrap_or(0));
// 簽名
let finalized = wallet.sign(&mut psbt, SignOptions::default())?;
if !finalized {
anyhow::bail!("簽名未完成");
}
// 提取最終交易
let tx = psbt.extract_tx();
let txid = tx.compute_txid();
// 廣播
blockchain.broadcast(&tx)?;
println!("交易已廣播: {}", txid);
Ok(txid)
}
BDK 還支持更複雜的場景,如手動選幣:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
fn advanced_transaction(wallet: &Wallet<MemoryDatabase>) -> anyhow::Result<()> {
let mut builder = wallet.build_tx();
// 手動選擇特定的 UTXO
let utxos = wallet.list_unspent()?;
if let Some(utxo) = utxos.first() {
builder.add_utxo(utxo.outpoint)?;
}
// 或者使用特定的選幣策略
// builder.coin_selection(LargestFirstCoinSelection);
// 添加 OP_RETURN 數據
// builder.add_data(&[1, 2, 3, 4]);
// 設置自定義序列號(用於時間鎖)
// builder.set_sequence(Sequence::from_height(144));
Ok(())
}
3. 安全考量
3.1 密鑰管理
密鑰管理是 Bitcoin 應用最關鍵的安全面向。私鑰一旦洩露,資金就會永久丟失。以下是一些最佳實踐。
內存安全:敏感數據(私鑰、助記詞)在使用後應該立即從內存中清除。Rust 的 zeroize crate 提供了這個功能。
1
2
3
4
5
6
7
8
9
use zeroize::Zeroize;
fn secure_key_handling() {
let mut secret = [0u8; 32];
// ... 使用 secret ...
// 使用完畢後清零
secret.zeroize();
}
永遠不要記錄敏感數據:日誌中不應該出現私鑰、助記詞或其他敏感信息。這是一個常見的錯誤。
1
2
3
4
5
// 錯誤!不要這樣做
println!("私鑰: {}", private_key);
// 正確:只記錄非敏感信息
println!("正在處理地址: {}", address);
考慮使用硬體錢包:對於生產環境,考慮整合硬體錢包(如 Ledger 或 Trezor)。私鑰永遠不離開硬體設備,大大降低了被盜風險。
3.2 輸入驗證
永遠不要信任外部輸入。在處理用戶提供的地址或金額之前,必須驗證其有效性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
fn validate_inputs(
address_str: &str,
amount_sat: u64,
network: Network,
) -> anyhow::Result<(Address, Amount)> {
// 驗證地址格式
let address = Address::from_str(address_str)
.map_err(|e| anyhow::anyhow!("無效的地址格式: {}", e))?;
// 驗證網路
if !address.is_valid_for_network(network) {
anyhow::bail!("地址與網路不匹配");
}
// 驗證金額
if amount_sat < 546 {
anyhow::bail!("金額低於粉塵限制");
}
if amount_sat > 21_000_000 * 100_000_000 {
anyhow::bail!("金額超過 Bitcoin 總供應量");
}
let amount = Amount::from_sat(amount_sat);
Ok((address.assume_checked(), amount))
}
3.3 錯誤處理
適當的錯誤處理對於安全和用戶體驗都很重要。使用結構化的錯誤類型,並確保不在錯誤消息中洩露敏感信息。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
use thiserror::Error;
#[derive(Error, Debug)]
pub enum WalletError {
#[error("餘額不足")]
InsufficientFunds {
required: u64,
available: u64,
},
#[error("無效的接收地址")]
InvalidAddress,
#[error("網路連接失敗")]
NetworkError(#[from] std::io::Error),
#[error("交易構建失敗")]
TransactionError(String),
}
// 在日誌中只顯示安全的信息
impl WalletError {
pub fn safe_message(&self) -> &str {
match self {
Self::InsufficientFunds { .. } => "餘額不足",
Self::InvalidAddress => "地址驗證失敗",
Self::NetworkError(_) => "網路連接問題",
Self::TransactionError(_) => "交易處理失敗",
}
}
}
4. 完整錢包應用範例
4.1 架構設計
一個生產級的錢包應用需要考慮許多面向:用戶界面、狀態管理、錯誤處理、日誌記錄等。這裡我們展示一個簡化但完整的命令列錢包。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
pub struct WalletApp {
wallet: Wallet<SqliteDatabase>,
blockchain: ElectrumBlockchain,
network: Network,
}
impl WalletApp {
/// 創建新錢包
pub fn create(
name: &str,
mnemonic: &str,
passphrase: &str,
network: Network,
data_dir: &Path,
) -> anyhow::Result<Self> {
// 驗證助記詞
let mnemonic = Mnemonic::parse(mnemonic)?;
// 派生密鑰
let seed = mnemonic.to_seed(passphrase);
let xprv = Xpriv::new_master(network, &seed)?;
// 構建描述符
let coin_type = if network == Network::Bitcoin { "0" } else { "1" };
let external = format!("wpkh({}/84'/{}'/ 0'/0/*)", xprv, coin_type);
let internal = format!("wpkh({}/84'/{}'/0'/1/*)", xprv, coin_type);
// 創建數據庫
let db_path = data_dir.join(format!("{}.db", name));
let database = SqliteDatabase::new(&db_path)?;
// 創建錢包
let wallet = Wallet::new(&external, Some(&internal), network, database)?;
// 連接區塊鏈
let electrum_url = match network {
Network::Bitcoin => "ssl://electrum.blockstream.info:60002",
_ => "ssl://electrum.blockstream.info:60002",
};
let blockchain = ElectrumBlockchain::from(
electrum_client::Client::new(electrum_url)?
);
Ok(Self { wallet, blockchain, network })
}
/// 同步錢包
pub fn sync(&self) -> anyhow::Result<()> {
self.wallet.sync(&self.blockchain, SyncOptions::default())?;
Ok(())
}
/// 獲取餘額
pub fn balance(&self) -> anyhow::Result<Balance> {
let b = self.wallet.get_balance()?;
Ok(Balance {
confirmed: b.confirmed,
pending: b.untrusted_pending + b.trusted_pending,
})
}
/// 獲取新地址
pub fn new_address(&self) -> anyhow::Result<Address> {
Ok(self.wallet.get_address(AddressIndex::New)?.address)
}
/// 發送交易
pub fn send(
&self,
recipient: &Address,
amount: Amount,
fee_rate: f32,
) -> anyhow::Result<Txid> {
// 驗證
if !recipient.is_valid_for_network(self.network) {
anyhow::bail!("地址網路不匹配");
}
// 構建交易
let mut builder = self.wallet.build_tx();
builder
.add_recipient(recipient.script_pubkey(), amount.to_sat())
.fee_rate(FeeRate::from_sat_per_vb(fee_rate))
.enable_rbf();
let (mut psbt, _) = builder.finish()?;
// 簽名
self.wallet.sign(&mut psbt, SignOptions::default())?;
// 廣播
let tx = psbt.extract_tx();
self.blockchain.broadcast(&tx)?;
Ok(tx.compute_txid())
}
}
pub struct Balance {
pub confirmed: u64,
pub pending: u64,
}
4.2 命令列介面
使用 clap crate 可以輕鬆創建命令列介面:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
use clap::{Parser, Subcommand};
#[derive(Parser)]
#[command(name = "btc-wallet")]
#[command(about = "簡單的 Bitcoin 錢包")]
struct Cli {
#[command(subcommand)]
command: Commands,
#[arg(long, default_value = "testnet")]
network: String,
#[arg(long, default_value = "~/.btc-wallet")]
data_dir: String,
}
#[derive(Subcommand)]
enum Commands {
/// 創建新錢包
Create { name: String },
/// 從助記詞恢復
Restore { name: String },
/// 顯示餘額
Balance { name: String },
/// 獲取新地址
Address { name: String },
/// 發送 Bitcoin
Send {
name: String,
to: String,
amount: u64,
#[arg(default_value = "10")]
fee_rate: f32,
},
/// 列出交易
History { name: String },
}
fn main() -> anyhow::Result<()> {
let cli = Cli::parse();
match cli.command {
Commands::Create { name } => {
// 生成助記詞並創建錢包
let mnemonic = Mnemonic::generate(12)?;
println!("您的助記詞(請安全備份):\n{}", mnemonic);
let wallet = WalletApp::create(
&name,
&mnemonic.to_string(),
"",
parse_network(&cli.network)?,
Path::new(&cli.data_dir),
)?;
let address = wallet.new_address()?;
println!("\n錢包已創建");
println!("您的第一個地址: {}", address);
}
Commands::Balance { name } => {
let wallet = load_wallet(&name, &cli)?;
wallet.sync()?;
let balance = wallet.balance()?;
println!("已確認: {} sat", balance.confirmed);
println!("待確認: {} sat", balance.pending);
}
// ... 其他命令 ...
}
Ok(())
}
5. 實戰專案:支付處理器
讓我們構建一個簡單的支付處理器,展示如何將所學知識應用到實際場景。
5.1 設計
支付處理器需要:
- 為每筆訂單生成唯一的支付地址
- 監控地址的入帳
- 確認付款後通知商家
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
pub struct PaymentProcessor {
wallet: WalletApp,
pending_payments: HashMap<String, PaymentRequest>,
}
pub struct PaymentRequest {
pub order_id: String,
pub address: Address,
pub amount: Amount,
pub expires_at: u64,
pub callback_url: String,
}
pub struct PaymentConfirmation {
pub order_id: String,
pub txid: Txid,
pub amount: Amount,
pub confirmations: u32,
}
5.2 創建支付請求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
impl PaymentProcessor {
pub fn create_payment(
&mut self,
order_id: String,
amount: Amount,
callback_url: String,
ttl_seconds: u64,
) -> anyhow::Result<PaymentRequest> {
// 生成新地址
let address = self.wallet.new_address()?;
// 計算過期時間
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)?
.as_secs();
let expires_at = now + ttl_seconds;
let request = PaymentRequest {
order_id: order_id.clone(),
address: address.clone(),
amount,
expires_at,
callback_url,
};
// 存儲待處理支付
self.pending_payments.insert(order_id, request.clone());
Ok(request)
}
}
5.3 監控支付
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
impl PaymentProcessor {
pub async fn monitor_payments(&mut self) {
let mut interval = tokio::time::interval(Duration::from_secs(30));
loop {
interval.tick().await;
// 同步錢包
if let Err(e) = self.wallet.sync() {
eprintln!("同步錯誤: {}", e);
continue;
}
// 檢查每個待處理支付
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs();
let mut completed = Vec::new();
let mut expired = Vec::new();
for (order_id, request) in &self.pending_payments {
// 檢查過期
if now > request.expires_at {
expired.push(order_id.clone());
continue;
}
// 檢查是否收到付款
if let Some(confirmation) = self.check_payment(request) {
if confirmation.confirmations >= 1 {
self.notify_payment(&request.callback_url, &confirmation).await;
completed.push(order_id.clone());
}
}
}
// 清理已完成和過期的支付
for id in completed {
self.pending_payments.remove(&id);
}
for id in expired {
self.pending_payments.remove(&id);
}
}
}
fn check_payment(&self, request: &PaymentRequest) -> Option<PaymentConfirmation> {
// 查詢地址的交易
// 檢查是否有符合金額的入帳
// 返回確認信息
None
}
async fn notify_payment(&self, url: &str, confirmation: &PaymentConfirmation) {
// 發送 HTTP 回調通知商家
}
}
6. 測試策略
6.1 單元測試
單元測試應該覆蓋所有核心邏輯,不依賴外部服務。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_address_generation() {
let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
// 這是一個已知的測試助記詞,可以驗證派生結果
let seed = Mnemonic::parse(mnemonic).unwrap().to_seed("");
let xprv = Xpriv::new_master(Network::Testnet, &seed).unwrap();
// 驗證派生的地址符合預期
}
#[test]
fn test_amount_validation() {
// 有效金額
assert!(validate_amount(1000, 546, 1_000_000).is_ok());
// 低於粉塵限制
assert!(validate_amount(100, 546, 1_000_000).is_err());
// 超過上限
assert!(validate_amount(10_000_000, 546, 1_000_000).is_err());
}
}
6.2 Regtest 整合測試
對於需要實際區塊鏈互動的測試,使用 regtest 網路。Regtest 是一個本地測試網路,你可以隨時生成區塊,非常適合測試。
1
2
3
4
5
# 啟動 regtest 節點
bitcoind -regtest -daemon
# 生成區塊
bitcoin-cli -regtest generatetoaddress 101 <your-address>
1
2
3
4
5
6
7
8
9
10
11
12
#[cfg(test)]
mod integration_tests {
#[test]
#[ignore] // 需要 regtest 環境
fn test_full_transaction_flow() {
// 1. 連接到 regtest 節點
// 2. 生成一些區塊以獲得可花費的 UTXO
// 3. 創建並發送交易
// 4. 生成區塊確認交易
// 5. 驗證結果
}
}
7. 總結
本系列帶你從零開始學習使用 Rust 開發 Bitcoin 應用。我們涵蓋了:
第一篇:Rust 環境設置、rust-bitcoin 基礎類型、密碼學原語
第二篇:HD 錢包、地址生成、UTXO 模型、交易構建
第三篇:Bitcoin Script、多簽、ECDSA 和 Schnorr 簽名、Miniscript
第四篇:節點互動、BDK 錢包開發、安全最佳實踐
進一步學習
掌握了這些基礎後,你可以繼續探索:
- Taproot 深入:MAST、腳本樹、MuSig2
- 閃電網路:使用 LDK(Lightning Development Kit)
- 隱私技術:CoinJoin、PayJoin、Silent Payments
- Layer 2:狀態通道、Rollups
Bitcoin 是一個不斷發展的生態系統。保持學習,關注 BIP 提案和社區討論,你會發現這個領域有無限的可能性。
參考資源
官方文檔
學習資源
社群
恭喜完成 Rust Bitcoin 開發入門系列!你現在有了構建 Bitcoin 應用的堅實基礎。實踐是最好的學習方式——開始構建你的第一個專案吧!
Cypherpunks Taiwan
密碼學使自由和隱私再次偉大。Cryptography makes freedom and privacy great again.