从零开始解析比特币CPU挖矿源码:原理与实现
比特币作为第一个去中心化数字货币,其核心机制“挖矿”一直是开发者关注的焦点,尽管如今比特币挖矿已由GPU和ASIC主导,但回顾早期基于CPU的挖矿实现,有助于理解区块链共识机制的本质,本文将以比特币早期源码为基础,解析CPU挖矿的核心原理、代码实现及关键逻辑。
比特币挖矿的本质是通过哈希运算竞争记账权,矿工需将待打包的交易数据、前一区块哈希值、随机数(Nonce)等组合成“区块头”,并通过不断调整Nonce值,使得区块头的双重SHA-256哈希结果满足特定条件(即哈希值小于某个目标值),第一个找到有效Nonce的矿工将获得区块奖励,其打包的交易也将被写入区块链。
在CPU挖矿时代,矿工利用计算机的中央处理器进行哈希运算,尽管CPU算力远低于现代GPU/ASIC,但其通用性和灵活性使其成为早期比特币网络的中坚力量。

比特币早期源码(如 Satoshi 客户端)中,CPU挖矿逻辑主要集中在miner.cpp、hash.h及core.h等文件中,以下从关键数据结构、哈希计算流程及挖矿循环三个方面展开分析。
区块头是挖矿的核心数据结构,其定义在block.h中,主要包括:
class CBlockHeader {
public:
int32_t nVersion; // 版本号
uint256 hashPrevBlock; // 前一区块哈希
uint256 hashMerkleRoot; // 默克尔根
uint32_t nTime; // 时间戳
uint32_t nBits; // 目标难度
uint32_t nNonce; // 随机数(挖矿变量)
};
nNonce是矿工需要不断调整的变量,范围从0到2^32-1。nBits决定了目标难度,值越小,挖矿难度越高。

候选区块(CBlock)包含区块头及交易列表,挖矿时矿工会填充待交易数据并计算默克尔根(hashMerkleRoot),最终生成待哈希的区块头。
比特币采用双重SHA-256哈希算法,即对数据先进行一次SHA-256哈希,再对结果进行第二次SHA-256哈希,源码中哈希计算的核心逻辑在hash.cpp中实现:
void SHA256D64(void *out, const void *in, size_t blocks) {
// 内联汇编优化(针对x86 CPU)
uint256 hash1, hash2;
SHA256_CTX ctx;
for (size_t i = 0; i < blocks; i) {
SHA256_Init(&ctx);
SHA256_Update(&ctx, (const uint8_t*)in i * 64, 64);
SHA256_Final((uint8_t*)&hash1, &ctx);
SHA256_Init(&ctx);
SHA256_Update(&ctx, (const uint8_t*)&hash1, 32);
SHA256_Final((uint8_t*)&hash2, &ctx);
memcpy((uint8_t*)out i * 32, &hash2, 32);
}
}
SHA256D64函数实现了批量双重SHA-256计算,in为输入数据(即序列化后的区块头),out为输出哈希值(32字节),源码中通过内联汇编对x86 CPU进行了优化,提升哈希计算效率。

CPU挖矿的核心循环在miner.cpp的BitcoinMiner函数中,其逻辑如下:
void BitcoinMiner(CWallet *pwallet, bool fProofOfStake) {
// 1. 构建候选区块
CBlock block;
block.vtx = mempool.pool; // 从内存池获取待交易数据
block.hashMerkleRoot = block.BuildMerkleTree();
block.nVersion = nBestChainTip->nVersion;
block.hashPrevBlock = nBestChainTip->GetBlockHash();
block.nTime = GetAdjustedTime();
block.nBits = GetNextWorkRequired(nBestChainTip, NULL);
// 2. 挖矿循环:调整Nonce
uint32_t nNonce = 0;
while (true) {
block.nNonce = nNonce ;
// 3. 计算区块头哈希
uint256 hash = block.GetHash();
// 4. 检查哈希是否满足目标难度
if (hash <= bnTarget) {
// 挖矿成功,广播区块
if (ProcessBlock(NULL, &block)) {
printf("BitcoinMiner: found block %s\n", hash.GetHex().c_str());
break;
}
}
// 5. 超时检查(避免无限循环)
if (GetTime() - nStart > nMaxGenerateGap) {
break;
}
}
}
关键步骤解析:
nNonce 循环遍历所有可能的Nonce值(0~2^32-1)。 block.GetHash()计算双重SHA-256哈希,若哈希值小于目标值bnTarget(由nBits转换而来),则挖矿成功。 nMaxGenerateGap(默认为1小时),则退出循环,避免CPU资源长期占用。 尽管早期比特币源码已针对CPU进行了一定优化(如SHA256D64的汇编优化),但CPU挖矿的固有局限逐渐显现:
在2010年后,GPU挖矿逐渐取代CPU,而2013年ASIC芯片的出现则彻底终结了通用处理器挖矿的时代。
尽管CPU挖矿已成为历史,但其源码仍具有重要的学习价值: