高級
GetBlockTemplate
深入了解 Bitcoin Core 的 getblocktemplate RPC,用於礦池軟體獲取區塊模板。
12 分鐘
什麼是 GetBlockTemplate?
GetBlockTemplate(GBT)是 BIP-22/23 定義的挖礦協議,允許礦池軟體從 Bitcoin Core 獲取區塊模板,自行選擇交易並構建區塊頭進行挖礦。
GBT vs getwork
| 特性 | getblocktemplate | getwork(已棄用) |
|---|---|---|
| 交易選擇 | 礦工可自選 | 節點決定 |
| Coinbase | 礦工構建 | 節點提供 |
| 長輪詢 | 支持 | 有限支持 |
| BIP | BIP-22/23 | 無 |
基本用法
# 獲取區塊模板
bitcoin-cli getblocktemplate '{"rules": ["segwit"]}'
# 輸出示例
{
"capabilities": ["proposal"],
"version": 536870912,
"rules": ["csv", "segwit", "taproot"],
"vbavailable": {},
"vbrequired": 0,
"previousblockhash": "00000000000000000002...",
"transactions": [
{
"data": "02000000...",
"txid": "abc123...",
"hash": "def456...",
"depends": [],
"fee": 5000,
"sigops": 1,
"weight": 561
},
...
],
"coinbaseaux": {"flags": ""},
"coinbasevalue": 625005000,
"longpollid": "00000000000000000002...123",
"target": "00000000000000000006...",
"mintime": 1700000000,
"mutable": ["time", "transactions", "prevblock"],
"noncerange": "00000000ffffffff",
"sigoplimit": 80000,
"sizelimit": 4000000,
"weightlimit": 4000000,
"curtime": 1700000100,
"bits": "17034219",
"height": 800001,
"default_witness_commitment": "6a24aa21a9ed..."
} 字段說明
| 字段 | 描述 |
|---|---|
| version | 區塊版本號 |
| previousblockhash | 前一區塊雜湊 |
| transactions | 交易列表(不含 coinbase) |
| coinbasevalue | 最大 coinbase 金額(區塊獎勵 + 手續費) |
| target | 難度目標(256 位) |
| bits | 壓縮難度目標 |
| height | 新區塊高度 |
| curtime | 當前時間戳 |
挖礦流程
使用 GBT 的挖礦流程:
1. 獲取模板
Mining Pool ──getblocktemplate──▶ Bitcoin Core
◀──────template──────
2. 構建 Coinbase 交易
┌─────────────────────────────────────────┐
│ Coinbase Transaction │
│ ├── Input: 空(coinbase 特殊輸入) │
│ │ └── scriptSig: 區塊高度 + 礦池標記 │
│ └── Outputs: │
│ ├── 礦池獎勵地址 │
│ └── witness commitment │
└─────────────────────────────────────────┘
3. 計算 Merkle Root
- Coinbase + 模板中的交易
- 計算 Merkle Tree 根
4. 構建區塊頭
┌───────────────────────────────────────┐
│ Block Header (80 bytes) │
│ ├── version (4 bytes) │
│ ├── previousblockhash (32 bytes) │
│ ├── merkleroot (32 bytes) │
│ ├── time (4 bytes) │
│ ├── bits (4 bytes) │
│ └── nonce (4 bytes) │
└───────────────────────────────────────┘
5. 挖礦(尋找有效 nonce)
while (SHA256(SHA256(header)) > target) {
nonce++;
// 也可以修改 coinbase 的 extraNonce
}
6. 提交區塊
Mining Pool ──submitblock──▶ Bitcoin Core 實現示例
import { createHash } from 'crypto';
interface BlockTemplate {
version: number;
previousblockhash: string;
transactions: Transaction[];
coinbasevalue: number;
target: string;
bits: string;
height: number;
curtime: number;
default_witness_commitment: string;
}
class Miner {
// 構建 Coinbase 交易
buildCoinbase(
template: BlockTemplate,
poolAddress: string,
extraNonce: Buffer
): Buffer {
// BIP-34: 區塊高度必須在 coinbase 中
const heightScript = this.encodeHeight(template.height);
// 構建 scriptSig
const scriptSig = Buffer.concat([
heightScript,
extraNonce,
Buffer.from('Mining Pool', 'utf8')
]);
// 構建 coinbase 交易
const coinbase = new Transaction();
coinbase.addInput(Buffer.alloc(32), 0xffffffff, scriptSig);
coinbase.addOutput(poolAddress, template.coinbasevalue);
// 添加 witness commitment(SegWit)
if (template.default_witness_commitment) {
coinbase.addOutput(
Buffer.from(template.default_witness_commitment, 'hex'),
0
);
}
return coinbase.serialize();
}
// 計算 Merkle Root
calculateMerkleRoot(coinbase: Buffer, transactions: Buffer[]): Buffer {
const hashes = [
this.doubleSha256(coinbase),
...transactions.map(tx => this.doubleSha256(tx))
];
while (hashes.length > 1) {
const newHashes: Buffer[] = [];
for (let i = 0; i < hashes.length; i += 2) {
const left = hashes[i];
const right = hashes[i + 1] || left;
newHashes.push(this.doubleSha256(Buffer.concat([left, right])));
}
hashes.length = 0;
hashes.push(...newHashes);
}
return hashes[0];
}
// 構建區塊頭
buildHeader(template: BlockTemplate, merkleRoot: Buffer, nonce: number): Buffer {
const header = Buffer.alloc(80);
let offset = 0;
// version
header.writeUInt32LE(template.version, offset);
offset += 4;
// previousblockhash
Buffer.from(template.previousblockhash, 'hex').reverse().copy(header, offset);
offset += 32;
// merkleroot
merkleRoot.copy(header, offset);
offset += 32;
// time
header.writeUInt32LE(template.curtime, offset);
offset += 4;
// bits
header.writeUInt32LE(parseInt(template.bits, 16), offset);
offset += 4;
// nonce
header.writeUInt32LE(nonce, offset);
return header;
}
// 挖礦主循環
async mine(template: BlockTemplate): Promise {
const target = Buffer.from(template.target, 'hex');
let extraNonce = 0;
while (true) {
const coinbase = this.buildCoinbase(
template,
this.poolAddress,
this.encodeExtraNonce(extraNonce)
);
const merkleRoot = this.calculateMerkleRoot(
coinbase,
template.transactions.map(tx => Buffer.from(tx.data, 'hex'))
);
// 嘗試所有 nonce 值
for (let nonce = 0; nonce < 0xffffffff; nonce++) {
const header = this.buildHeader(template, merkleRoot, nonce);
const hash = this.doubleSha256(header);
if (hash.compare(target) < 0) {
// 找到有效區塊!
return this.assembleBlock(header, coinbase, template.transactions);
}
}
extraNonce++;
}
}
} 長輪詢
# 使用長輪詢等待新模板
bitcoin-cli getblocktemplate '{
"rules": ["segwit"],
"longpollid": "00000000000000000002...123"
}'
# longpollid 來自上一次 getblocktemplate 響應
# 當有新區塊或 mempool 顯著變化時返回 長輪詢工作流程:
┌──────────────┐ ┌──────────────┐
│ Mining Pool │ │ Bitcoin Core │
└──────┬───────┘ └──────┬───────┘
│ │
│──getblocktemplate(longpollid)────▶│
│ │
│ (等待...) │
│ │
│◀────────new template──────────────│ 新區塊到達
│ │
│──getblocktemplate(new longpollid)▶│
│ │
▼ ▼ 提交區塊
# 提交挖到的區塊
bitcoin-cli submitblock "hexdata"
# 返回值:
# null - 成功
# "duplicate" - 區塊已存在
# "duplicate-invalid" - 區塊已被標記為無效
# "inconclusive" - 有效但未成為最佳鏈
# "rejected" - 被拒絕
# 也可以使用 submitheader 提交區塊頭
bitcoin-cli submitheader "hexheader" 總結
- ✓ BIP-22/23:標準化的挖礦協議
- ✓ 交易選擇:礦工可自行選擇交易
- ✓ 長輪詢:高效的模板更新機制
- ⚠ 注意:大多數礦池使用 Stratum 協議
已複製連結