news 2026/6/11 22:06:45

从零手搓一个SHA-256:用C语言一步步实现比特币的‘心脏’算法

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从零手搓一个SHA-256:用C语言一步步实现比特币的‘心脏’算法

从零手搓一个SHA-256:用C语言一步步实现比特币的‘心脏’算法

当你用比特币转账时,是否好奇过那串看似随机的交易ID是如何生成的?或者当矿工们谈论"算力"时,他们究竟在计算什么?这一切的核心,都离不开一个名为SHA-256的密码学算法。今天,我们将抛开现成的库函数,用C语言从零开始实现这个支撑整个区块链网络的"数字指纹"生成器。

1. 为什么选择SHA-256?

在开始编码之前,我们需要理解SHA-256为何能成为区块链技术的基石。想象你要给朋友发送一封电子邮件,如何确保内容在传输过程中没有被篡改?哈希算法就是解决这个问题的"数字指纹"生成器。

SHA-256的三个关键特性

  • 确定性:相同输入永远产生相同输出
  • 雪崩效应:1比特的输入变化会导致约50%的输出比特改变
  • 不可逆性:无法从哈希值反推出原始数据

在比特币网络中,每个区块都包含前一个区块的哈希值,形成不可篡改的链条。矿工们竞争的正是找到一个满足特定条件的SHA-256哈希值,这个过程就是我们常说的"挖矿"。

2. 搭建SHA-256的基础结构

2.1 定义基本宏操作

SHA-256的核心是一系列位操作,我们先定义关键的宏:

#define ROTR(x, n) (((x) >> (n)) | ((x) << (32 - (n)))) #define CH(x, y, z) (((x) & (y)) ^ (~(x) & (z))) #define MAJ(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) #define SIG0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ ((x) >> 3)) #define SIG1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ ((x) >> 10))

这些看似复杂的操作实际上是SHA-256实现"混淆"和"扩散"的关键。例如,ROTR实现32位字的循环右移,这是密码学中常用的操作。

2.2 初始化哈希常量

SHA-256使用两组预设的常量值:

// 初始哈希值(前8个质数的平方根小数部分前32位) uint32_t H[8] = { 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 }; // 64个常量(前64个质数的立方根小数部分前32位) uint32_t K[64] = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, // ... 完整列表见标准文档 };

这些"魔法数字"并非随意选择,而是来自数学常数的特定部分,确保了算法的随机性和安全性。

3. 实现消息预处理

3.1 填充消息

SHA-256要求输入长度必须是512位的整数倍。我们的填充规则是:

  1. 在消息末尾添加一个'1'位
  2. 添加足够多的'0'位
  3. 最后64位表示原始消息的位长度
void pad_message(uint8_t *message, uint64_t length) { uint64_t bit_length = length * 8; uint64_t new_length = ((length + 8) / 64 + 1) * 64; // 添加'1'位 message[length] = 0x80; // 添加'0'位 for (uint64_t i = length + 1; i < new_length - 8; i++) { message[i] = 0; } // 添加原始位长度(大端序) for (int i = 0; i < 8; i++) { message[new_length - 8 + i] = (bit_length >> (56 - i * 8)) & 0xFF; } }

3.2 消息分块处理

填充后的消息被分割成512位的块,每个块又分为16个32位字:

void process_block(const uint8_t *block, uint32_t *hash) { uint32_t W[64]; uint32_t a, b, c, d, e, f, g, h; // 1. 准备消息调度表W for (int t = 0; t < 16; t++) { W[t] = (block[t*4] << 24) | (block[t*4+1] << 16) | (block[t*4+2] << 8) | block[t*4+3]; } for (int t = 16; t < 64; t++) { W[t] = SIG1(W[t-2]) + W[t-7] + SIG0(W[t-15]) + W[t-16]; } // 2. 初始化工作变量 a = hash[0]; b = hash[1]; c = hash[2]; d = hash[3]; e = hash[4]; f = hash[5]; g = hash[6]; h = hash[7]; // 3. 执行64轮压缩 for (int t = 0; t < 64; t++) { uint32_t T1 = h + EP1(e) + CH(e,f,g) + K[t] + W[t]; uint32_t T2 = EP0(a) + MAJ(a,b,c); h = g; g = f; f = e; e = d + T1; d = c; c = b; b = a; a = T1 + T2; } // 4. 更新哈希值 hash[0] += a; hash[1] += b; hash[2] += c; hash[3] += d; hash[4] += e; hash[5] += f; hash[6] += g; hash[7] += h; }

4. 完整SHA-256实现

现在我们将各个部分组合成完整的实现:

#include <stdio.h> #include <stdint.h> #include <string.h> typedef struct { uint8_t data[64]; uint32_t datalen; uint64_t bitlen; uint32_t state[8]; } SHA256_CTX; void sha256_init(SHA256_CTX *ctx) { ctx->datalen = 0; ctx->bitlen = 0; memcpy(ctx->state, H, 8 * sizeof(uint32_t)); } void sha256_update(SHA256_CTX *ctx, const uint8_t *data, size_t len) { for (size_t i = 0; i < len; i++) { ctx->data[ctx->datalen] = data[i]; ctx->datalen++; if (ctx->datalen == 64) { process_block(ctx->data, ctx->state); ctx->bitlen += 512; ctx->datalen = 0; } } } void sha256_final(SHA256_CTX *ctx, uint8_t *hash) { uint32_t i = ctx->datalen; // 填充消息 if (ctx->datalen < 56) { ctx->data[i++] = 0x80; while (i < 56) ctx->data[i++] = 0x00; } else { ctx->data[i++] = 0x80; while (i < 64) ctx->data[i++] = 0x00; process_block(ctx->data, ctx->state); memset(ctx->data, 0, 56); } // 添加位长度 ctx->bitlen += ctx->datalen * 8; for (i = 0; i < 8; i++) { ctx->data[56 + i] = (ctx->bitlen >> (56 - i * 8)) & 0xFF; } process_block(ctx->data, ctx->state); // 生成最终哈希值 for (i = 0; i < 8; i++) { hash[i*4] = (ctx->state[i] >> 24) & 0xFF; hash[i*4+1] = (ctx->state[i] >> 16) & 0xFF; hash[i*4+2] = (ctx->state[i] >> 8) & 0xFF; hash[i*4+3] = ctx->state[i] & 0xFF; } }

5. 测试我们的实现

让我们用经典的"abc"字符串测试我们的实现:

int main() { SHA256_CTX ctx; uint8_t hash[32]; char *msg = "abc"; sha256_init(&ctx); sha256_update(&ctx, (uint8_t*)msg, strlen(msg)); sha256_final(&ctx, hash); printf("SHA-256 hash of '%s':\n", msg); for (int i = 0; i < 32; i++) { printf("%02x", hash[i]); } printf("\n"); return 0; }

正确输出应该是:

ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad

6. SHA-256与比特币挖矿

现在你理解了SHA-256的工作原理,就能真正理解比特币挖矿的本质。矿工们实际上是在寻找一个随机数(nonce),使得:

SHA-256(SHA-256(区块头)) < 目标难度值

这个过程的计算复杂度正是比特币安全性的基础。我们实现的SHA-256虽然功能完整,但为了实际挖矿,你还需要:

  1. 优化实现(如使用SIMD指令)
  2. 添加矿工逻辑(改变nonce并重复计算)
  3. 连接比特币网络验证区块

在开发过程中,我遇到的一个有趣现象是:即使使用相同的代码,在不同平台上可能会因为字节序问题得到不同的结果。这提醒我们密码学实现中细节的重要性——一个微小的错误就可能完全破坏安全性。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/11 22:04:54

【Android】基于VLC与SurfaceView实现RTSP监控流的低延迟播放

1. 为什么选择VLCSurfaceView方案 在Android平台上播放RTSP监控视频流时&#xff0c;开发者常会遇到两个致命问题&#xff1a;延迟高和画面卡顿。我做过不少安防监控类项目&#xff0c;实测过各种方案后发现&#xff0c;VLCSurfaceView的组合在延迟控制方面表现突出。VLC作为老…

作者头像 李华
网站建设 2026/6/11 21:54:53

2026年企业AI基础设施选型指南:看什么、怎么选、怎么避雷

开篇&#xff1a;选型选错了&#xff0c;后面全是弯路企业AI基础设施的选型&#xff0c;是一个一旦选错就很难回头的事情。为什么&#xff1f;因为AI基础设施不是一件工具&#xff0c;而是一套能力体系——你的Agent、知识库、技能库、治理体系都会长在这套基础设施之上。一旦选…

作者头像 李华
网站建设 2026/6/11 21:52:00

从自建微信群发机器人到工业级企微 API 中台:基于响应式 Web 面板的高并发调度架构

在企业私域流量运营与数字化协作的早期阶段&#xff0c;许多技术团队都会尝试基于开源协议开发一些简单的微信群发机器人。这种模式在账号规模较小、群发频率较低的场景下运作尚可&#xff1b;然而&#xff0c;随着企业业务体量扩大&#xff0c;托管账号破百、外部群上千、消息…

作者头像 李华
网站建设 2026/6/11 21:51:59

动态激励合约设计:通证经济的未来之路

发散创新:基于 Solidity 的通证经济动态激励合约设计与实战 在 Web— 一、为什么静态通胀模型正在失效? 当前多数项目采s.lastActiveBlock; uint256 decayedScore = (s.s0 * avgScore(生产环境需维护 totalScore) return (normalizedScore * rewardPerBlock) / 1e21; //…

作者头像 李华