<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          基于Java開(kāi)發(fā)一套完整的區(qū)塊鏈系統(tǒng)(附源碼)

          共 25491字,需瀏覽 51分鐘

           ·

          2021-01-15 17:27

          來(lái)源:https://blog.csdn.net/victory_long



          前言

          近幾年區(qū)塊鏈概念越來(lái)越火,特別是區(qū)塊鏈技術(shù)被納入國(guó)家基礎(chǔ)設(shè)施建設(shè)名單后,各大企業(yè)也開(kāi)始招兵買(mǎi)馬,對(duì)區(qū)塊鏈技術(shù)進(jìn)行研究,從各大招聘網(wǎng)站的區(qū)塊鏈職位來(lái)看,薪資待遇都很不錯(cuò),月薪30K到80K的都有,這對(duì)于我們程序員來(lái)說(shuō)也是一種機(jī)遇,說(shuō)明學(xué)習(xí)區(qū)塊鏈技術(shù)刻不容緩。

          我個(gè)人從2016年就開(kāi)始在做區(qū)塊鏈系統(tǒng)開(kāi)發(fā)的相關(guān)工作,最近身邊很多朋友都在找我,想讓我給他們講講區(qū)塊鏈技術(shù)開(kāi)發(fā)的相關(guān)知識(shí),介于此,索性我就手?jǐn)]了一套簡(jiǎn)單的java區(qū)塊鏈系統(tǒng),并寫(xiě)了一份詳細(xì)的開(kāi)發(fā)教程,發(fā)布到博客,希望能夠?qū)釔?ài)區(qū)塊鏈技術(shù)的小伙伴學(xué)習(xí)入門(mén)有所幫助。

          這套區(qū)塊鏈系統(tǒng)代碼非常簡(jiǎn)潔清晰,對(duì)于新手來(lái)說(shuō)非常好理解,旨在告訴大家其實(shí)區(qū)塊鏈技術(shù)并沒(méi)有那么高深復(fù)雜。系統(tǒng)中除了springboot框架外,其他基本都是純?cè)_(kāi)發(fā),就連P2P網(wǎng)絡(luò)也是用的java socket來(lái)實(shí)現(xiàn)的。

          文末有本文完整源碼鏈接。

          一、區(qū)塊鏈技術(shù)理論基礎(chǔ)

          1、基本概念

          (1)區(qū)塊鏈

          從技術(shù)層面來(lái)看,區(qū)塊鏈?zhǔn)怯砂灰仔畔⒌膮^(qū)塊按照時(shí)間順序從后向前有序鏈接起來(lái)的數(shù)據(jù)結(jié)構(gòu)。

          從應(yīng)用層面來(lái)說(shuō),區(qū)塊鏈?zhǔn)且粋€(gè)分布式的共享賬本和數(shù)據(jù)庫(kù),具有去中心化、不可篡改、全程留痕、集體維護(hù)、公開(kāi)透明等特點(diǎn)。基于這些特點(diǎn),區(qū)塊鏈技術(shù)可以開(kāi)發(fā)出自帶信任體系特征的系統(tǒng),實(shí)現(xiàn)多個(gè)主體之間的協(xié)作信任與一致行動(dòng)。

          區(qū)塊是區(qū)塊鏈中的最小組成單位,主要有包含元數(shù)據(jù)的區(qū)塊頭和存放一條或者多條交易信息的區(qū)塊體兩部分組成,每個(gè)區(qū)塊都記錄著當(dāng)前區(qū)塊的哈希和上一個(gè)區(qū)塊的哈希,通過(guò)兩個(gè)哈希值的關(guān)聯(lián),讓所有的區(qū)塊以鏈?zhǔn)浇Y(jié)構(gòu)串起來(lái),就形成了一個(gè)完整的區(qū)塊鏈。

          區(qū)塊鏈中的第一個(gè)區(qū)塊被稱作為創(chuàng)世區(qū)塊,無(wú)需關(guān)聯(lián)上一個(gè)區(qū)塊。以BTC網(wǎng)絡(luò)為例,每個(gè)區(qū)塊主要包含如下信息字段:

          • 區(qū)塊大小:用字節(jié)表示的區(qū)塊數(shù)據(jù)大小

          • 區(qū)塊頭:組成區(qū)塊頭的包括以下幾個(gè)字段:
            1. 區(qū)塊頭hash值
            2. 父區(qū)塊頭hash值
            3. 時(shí)間戳:區(qū)塊產(chǎn)生的近似時(shí)間
            4. Merkle根:該區(qū)塊中交易的merkle樹(shù)根的哈希值
            5. 難度目標(biāo):該區(qū)塊工作量證明算法的難度目標(biāo)
            6. Nonce:用于工作量證明算法的計(jì)數(shù)器

          • 交易計(jì)數(shù)器:交易的數(shù)量

          • 交易:記錄在區(qū)塊里的交易信息

          區(qū)塊鏈結(jié)構(gòu)的簡(jiǎn)易模型,如下圖所示:

          區(qū)塊中的交易集合記錄的是一些特定的信息,在BTC網(wǎng)絡(luò)中主要記錄的是交易信息,在其他區(qū)塊鏈網(wǎng)絡(luò)中可以按照業(yè)務(wù)邏輯來(lái)保存相應(yīng)的業(yè)務(wù)數(shù)據(jù),如審計(jì)信息、版權(quán)信息、票據(jù)信息等,這也是區(qū)塊鏈經(jīng)常用來(lái)當(dāng)做共享賬本的原因。

          打個(gè)比方,可以把區(qū)塊鏈當(dāng)做一個(gè)用來(lái)記賬的筆記本,一個(gè)區(qū)塊就相當(dāng)于一頁(yè)紙,上面記錄了某一時(shí)間段內(nèi)所有的賬務(wù)信息,從第一頁(yè)到最后一頁(yè),按照頁(yè)碼順序排列起來(lái)就是一個(gè)完整的賬本。

          (2)區(qū)塊鏈網(wǎng)絡(luò)

          實(shí)際的區(qū)塊鏈系統(tǒng)由多個(gè)區(qū)塊鏈節(jié)點(diǎn)組成,每個(gè)節(jié)點(diǎn)都運(yùn)行著相同一套區(qū)塊鏈主干網(wǎng)絡(luò)的副本,且各個(gè)節(jié)點(diǎn)間通過(guò)P2P網(wǎng)絡(luò)進(jìn)行交互,并最終形成一個(gè)完整的區(qū)塊鏈網(wǎng)絡(luò)系統(tǒng)。

          P2P網(wǎng)絡(luò)具有可靠性、去中心化,以及開(kāi)放性,各個(gè)節(jié)點(diǎn)之間交互運(yùn)作、協(xié)同處理,每個(gè)節(jié)點(diǎn)在對(duì)外提供服務(wù)的同時(shí)也使用網(wǎng)絡(luò)中其他節(jié)點(diǎn)所提供的服務(wù)。當(dāng)某一個(gè)區(qū)塊鏈節(jié)點(diǎn)產(chǎn)生新的區(qū)塊時(shí),會(huì)通過(guò)廣播的方式告訴其他節(jié)點(diǎn),其他節(jié)點(diǎn)通過(guò)網(wǎng)絡(luò)接收到該區(qū)塊信息時(shí),會(huì)對(duì)這個(gè)區(qū)塊信息進(jìn)行驗(yàn)證,當(dāng)有一定數(shù)量的節(jié)點(diǎn)都驗(yàn)證通過(guò)后,各個(gè)節(jié)點(diǎn)會(huì)把該區(qū)塊更新到各自現(xiàn)有的區(qū)塊鏈上,最終使得整個(gè)區(qū)塊鏈網(wǎng)絡(luò)中的各個(gè)節(jié)點(diǎn)信息保持一致,這也是區(qū)塊鏈去中心化、可信任特性的體現(xiàn)。

          區(qū)塊鏈網(wǎng)絡(luò)簡(jiǎn)易模型,如下圖所示:

          2、區(qū)塊鏈分類

          (1)公有鏈

          公有區(qū)塊鏈(Public Block Chains)是指:世界上任何個(gè)體或者團(tuán)體都可以發(fā)送交易,且交易能夠獲得該區(qū)塊鏈的有效確認(rèn),任何人都可以參與使用和維護(hù)該區(qū)塊鏈,信息公開(kāi)透明。公有區(qū)塊鏈?zhǔn)亲钤绲膮^(qū)塊鏈,例如BTC、以太坊等虛擬數(shù)字貨幣均基于公有區(qū)塊鏈。不過(guò)目前公有鏈實(shí)際應(yīng)用價(jià)值不大,并沒(méi)有產(chǎn)生特別合適的應(yīng)用場(chǎng)景。

          (2)聯(lián)盟鏈

          行業(yè)區(qū)塊鏈(Consortium Block Chains):由某個(gè)群體內(nèi)部指定多個(gè)預(yù)選的節(jié)點(diǎn)為記賬人,每個(gè)塊的生成由所有的預(yù)選節(jié)點(diǎn)共同決定(預(yù)選節(jié)點(diǎn)參與共識(shí)過(guò)程),其他接入節(jié)點(diǎn)可以參與交易,但有權(quán)限限制,信息受保護(hù),如銀聯(lián)組織。目前聯(lián)盟鏈?zhǔn)歉鱾€(gè)區(qū)塊鏈技術(shù)團(tuán)隊(duì)主要研究的對(duì)象,由于聯(lián)盟鏈擁有區(qū)塊鏈技術(shù)的大部分特征,并且在權(quán)限管理、數(shù)據(jù)安全、監(jiān)管方面更有優(yōu)勢(shì),是企業(yè)優(yōu)先考慮的區(qū)塊鏈技術(shù)方案。

          市面上也有一些比較主流的聯(lián)盟鏈技術(shù)框架,讓開(kāi)發(fā)維護(hù)聯(lián)盟鏈更加便捷。國(guó)內(nèi)一些大的軟件廠商也都有自己的企業(yè)區(qū)塊鏈技術(shù)解決方案,例如螞蟻金服區(qū)塊鏈平臺(tái),騰訊的TrustSQL平臺(tái),東軟的SaCa EchoTrust區(qū)塊鏈應(yīng)用平臺(tái)以及京東區(qū)塊鏈防偽追溯平臺(tái)等等。

          (3)私有鏈

          私有區(qū)塊鏈(Private Block Chains):僅僅使用區(qū)塊鏈的總賬技術(shù)進(jìn)行記賬,可以是一個(gè)公司,也可以是個(gè)人,獨(dú)享該區(qū)塊鏈的寫(xiě)入權(quán)限,利用區(qū)塊鏈的不易篡改特性,把區(qū)塊鏈作為賬本數(shù)據(jù)庫(kù)來(lái)使用。

          3、關(guān)鍵技術(shù)與特性

          (1)共識(shí)機(jī)制

          共識(shí)機(jī)制被稱作為區(qū)塊鏈系統(tǒng)的靈魂,是區(qū)塊鏈系統(tǒng)信任體系的基礎(chǔ)。區(qū)塊鏈系統(tǒng)作為一個(gè)多節(jié)點(diǎn)的分布式賬本系統(tǒng),當(dāng)有新的信息需要記錄時(shí),哪個(gè)節(jié)點(diǎn)來(lái)負(fù)責(zé)記賬,記賬獎(jiǎng)勵(lì)發(fā)放給哪個(gè)節(jié)點(diǎn),哪些節(jié)點(diǎn)負(fù)責(zé)驗(yàn)證記賬結(jié)果,如何讓各個(gè)節(jié)點(diǎn)達(dá)成最終一致,將記賬結(jié)果被網(wǎng)絡(luò)中所有節(jié)點(diǎn)以同樣的順序復(fù)制并記錄下來(lái),就是共識(shí)機(jī)制要做的事情。

          而按照百度百科上的說(shuō)法:

          所謂“共識(shí)機(jī)制”是通過(guò)特殊節(jié)點(diǎn)的投票,在很短的時(shí)間內(nèi)完成對(duì)交易的驗(yàn)證和確認(rèn),對(duì)一筆交易,如果利益不相干的若干個(gè)節(jié)點(diǎn)能夠達(dá)成共識(shí),我們就可以認(rèn)為全網(wǎng)對(duì)此也能夠達(dá)成共識(shí)。再通俗一點(diǎn)來(lái)講,如果中國(guó)一名微博大V、美國(guó)一名虛擬幣玩家、一名非洲留學(xué)生和一名歐洲旅行者互不相識(shí),但他們都一致認(rèn)為你是個(gè)好人,那么基本上就可以斷定你這人還不壞。

          目前,較為主流的共識(shí)算法有PoW、PoS、DPoS、PBFT等,在實(shí)際使用時(shí),每種算法都有各自的優(yōu)點(diǎn)和缺點(diǎn)。在應(yīng)用于不同場(chǎng)景時(shí),區(qū)塊鏈項(xiàng)目將會(huì)采用不同的共識(shí)機(jī)制和算法。

          (2)去中心化

          去中心化,是互聯(lián)網(wǎng)發(fā)展過(guò)程中形成的社會(huì)關(guān)系形態(tài)和內(nèi)容產(chǎn)生形態(tài),是相對(duì)于“中心化”而言的新型網(wǎng)絡(luò)內(nèi)容生產(chǎn)過(guò)程。在一個(gè)分布有眾多節(jié)點(diǎn)的區(qū)塊鏈系統(tǒng)中,每個(gè)節(jié)點(diǎn)都具有高度自治的特征。任何一個(gè)節(jié)點(diǎn)都可能成為階段性的中心,但不具備強(qiáng)制性的中心控制功能。節(jié)點(diǎn)與節(jié)點(diǎn)之間的影響,會(huì)通過(guò)網(wǎng)絡(luò)而形成關(guān)聯(lián)關(guān)系。這種開(kāi)放式、扁平化、平等性的系統(tǒng)現(xiàn)象或結(jié)構(gòu),我們稱之為去中心化。

          去中心化的系統(tǒng)具有容錯(cuò)力高、抗攻擊力強(qiáng)的特征。中心化的系統(tǒng)一旦中心出現(xiàn)問(wèn)題,整個(gè)系統(tǒng)都會(huì)崩潰,但是區(qū)塊鏈系統(tǒng)中的任何一個(gè)節(jié)點(diǎn)出現(xiàn)問(wèn)題,并不會(huì)對(duì)整個(gè)區(qū)塊鏈網(wǎng)絡(luò)產(chǎn)生太大的影響。

          另外,去中介化并不代表著不接受監(jiān)管,“去中心化”去的是中央控制方和中介方,而不是監(jiān)管方。監(jiān)管節(jié)點(diǎn)可以方便地接入任何一個(gè)區(qū)塊鏈網(wǎng)絡(luò)。并且由于區(qū)塊鏈的公開(kāi)透明特性,監(jiān)管機(jī)構(gòu)反而可以更加方便地監(jiān)控整個(gè)系統(tǒng)的交易數(shù)據(jù)。

          (3)智能合約

          從技術(shù)層面講,智能合約是一段部署在在區(qū)塊鏈上的程序代碼,當(dāng)滿足程序設(shè)定的條件時(shí),它便會(huì)在區(qū)塊鏈上運(yùn)行,并得到相應(yīng)的結(jié)果。這種情況有點(diǎn)類似于微信的小程序,區(qū)塊鏈提供虛擬機(jī)和腳本語(yǔ)言,用戶根據(jù)腳本語(yǔ)言的語(yǔ)法開(kāi)發(fā)帶有一定業(yè)務(wù)邏輯的程序,部署在區(qū)塊鏈上,當(dāng)滿足執(zhí)行的條件時(shí),智能合約便會(huì)被區(qū)塊鏈虛擬機(jī)解釋并運(yùn)行。

          典型的應(yīng)用便是以太坊平臺(tái)的智能合約,在這個(gè)平臺(tái)里可以支持用戶通過(guò)簡(jiǎn)單的幾行代碼就能實(shí)現(xiàn)他們想要的合約,實(shí)現(xiàn)無(wú)需人為監(jiān)督的、不可篡改、自動(dòng)化運(yùn)行的合約,買(mǎi)賣房子不需要再找中介、借錢(qián)不需要再找公證人……人們可以隨時(shí)隨地根據(jù)自身需求發(fā)起合約,它的執(zhí)行不依賴某個(gè)人和組織,所有的信任完全基于以太坊區(qū)塊鏈平臺(tái)本身。

          (4)不可篡改性

          大部分人習(xí)慣稱它為不可篡改性,但是從技術(shù)層面來(lái)說(shuō),我個(gè)人覺(jué)得叫做不可逆轉(zhuǎn)性更貼切,既然是一個(gè)計(jì)算機(jī)系統(tǒng),增刪改查是基本的功能屬性,只不過(guò)區(qū)塊鏈系統(tǒng)刪除和修改操作比較特殊一點(diǎn)。

          區(qū)塊鏈?zhǔn)怯擅總€(gè)區(qū)塊的哈希值串連起來(lái)的鏈?zhǔn)浇Y(jié)構(gòu),而區(qū)塊的哈希值=SHA256(“當(dāng)前區(qū)塊內(nèi)容+上一個(gè)區(qū)塊的哈希值”),任何一個(gè)區(qū)塊的內(nèi)容發(fā)生修改,都會(huì)引起哈希值的變化,而哈希值的變化也會(huì)引起子區(qū)塊哈希值發(fā)生變化,進(jìn)而引起整個(gè)區(qū)塊鏈的改變。

          因此任何人想要修改區(qū)塊的數(shù)據(jù)幾乎是不可能的,除非他把整個(gè)區(qū)塊鏈中從創(chuàng)世區(qū)塊到最新的區(qū)塊的所有哈希值全部重新修改一遍,并且修改完之后,還得廣播告訴網(wǎng)絡(luò)中的其他所有節(jié)點(diǎn),讓其他所有節(jié)點(diǎn)接受修改。

          不過(guò)按照目前計(jì)算機(jī)的算力,想要在短時(shí)間內(nèi)從區(qū)塊鏈頭部到尾部全部修改一遍,是一件非常困難的事,并且即使修改完了,其他節(jié)點(diǎn)也不會(huì)接受修改,因?yàn)閼{一己之力,沒(méi)有能夠讓所有節(jié)點(diǎn)達(dá)成共識(shí)的條件。

          4、流行的區(qū)塊鏈框架與應(yīng)用

          (1)公有鏈應(yīng)用:BTC網(wǎng)絡(luò)

          區(qū)塊鏈1.0產(chǎn)品,對(duì)于比特幣,中本聰是這樣定義的:是一種完全通過(guò)點(diǎn)對(duì)點(diǎn)技術(shù)實(shí)現(xiàn)的電子現(xiàn)金系統(tǒng),它使得在線支付能夠直接由一方發(fā)起并支付給另外一方,中間不需要通過(guò)任何的金融機(jī)構(gòu)。

          與所有的貨幣不同,比特幣不依靠特定貨幣機(jī)構(gòu)發(fā)行,它依據(jù)特定算法,通過(guò)大量的計(jì)算產(chǎn)生,比特幣經(jīng)濟(jì)使用整個(gè)P2P網(wǎng)絡(luò)中眾多節(jié)點(diǎn)構(gòu)成的分布式數(shù)據(jù)庫(kù)來(lái)確認(rèn)并記錄所有的交易行為,并使用密碼學(xué)的設(shè)計(jì)來(lái)確保貨幣流通各個(gè)環(huán)節(jié)安全性。之后人們根據(jù)比特幣網(wǎng)絡(luò)技術(shù)整理出了區(qū)塊鏈技術(shù)體系,去解決信任的問(wèn)題,而比特幣網(wǎng)絡(luò)原理也成為了區(qū)塊鏈技術(shù)初學(xué)者的經(jīng)典教材。

          (2)公有鏈應(yīng)用:以太坊網(wǎng)絡(luò)

          區(qū)塊鏈2.0產(chǎn)品的代表,以太坊是一個(gè)為去中心化應(yīng)用(Dapp)而生的開(kāi)源區(qū)塊鏈平臺(tái),擁有著大部分區(qū)塊鏈技術(shù)的特征,但與其它區(qū)塊鏈不同的是,以太坊是可編程的,開(kāi)發(fā)者可以用它來(lái)構(gòu)建不同的應(yīng)用程序,通過(guò)其專用加密貨幣以太幣(簡(jiǎn)稱“ETH”)提供去中心化的以太虛擬機(jī)(Ethereum Virtual Machine)來(lái)處理點(diǎn)對(duì)點(diǎn)合約(就是一些腳本程序代碼)。如果把比特幣網(wǎng)絡(luò)看作是一套分布式的數(shù)據(jù)庫(kù),而以太坊則更進(jìn)一步,它可以看作是一臺(tái)分布式的計(jì)算機(jī):區(qū)塊鏈?zhǔn)怯?jì)算機(jī)的ROM,合約是程序,而以太坊的礦工們則負(fù)責(zé)計(jì)算,擔(dān)任CPU的角色。


          以太坊的概念首次在2013至2014年間由程序員Vitalik Buterin受比特幣啟發(fā)后提出,大意為“下一代加密貨幣與去中心化應(yīng)用平臺(tái)”。雖然以太坊作為平臺(tái)可以在其上開(kāi)發(fā)新的應(yīng)用,但是由于以太坊的運(yùn)行和BTC網(wǎng)絡(luò)一樣,采用的是Token機(jī)制,且平臺(tái)性能不足,經(jīng)常出現(xiàn)網(wǎng)絡(luò)擁堵的情況,平臺(tái)用來(lái)學(xué)習(xí)開(kāi)發(fā)與測(cè)試區(qū)塊鏈技術(shù)還可以,用于實(shí)際生產(chǎn)的話不太現(xiàn)實(shí)。

          (3)聯(lián)盟鏈開(kāi)發(fā)框架:Hyperledger Fabric

          Hyperledger Fabric 也叫超級(jí)賬本,它是 IBM 貢獻(xiàn)給 Linux 基金會(huì)的商用分布式賬本,是面向企業(yè)應(yīng)用的全球最大的分布式開(kāi)源項(xiàng)目。像其他區(qū)塊鏈技術(shù)一樣,它也有一個(gè)賬本,可以使用智能合約。Fabric的智能合約可以有多種架構(gòu),它可以用主流語(yǔ)言編程,例如Go、Java和Javascript,此外也可以使用Solidity。

          至今,F(xiàn)abric已獲得了阿里巴巴、AWS、Azure、百度、谷歌、華為、IBM、甲骨文、騰訊等互聯(lián)網(wǎng)巨頭的支持。許多企業(yè)的區(qū)塊鏈平臺(tái)都把Fabric作為底層框架來(lái)使用,例如甲骨文。不過(guò)由于IBM對(duì)區(qū)塊鏈的定義強(qiáng)調(diào)了區(qū)塊鏈的分布式和不可變兩個(gè)元素,對(duì)共識(shí)機(jī)制進(jìn)行了削弱,采用了Kafka和zookeeper的“排序服務(wù)”實(shí)現(xiàn)共識(shí),因此部分業(yè)內(nèi)人士也稱超級(jí)賬本是“偽區(qū)塊鏈”,但是即便如此,也抵擋不了企業(yè)對(duì)超級(jí)賬本的喜愛(ài),目前Fabric 2.0版本已經(jīng)正式發(fā)布。

          (4)小結(jié)

          目前公有鏈在實(shí)際應(yīng)用中并沒(méi)有太多的業(yè)務(wù)場(chǎng)景落地,大部分都是以挖礦為主題或者線上寵物飼養(yǎng)的游戲?yàn)橹鳎⑶矣捎跀?shù)字貨幣的匿名性,有些不法分子利用這一特點(diǎn),將數(shù)字貨幣用于洗錢(qián)、暗網(wǎng)買(mǎi)賣等違法行為,是各個(gè)國(guó)家的打擊對(duì)象,我國(guó)政策法規(guī)也嚴(yán)厲禁止,因此對(duì)于技術(shù)人員來(lái)說(shuō),公有鏈可以作為研究學(xué)習(xí)的對(duì)象,其他方面暫時(shí)沒(méi)有太多實(shí)際意義。

          目前大部分區(qū)塊鏈企業(yè)的研究方向主要是針對(duì)企業(yè)的聯(lián)盟鏈和私有鏈,并且國(guó)家層面也在大力支持區(qū)塊鏈技術(shù)的發(fā)展,特別是區(qū)塊鏈底層核心技術(shù)的研發(fā),倡導(dǎo)把區(qū)塊鏈作為核心技術(shù)自主創(chuàng)新的重要突破口,明確主攻方向,加大投入力度,著力攻克一批關(guān)鍵核心技術(shù),加快推動(dòng)區(qū)塊鏈技術(shù)和產(chǎn)業(yè)創(chuàng)新發(fā)展。不過(guò)現(xiàn)在市面上主流的區(qū)塊鏈平臺(tái)大部分還是以國(guó)外公司主導(dǎo)的為主,國(guó)內(nèi)區(qū)塊鏈底層核心技術(shù)的發(fā)展,還需要技術(shù)人員的加倍努力。

          二、區(qū)塊鏈技術(shù)Java實(shí)現(xiàn)

          1、區(qū)塊鏈技術(shù)架構(gòu)


          目前主流的區(qū)塊鏈技術(shù)架構(gòu)主要分為五層,數(shù)據(jù)層是最底層的技術(shù),主要實(shí)現(xiàn)了數(shù)據(jù)存儲(chǔ)、賬戶信息、交易信息等模塊,數(shù)據(jù)存儲(chǔ)主要基于Merkle樹(shù),通過(guò)區(qū)塊的方式和鏈?zhǔn)浇Y(jié)構(gòu)實(shí)現(xiàn),而賬戶和交易基于數(shù)字簽名、哈希函數(shù)和非對(duì)稱加密技術(shù)等多種密碼學(xué)算法和技術(shù),來(lái)保證區(qū)塊鏈中數(shù)據(jù)的安全性。

          網(wǎng)絡(luò)層主要實(shí)現(xiàn)網(wǎng)絡(luò)節(jié)點(diǎn)的連接和通訊,又稱點(diǎn)對(duì)點(diǎn)技術(shù),各個(gè)區(qū)塊鏈節(jié)點(diǎn)通過(guò)網(wǎng)絡(luò)進(jìn)行通信。共識(shí)層是通過(guò)共識(shí)算法,讓網(wǎng)絡(luò)中的各個(gè)節(jié)點(diǎn)對(duì)全網(wǎng)所有的區(qū)塊數(shù)據(jù)真實(shí)性正確性達(dá)成一致,防止出現(xiàn)拜占庭攻擊、51攻擊等區(qū)塊鏈共識(shí)算法攻擊。

          激勵(lì)層主要是實(shí)現(xiàn)區(qū)塊鏈代幣的發(fā)行和分配機(jī)制,是公有鏈的范疇,我們不做分析。應(yīng)用層一般把區(qū)塊鏈系統(tǒng)作為一個(gè)平臺(tái),在平臺(tái)之上實(shí)現(xiàn)一些去中心化的應(yīng)用程序或者智能合約,平臺(tái)提供運(yùn)行這些應(yīng)用的虛擬機(jī)。

          接下來(lái)我們基于Java語(yǔ)言來(lái)開(kāi)發(fā)一套小型的區(qū)塊鏈系統(tǒng),來(lái)實(shí)現(xiàn)數(shù)據(jù)層、網(wǎng)絡(luò)層、共識(shí)層的一些功能,用簡(jiǎn)單的代碼來(lái)直觀抽象的概念,以便加深對(duì)以上區(qū)塊鏈技術(shù)基礎(chǔ)理論的理解。

          2、基于java的區(qū)塊鏈開(kāi)發(fā)實(shí)戰(zhàn)

          (1)開(kāi)發(fā)環(huán)境

          開(kāi)發(fā)工具VSCode
          開(kāi)發(fā)語(yǔ)言Java
          JDK版本JDK1.8或者OpenJDK11
          開(kāi)發(fā)框架SpringBoot2.2.1
          工程管理Maven3.6
          測(cè)試工具Postman

          (2)區(qū)塊鏈基本模型構(gòu)建

          區(qū)塊是區(qū)塊鏈系統(tǒng)的最小單元,第一步我們先實(shí)現(xiàn)最簡(jiǎn)單的區(qū)塊結(jié)構(gòu),新建Block.java類,主要包含以下幾個(gè)字段:
          Block.java

          /** * 區(qū)塊結(jié)構(gòu) *  * @author Jared Jia * */public class Block implements Serializable {
          private static final long serialVersionUID = 1L; /** * 區(qū)塊索引號(hào)(區(qū)塊高度) */ private int index; /** * 當(dāng)前區(qū)塊的hash值,區(qū)塊唯一標(biāo)識(shí) */ private String hash; /** * 前一個(gè)區(qū)塊的hash值 */ private String previousHash; /** * 生成區(qū)塊的時(shí)間戳 */ private long timestamp; /** * 工作量證明,計(jì)算正確hash值的次數(shù) */ private int nonce; /** * 當(dāng)前區(qū)塊存儲(chǔ)的業(yè)務(wù)數(shù)據(jù)集合(例如轉(zhuǎn)賬交易信息、票據(jù)信息、合同信息等) */ private List transactions; /*** 省略get set方法****/??}

          區(qū)塊鏈?zhǔn)怯蓞^(qū)塊按照區(qū)塊哈希前后順序串聯(lián)起來(lái)的數(shù)據(jù)結(jié)構(gòu),哈希值通過(guò)散列算法對(duì)區(qū)塊進(jìn)行二次哈希計(jì)算而得到的數(shù)字摘要信息(不了解散列函數(shù)的,可以先百度了解一下SHA算法),用于保證區(qū)塊的信息安全以及整條區(qū)塊鏈的有效性。因此第二步我們新增計(jì)算區(qū)塊Hash值的方法,采用SHA256算法,通過(guò)java實(shí)現(xiàn):
          CryptoUtil.java

          /** * 密碼學(xué)工具類 *  * @author Jared Jia * */public class CryptoUtil {
          /** * SHA256散列函數(shù) * @param str * @return */ public static String SHA256(String str) { MessageDigest messageDigest; String encodeStr = ""; try { messageDigest = MessageDigest.getInstance("SHA-256"); messageDigest.update(str.getBytes("UTF-8")); encodeStr = byte2Hex(messageDigest.digest()); } catch (Exception e) { System.out.println("getSHA256 is error" + e.getMessage()); } return encodeStr; } private static String byte2Hex(byte[] bytes) { StringBuilder builder = new StringBuilder(); String temp; for (int i = 0; i < bytes.length; i++) { temp = Integer.toHexString(bytes[i] & 0xFF); if (temp.length() == 1) { builder.append("0"); } builder.append(temp); } return builder.toString(); }}

          第三步,創(chuàng)建一個(gè)鏈?zhǔn)浇Y(jié)構(gòu)對(duì)象,按照先后順序來(lái)保存區(qū)塊對(duì)象,從來(lái)形成一個(gè)有序的區(qū)塊鏈表,考慮到線程安全問(wèn)題,采用CopyOnWriteArrayList來(lái)實(shí)現(xiàn),為了方便測(cè)試,暫且把區(qū)塊鏈結(jié)構(gòu)保存在本地緩存中,實(shí)際的區(qū)塊鏈網(wǎng)絡(luò)最終會(huì)實(shí)現(xiàn)持久層的功能,把區(qū)塊鏈數(shù)據(jù)保存至數(shù)據(jù)庫(kù)中,例如BTC核心網(wǎng)絡(luò)采用的是K-V數(shù)據(jù)庫(kù)LevelDB:
          BlockCache.java

          public class BlockCache {
          /** * 當(dāng)前節(jié)點(diǎn)的區(qū)塊鏈結(jié)構(gòu) */ private List blockChain = new CopyOnWriteArrayList(); public List getBlockChain() { return blockChain; }
          public void setBlockChain(List blockChain) { this.blockChain = blockChain; }?}

          第四步,有了區(qū)塊鏈結(jié)構(gòu)后,需要新增向區(qū)塊鏈中添加區(qū)塊的方法,同時(shí)每次添加區(qū)塊的時(shí)候,我們需要驗(yàn)證新區(qū)塊的有效性,例如Hash值是否正確,新區(qū)塊中上一區(qū)塊的Hash屬性的值,與上一區(qū)塊的Hash值是否相等。

          另外,區(qū)塊鏈中必須有個(gè)創(chuàng)世區(qū)塊,我們直接通過(guò)硬編碼實(shí)現(xiàn):
          BlockService.java

          /** * 區(qū)塊鏈核心服務(wù) *  * @author Jared Jia * */@Servicepublic class BlockService {
          @Autowired BlockCache blockCache; /** * 創(chuàng)建創(chuàng)世區(qū)塊 * @return */ public String createGenesisBlock() { Block genesisBlock = new Block(); //設(shè)置創(chuàng)世區(qū)塊高度為1 genesisBlock.setIndex(1); genesisBlock.setTimestamp(System.currentTimeMillis()); genesisBlock.setNonce(1); //封裝業(yè)務(wù)數(shù)據(jù) List tsaList = new ArrayList(); Transaction tsa = new Transaction(); tsa.setId("1"); tsa.setBusinessInfo("這是創(chuàng)世區(qū)塊"); tsaList.add(tsa); Transaction tsa2 = new Transaction(); tsa2.setId("2"); tsa2.setBusinessInfo("區(qū)塊鏈高度為:1"); tsaList.add(tsa2); genesisBlock.setTransactions(tsaList); //設(shè)置創(chuàng)世區(qū)塊的hash值 genesisBlock.setHash(calculateHash("",tsaList,1)); //添加到已打包保存的業(yè)務(wù)數(shù)據(jù)集合中 blockCache.getPackedTransactions().addAll(tsaList); //添加到區(qū)塊鏈中 blockCache.getBlockChain().add(genesisBlock); return JSON.toJSONString(genesisBlock); } /** * 創(chuàng)建新區(qū)塊 * @param nonce * @param previousHash * @param hash * @param blockTxs * @return */ public Block createNewBlock(int nonce, String previousHash, String hash, List blockTxs) { Block block = new Block(); block.setIndex(blockCache.getBlockChain().size() + 1); //時(shí)間戳 block.setTimestamp(System.currentTimeMillis()); block.setTransactions(blockTxs); //工作量證明,計(jì)算正確hash值的次數(shù) block.setNonce(nonce); //上一區(qū)塊的哈希 block.setPreviousHash(previousHash); //當(dāng)前區(qū)塊的哈希 block.setHash(hash); if (addBlock(block)) { return block; } return null; }
          /** * 添加新區(qū)塊到當(dāng)前節(jié)點(diǎn)的區(qū)塊鏈中 * * @param newBlock */ public boolean addBlock(Block newBlock) { //先對(duì)新區(qū)塊的合法性進(jìn)行校驗(yàn) if (isValidNewBlock(newBlock, blockCache.getLatestBlock())) { blockCache.getBlockChain().add(newBlock); // 新區(qū)塊的業(yè)務(wù)數(shù)據(jù)需要加入到已打包的業(yè)務(wù)數(shù)據(jù)集合里去 blockCache.getPackedTransactions().addAll(newBlock.getTransactions()); return true; } return false; } /** * 驗(yàn)證新區(qū)塊是否有效 * * @param newBlock * @param previousBlock * @return */ public boolean isValidNewBlock(Block newBlock, Block previousBlock) { if (!previousBlock.getHash().equals(newBlock.getPreviousHash())) { System.out.println("新區(qū)塊的前一個(gè)區(qū)塊hash驗(yàn)證不通過(guò)"); return false; } else { // 驗(yàn)證新區(qū)塊hash值的正確性 String hash = calculateHash(newBlock.getPreviousHash(), newBlock.getTransactions(), newBlock.getNonce()); if (!hash.equals(newBlock.getHash())) { System.out.println("新區(qū)塊的hash無(wú)效: " + hash + " " + newBlock.getHash()); return false; } if (!isValidHash(newBlock.getHash())) { return false; } }
          return true; }?}

          以上關(guān)鍵代碼實(shí)現(xiàn)之后,我們就構(gòu)建了一個(gè)非常簡(jiǎn)單的區(qū)塊鏈模型,包含一個(gè)基本的區(qū)塊模型和一個(gè)區(qū)塊鏈模型,并且能夠生成新的區(qū)塊并添加到區(qū)塊鏈中,接下來(lái)我們進(jìn)行測(cè)試。

          第五步,我們編寫(xiě)一個(gè)Controller類進(jìn)行調(diào)用:
          BlockController.java

          @Controllerpublic class BlockController {
          @Resource BlockService blockService; @Autowired BlockCache blockCache; /** * 查看當(dāng)前節(jié)點(diǎn)區(qū)塊鏈數(shù)據(jù) * @return */ @GetMapping("/scan") @ResponseBody public String scanBlock() { return JSON.toJSONString(blockCache.getBlockChain()); } /** * 創(chuàng)建創(chuàng)世區(qū)塊 * @return */ @GetMapping("/create") @ResponseBody public String createFirstBlock() { blockService.createGenesisBlock(); return JSON.toJSONString(blockCache.getBlockChain()); }?}

          第六步,系統(tǒng)測(cè)試
          首先系統(tǒng)啟動(dòng)后,先查看區(qū)塊鏈中的數(shù)據(jù),可以看到當(dāng)前系統(tǒng)中的區(qū)塊鏈為空:

          然后我們調(diào)用創(chuàng)建創(chuàng)世區(qū)塊的方法,查看返回結(jié)果:

          我們把生成的創(chuàng)世區(qū)塊添加到本地區(qū)塊鏈中后,轉(zhuǎn)換成JSON字符串返回,可以看到當(dāng)前區(qū)塊鏈中存儲(chǔ)的有一個(gè)區(qū)塊對(duì)象,至此我們已經(jīng)實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的區(qū)塊鏈。實(shí)際的區(qū)塊鏈系統(tǒng)模型要復(fù)雜的多,需要根據(jù)不同的業(yè)務(wù)場(chǎng)景擴(kuò)展相應(yīng)的字段,但是基本特征都是一樣的。

          (3)共識(shí)機(jī)制實(shí)現(xiàn)

          在上章節(jié)中,我們實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的區(qū)塊鏈結(jié)構(gòu),并且能夠生成并添加新的區(qū)塊,但是問(wèn)題來(lái)了,實(shí)際的區(qū)塊鏈系統(tǒng)是一個(gè)多節(jié)點(diǎn)、分布式、去中心化的網(wǎng)絡(luò),每個(gè)節(jié)點(diǎn)通過(guò)網(wǎng)絡(luò)交互,實(shí)時(shí)同步保存著同樣的整條區(qū)塊鏈數(shù)據(jù),那么我們生成的區(qū)塊,如何才能被其他節(jié)點(diǎn)認(rèn)可,并同步添加到其他所有節(jié)點(diǎn)上呢,這個(gè)時(shí)候我們就需要一套規(guī)則,讓所有網(wǎng)絡(luò)節(jié)點(diǎn)的參與者達(dá)成能夠達(dá)成共識(shí),接納并保存新的區(qū)塊,也就是所謂的“共識(shí)機(jī)制”。

          理論基礎(chǔ)部分已經(jīng)提到了,共識(shí)機(jī)制有很多種,各有各的優(yōu)勢(shì)與缺點(diǎn),接下來(lái)我們就用java代碼來(lái)模擬實(shí)現(xiàn)我們最為熟知的一種機(jī)制:工作量證明(Proof of Work),顧名思義就是對(duì)工作量的證明,在基于POW機(jī)制構(gòu)建的區(qū)塊鏈網(wǎng)絡(luò)中,節(jié)點(diǎn)通過(guò)計(jì)算隨機(jī)哈希散列的數(shù)值爭(zhēng)奪記賬權(quán),求得正確的數(shù)值并生成區(qū)塊的能力是節(jié)點(diǎn)算力的具體表現(xiàn),計(jì)算的過(guò)程一般被形象地稱為“挖礦”。

          簡(jiǎn)單來(lái)說(shuō)就是,區(qū)塊鏈系統(tǒng)設(shè)定一套計(jì)算規(guī)則或者說(shuō)是一套計(jì)算題,在新區(qū)塊生成前,各個(gè)節(jié)點(diǎn)都投入到這道題的求解計(jì)算中,哪個(gè)節(jié)點(diǎn)先計(jì)算出結(jié)果,并得到其它節(jié)點(diǎn)的驗(yàn)證和認(rèn)可,這個(gè)節(jié)點(diǎn)就會(huì)獲得新區(qū)塊的記賬權(quán),并獲得系統(tǒng)相應(yīng)的獎(jiǎng)勵(lì),共識(shí)結(jié)束。

          典型的PoW共識(shí)機(jī)制應(yīng)用就是BTC網(wǎng)絡(luò),在BTC網(wǎng)絡(luò)中,共識(shí)計(jì)算的目標(biāo)是找到滿足某個(gè)特定要求的區(qū)塊Hash(哈希值)。這個(gè)區(qū)塊哈希值就是工作結(jié)果的一個(gè)證明,計(jì)算工作的目的就是為了尋找到這個(gè)證明值,上一章節(jié)中,測(cè)試時(shí)我們已經(jīng)見(jiàn)過(guò)這個(gè)Hash值:

          [    {        "hash": "25931395e736653212f0258824df4222ae739ec2d5897310258b0857d4d3870c",        "index": 1,        "nonce": 1,        "timestamp": 1580970554734,        "transactions": [            {                "businessInfo": "這是創(chuàng)世區(qū)塊",                "id": "1"            }        ]    }]

          BTC網(wǎng)絡(luò)PoW使用的Hashcash算法,大致邏輯如下:

          1. 獲取某種公開(kāi)可知的數(shù)據(jù)data(BTC網(wǎng)絡(luò)中,指的是區(qū)塊頭);

          2. 添加一個(gè)計(jì)數(shù)器nonce,初始值設(shè)置為0;

          3. 計(jì)算data與nonce拼接字符串的哈希值;

          4. 檢查上一步的哈希值是否滿足某個(gè)條件,滿足則停止計(jì)算,不滿足則nonce加1,然后重復(fù)第3步和第4步,直到滿足這個(gè)特定的條件為止。

          接下來(lái)我們用Java代碼實(shí)現(xiàn)這個(gè)算法,設(shè)定滿足的特定條件為,Hash值前4位都是0,則計(jì)算成功(實(shí)際區(qū)塊鏈網(wǎng)絡(luò)中的特定條件要求更高,對(duì)計(jì)算的運(yùn)算能力要求也高,并且系統(tǒng)隨著計(jì)算難度動(dòng)態(tài)調(diào)整滿足的特定條件,來(lái)保證區(qū)塊生成的速度)。

          第一步,我們新建一個(gè)共識(shí)機(jī)制服務(wù)類,添加一個(gè)“挖礦”方法,計(jì)算成功后,獲取記賬權(quán),調(diào)用添加區(qū)塊的方法,把區(qū)塊添加到區(qū)塊鏈中:
          PowService.java

          /** * 共識(shí)機(jī)制 * 采用POW即工作量證明實(shí)現(xiàn)共識(shí) * @author Administrator * */@Servicepublic class PowService {
          @Autowired BlockCache blockCache; @Autowired BlockService blockService; /** * 通過(guò)“挖礦”進(jìn)行工作量證明,實(shí)現(xiàn)節(jié)點(diǎn)間的共識(shí) * * @return */ public Block mine(){ // 封裝業(yè)務(wù)數(shù)據(jù)集合,記錄區(qū)塊產(chǎn)生的節(jié)點(diǎn)信息,臨時(shí)硬編碼實(shí)現(xiàn) List tsaList = new ArrayList(); Transaction tsa1 = new Transaction(); tsa1.setId("1"); tsa1.setBusinessInfo("這是IP為:"+CommonUtil.getLocalIp()+",端口號(hào)為:"+blockCache.getP2pport()+"的節(jié)點(diǎn)挖礦生成的區(qū)塊"); tsaList.add(tsa1); Transaction tsa2 = new Transaction(); tsa2.setId("2"); tsa2.setBusinessInfo("區(qū)塊鏈高度為:"+(blockCache.getLatestBlock().getIndex()+1)); tsaList.add(tsa2); // 定義每次哈希函數(shù)的結(jié)果 String newBlockHash = ""; int nonce = 0; long start = System.currentTimeMillis(); System.out.println("開(kāi)始挖礦"); while (true) { // 計(jì)算新區(qū)塊hash值 newBlockHash = blockService.calculateHash(blockCache.getLatestBlock().getHash(), tsaList, nonce); // 校驗(yàn)hash值 if (blockService.isValidHash(newBlockHash)) { System.out.println("挖礦完成,正確的hash值:" + newBlockHash); System.out.println("挖礦耗費(fèi)時(shí)間:" + (System.currentTimeMillis() - start) + "ms"); break; } System.out.println("第"+(nonce+1)+"次嘗試計(jì)算的hash值:" + newBlockHash); nonce++; } // 創(chuàng)建新的區(qū)塊 Block block = blockService.createNewBlock(nonce, blockCache.getLatestBlock().getHash(), newBlockHash, tsaList); return block; } /** * 驗(yàn)證hash值是否滿足系統(tǒng)條件 * 暫定前4位是0則滿足條件 * @param hash * @return */ public boolean isValidHash(String hash) { //System.out.println("難度系數(shù):"+blockCache.getDifficulty()); return hash.startsWith("0000"); }}

          第二步,編寫(xiě)測(cè)試共識(shí)機(jī)制服務(wù)的Controller類方法:
          BlockController.java

          /**   * 工作量證明PoW   * 挖礦生成新的區(qū)塊    */  @GetMapping("/mine")  @ResponseBody  public String createNewBlock() {    powService.mine();    return JSON.toJSONString(blockCache.getBlockChain());??}

          第三步,啟動(dòng)系統(tǒng),進(jìn)行測(cè)試。
          首先執(zhí)行http://localhost:8080/create方法,生成創(chuàng)世區(qū)塊。
          其次調(diào)用http://localhost:8080/mine方法進(jìn)行工作量計(jì)算證明,生成新的區(qū)塊,并添加到本地區(qū)塊鏈中:

          我們來(lái)看一下,系統(tǒng)后臺(tái)計(jì)算的過(guò)程,此次計(jì)算共花費(fèi)1048ms計(jì)算出滿足條件的Hash值,共計(jì)算4850次:

          至此,我們實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的工作量證明機(jī)制,并在當(dāng)前區(qū)塊鏈系統(tǒng)節(jié)點(diǎn)上運(yùn)行,完成了正確結(jié)果的計(jì)算,生成了一個(gè)新的區(qū)塊。

          接下來(lái)我們將會(huì)開(kāi)發(fā)一個(gè)P2P網(wǎng)絡(luò),實(shí)現(xiàn)多個(gè)節(jié)點(diǎn)的同時(shí)運(yùn)行,當(dāng)一個(gè)節(jié)點(diǎn)挖礦完成后,通過(guò)P2P網(wǎng)絡(luò)廣播給其他節(jié)點(diǎn),其他節(jié)點(diǎn)驗(yàn)證通過(guò)后,會(huì)把新產(chǎn)生的區(qū)塊添加到自己的區(qū)塊鏈上,進(jìn)而保證整個(gè)區(qū)塊鏈網(wǎng)絡(luò)所有節(jié)點(diǎn)的數(shù)據(jù)一致性。

          (4)P2P網(wǎng)絡(luò)開(kāi)發(fā)

          前面我們已經(jīng)實(shí)現(xiàn)了一個(gè)基本的區(qū)塊鏈系統(tǒng),并且實(shí)現(xiàn)了PoW工作量證明共識(shí)機(jī)制,通過(guò)挖礦計(jì)算出正確的結(jié)果同時(shí)生成一個(gè)新的區(qū)塊添加到區(qū)塊鏈中,但是這些都是基于單節(jié)點(diǎn)的運(yùn)行,實(shí)際的區(qū)塊鏈?zhǔn)怯卸鄠€(gè)節(jié)點(diǎn)同時(shí)運(yùn)行的分布式網(wǎng)絡(luò)系統(tǒng),所有節(jié)點(diǎn)同時(shí)計(jì)算搶奪記賬權(quán),共同維護(hù)一條完整的區(qū)塊鏈。

          接下來(lái)我們基于Java的WebSocket實(shí)現(xiàn)一個(gè)Peer-to-Peer網(wǎng)絡(luò),實(shí)現(xiàn)多個(gè)節(jié)點(diǎn)間的相互通信,通過(guò)本章節(jié),我們將要實(shí)現(xiàn)以下功能:

            - 創(chuàng)建一個(gè)基于java的p2p網(wǎng)絡(luò)  - 運(yùn)行多個(gè)節(jié)點(diǎn),且多個(gè)節(jié)點(diǎn)通過(guò)p2p網(wǎng)絡(luò)自動(dòng)同步區(qū)塊信息  - 一個(gè)節(jié)點(diǎn)挖礦生成新的區(qū)塊后,自動(dòng)廣播給其他所有節(jié)點(diǎn)  - 每個(gè)節(jié)點(diǎn)在接收到其他節(jié)點(diǎn)發(fā)送的區(qū)塊內(nèi)容后,進(jìn)行驗(yàn)證,驗(yàn)證通過(guò)添加到本地區(qū)塊鏈上??-?在自我節(jié)點(diǎn)查看整個(gè)區(qū)塊鏈內(nèi)容

          開(kāi)發(fā)測(cè)試本章節(jié)的功能,我們最好準(zhǔn)備兩臺(tái)電腦或者虛擬機(jī),再或者Docker集群環(huán)境也可以,便于多節(jié)點(diǎn)的運(yùn)行測(cè)試。如果只有一臺(tái)電腦也可以,各個(gè)節(jié)點(diǎn)運(yùn)行的端口號(hào)設(shè)置為不相同即可。

          第一步,開(kāi)發(fā)思路整理
          目前我們已經(jīng)實(shí)現(xiàn)了單節(jié)點(diǎn)的區(qū)塊生成,那么我們接下來(lái)只需要實(shí)現(xiàn)各個(gè)節(jié)點(diǎn)的消息同步即可。

          • 首先,通過(guò)java代碼實(shí)現(xiàn)p2p網(wǎng)絡(luò)的server端和client端,每個(gè)節(jié)點(diǎn)既是服務(wù)端也是客戶端。

          • 然后,一個(gè)節(jié)點(diǎn)啟動(dòng)時(shí),會(huì)尋找區(qū)塊鏈網(wǎng)絡(luò)上的有效節(jié)點(diǎn),并建立socket連接(BTC網(wǎng)絡(luò)可以通過(guò)使用“DNS”種子方式獲取BTC有效節(jié)點(diǎn),DNS種子提供比特幣節(jié)點(diǎn)的IP地址列表),我們直接把節(jié)點(diǎn)列表配置到application.yml文件中。

          • 接著,從連接上的節(jié)點(diǎn)獲取最新的區(qū)塊信息,如果當(dāng)前節(jié)點(diǎn)首次運(yùn)行,則獲取整個(gè)區(qū)塊鏈信息,并替換到本地。

          • 之后,各個(gè)節(jié)點(diǎn)同時(shí)挖礦計(jì)算,哪個(gè)節(jié)點(diǎn)先計(jì)算完成,就把生成的新區(qū)塊全網(wǎng)廣播給其他所有節(jié)點(diǎn)(實(shí)際的區(qū)塊鏈網(wǎng)絡(luò)一直在計(jì)算,我們?yōu)榱吮阌跍y(cè)試,手動(dòng)觸發(fā)一個(gè)節(jié)點(diǎn)挖礦產(chǎn)生區(qū)塊,然后全網(wǎng)廣播給其他所有節(jié)點(diǎn))。

          • 最后,當(dāng)一個(gè)節(jié)點(diǎn)收到廣播內(nèi)容后,對(duì)接收到的新區(qū)塊進(jìn)行驗(yàn)證,驗(yàn)證通過(guò)后添加到本地區(qū)塊鏈上,驗(yàn)證的首要條件是新區(qū)塊的高度必須比本地的區(qū)塊鏈高度要高。

          第二步,先實(shí)現(xiàn)P2P網(wǎng)絡(luò)server端
          新建一個(gè)P2PServer類,并添加一個(gè)初始化server端的方法:
          P2PServer.java

          /** * p2p服務(wù)端 *  * @author Jared Jia * */@Componentpublic class P2PServer {
          @Autowired P2PService p2pService;
          public void initP2PServer(int port) { WebSocketServer socketServer = new WebSocketServer(new InetSocketAddress(port)) {
          /** * 連接建立后觸發(fā) */ @Override public void onOpen(WebSocket webSocket, ClientHandshake clientHandshake) { p2pService.getSockets().add(webSocket); }
          /** * 連接關(guān)閉后觸發(fā) */ @Override public void onClose(WebSocket webSocket, int i, String s, boolean b) { p2pService.getSockets().remove(webSocket); System.out.println("connection closed to address:" + webSocket.getRemoteSocketAddress()); }
          /** * 接收到客戶端消息時(shí)觸發(fā) */ @Override public void onMessage(WebSocket webSocket, String msg) { //作為服務(wù)端,業(yè)務(wù)邏輯處理 p2pService.handleMessage(webSocket, msg, p2pService.getSockets()); }
          /** * 發(fā)生錯(cuò)誤時(shí)觸發(fā) */ @Override public void onError(WebSocket webSocket, Exception e) { p2pService.getSockets().remove(webSocket); System.out.println("connection failed to address:" + webSocket.getRemoteSocketAddress()); }
          @Override public void onStart() {
          }
          }; socketServer.start(); System.out.println("listening websocket p2p port on: " + port); }}

          第三步,實(shí)現(xiàn)P2P網(wǎng)絡(luò)client端
          P2PClient.java

          /** * p2p客戶端 *  * @author Jared Jia * */@Componentpublic class P2PClient {    @Autowired  P2PService p2pService;
          public void connectToPeer(String addr) { try { final WebSocketClient socketClient = new WebSocketClient(new URI(addr)) { @Override public void onOpen(ServerHandshake serverHandshake) { //客戶端發(fā)送請(qǐng)求,查詢最新區(qū)塊 p2pService.write(this, p2pService.queryLatestBlockMsg()); p2pService.getSockets().add(this); }
          /** * 接收到消息時(shí)觸發(fā) * @param msg */ @Override public void onMessage(String msg) { p2pService.handleMessage(this, msg, p2pService.getSockets()); }
          @Override public void onClose(int i, String msg, boolean b) { p2pService.getSockets().remove(this); System.out.println("connection closed"); }
          @Override public void onError(Exception e) { p2pService.getSockets().remove(this); System.out.println("connection failed"); } }; socketClient.connect(); } catch (URISyntaxException e) { System.out.println("p2p connect is error:" + e.getMessage()); } }}

          第四步,定義P2P網(wǎng)絡(luò)同步的消息模型
          同步的消息模型,我們定義為四類,分別是:
          BlockConstant.java

            // 查詢最新的區(qū)塊  public final static int QUERY_LATEST_BLOCK = 1;
          // 返回最新的區(qū)塊 public final static int RESPONSE_LATEST_BLOCK = 2;
          // 查詢整個(gè)區(qū)塊鏈 public final static int QUERY_BLOCKCHAIN = 3;
          // 返回整個(gè)區(qū)塊鏈??public?final?static?int?RESPONSE_BLOCKCHAIN?=?4;

          定義一個(gè)各個(gè)節(jié)點(diǎn)間傳遞的消息模型:
          Message.java

          /** * p2p通訊消息 * * @author Jared Jia *  */public class Message implements Serializable {      private static final long serialVersionUID = 1L;    /**     * 消息類型     */    private int type;    /**     * 消息內(nèi)容     */    private String data;        /****set get方法省略****/    ??}

          第五步,實(shí)現(xiàn)向其他節(jié)點(diǎn)廣播的方法

          新建一個(gè)p2p網(wǎng)絡(luò)服務(wù)類,向外發(fā)送消息,或者處理當(dāng)前節(jié)點(diǎn)收到其他節(jié)點(diǎn)發(fā)送的請(qǐng)求。
          P2PService.java

          /**   * 全網(wǎng)廣播消息   * @param message   */  public void broatcast(String message) {    List socketsList = this.getSockets();    if (CollectionUtils.isEmpty(socketsList)) {      return;    }    System.out.println("======全網(wǎng)廣播消息開(kāi)始:");    for (WebSocket socket : socketsList) {      this.write(socket, message);    }    System.out.println("======全網(wǎng)廣播消息結(jié)束");??}

          第六步,開(kāi)發(fā)消息處理路由

          第五步中已經(jīng)實(shí)現(xiàn)了向外發(fā)送消息,本步驟實(shí)現(xiàn)接收消息。
          首先設(shè)計(jì)一個(gè)服務(wù)端和客戶端共用的消息路由,來(lái)分發(fā)消息給對(duì)應(yīng)的處理單元。
          P2PService.java

          /**   * 客戶端和服務(wù)端共用的消息處理方法   * @param webSocket   * @param msg   * @param sockets   */  public void handleMessage(WebSocket webSocket, String msg, List sockets) {    try {      Message message = JSON.parseObject(msg, Message.class);      System.out.println("接收到IP地址為:" +webSocket.getRemoteSocketAddress().getAddress().toString()          +",端口號(hào)為:"+ webSocket.getRemoteSocketAddress().getPort() + "的p2p消息:"              + JSON.toJSONString(message));      switch (message.getType()) {      //客戶端請(qǐng)求查詢最新的區(qū)塊:1      case BlockConstant.QUERY_LATEST_BLOCK:        write(webSocket, responseLatestBlockMsg());//服務(wù)端調(diào)用方法返回最新區(qū)塊:2        break;      //接收到服務(wù)端返回的最新區(qū)塊:2      case BlockConstant.RESPONSE_LATEST_BLOCK:        handleBlockResponse(message.getData(), sockets);        break;      //客戶端請(qǐng)求查詢整個(gè)區(qū)塊鏈:3      case BlockConstant.QUERY_BLOCKCHAIN:        write(webSocket, responseBlockChainMsg());//服務(wù)端調(diào)用方法返回最新區(qū)塊:4        break;      //直接接收到其他節(jié)點(diǎn)發(fā)送的整條區(qū)塊鏈信息:4      case BlockConstant.RESPONSE_BLOCKCHAIN:        handleBlockChainResponse(message.getData(), sockets);        break;      }    } catch (Exception e) {      System.out.println("處理IP地址為:" +webSocket.getRemoteSocketAddress().getAddress().toString()        +",端口號(hào)為:"+ webSocket.getRemoteSocketAddress().getPort() + "的p2p消息錯(cuò)誤:"         + e.getMessage());    }??}

          第七步,開(kāi)發(fā)消息處理單元

          有了消息路由之后,接著編寫(xiě)不同的處理單元,處理其他節(jié)點(diǎn)發(fā)送來(lái)的區(qū)塊或者區(qū)塊鏈信息,總體原則是:先校驗(yàn)其他節(jié)點(diǎn)發(fā)送來(lái)的區(qū)塊或者區(qū)塊鏈的有效性,然后判斷它們的高度比當(dāng)前節(jié)點(diǎn)的區(qū)塊鏈高度要高,如果高則替換本地的區(qū)塊鏈,或者把新區(qū)塊添加到本地區(qū)塊鏈上。
          P2PService.java

          /**   * 處理其它節(jié)點(diǎn)發(fā)送過(guò)來(lái)的區(qū)塊信息   * @param blockData   * @param sockets   */  public synchronized void handleBlockResponse(String blockData, List sockets) {    //反序列化得到其它節(jié)點(diǎn)的最新區(qū)塊信息    Block latestBlockReceived = JSON.parseObject(blockData, Block.class);    //當(dāng)前節(jié)點(diǎn)的最新區(qū)塊    Block latestBlock = blockCache.getLatestBlock();        if (latestBlockReceived != null) {      if(latestBlock != null) {        //如果接收到的區(qū)塊高度比本地區(qū)塊高度大的多        if(latestBlockReceived.getIndex() > latestBlock.getIndex() + 1) {          broatcast(queryBlockChainMsg());          System.out.println("重新查詢所有節(jié)點(diǎn)上的整條區(qū)塊鏈");        }else if (latestBlockReceived.getIndex() > latestBlock.getIndex() &&             latestBlock.getHash().equals(latestBlockReceived.getPreviousHash())) {          if (blockService.addBlock(latestBlockReceived)) {            broatcast(responseLatestBlockMsg());          }          System.out.println("將新接收到的區(qū)塊加入到本地的區(qū)塊鏈");        }      }else if(latestBlock == null) {        broatcast(queryBlockChainMsg());        System.out.println("重新查詢所有節(jié)點(diǎn)上的整條區(qū)塊鏈");      }    }  }    /**   * 處理其它節(jié)點(diǎn)發(fā)送過(guò)來(lái)的區(qū)塊鏈信息   * @param blockData   * @param sockets   */  public synchronized void handleBlockChainResponse(String blockData, List sockets) {    //反序列化得到其它節(jié)點(diǎn)的整條區(qū)塊鏈信息    List receiveBlockchain = JSON.parseArray(blockData, Block.class);    if(!CollectionUtils.isEmpty(receiveBlockchain) && blockService.isValidChain(receiveBlockchain)) {      //根據(jù)區(qū)塊索引先對(duì)區(qū)塊進(jìn)行排序      Collections.sort(receiveBlockchain, new Comparator() {        public int compare(Block block1, Block block2) {          return block1.getIndex() - block2.getIndex();        }      });            //其它節(jié)點(diǎn)的最新區(qū)塊      Block latestBlockReceived = receiveBlockchain.get(receiveBlockchain.size() - 1);      //當(dāng)前節(jié)點(diǎn)的最新區(qū)塊      Block latestBlock = blockCache.getLatestBlock();            if(latestBlock == null) {        //替換本地的區(qū)塊鏈        blockService.replaceChain(receiveBlockchain);      }else {        //其它節(jié)點(diǎn)區(qū)塊鏈如果比當(dāng)前節(jié)點(diǎn)的長(zhǎng),則處理當(dāng)前節(jié)點(diǎn)的區(qū)塊鏈        if (latestBlockReceived.getIndex() > latestBlock.getIndex()) {          if (latestBlock.getHash().equals(latestBlockReceived.getPreviousHash())) {            if (blockService.addBlock(latestBlockReceived)) {              broatcast(responseLatestBlockMsg());            }            System.out.println("將新接收到的區(qū)塊加入到本地的區(qū)塊鏈");          } else {            // 用長(zhǎng)鏈替換本地的短鏈            blockService.replaceChain(receiveBlockchain);          }        }      }    }??}

          3、完整系統(tǒng)運(yùn)行與測(cè)試

          第一步,打包生成測(cè)試用的可執(zhí)行jar包
          準(zhǔn)備兩臺(tái)機(jī)器(虛擬機(jī)或者Docker集群都行),同時(shí)運(yùn)行兩個(gè)節(jié)點(diǎn),節(jié)點(diǎn)信息如下:


          通過(guò)mvn package -Dmaven.test.skip=true命令對(duì)工程進(jìn)行打包,生成可直接運(yùn)行的jar包,打包前對(duì)工程進(jìn)行配置,配置信息如下圖:

          節(jié)點(diǎn)Node1打包:

          節(jié)點(diǎn)Node2打包:

          分別打包之后,生成兩個(gè)節(jié)點(diǎn)的可執(zhí)行jar包,如下:

          把兩個(gè)jar包分別放在對(duì)應(yīng)IP的windows機(jī)器上,打開(kāi)命令行模式,進(jìn)入jar所在文件夾,分別執(zhí)行以下命令運(yùn)行兩個(gè)節(jié)點(diǎn):
          java -jar dce-blockchain-node1.jar
          java -jar dce-blockchain-node2.jar

          啟動(dòng)節(jié)點(diǎn)2的時(shí)候,可以看到后臺(tái)日志,已經(jīng)連接上節(jié)點(diǎn)1,如下圖所示:

          第二步,對(duì)兩個(gè)節(jié)點(diǎn)進(jìn)行測(cè)試

          首先,兩個(gè)節(jié)點(diǎn)啟動(dòng)后,用postman分別執(zhí)行http://192.168.0.104:8080/scan和http://192.168.0.112:8090/scan請(qǐng)求,可以看到兩個(gè)節(jié)點(diǎn)的區(qū)塊鏈內(nèi)容都為空。

          然后,在節(jié)點(diǎn)1上分別執(zhí)行http://192.168.0.104:8080/create和http://192.168.0.104:8080/mine請(qǐng)求,來(lái)生成創(chuàng)世區(qū)塊,以及通過(guò)挖礦產(chǎn)生第二個(gè)區(qū)塊,執(zhí)行后查看節(jié)點(diǎn)1的區(qū)塊鏈信息如下:
          執(zhí)行http://192.168.0.104:8080/scan結(jié)果:

          [    {        "hash": "5303d2990c139992bdb5a22aa1dac4f2719755304e45bac03ca4a1f1688c909e",        "index": 1,        "nonce": 1,        "timestamp": 1581064647736,        "transactions": [            {                "businessInfo": "這是創(chuàng)世區(qū)塊",                "id": "1"            },            {                "businessInfo": "區(qū)塊鏈高度為:1",                "id": "2"            }        ]    },    {        "hash": "0000de5eea0c20c2e7d06220bc023886e88dd8784eaa2fd2d1d6c5e581061d85",        "index": 2,        "nonce": 4850,        "previousHash": "5303d2990c139992bdb5a22aa1dac4f2719755304e45bac03ca4a1f1688c909e",        "timestamp": 1581064655139,        "transactions": [            {                "businessInfo": "這是IP為:192.168.0.104,端口號(hào)為:7001的節(jié)點(diǎn)挖礦生成的區(qū)塊",                "id": "1"            },            {                "businessInfo": "區(qū)塊鏈高度為:2",                "id": "2"            }        ]    }]

          最后,我們來(lái)驗(yàn)證節(jié)點(diǎn)2是否已經(jīng)完成了節(jié)點(diǎn)1生成的區(qū)塊鏈信息的網(wǎng)絡(luò)同步,Postman執(zhí)行http://192.168.0.112:8090/scan請(qǐng)求,查看返回結(jié)果:
          ?從結(jié)果可以看到,區(qū)塊鏈網(wǎng)絡(luò)節(jié)點(diǎn)2已經(jīng)接收到節(jié)點(diǎn)1發(fā)送的區(qū)塊鏈信息,系統(tǒng)日志如下:

          反過(guò)來(lái),我們?cè)诠?jié)點(diǎn)2上再執(zhí)行一次挖礦操作,可以看到節(jié)點(diǎn)1上,已經(jīng)接收到節(jié)點(diǎn)2挖礦新產(chǎn)生的區(qū)塊信息,并添加到節(jié)點(diǎn)1的區(qū)塊鏈上:

          至此,我們已經(jīng)實(shí)現(xiàn)了一個(gè)完整的小型區(qū)塊鏈網(wǎng)絡(luò),并實(shí)現(xiàn)各個(gè)節(jié)點(diǎn)間的通信,多個(gè)節(jié)點(diǎn)共同維護(hù)同一個(gè)區(qū)塊鏈信息。

          結(jié)語(yǔ):
          區(qū)塊鏈系統(tǒng)非常龐大,涉及方方面面的技術(shù),本人所演示的代碼主要對(duì)區(qū)塊鏈基礎(chǔ)的一些概念進(jìn)行了詮釋,感興趣的同學(xué),還可以在此基礎(chǔ)上繼續(xù)開(kāi)發(fā),來(lái)實(shí)現(xiàn)例如持久層、消息的加密解密、系統(tǒng)賬戶模型、預(yù)言機(jī)、側(cè)鏈技術(shù)以及智能合約等區(qū)塊鏈系統(tǒng)功能。


          寫(xiě)給每個(gè)區(qū)塊鏈技術(shù)人員:
          目前市面上流行的企業(yè)級(jí)區(qū)塊鏈框架,例如超級(jí)賬本Fabric都是國(guó)外人員在主導(dǎo),而我們國(guó)內(nèi)除了幾家大廠外,其他很多區(qū)塊鏈公司基本都是把人家的東西拿過(guò)來(lái)進(jìn)行二次封裝,然后對(duì)外聲稱自己公司已經(jīng)掌握了區(qū)塊鏈核心技術(shù),并對(duì)企業(yè)提供服務(wù),這是一種不好的現(xiàn)象。大家可以想想我們現(xiàn)在用的開(kāi)發(fā)語(yǔ)言、框架有幾個(gè)真正是國(guó)產(chǎn)的,我們?cè)俾?lián)想一下前段時(shí)間中興、華為被人家核心技術(shù)卡脖子事件,就知道我們要做的事情有很多,我們需要去除浮躁,靜下心來(lái)好好研究底層核心技術(shù),這樣才能實(shí)現(xiàn)真正的“彎道超車”!

          三、源代碼

          本文完整源代碼地址:

          https://gitee.com/luckytuan/dce-blockchain

          PS:如果覺(jué)得我的分享不錯(cuò),歡迎大家隨手點(diǎn)贊、在看。
          END
          瀏覽 51
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  操婷婷五月天 | 国产午夜精品久久久 | 久久香蕉网站 | 国产黄色视频在线观看免费 | 久久好色 |