探索比特币源码8-哈希2
上一文,我們介紹了Bitcoin Core中哈希相關的代碼實現中的一部分
主要是存儲哈希函數映射結果——哈希值的數據結構uint256、uint160及他們的基類base_blob
這篇文章我們繼續來探討其他哈希相關的代碼,主要是:
- Bitcoin Core對于Hash的封裝
- SHA-256的底層實現
- RIPEMD-160的底層實現
Bitcoin Core對于Hash的封裝
比特幣核心對于哈希相關的封裝位于hash.h中
其中提供了進行SHA256d和Hash160的相關接口
其中:
SHA256d(x) = SHA256(SHA256(x))
Hash160(x) = RIPEMD160(SHA256(x))
直接來看源碼(只給出了前半部分主要內容):
// Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2018 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php.#ifndef BITCOIN_HASH_H #define BITCOIN_HASH_H#include <crypto/ripemd160.h> // ripemd160的底層實現 #include <crypto/sha256.h> // sha256的底層實現 #include <prevector.h> #include <serialize.h> #include <uint256.h> #include <version.h>#include <vector>typedef uint256 ChainCode;/** A hasher class for Bitcoin's 256-bit hash (double SHA-256). * 封裝了比特幣 256-bit 哈希算法的類型 * 注意!這個類計算的 Bitcoin 256 hash 通常被稱為 SHA256d * SHA256d != SHA-256, 實際上,SHA256d(x) = SHA256(SHA256(x)) */ class CHash256 { private:CSHA256 sha; // 定義于`crypto/sha256.h`中 public:static const size_t OUTPUT_SIZE = CSHA256::OUTPUT_SIZE;// 完成HASH映射,需配合Write()函數使用void Finalize(unsigned char hash[OUTPUT_SIZE]) {unsigned char buf[CSHA256::OUTPUT_SIZE]; // 存儲hash結果sha.Finalize(buf); // 完成第一次SHA256計算sha.Reset().Write(buf, CSHA256::OUTPUT_SIZE).Finalize(hash); // 重置后,使用第一次的結果作為輸入,再進行一個SHA256,得到SHA256d的最終結果}// 寫入待hash的數據CHash256& Write(const unsigned char *data, size_t len) {sha.Write(data, len);return *this;}// 重置類的實例(使用同一CHash256的實例進行多次SHA256d計算前,需要先使用本方法重置)CHash256& Reset() {sha.Reset();return *this;} };/** A hasher class for Bitcoin's 160-bit hash (SHA-256 + RIPEMD-160). * 封裝了比特幣 160-bit 哈希算法的類型 * Hash160(x) = RIPEMD160(SHA256(x)) */ class CHash160 { private:CSHA256 sha; // 定義于`crypto/ripemd160.h`中 public:static const size_t OUTPUT_SIZE = CRIPEMD160::OUTPUT_SIZE;// 完成HASH映射,需配合Write()函數使用void Finalize(unsigned char hash[OUTPUT_SIZE]) {unsigned char buf[CSHA256::OUTPUT_SIZE]; // 存儲hash結果sha.Finalize(buf); // 完成第一次SHA256計算CRIPEMD160().Write(buf, CSHA256::OUTPUT_SIZE).Finalize(hash); // 重置后,使用第一次的結果作為輸入,再進行一個RIPEMD160,得到Hash160的最終結果}// 寫入待hash的數據CHash160& Write(const unsigned char *data, size_t len) {sha.Write(data, len);return *this;}// 重置類的實例(使用同一CHash256的實例進行多次SHA256d計算前,需要先使用本方法重置)CHash160& Reset() {sha.Reset();return *this;} };/** Compute the 256-bit hash of an object. * 該函數提供了計算SHA256d的簡便方法。否則你需要人為構造CHash256的實例,并調用Write()和Finalize()函數 * 注意! SHA256d(x) = SHA256(SHA256(x)) */ template<typename T1> inline uint256 Hash(const T1 pbegin, const T1 pend) {static const unsigned char pblank[1] = {};uint256 result;CHash256().Write(pbegin == pend ? pblank : (const unsigned char*)&pbegin[0], (pend - pbegin) * sizeof(pbegin[0])).Finalize((unsigned char*)&result);return result; }/** Compute the 256-bit hash of the concatenation of two objects. */ template<typename T1, typename T2> inline uint256 Hash(const T1 p1begin, const T1 p1end,const T2 p2begin, const T2 p2end) {static const unsigned char pblank[1] = {};uint256 result;CHash256().Write(p1begin == p1end ? pblank : (const unsigned char*)&p1begin[0], (p1end - p1begin) * sizeof(p1begin[0])).Write(p2begin == p2end ? pblank : (const unsigned char*)&p2begin[0], (p2end - p2begin) * sizeof(p2begin[0])).Finalize((unsigned char*)&result);return result; }/** Compute the 160-bit hash an object. * 該函數提供了計算Hash160的簡便方法。否則你需要人為構造CHash160的實例,并調用Write()和Finalize()函數 * 注意! Hash160(x) = RIPEMD160(SHA256(x)) */ template<typename T1> inline uint160 Hash160(const T1 pbegin, const T1 pend) {static unsigned char pblank[1] = {};uint160 result;CHash160().Write(pbegin == pend ? pblank : (const unsigned char*)&pbegin[0], (pend - pbegin) * sizeof(pbegin[0])).Finalize((unsigned char*)&result);return result; }/** Compute the 160-bit hash of a vector. */ inline uint160 Hash160(const std::vector<unsigned char>& vch) {return Hash160(vch.begin(), vch.end()); }/** Compute the 160-bit hash of a vector. */ template<unsigned int N> inline uint160 Hash160(const prevector<N, unsigned char>& vch) {return Hash160(vch.begin(), vch.end()); }SHA-256的底層實現
進行SHA-256和RIPEMD-160哈希映射的真正底層實現位于crypto/目錄下
// Copyright (c) 2014-2018 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php.#ifndef BITCOIN_CRYPTO_SHA256_H #define BITCOIN_CRYPTO_SHA256_H#include <stdint.h> #include <stdlib.h> #include <string>/** A hasher class for SHA-256. */ class CSHA256 { private:uint32_t s[8]; // 8個哈希初值,Initialize中進行初始化// 緩沖區(SHA256以512bit為單位,一個block一個block的進行hash映射,因此緩沖區為64byte)// 將待哈希的序列添加進緩沖區,每當緩沖區填滿,進行一次變換,將緩沖區清空unsigned char buf[64];// 記錄曾經加入到緩沖區的總字節數// 因此當全部數據寫入后,bytes代表了待Hash的數據的總字節數// 還可以通過對64取余可以判斷當前緩沖區中存在的剩余數據的字節數uint64_t bytes;// 這兩個私有成員的含義不太好理解,可以通過閱讀我寫的博客更好的理解源代碼// https://blog.csdn.net/u011583927/article/details/80905740 public:static const size_t OUTPUT_SIZE = 32;CSHA256();CSHA256& Write(const unsigned char* data, size_t len);void Finalize(unsigned char hash[OUTPUT_SIZE]);CSHA256& Reset(); };/** Autodetect the best available SHA256 implementation.* Returns the name of the implementation.*/ std::string SHA256AutoDetect();/** Compute multiple double-SHA256's of 64-byte blobs.* output: pointer to a blocks*32 byte output buffer* input: pointer to a blocks*64 byte input buffer* blocks: the number of hashes to compute.*/ void SHA256D64(unsigned char* output, const unsigned char* input, size_t blocks);#endif // BITCOIN_CRYPTO_SHA256_H其中Write()和Finalize() 是兩個核心方法,其源代碼為:
// 將制定數據寫入緩沖區中并處理 // 對數據進行處理的方法為:每填滿一個緩沖區,進行一次hash變換,未能填滿緩沖區不進行額外操作 CSHA256& CSHA256::Write(const unsigned char* data, size_t len) {const unsigned char* end = data + len;size_t bufsize = bytes % 64; // 緩沖區中已有的byte數(需一直維護使得該定義持續成立)// 考慮到Write可能被多次調用,因此緩沖區中的可能存在剩余數據,即 bufsize!=0if (bufsize && bufsize + len >= 64) { // 緩沖區中已有一部分數據,且加上待拷貝的len個byte將會超出緩沖區大小// Fill the buffer, and process it.// 將緩沖區填滿(緩沖區現有byte字節,因此填充 64 - bufsize個字節進來就填滿了)memcpy(buf + bufsize, data, 64 - bufsize);bytes += 64 - bufsize;data += 64 - bufsize;Transform(s, buf, 1);bufsize = 0;}// 執行到這里有兩種情況:// 1.緩沖區有數據,但加上待寫入的len個byte也填充不滿緩沖區// 2.緩沖區內為空(因為即使緩沖區不為空,也已在上一個if語句塊中處理,將緩沖區填滿并進行變換)if (end - data >= 64) {// 有多少個 512-bits,就進行多少次Hash變換size_t blocks = (end - data) / 64; // 計算寫入的數據有多少個完整的512bits塊Transform(s, data, blocks); // 進行blocks次hash映射data += 64 * blocks;bytes += 64 * blocks;}// 執行到這里,data中可能有少部分剩余數據待寫入,但剩余數據不會超過64byteif (end > data) {// Fill the buffer with what remains. 將剩余數據加入緩沖區就好// 由于bufsize代表緩沖區中已存在的字節數,將剩余數據從 buf + bufsize 指向的位置開始拷貝memcpy(buf + bufsize, data, end - data);bytes += end - data;}return *this; } // Finalize 函數最終確定SHA-256的結果 // 由于SHA-256算法最后需要完成填充數據的操作 // 因此需要 Write + Finalize 的調用方法來計算SHA-256算法 void CSHA256::Finalize(unsigned char hash[OUTPUT_SIZE]) {static const unsigned char pad[64] = {0x80}; // 第一步待填充的附加比特{1000 0000 0000 0000 ...}unsigned char sizedesc[8]; // 第二步待填充的數據長度值WriteBE64(sizedesc, bytes << 3); // 長度值為bytes*8Write(pad, 1 + ((119 - (bytes % 64)) % 64)); // 填充附加比特Write(sizedesc, 8); // 填充長度值// 將哈希結果寫入數組hash中WriteBE32(hash, s[0]);WriteBE32(hash + 4, s[1]);WriteBE32(hash + 8, s[2]);WriteBE32(hash + 12, s[3]);WriteBE32(hash + 16, s[4]);WriteBE32(hash + 20, s[5]);WriteBE32(hash + 24, s[6]);WriteBE32(hash + 28, s[7]); }其中WriteBE32()函數的含義尚不明確,其源碼位于crypto/common.h中
void static inline WriteBE32(unsigned char* ptr, uint32_t x) {uint32_t v = htobe32(x); // 四字節整數的大小端轉換memcpy(ptr, (char*)&v, 4); }而htobe32()的代碼又位于compat/endian.h中
// ... 此處省略若干代碼 #if defined(WORDS_BIGENDIAN) // ... 此處省略若干代碼 #if HAVE_DECL_HTOBE32 == 0 inline uint32_t htobe32(uint32_t host_32bits) {return host_32bits; } #endif // HAVE_DECL_HTOBE32 // ... 此處省略若干代碼 #else // WORDS_BIGENDIAN // ... 此處省略若干代碼 #if HAVE_DECL_HTOBE32 == 0 inline uint32_t htobe32(uint32_t host_32bits) {return bswap_32(host_32bits); } #endif // HAVE_DECL_HTOBE32 // ... 此處省略若干代碼 #endif // WORDS_BIGENDIAN因此可以看到,htobe32()根據WORDS_BIGENDIAN標識進行不同的操作
再探究bswap_32的實現最終追溯到compat/byteswap.h的uint32_t bswap_32()函數中
inline uint32_t bswap_32(uint32_t x) // 四字節整數的大小端轉換 {return (((x & 0xff000000U) >> 24) | ((x & 0x00ff0000U) >> 8) |((x & 0x0000ff00U) << 8) | ((x & 0x000000ffU) << 24)); }因此最終確定WriteBE32(unsigned char* ptr, uint32_t x)函數的功能是將指定的整數x按照大端格式,寫入ptr指向的內存上
RIPEMD-160的底層實現
對于RIPEMD-160,接口完全一致,詳細實現這里就不做過多分析了
RIPEMD-160定義部分的代碼如下:
// Copyright (c) 2014-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php.#ifndef BITCOIN_CRYPTO_RIPEMD160_H #define BITCOIN_CRYPTO_RIPEMD160_H#include <stdint.h> #include <stdlib.h>/** A hasher class for RIPEMD-160. */ class CRIPEMD160 { private:uint32_t s[5];unsigned char buf[64];uint64_t bytes;public:static const size_t OUTPUT_SIZE = 20;CRIPEMD160();CRIPEMD160& Write(const unsigned char* data, size_t len);void Finalize(unsigned char hash[OUTPUT_SIZE]);CRIPEMD160& Reset(); };#endif // BITCOIN_CRYPTO_RIPEMD160_H總結
以上是生活随笔為你收集整理的探索比特币源码8-哈希2的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: leetcode No.21 合并两个有
- 下一篇: 模拟网页行为之工具篇