利用Java代碼實現(xiàn)區(qū)塊鏈技術
前言:
比特幣很熱門——這是多么輕描淡寫的說法啊。雖然加密貨幣的未來有些不確定,但用于驅動比特幣的區(qū)塊鏈技術也非常流行。
區(qū)塊鏈的應用范圍幾乎無窮無盡??梢哉f,它還有可能破壞企業(yè)自動化。
本文將重點關注區(qū)塊鏈體系結構,特別是演示“不可變、僅附加”分布式賬本如何與簡化的代碼示例一起工作。
作為開發(fā)人員,與簡單地閱讀技術文章相比,在理解代碼的工作原理時,從代碼中看到東西要有用得多。至少對我來說是這樣。那么,讓我們開始吧!
簡言之,區(qū)塊鏈
首先讓我們快速總結一下區(qū)塊鏈。一個塊包含一些標題信息和一組或一塊任何類型數(shù)據(jù)的事務。鏈從第一個(起源)塊開始。在添加/追加事務時,將根據(jù)一個塊中可以存儲的事務數(shù)量創(chuàng)建新的塊。
當超過塊閾值大小時,將創(chuàng)建一個新的事務塊。新區(qū)塊與前一區(qū)塊相連,因此稱為區(qū)塊鏈。
不變性
區(qū)塊鏈是不可變的,因為為交易計算SHA-256哈希。塊的內容也會被散列,從而提供唯一的標識符。此外,來自鏈接的前一個塊的散列也被存儲并散列在塊頭中。
這就是為什么試圖篡改區(qū)塊鏈區(qū)塊基本上是不可能的,至少在目前的計算能力下是如此。下面是一個顯示塊屬性的部分Java類定義。
... 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>(); ...
請注意,注入的泛型類型是Tx類型。這允許事務數(shù)據(jù)發(fā)生變化。此外,previousHash
屬性將引用前一個塊的哈希。merkleRoot
和nonce
屬性將在稍后進行描述。
塊散列
每個塊可以計算一個塊散列。這本質上是連接在一起的所有塊屬性的散列,包括前一個塊的散列和由此計算出的SHA-256散列。
下面是塊中定義的方法。計算散列的java類。
... 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)); } ...
塊事務被序列化為JSON字符串,以便在散列之前將其附加到塊屬性中。
Chain 鏈
區(qū)塊鏈通過接受交易來管理區(qū)塊。當達到預定閾值時,創(chuàng)建塊。這是一個簡單的Chain鏈。java部分實現(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類型鍵入的塊的列表。此外,在創(chuàng)建鏈時,無參數(shù)構造函數(shù)會創(chuàng)建一個初始的“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; }
這個新的block方法將創(chuàng)建一個新的block實例,為適當?shù)闹翟O定種子,并分配前一個block的hash(即鏈頭的hash)。然后它將返回塊。
在將塊添加到鏈之前,可以通過將新塊的上一個哈希與鏈的最后一個塊(頭)進行比較來驗證塊,以確保它們匹配。這是一個簡單的鎖鏈。java方法描述了這一點。
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); }
整個區(qū)塊鏈通過鏈的循環(huán)進行驗證,以確保一個區(qū)塊的哈希仍然與前一個區(qū)塊的哈希匹配。
這是SimpleBlockChain.java
的 validate()
方法實現(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ù)或任何其他財產都是非常困難的。而且,隨著鏈條的增長,它繼續(xù)變得非常、非常、非常困難,基本上是不可能的。直到量子計算機問世
添加事務
區(qū)塊鏈技術的另一個重要技術點是它是分布式的。它們是附加的這一事實只會有助于在參與區(qū)塊鏈網絡的節(jié)點之間復制區(qū)塊鏈。節(jié)點通常以點對點的方式進行通信,比特幣就是這樣,但不一定要這樣。其他區(qū)塊鏈實施使用分散的方法,比如通過HTTP使用API。然而,這是另一個博客的主題。
事務幾乎可以代表任何東西。事務可以包含要執(zhí)行的代碼(即智能合約),或者存儲和附加有關某種業(yè)務事務的信息。
智能合同:旨在以數(shù)字方式促進、驗證或強制執(zhí)行合同談判或履行的計算機協(xié)議。
就比特幣而言,交易包含從所有者賬戶到其他賬戶的金額(即在賬戶之間轉移比特幣金額)。交易中還包括公鑰和帳戶ID,因此傳輸是安全的。但這是比特幣特有的。
將交易添加到網絡并合并;它們不在一個區(qū)塊或鏈條中。
這就是區(qū)塊鏈共識機制發(fā)揮作用的地方。這里有許多經過驗證的共識算法和模式,超出了本博客的范圍。
挖掘是比特幣區(qū)塊鏈使用的共識機制。這就是本博客進一步討論的共識類型。共識機制收集事務,用它們構建塊,然后將塊添加到鏈中。然后,鏈在添加到鏈之前驗證新的事務塊。
Merkle樹
事務被散列并添加到塊中。創(chuàng)建Merkle樹數(shù)據(jù)結構來計算Merkle根哈希。每個塊將存儲Merkle樹的根,這是一個平衡的哈希二叉樹,其中內部節(jié)點是兩個子哈希的哈希,一直到根哈希,即Merkle根。
此樹用于驗證塊事務,如果在任何事務中更改了單個信息位,Merkle根將無效。此外,它們還可以幫助以分布式方式傳輸塊,因為該結構只允許添加和驗證整個事務塊所需的事務哈希的單個分支。
下面是模塊中的方法。從事務列表中創(chuàng)建Merkle樹的java類。
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; }
該方法用于計算塊的Merkle樹根。配套項目有一個Merkle樹單元測試,它試圖將事務添加到塊中,并驗證Merkle根是否已更改。下面是單元測試的源代碼。
@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()); }
此單元測試模擬驗證事務,然后在一致性機制之外的塊中更改事務,即如果有人試圖更改事務數(shù)據(jù)。
記住,區(qū)塊鏈只是附加的,由于區(qū)塊鏈數(shù)據(jù)結構在節(jié)點之間共享,塊數(shù)據(jù)結構(包括Merkle根)被散列并連接到其他塊。所有節(jié)點都可以驗證新塊,現(xiàn)有塊可以很容易地被證明是有效的。因此,一個礦工試圖添加一個虛假的區(qū)塊,或者一個節(jié)點試圖調整舊的交易,實際上是不可能的,在太陽長成超新星并給所有人一個非常好的棕褐色之前。
采礦工作證明
將交易組合成一個區(qū)塊,然后提交給鏈成員驗證的過程在比特幣領域被稱為“挖掘”。
更一般地說,在區(qū)塊鏈中,這被稱為共識。有不同類型的經驗證的分布式一致性算法。使用哪種機制取決于您是否擁有公共區(qū)塊鏈或許可區(qū)塊鏈。我們的白皮書對此進行了更深入的描述,但本博客的重點是區(qū)塊鏈機制,因此本例我們將應用一種工作證明共識機制。
因此,挖掘節(jié)點將監(jiān)聽區(qū)塊鏈正在執(zhí)行的交易,并執(zhí)行一個簡單的數(shù)學謎題。這個謎題使用一個在每次迭代中都會更改的nonce值生成具有一組預先確定的前導零的塊散列,直到找到前導零散列為止。
示例Java項目有一個Miner.java
類,帶有一個proofwork(Block)
方法實現(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; }
同樣,這是簡化的,但是一旦收到一定數(shù)量的事務,miner實現(xiàn)將對塊執(zhí)行工作證明哈希。該算法只是循環(huán)并創(chuàng)建塊的SHA-256散列,直到產生前導數(shù)散列。
這可能需要很多時間,這就是為什么要實現(xiàn)特定的GPU微處理器來盡可能快地執(zhí)行和解決這個問題。
單元測試
您可以在GitHub上看到所有這些概念與Java示例項目的JUnit測試結合在一起。
最后的想法
希望這篇文章能給你帶來足夠的興趣和洞察力,讓你繼續(xù)研究區(qū)塊鏈技術。
到此這篇關于利用Java代碼實現(xiàn)區(qū)塊鏈技術的文章就介紹到這了,更多相關Java實現(xiàn)區(qū)塊鏈內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
SpringBoot利用jpa連接MySQL數(shù)據(jù)庫的方法
這篇文章主要介紹了SpringBoot利用jpa連接MySQL數(shù)據(jù)庫的方法,本文通過示例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-10-10Java數(shù)組模擬優(yōu)先級隊列數(shù)據(jù)結構的實例
這篇文章主要介紹了Java數(shù)組模擬優(yōu)先級隊列數(shù)據(jù)結構的實例,優(yōu)先級隊列中的元素會被設置優(yōu)先權,本文的例子借助了Java中的TreeSet和TreeMap,需要的朋友可以參考下2016-04-04教你用Springboot實現(xiàn)攔截器獲取header內容
項目中遇到一個需求,對接上游系統(tǒng)是涉及到需要增加請求頭,請求頭的信息是動態(tài)獲取的,需要動態(tài)從下游拿到之后轉給上游,文中非常詳細的介紹了該需求的實現(xiàn),需要的朋友可以參考下2021-05-05第一次使用Android Studio時你應該知道的一切配置(推薦)
這篇文章主要介紹了第一次使用Android Studio時你應該知道的一切配置(推薦) ,需要的朋友可以參考下2017-09-09