利用Java代碼實(shí)現(xiàn)區(qū)塊鏈技術(shù)
前言:
比特幣很熱門(mén)——這是多么輕描淡寫(xiě)的說(shuō)法啊。雖然加密貨幣的未來(lái)有些不確定,但用于驅(qū)動(dòng)比特幣的區(qū)塊鏈技術(shù)也非常流行。
區(qū)塊鏈的應(yīng)用范圍幾乎無(wú)窮無(wú)盡??梢哉f(shuō),它還有可能破壞企業(yè)自動(dòng)化。
本文將重點(diǎn)關(guān)注區(qū)塊鏈體系結(jié)構(gòu),特別是演示“不可變、僅附加”分布式賬本如何與簡(jiǎn)化的代碼示例一起工作。
作為開(kāi)發(fā)人員,與簡(jiǎn)單地閱讀技術(shù)文章相比,在理解代碼的工作原理時(shí),從代碼中看到東西要有用得多。至少對(duì)我來(lái)說(shuō)是這樣。那么,讓我們開(kāi)始吧!
簡(jiǎn)言之,區(qū)塊鏈
首先讓我們快速總結(jié)一下區(qū)塊鏈。一個(gè)塊包含一些標(biāo)題信息和一組或一塊任何類(lèi)型數(shù)據(jù)的事務(wù)。鏈從第一個(gè)(起源)塊開(kāi)始。在添加/追加事務(wù)時(shí),將根據(jù)一個(gè)塊中可以存儲(chǔ)的事務(wù)數(shù)量創(chuàng)建新的塊。
當(dāng)超過(guò)塊閾值大小時(shí),將創(chuàng)建一個(gè)新的事務(wù)塊。新區(qū)塊與前一區(qū)塊相連,因此稱(chēng)為區(qū)塊鏈。
不變性
區(qū)塊鏈?zhǔn)遣豢勺兊?,因?yàn)闉榻灰子?jì)算SHA-256哈希。塊的內(nèi)容也會(huì)被散列,從而提供唯一的標(biāo)識(shí)符。此外,來(lái)自鏈接的前一個(gè)塊的散列也被存儲(chǔ)并散列在塊頭中。
這就是為什么試圖篡改區(qū)塊鏈區(qū)塊基本上是不可能的,至少在目前的計(jì)算能力下是如此。下面是一個(gè)顯示塊屬性的部分Java類(lèi)定義。
... public class Block<T extends Tx> { public long timeStamp; private int index; private List<T> transactions = new ArrayList<T>(); private String hash; private String previousHash; private String merkleRoot; private String nonce = "0000"; // caches Transaction SHA256 hashes public Map<String,T> map = new HashMap<String,T>(); ...
請(qǐng)注意,注入的泛型類(lèi)型是Tx類(lèi)型。這允許事務(wù)數(shù)據(jù)發(fā)生變化。此外,previousHash
屬性將引用前一個(gè)塊的哈希。merkleRoot
和nonce
屬性將在稍后進(jìn)行描述。
塊散列
每個(gè)塊可以計(jì)算一個(gè)塊散列。這本質(zhì)上是連接在一起的所有塊屬性的散列,包括前一個(gè)塊的散列和由此計(jì)算出的SHA-256散列。
下面是塊中定義的方法。計(jì)算散列的java類(lèi)。
... public void computeHash() { Gson parser = new Gson(); // probably should cache this instance String serializedData = parser.toJson(transactions); setHash(SHA256.generateHash(timeStamp + index + merkleRoot + serializedData + nonce + previousHash)); } ...
塊事務(wù)被序列化為JSON字符串,以便在散列之前將其附加到塊屬性中。
Chain 鏈
區(qū)塊鏈通過(guò)接受交易來(lái)管理區(qū)塊。當(dāng)達(dá)到預(yù)定閾值時(shí),創(chuàng)建塊。這是一個(gè)簡(jiǎn)單的Chain鏈。java部分實(shí)現(xiàn):
... public class SimpleBlockchain<T extends Tx> { public static final int BLOCK_SIZE = 10; public List<Block<T>> chain = new ArrayList<Block<T>>(); public SimpleBlockchain() { // create genesis block chain.add(newBlock()); } ...
注意,chain屬性包含使用Tx類(lèi)型鍵入的塊的列表。此外,在創(chuàng)建鏈時(shí),無(wú)參數(shù)構(gòu)造函數(shù)會(huì)創(chuàng)建一個(gè)初始的“genesis
”塊。下面是newBlock()
方法的源代碼。
public Block<T> newBlock() { int count = chain.size(); String previousHash = "root"; if (count > 0) previousHash = blockChainHash(); Block<T> block = new Block<T>(); block.setTimeStamp(System.currentTimeMillis()); block.setIndex(count); block.setPreviousHash(previousHash); return block; }
這個(gè)新的block方法將創(chuàng)建一個(gè)新的block實(shí)例,為適當(dāng)?shù)闹翟O(shè)定種子,并分配前一個(gè)block的hash(即鏈頭的hash)。然后它將返回塊。
在將塊添加到鏈之前,可以通過(guò)將新塊的上一個(gè)哈希與鏈的最后一個(gè)塊(頭)進(jìn)行比較來(lái)驗(yàn)證塊,以確保它們匹配。這是一個(gè)簡(jiǎn)單的鎖鏈。java方法描述了這一點(diǎn)。
public void addAndValidateBlock(Block<T> block) { // compare previous block hash, add if valid Block<T> current = block; for (int i = chain.size() - 1; i >= 0; i--) { Block<T> b = chain.get(i); if (b.getHash().equals(current.getPreviousHash())) { current = b; } else { throw new RuntimeException("Block Invalid"); } } this.chain.add(block); }
整個(gè)區(qū)塊鏈通過(guò)鏈的循環(huán)進(jìn)行驗(yàn)證,以確保一個(gè)區(qū)塊的哈希仍然與前一個(gè)區(qū)塊的哈希匹配。
這是SimpleBlockChain.java
的 validate()
方法實(shí)現(xiàn)。
public boolean validate() { String previousHash = null; for (Block<T> block : chain) { String currentHash = block.getHash(); if (!currentHash.equals(previousHash)) { return false; } previousHash = currentHash; } return true; }
你可以看到,試圖以任何方式偽造交易數(shù)據(jù)或任何其他財(cái)產(chǎn)都是非常困難的。而且,隨著鏈條的增長(zhǎng),它繼續(xù)變得非常、非常、非常困難,基本上是不可能的。直到量子計(jì)算機(jī)問(wèn)世
添加事務(wù)
區(qū)塊鏈技術(shù)的另一個(gè)重要技術(shù)點(diǎn)是它是分布式的。它們是附加的這一事實(shí)只會(huì)有助于在參與區(qū)塊鏈網(wǎng)絡(luò)的節(jié)點(diǎn)之間復(fù)制區(qū)塊鏈。節(jié)點(diǎn)通常以點(diǎn)對(duì)點(diǎn)的方式進(jìn)行通信,比特幣就是這樣,但不一定要這樣。其他區(qū)塊鏈實(shí)施使用分散的方法,比如通過(guò)HTTP使用API。然而,這是另一個(gè)博客的主題。
事務(wù)幾乎可以代表任何東西。事務(wù)可以包含要執(zhí)行的代碼(即智能合約),或者存儲(chǔ)和附加有關(guān)某種業(yè)務(wù)事務(wù)的信息。
智能合同:旨在以數(shù)字方式促進(jìn)、驗(yàn)證或強(qiáng)制執(zhí)行合同談判或履行的計(jì)算機(jī)協(xié)議。
就比特幣而言,交易包含從所有者賬戶(hù)到其他賬戶(hù)的金額(即在賬戶(hù)之間轉(zhuǎn)移比特幣金額)。交易中還包括公鑰和帳戶(hù)ID,因此傳輸是安全的。但這是比特幣特有的。
將交易添加到網(wǎng)絡(luò)并合并;它們不在一個(gè)區(qū)塊或鏈條中。
這就是區(qū)塊鏈共識(shí)機(jī)制發(fā)揮作用的地方。這里有許多經(jīng)過(guò)驗(yàn)證的共識(shí)算法和模式,超出了本博客的范圍。
挖掘是比特幣區(qū)塊鏈?zhǔn)褂玫墓沧R(shí)機(jī)制。這就是本博客進(jìn)一步討論的共識(shí)類(lèi)型。共識(shí)機(jī)制收集事務(wù),用它們構(gòu)建塊,然后將塊添加到鏈中。然后,鏈在添加到鏈之前驗(yàn)證新的事務(wù)塊。
Merkle樹(shù)
事務(wù)被散列并添加到塊中。創(chuàng)建Merkle樹(shù)數(shù)據(jù)結(jié)構(gòu)來(lái)計(jì)算Merkle根哈希。每個(gè)塊將存儲(chǔ)Merkle樹(shù)的根,這是一個(gè)平衡的哈希二叉樹(shù),其中內(nèi)部節(jié)點(diǎn)是兩個(gè)子哈希的哈希,一直到根哈希,即Merkle根。
此樹(shù)用于驗(yàn)證塊事務(wù),如果在任何事務(wù)中更改了單個(gè)信息位,Merkle根將無(wú)效。此外,它們還可以幫助以分布式方式傳輸塊,因?yàn)樵摻Y(jié)構(gòu)只允許添加和驗(yàn)證整個(gè)事務(wù)塊所需的事務(wù)哈希的單個(gè)分支。
下面是模塊中的方法。從事務(wù)列表中創(chuàng)建Merkle樹(shù)的java類(lèi)。
public List<String> merkleTree() { ArrayList<String> tree = new ArrayList<>(); // Start by adding all the hashes of the transactions as leaves of the // tree. for (T t : transactions) { tree.add(t.hash()); } int levelOffset = 0; // Offset in the list where the currently processed // level starts. // Step through each level, stopping when we reach the root (levelSize // == 1). for (int levelSize = transactions.size(); levelSize > 1; levelSize = (levelSize + 1) / 2) { // For each pair of nodes on that level: for (int left = 0; left < levelSize; left += 2) { // The right hand node can be the same as the left hand, in the // case where we don't have enough // transactions. int right = Math.min(left + 1, levelSize - 1); String tleft = tree.get(levelOffset + left); String tright = tree.get(levelOffset + right); tree.add(SHA256.generateHash(tleft + tright)); } // Move to the next level. levelOffset += levelSize; } return tree; }
該方法用于計(jì)算塊的Merkle樹(shù)根。配套項(xiàng)目有一個(gè)Merkle樹(shù)單元測(cè)試,它試圖將事務(wù)添加到塊中,并驗(yàn)證Merkle根是否已更改。下面是單元測(cè)試的源代碼。
@Test public void merkleTreeTest() { // create chain, add transaction SimpleBlockchain<Transaction> chain1 = new SimpleBlockchain<Transaction>(); chain1.add(new Transaction("A")).add(new Transaction("B")).add(new Transaction("C")).add(new Transaction("D")); // get a block in chain Block<Transaction> block = chain1.getHead(); System.out.println("Merkle Hash tree :" + block.merkleTree()); // get a transaction from block Transaction tx = block.getTransactions().get(0); // see if block transactions are valid, they should be block.transasctionsValid(); assertTrue(block.transasctionsValid()); // mutate the data of a transaction tx.setValue("Z"); // block should no longer be valid, blocks MerkleRoot does not match computed merkle tree of transactions assertFalse(block.transasctionsValid()); }
此單元測(cè)試模擬驗(yàn)證事務(wù),然后在一致性機(jī)制之外的塊中更改事務(wù),即如果有人試圖更改事務(wù)數(shù)據(jù)。
記住,區(qū)塊鏈只是附加的,由于區(qū)塊鏈數(shù)據(jù)結(jié)構(gòu)在節(jié)點(diǎn)之間共享,塊數(shù)據(jù)結(jié)構(gòu)(包括Merkle根)被散列并連接到其他塊。所有節(jié)點(diǎn)都可以驗(yàn)證新塊,現(xiàn)有塊可以很容易地被證明是有效的。因此,一個(gè)礦工試圖添加一個(gè)虛假的區(qū)塊,或者一個(gè)節(jié)點(diǎn)試圖調(diào)整舊的交易,實(shí)際上是不可能的,在太陽(yáng)長(zhǎng)成超新星并給所有人一個(gè)非常好的棕褐色之前。
采礦工作證明
將交易組合成一個(gè)區(qū)塊,然后提交給鏈成員驗(yàn)證的過(guò)程在比特幣領(lǐng)域被稱(chēng)為“挖掘”。
更一般地說(shuō),在區(qū)塊鏈中,這被稱(chēng)為共識(shí)。有不同類(lèi)型的經(jīng)驗(yàn)證的分布式一致性算法。使用哪種機(jī)制取決于您是否擁有公共區(qū)塊鏈或許可區(qū)塊鏈。我們的白皮書(shū)對(duì)此進(jìn)行了更深入的描述,但本博客的重點(diǎn)是區(qū)塊鏈機(jī)制,因此本例我們將應(yīng)用一種工作證明共識(shí)機(jī)制。
因此,挖掘節(jié)點(diǎn)將監(jiān)聽(tīng)區(qū)塊鏈正在執(zhí)行的交易,并執(zhí)行一個(gè)簡(jiǎn)單的數(shù)學(xué)謎題。這個(gè)謎題使用一個(gè)在每次迭代中都會(huì)更改的nonce值生成具有一組預(yù)先確定的前導(dǎo)零的塊散列,直到找到前導(dǎo)零散列為止。
示例Java項(xiàng)目有一個(gè)Miner.java
類(lèi),帶有一個(gè)proofwork(Block)
方法實(shí)現(xiàn),如下所示。
private String proofOfWork(Block block) { String nonceKey = block.getNonce(); long nonce = 0; boolean nonceFound = false; String nonceHash = ""; Gson parser = new Gson(); String serializedData = parser.toJson(transactionPool); String message = block.getTimeStamp() + block.getIndex() + block.getMerkleRoot() + serializedData + block.getPreviousHash(); while (!nonceFound) { nonceHash = SHA256.generateHash(message + nonce); nonceFound = nonceHash.substring(0, nonceKey.length()).equals(nonceKey); nonce++; } return nonceHash; }
同樣,這是簡(jiǎn)化的,但是一旦收到一定數(shù)量的事務(wù),miner實(shí)現(xiàn)將對(duì)塊執(zhí)行工作證明哈希。該算法只是循環(huán)并創(chuàng)建塊的SHA-256散列,直到產(chǎn)生前導(dǎo)數(shù)散列。
這可能需要很多時(shí)間,這就是為什么要實(shí)現(xiàn)特定的GPU微處理器來(lái)盡可能快地執(zhí)行和解決這個(gè)問(wèn)題。
單元測(cè)試
您可以在GitHub上看到所有這些概念與Java示例項(xiàng)目的JUnit測(cè)試結(jié)合在一起。
最后的想法
希望這篇文章能給你帶來(lái)足夠的興趣和洞察力,讓你繼續(xù)研究區(qū)塊鏈技術(shù)。
到此這篇關(guān)于利用Java代碼實(shí)現(xiàn)區(qū)塊鏈技術(shù)的文章就介紹到這了,更多相關(guān)Java實(shí)現(xiàn)區(qū)塊鏈內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot利用jpa連接MySQL數(shù)據(jù)庫(kù)的方法
這篇文章主要介紹了SpringBoot利用jpa連接MySQL數(shù)據(jù)庫(kù)的方法,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-10-10使用socket實(shí)現(xiàn)網(wǎng)絡(luò)聊天室和私聊功能
這篇文章主要介紹了使用socket實(shí)現(xiàn)網(wǎng)絡(luò)聊天室和私聊功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12Java實(shí)現(xiàn)滑動(dòng)驗(yàn)證碼的示例代碼
這篇文章主要介紹了Java實(shí)現(xiàn)滑動(dòng)驗(yàn)證碼的示例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-01-01java8 stream sort自定義復(fù)雜排序案例
這篇文章主要介紹了java8 stream sort自定義復(fù)雜排序案例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-10-10Java數(shù)組模擬優(yōu)先級(jí)隊(duì)列數(shù)據(jù)結(jié)構(gòu)的實(shí)例
這篇文章主要介紹了Java數(shù)組模擬優(yōu)先級(jí)隊(duì)列數(shù)據(jù)結(jié)構(gòu)的實(shí)例,優(yōu)先級(jí)隊(duì)列中的元素會(huì)被設(shè)置優(yōu)先權(quán),本文的例子借助了Java中的TreeSet和TreeMap,需要的朋友可以參考下2016-04-04教你用Springboot實(shí)現(xiàn)攔截器獲取header內(nèi)容
項(xiàng)目中遇到一個(gè)需求,對(duì)接上游系統(tǒng)是涉及到需要增加請(qǐng)求頭,請(qǐng)求頭的信息是動(dòng)態(tài)獲取的,需要?jiǎng)討B(tài)從下游拿到之后轉(zhuǎn)給上游,文中非常詳細(xì)的介紹了該需求的實(shí)現(xiàn),需要的朋友可以參考下2021-05-05第一次使用Android Studio時(shí)你應(yīng)該知道的一切配置(推薦)
這篇文章主要介紹了第一次使用Android Studio時(shí)你應(yīng)該知道的一切配置(推薦) ,需要的朋友可以參考下2017-09-09Java字符串技巧之刪除標(biāo)點(diǎn)或最后字符的方法
這篇文章主要介紹了Java字符串技巧之刪除標(biāo)點(diǎn)或最后字符的方法,是Java入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-11-11