跳至主要內容
入門

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:最接近主網的體驗
  • 永遠不要在主網測試:避免損失真實資金
  • 使用不同的錢包:測試網錢包和主網錢包分開

相關資源

已複製連結
已複製到剪貼簿