入門
Test Networks
比特幣測試網路:Mainnet、Testnet、Signet 和 Regtest 的使用場景和配置
15 分鐘
概述
比特幣提供多種網路環境供開發和測試使用。除了主網(Mainnet)之外, 還有 Testnet、Signet 和 Regtest,各有不同的特性和用途。 開發者應該在這些測試網路上充分測試後,才將應用部署到主網。
網路比較
| 網路 | 用途 | 挖礦難度 | 區塊時間 | 地址前綴 |
|---|---|---|---|---|
| Mainnet | 生產環境 | 真實難度 | ~10 分鐘 | 1, 3, bc1 |
| Testnet | 公開測試 | 可變(低) | ~10 分鐘 | m, n, tb1 |
| Signet | 可控測試 | 簽名控制 | ~10 分鐘 | tb1 |
| Regtest | 本地開發 | 最低 | 即時 | bcrt1 |
Mainnet(主網)
主網是比特幣的生產網路,所有真實的比特幣交易都在這裡進行。
特點
- 真實的經濟價值
- 完整的安全保障
- 不可逆的交易
- 需要真實的手續費
配置
# bitcoin.conf (預設為 mainnet)
# 無需特別設定
# 或明確指定
chain=main
# 資料目錄
# Linux: ~/.bitcoin/
# macOS: ~/Library/Application Support/Bitcoin/
# Windows: %APPDATA%\Bitcoin\ Testnet(測試網)
Testnet 是公開的測試網路,目前運行的是 Testnet3。 幣沒有價值,可以從水龍頭免費獲取。
注意: Testnet 可能會被重置,且容易受到大量算力攻擊導致區塊時間不穩定。 對於需要穩定環境的測試,建議使用 Signet。
特點
- 公開網路,任何人都可以加入
- 幣沒有價值,可免費獲取
- 難度調整規則較寬鬆
- 可能有區塊時間不穩定的問題
配置
# 啟動 testnet
bitcoind -testnet
# 或在 bitcoin.conf 中設定
chain=test
# CLI 使用
bitcoin-cli -testnet getblockchaininfo
# 資料目錄
# ~/.bitcoin/testnet3/ 水龍頭(獲取測試幣)
Signet(簽名網)
Signet(BIP-325)是一種由簽名控制出塊的測試網路。 區塊必須由指定的密鑰簽名才有效,提供更可控的測試環境。
特點
- 區塊產生穩定可預測
- 不受算力波動影響
- 可以創建自定義 Signet
- 預設的公開 Signet 由 Bitcoin Core 維護
配置
# 啟動預設 signet
bitcoind -signet
# 或在 bitcoin.conf 中設定
chain=signet
# CLI 使用
bitcoin-cli -signet getblockchaininfo
# 資料目錄
# ~/.bitcoin/signet/ 自定義 Signet
# bitcoin.conf
chain=signet
# 指定簽名者的公鑰(挑戰腳本)
signetchallenge=5121...公鑰...51ae
# 指定自定義 signet 的種子節點
signetseednode=192.168.1.100:38333 Signet 水龍頭
Regtest(迴歸測試)
Regtest 是完全本地的測試環境,可以即時生成區塊, 非常適合自動化測試和開發。
特點
- 完全本地,無需網路連接
- 可以即時生成區塊
- 完全控制區塊鏈狀態
- 適合單元測試和整合測試
配置
# 啟動 regtest
bitcoind -regtest
# 或在 bitcoin.conf 中設定
chain=regtest
# CLI 使用
bitcoin-cli -regtest getblockchaininfo
# 資料目錄
# ~/.bitcoin/regtest/ 基本操作
# 創建錢包
bitcoin-cli -regtest createwallet "test"
# 獲取新地址
bitcoin-cli -regtest getnewaddress
# 生成區塊(挖礦)
bitcoin-cli -regtest generatetoaddress 101 "bcrt1q..."
# 為什麼是 101 個區塊?
# Coinbase 獎勵需要 100 個確認才能使用
# 發送交易
bitcoin-cli -regtest sendtoaddress "bcrt1q..." 1.0
# 確認交易(生成新區塊)
bitcoin-cli -regtest generatetoaddress 1 "bcrt1q..." TypeScript 實作
網路配置
import * as bitcoin from 'bitcoinjs-lib';
// 網路定義
const networks = {
mainnet: bitcoin.networks.bitcoin,
testnet: bitcoin.networks.testnet,
regtest: bitcoin.networks.regtest,
};
// Signet 網路定義(bitcoinjs-lib 可能需要自定義)
const signet: bitcoin.Network = {
messagePrefix: '\x18Bitcoin Signed Message:\n',
bech32: 'tb',
bip32: {
public: 0x043587cf,
private: 0x04358394,
},
pubKeyHash: 0x6f,
scriptHash: 0xc4,
wif: 0xef,
};
// 根據環境選擇網路
function getNetwork(env: string): bitcoin.Network {
switch (env) {
case 'mainnet':
return bitcoin.networks.bitcoin;
case 'testnet':
return bitcoin.networks.testnet;
case 'signet':
return signet;
case 'regtest':
return bitcoin.networks.regtest;
default:
throw new Error(`Unknown network: ${env}`);
}
} RPC 客戶端
interface RPCConfig {
host: string;
port: number;
username: string;
password: string;
}
const networkPorts: Record<string, number> = {
mainnet: 8332,
testnet: 18332,
signet: 38332,
regtest: 18443,
};
function createRPCClient(network: string, config?: Partial<RPCConfig>) {
const defaults: RPCConfig = {
host: '127.0.0.1',
port: networkPorts[network] || 8332,
username: 'user',
password: 'password',
};
const settings = { ...defaults, ...config };
return {
async call(method: string, params: any[] = []): Promise<any> {
const response = await fetch(
`http://${settings.host}:${settings.port}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization:
'Basic ' +
Buffer.from(`${settings.username}:${settings.password}`).toString(
'base64'
),
},
body: JSON.stringify({
jsonrpc: '2.0',
id: Date.now(),
method,
params,
}),
}
);
const data = await response.json();
if (data.error) {
throw new Error(data.error.message);
}
return data.result;
},
};
}
// 使用範例
const regtest = createRPCClient('regtest');
const blockCount = await regtest.call('getblockcount'); Regtest 測試輔助
class RegtestHelper {
private rpc: ReturnType<typeof createRPCClient>;
private minerAddress?: string;
constructor() {
this.rpc = createRPCClient('regtest');
}
async setup(): Promise<void> {
// 創建錢包
try {
await this.rpc.call('createwallet', ['test']);
} catch (e) {
// 錢包可能已存在
await this.rpc.call('loadwallet', ['test']);
}
// 獲取挖礦地址
this.minerAddress = await this.rpc.call('getnewaddress');
// 生成初始區塊
await this.mine(101);
}
async mine(blocks: number = 1): Promise<string[]> {
if (!this.minerAddress) {
throw new Error('Call setup() first');
}
return this.rpc.call('generatetoaddress', [blocks, this.minerAddress]);
}
async getBalance(): Promise<number> {
return this.rpc.call('getbalance');
}
async sendToAddress(address: string, amount: number): Promise<string> {
const txid = await this.rpc.call('sendtoaddress', [address, amount]);
await this.mine(1); // 確認交易
return txid;
}
async fundAddress(address: string, amount: number): Promise<string> {
return this.sendToAddress(address, amount);
}
async getNewAddress(): Promise<string> {
return this.rpc.call('getnewaddress');
}
async reset(): Promise<void> {
// 停止節點並刪除資料目錄來重置
// 這通常在測試框架中處理
}
}
// 測試範例
async function runTest() {
const helper = new RegtestHelper();
await helper.setup();
console.log('Balance:', await helper.getBalance());
const testAddress = await helper.getNewAddress();
const txid = await helper.sendToAddress(testAddress, 1.0);
console.log('Sent tx:', txid);
} Docker 環境
# docker-compose.yml
version: '3.8'
services:
bitcoin-regtest:
image: ruimarinho/bitcoin-core:latest
command:
- -regtest
- -rpcuser=user
- -rpcpassword=password
- -rpcallowip=0.0.0.0/0
- -rpcbind=0.0.0.0
- -fallbackfee=0.0001
ports:
- "18443:18443"
volumes:
- bitcoin-regtest-data:/home/bitcoin/.bitcoin
bitcoin-testnet:
image: ruimarinho/bitcoin-core:latest
command:
- -testnet
- -rpcuser=user
- -rpcpassword=password
- -rpcallowip=0.0.0.0/0
- -rpcbind=0.0.0.0
ports:
- "18332:18332"
volumes:
- bitcoin-testnet-data:/home/bitcoin/.bitcoin
volumes:
bitcoin-regtest-data:
bitcoin-testnet-data: 測試框架整合
Jest 整合
// jest.setup.ts
import { RegtestHelper } from './regtest-helper';
let regtest: RegtestHelper;
beforeAll(async () => {
regtest = new RegtestHelper();
await regtest.setup();
});
afterEach(async () => {
// 每個測試後生成區塊確保狀態一致
await regtest.mine(1);
});
// test.spec.ts
describe('Bitcoin transactions', () => {
it('should send and receive bitcoin', async () => {
const address = await regtest.getNewAddress();
const txid = await regtest.sendToAddress(address, 0.5);
expect(txid).toMatch(/^[a-f0-9]{64}$/);
});
}); 最佳實踐
- 開發階段用 Regtest:快速迭代,完全控制
- 整合測試用 Signet:穩定的公開環境
- 上線前用 Testnet:最接近主網的體驗
- 永遠不要在主網測試:避免損失真實資金
- 使用不同的錢包:測試網錢包和主網錢包分開
相關資源
已複製連結