1. 程式人生 > >eos原始碼賞析(二十一):EOS智慧合約之區塊簽名的天龍八“步”

eos原始碼賞析(二十一):EOS智慧合約之區塊簽名的天龍八“步”

在上篇文章中我們提到了,由使用者操作會產生各種事務,事務的鏈上執行是由push_transaction來完成的,我們簡單的劃分了下,具體可參考eos原始碼賞析(二十):EOS智慧合約之push_transaction的天龍八“步” 。我們知道,在區塊生產或者打包事務資訊的時候免不了對資料進行簽名,同時對於非本節點的區塊資訊也要進行驗籤。當然,針對每一個事務也都有簽名及驗籤的過程,我們今天以區塊的簽名過程為例,來談談eosio中的簽名是如何實現的。由於本人在該領域接觸較少,行文中難免出現紕漏和差錯,還望各位讀者能及時批評指正。

本文主要分為以下內容:

  1. SHA256簡介
  2. eos區塊簽名的天龍八“步”

1、SHA256簡介

我們在eos的原始碼閱讀過程中,不管有沒有在意,或多或少的都會遇到SHA256,或者在合約的開發過程中遇到checksum256。我們來看:

SHA全拼:Secure Hash Algorithm,又稱為安全雜湊演算法,是一種能計算出一個數字訊息所對應到的,長度固定的字串(又稱訊息摘要)的演算法。且若輸入的訊息不同,它們對應到不同字串的概率很高;而SHA是FIPS所認證的五種安全雜湊演算法。這些演算法之所以稱作“安全”是基於以下兩點(根據官方標準的描述):由訊息摘要反推原輸入訊息,從計算理論上來說是很困難的。想要找到兩組不同的訊息對應到相同的訊息摘要,從計算理論上來說也是很困難的。任何對輸入訊息的變動,都有很高的概率導致其產生的訊息摘要迥異。

對於任意長度的訊息,SHA256都會產生一個256bit長的雜湊值,稱作訊息摘要。這個摘要相當於是個長度為32個位元組的陣列,通常用一個長度為64的十六進位制字串來表示,這就是我們看到在eosio中,一些資料經過hash之後變成了64位的字串的原因。

在eosio中基於安全褲openssl實現了SHA的部分功能,關於如何實現以及SHA實現的原理不作為本文的主要內容,包括中秋節期間被媒體大做文章的黎曼猜想,感興趣的朋友也可以在群內一起討論,我們接下來看區塊生產之後是如何進行簽名的。

2、eos區塊簽名的天龍八“步”

在上篇文章中,我們將push_transaction簡單的分為八步,有利於我們進行程式碼的閱讀,在本文中同樣將區塊簽名的過程分為八步,通過每一步日誌列印的結果來檢視eos中區塊簽名進行了哪些動作:

  • 第一步:producer_plugin區塊生產之後啟動簽名
1void producer_plugin_impl::produce_block() {
2   //獲取chain及block_header相關內容
3   auto signature_provider_itr = _signature_providers.find( pbs->block_signing_key );
4   ....
5   //等等操作,這裡根據當前節點的sign_key進行簽名
6   ....
7   //啟動簽名
8   chain.sign_block( [&]( const digest_type& d ) {
9      auto debug_logger = maybe_make_debug_time_logger();
10      return signature_provider_itr->second(d);
11   } );
12}
  • 第二步:controller中開始進行區塊簽名,這裡我們加了日誌方便接下來的對比
1   void sign_block( const std::function<signature_type( const digest_type& )>& signer_callback  ) {
2       std::string strState = "";
3      auto p = pending->_pending_block_state;
4       strState = fc::json::to_string(*p);
5      dlog("contorller sign_block begin:${state}", ("state", strPending));
6      p->sign( signer_callback );
7       strState = fc::json::to_string(*p);
8       dlog("contorller sign_block end:${state}", ("state", strPending));
9
10      static_cast<signed_block_header&>(*p->block) = p->header;
11   } /// sign_block
  • 第三步:呼叫block_header_state中sign
1 void block_header_state::sign( const std::function<signature_type(const digest_type&)>& signer ) {
2     auto d = sig_digest();
3     dlog(block_header_state::sign":${state}", ("state", d));
4     header.producer_signature = signer( d );
5     EOS_ASSERT( block_signing_key == fc::crypto::public_key( header.producer_signature, d ), wrong_signing_key, "block is signed with unexpected key" );
6  }
  • 第四步:呼叫block_header_state中的sign_digest獲取摘要資訊

這裡我們也加了相應的日誌方便對比:

1  digest_type   block_header_state::sig_digest()const {
 2      std::string strHeaderDig = "";
 3      strHeaderDig = fc::json::to_string(header.digest());
 4      dlog("block_header_state::sig_digest begin,header digest:${state}", ("state", strHeaderDig));
 5      std::string strBmRoot = "";
 6      strBmRoot = fc::json::to_string(blockroot_merkle.get_root());
 7      dlog("block_header_state::sig_digest begin,bm root:${state}", ("state", strBmRoot));
 8     auto header_bmroot = digest_type::hash( std::make_pair( header.digest(), blockroot_merkle.get_root() ) );
 9      dlog("header_bmroot:${state}", ("state", header_bmroot));
10      dlog("pending_schedule_hash:${state}", ("state", pending_schedule_hash));
11     return digest_type::hash( std::make_pair(header_bmroot, pending_schedule_hash) );
12  }

在這一步中,我們看到首先對區塊的頭資訊header進行了hash獲取了其摘要資訊,而後將摘要資訊和默克爾樹的最後一個元素pair之後再次進行hash,最後將本次hash的結果和本節點輪流出塊的hash(每個生產節點是固定的)pair之後再次進行hash,也就是進行了三次hash的過程。關於默克爾樹在區塊鏈或者說在eos中的應用,我們在後續的文章中也會做一些簡單的介紹,然後我們來看取區塊頭本身的hash是如何實現的。

  • 第五步:獲取區塊頭資訊的摘要資訊及默克爾樹的最後一個元素可以看到,獲取區塊頭信心的摘要資訊也是經過一次hash雜湊完成。
1   digest_type block_header::digest()const
 2   {
 3      return digest_type::hash(*this);
 4   }
 5   //按遞增順序獲取當前節點的默克爾樹
 6   DigestType get_root() const {
 7   if (_node_count > 0) {
 8      return _active_nodes.back();
 9    } else {
10        return DigestType();
11     }
12  }
  • 第六步:基於openssl的sha256 hash的實現
 1    static sha256 hash( const T& t ) 
 2    { 
 3      sha256::encoder e; 
 4      fc::raw::pack(e,t);//將需要雜湊的資訊t打包至加密資訊e裡面
 5      return e.result(); //返回打包的結果
 6    }
 7
 8   //sha256的結果
 9    sha256 sha256::encoder::result() {
10      sha256 h;
11      SHA256_Final((uint8_t*)h.data(), &my->ctx );
12      return h;
13    }

在hash的實現過程中,我們可以看到使用了fc庫中的pack將需要雜湊的資訊打包到加密變數e裡面,而在檢視pack的過程中可以發現其依據變數型別對pack進行了多次過載,最終使用openssl庫中的SHA256_Final將hash結果返回。

  • 第七步:將簽名結果放到區塊頭資訊中
1header.producer_signature = signer( d );
  • 第八步:簽名前幾簽名後資訊對比
1//區塊頭資訊簽名之前
 2    "header": {
 3        "timestamp": "2018-09-26T10:58:49.000",
 4        "producer": "eosio",
 5        "confirmed": 0,
 6        "previous": "0001336d4c819c9656e3d8f9619afb65b4d94eb368cb7cbf1b8a0b3175dcfdff",
 7        "transaction_mroot": "0000000000000000000000000000000000000000000000000000000000000000",
 8        "action_mroot": "c2fd5cfedbf61c357b14a05dcdb3ab186aabb394c385f2f2a4daf79fb35cf454",
 9        "schedule_version": 0,
10        "header_extensions": [],
11        "producer_signature": "SIG_K1_111111111111111111111111111111111111111111111111111111111111111116uk5ne"
12    },
13//區塊頭資訊簽名之後
14    "header": {
15        "timestamp": "2018-09-26T10:58:49.000",
16        "producer": "eosio",
17        "confirmed": 0,
18        "previous": "0001336d4c819c9656e3d8f9619afb65b4d94eb368cb7cbf1b8a0b3175dcfdff",
19        "transaction_mroot": "0000000000000000000000000000000000000000000000000000000000000000",
20        "action_mroot": "c2fd5cfedbf61c357b14a05dcdb3ab186aabb394c385f2f2a4daf79fb35cf454",
21        "schedule_version": 0,
22        "header_extensions": [],
23        "producer_signature": "SIG_K1_KdhkFF5W2YVtNdwmCVYmdw3WMoKCcCgestut6wHsWtRuekjaHv7BZkWU4UXJqboozf6JonXru9hVfQVcptWCN23s6YpFjX"
24    }

我們在簽名的各個步驟中分別加了日誌,由於區塊資訊較長,這裡我們貼出區塊頭資訊在簽名前和簽名後的對比。 通過對比可以發現,在基本資訊保持一致的情況下,經過上面的八步操作,producer_signatrue發生了變化,至此也完成了區塊簽名的整個流程。

本文從區塊生產過程出發,一步步介紹區塊在生產過程中是如何實現SHA256簽名的。eos中關於hash的內容很多,從錢包到賬戶,從action到transaction皆是如此,感興趣的同學可以自己摸索下。 如果你覺得我的文章對你有一定的幫助,請點選文章末尾的喜歡該作者。

如果你對eos開發感興趣,歡迎關注本公眾號,一起學習eos開發。

這裡寫圖片描述 微信公眾號 有任何疑問或者指教請新增本人個人微信,當然有對eos開發感興趣或者金庸粉的也可以新增一起交流,備註eos開發或金庸。 這裡寫圖片描述

個人微訊號