探索比特币源码7-哈希
在之前的兩篇文章中,我們已經(jīng)大致了解了私鑰和公鑰相關(guān)的代碼:
探索比特幣源碼5-私鑰
探索比特幣源碼6-公鑰
接下來,我們打算閱讀生成比特幣地址的相關(guān)代碼。
比特幣地址的生成過程中,涉及了哈希函數(shù)的計(jì)算,這些是更底層的一些實(shí)現(xiàn)。
為了更好的進(jìn)行后面的源碼閱讀,本文先來探索下Bitcoin Core中哈希相關(guān)的代碼實(shí)現(xiàn)。
哈希值的數(shù)據(jù)結(jié)構(gòu)
哈希函數(shù)可以將任意長(zhǎng)度的二進(jìn)制數(shù)據(jù)映射為某一固定長(zhǎng)度的哈希值,或稱哈希散列
比特幣使用的哈希函數(shù)是SHA-256和RIPEMD-160
如何描述這兩種哈希函數(shù)映射的結(jié)果呢(其實(shí)就是 256bit二進(jìn)制數(shù)組 和 160bit二進(jìn)制數(shù)組)?
比特幣系統(tǒng)將相應(yīng)的數(shù)據(jù)結(jié)構(gòu)uint256和uint160封裝于uint256.h中
其中,uint256和uint160均繼承自模板基類base_blob
base_blob的描述是:固定大小的不透明二進(jìn)制大對(duì)象blob(Binary Large OBject)的模板基類。
也就是說,我們可以通過繼承base_blob,構(gòu)造出描述任意長(zhǎng)度二進(jìn)制數(shù)據(jù)的對(duì)象,可用于擴(kuò)展對(duì)于其他哈希函數(shù)的散列值的定義。
下面來看uint256.h的源代碼
// 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_UINT256_H #define BITCOIN_UINT256_H#include <assert.h> #include <cstring> #include <stdexcept> #include <stdint.h> #include <string> #include <vector> #include <crypto/common.h>/** Template base class for fixed-sized opaque blobs.* 固定大小的不透明二進(jìn)制大對(duì)象(blob, Binary Large OBject)的模板基類* 模板參數(shù)為二進(jìn)制對(duì)象的長(zhǎng)度 */ template<unsigned int BITS> class base_blob { protected:static constexpr int WIDTH = BITS / 8; // 字節(jié)寬度(雖然是二進(jìn)制,但也得以byte為單位存儲(chǔ))uint8_t data[WIDTH]; // 存儲(chǔ)二進(jìn)制數(shù)據(jù)的byte數(shù)組 public:// 無參數(shù)構(gòu)造函數(shù)base_blob(){memset(data, 0, sizeof(data)); // 全0二進(jìn)制數(shù)組,相當(dāng)于初始化為空}// 使用vector<unsigned char>初始化二進(jìn)制對(duì)象explicit base_blob(const std::vector<unsigned char>& vch);// 二進(jìn)制對(duì)象是否為空(0即是空)bool IsNull() const{for (int i = 0; i < WIDTH; i++)if (data[i] != 0)return false;return true;}// 將二進(jìn)制對(duì)象設(shè)置為空void SetNull(){memset(data, 0, sizeof(data));}// 與兩一個(gè)二進(jìn)制對(duì)象比較大小(按照memcmp方法比較)inline int Compare(const base_blob& other) const { return memcmp(data, other.data, sizeof(data)); }// 重載 == != < 運(yùn)算符// 二進(jìn)制對(duì)象的比較涉及到私有成員data,因此定義為友元函數(shù)friend inline bool operator==(const base_blob& a, const base_blob& b) { return a.Compare(b) == 0; }friend inline bool operator!=(const base_blob& a, const base_blob& b) { return a.Compare(b) != 0; }friend inline bool operator<(const base_blob& a, const base_blob& b) { return a.Compare(b) < 0; }// 獲取二進(jìn)制對(duì)象對(duì)應(yīng)的16進(jìn)制字符串(注意!代碼采用的小端實(shí)現(xiàn),詳見后文)std::string GetHex() const;// 使用16進(jìn)制字符串為二進(jìn)制對(duì)象賦值// 注1:16進(jìn)制字符串應(yīng)為小端形式// 注2:從源碼實(shí)現(xiàn)來看,調(diào)用者應(yīng)自行確保字符串長(zhǎng)度與待賦值二進(jìn)制對(duì)象長(zhǎng)度的一致性void SetHex(const char* psz);void SetHex(const std::string& str);std::string ToString() const;// 使得base_blob類像vector<T>一樣使用的接口unsigned char* begin(){return &data[0];}unsigned char* end(){return &data[WIDTH];}const unsigned char* begin() const{return &data[0];}const unsigned char* end() const{return &data[WIDTH];}unsigned int size() const{return sizeof(data);}// 讀取二進(jìn)制對(duì)象中的第pos個(gè)uint64// 注意也是小端實(shí)現(xiàn),暫不知道該函數(shù)作用uint64_t GetUint64(int pos) const{const uint8_t* ptr = data + pos * 8;return ((uint64_t)ptr[0]) | \((uint64_t)ptr[1]) << 8 | \((uint64_t)ptr[2]) << 16 | \((uint64_t)ptr[3]) << 24 | \((uint64_t)ptr[4]) << 32 | \((uint64_t)ptr[5]) << 40 | \((uint64_t)ptr[6]) << 48 | \((uint64_t)ptr[7]) << 56;}template<typename Stream>void Serialize(Stream& s) const{s.write((char*)data, sizeof(data));}template<typename Stream>void Unserialize(Stream& s){s.read((char*)data, sizeof(data));} };/** 160-bit opaque blob.* @note This type is called uint160 for historical reasons only. It is an opaque* blob of 160 bits and has no integer operations.* 如注釋中所述,這個(gè)類叫uint160,標(biāo)識(shí)一個(gè)160bit的二進(jìn)制對(duì)象,其實(shí)跟uint一點(diǎn)關(guān)系沒有(估計(jì)僅僅是因?yàn)橹斜韭斪铋_始是這么命名的)*/ class uint160 : public base_blob<160> { public:uint160() {}explicit uint160(const std::vector<unsigned char>& vch) : base_blob<160>(vch) {} };/** 256-bit opaque blob.* @note This type is called uint256 for historical reasons only. It is an* opaque blob of 256 bits and has no integer operations. Use arith_uint256 if* those are required.*/ class uint256 : public base_blob<256> { public:uint256() {}explicit uint256(const std::vector<unsigned char>& vch) : base_blob<256>(vch) {}/** A cheap hash function that just returns 64 bits from the result, it can be* used when the contents are considered uniformly random. It is not appropriate* when the value can easily be influenced from outside as e.g. a network adversary could* provide values to trigger worst-case behavior.*/// 一個(gè)廉價(jià)的哈希函數(shù),只從結(jié)果中返回64位,當(dāng)內(nèi)容被認(rèn)為是均勻隨機(jī)時(shí),可以使用它。// 當(dāng)值很容易受到來自外部的影響時(shí),例如網(wǎng)絡(luò)中的敵對(duì)者可以提供值來觸發(fā)最壞情況的行為時(shí),這是不合適的。// 此函數(shù)的作用尚不明確???uint64_t GetCheapHash() const{return ReadLE64(data);} };/* uint256 from const char *.* This is a separate function because the constructor uint256(const char*) can result* in dangerously catching uint256(0).* 這里作者單獨(dú)分離出了一個(gè)起到構(gòu)造函數(shù)功能的函數(shù),使用SetHex更安全的返回一個(gè)uint256*/ inline uint256 uint256S(const char *str) {uint256 rv;rv.SetHex(str);return rv; } /* uint256 from std::string.* This is a separate function because the constructor uint256(const std::string &str) can result* in dangerously catching uint256(0) via std::string(const char*).*/ inline uint256 uint256S(const std::string& str) {uint256 rv;rv.SetHex(str);return rv; }#endif // BITCOIN_UINT256_H其中GetHex()函數(shù)需要詳細(xì)研究下
該函數(shù)獲取二進(jìn)制對(duì)象對(duì)應(yīng)的16進(jìn)制字符串,uint256.cpp中的實(shí)現(xiàn)為:
template <unsigned int BITS> std::string base_blob<BITS>::GetHex() const {return HexStr(std::reverse_iterator<const uint8_t*>(data + sizeof(data)), std::reverse_iterator<const uint8_t*>(data)); }可以看到,GetHex()內(nèi)部調(diào)用utilstrencodings.h中的HexStr方法
在調(diào)用的時(shí)候,源碼使用了反向迭代器(Reverse Iterator)的技巧。
這說明比特幣系統(tǒng)內(nèi)部對(duì)于哈希散列值,使用小端的方式顯示為16進(jìn)制字符串。這點(diǎn)我是很疑惑的,遂google之,發(fā)現(xiàn)大家對(duì)于比特幣的小端實(shí)現(xiàn)也是爭(zhēng)議頗多的。
HexStr方法的源代碼如下:
template<typename T> std::string HexStr(const T itbegin, const T itend, bool fSpaces=false) {std::string rv;static const char hexmap[16] = { '0', '1', '2', '3', '4', '5', '6', '7','8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };// 為十六進(jìn)制字符串分配空間,一個(gè)字節(jié)轉(zhuǎn)換為兩個(gè)十六進(jìn)制字符,加上可能存在空格間隔符,因此*3rv.reserve((itend-itbegin)*3);for(T it = itbegin; it < itend; ++it){unsigned char val = (unsigned char)(*it);if(fSpaces && it != itbegin) // 判斷是否需要添加空格間隔符rv.push_back(' ');rv.push_back(hexmap[val>>4]); // 取前4位rv.push_back(hexmap[val&15]); // 取后4位}return rv; }再來看下SetHex()函數(shù)
從uint256.h最后定義的兩個(gè)具有類型構(gòu)造函數(shù)功能的函數(shù)可以看出,作者應(yīng)該是更推薦使用SetHex為二進(jìn)制對(duì)象進(jìn)行賦值。
我們來看看源碼:
template <unsigned int BITS> void base_blob<BITS>::SetHex(const char* psz) {memset(data, 0, sizeof(data));// skip leading spaces 跳過頭部的空白字符while (isspace(*psz)) // isspace判斷參數(shù)是否為空白字符(' '、'\t'、'\r'、'\n'、'\v'或'\f')psz++;// skip 0x (不能排除調(diào)用者提供的16進(jìn)制字符串有0x前綴)if (psz[0] == '0' && tolower(psz[1]) == 'x')psz += 2;// hex string to uintconst char* pbegin = psz;// 將psz指向字符串的末尾(注意:比特幣哈希值的16進(jìn)制字符串為小端實(shí)現(xiàn))while (::HexDigit(*psz) != -1) // HexDigit返回字符char對(duì)應(yīng)的16進(jìn)制數(shù)字,非16進(jìn)制數(shù)字(0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f)返回-1psz++;psz--;unsigned char* p1 = (unsigned char*)data;unsigned char* pend = p1 + WIDTH;while (psz >= pbegin && p1 < pend) {*p1 = ::HexDigit(*psz--);if (psz >= pbegin) { // 檢查下一個(gè)字符是否存在(確保16進(jìn)制字符串成對(duì)出現(xiàn))*p1 |= ((unsigned char)::HexDigit(*psz--) << 4);p1++;}} }本文就到這里,主要是介紹了用于存儲(chǔ)哈希值的數(shù)據(jù)結(jié)構(gòu)uint256和uint160,以及他們的基類base_blob。
未完待續(xù)…
總結(jié)
以上是生活随笔為你收集整理的探索比特币源码7-哈希的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Modbus协议栈开发笔记之七:Modb
- 下一篇: Android的ELF文件重定位详解,包