App下載

Java實(shí)現(xiàn)簡(jiǎn)單的區(qū)塊鏈程序的方法 附實(shí)例代碼

猿友 2021-07-19 16:59:50 瀏覽數(shù) (3350)
反饋

本篇文章將和大家一起分享區(qū)塊鏈技術(shù)的概念,還會(huì)使用 Java 來(lái)實(shí)現(xiàn)一個(gè)基本的應(yīng)用程序。此外還會(huì)討論關(guān)于該技術(shù)的一些先進(jìn)概念以及實(shí)際應(yīng)用。以下是詳情內(nèi)容。

什么是區(qū)塊鏈?

那么,讓我們先來(lái)了解一下區(qū)塊鏈到底是什么…

好吧,它的起源可以追溯到Satoshi Nakamoto在2008年發(fā)表的關(guān)于比特幣的白皮書(shū)。

區(qū)塊鏈?zhǔn)且粋€(gè)分散的信息分類(lèi)帳。它由通過(guò)使用密碼學(xué)連接的數(shù)據(jù)塊組成。它屬于通過(guò)公共網(wǎng)絡(luò)連接的節(jié)點(diǎn)網(wǎng)絡(luò)。當(dāng)我們稍后嘗試構(gòu)建一個(gè)基本教程時(shí),我們將更好地理解這一點(diǎn)。

我們必須了解一些重要的屬性,讓我們來(lái)了解一下:

  • 防篡改 :首先,數(shù)據(jù)作為塊的一部分是防篡改的。每個(gè)塊都由一個(gè)加密摘要(通常稱(chēng)為散列)引用,使得塊能夠防篡改。
  • 去中心化 :整個(gè)區(qū)塊鏈在網(wǎng)絡(luò)上完全去中心化。這意味著沒(méi)有主節(jié)點(diǎn),網(wǎng)絡(luò)中的每個(gè)節(jié)點(diǎn)都有相同的副本。
  • 透明 :每個(gè)參與網(wǎng)絡(luò)的節(jié)點(diǎn)通過(guò)與其他節(jié)點(diǎn)協(xié)商一致,對(duì)其鏈進(jìn)行驗(yàn)證并添加新的塊。因此,每個(gè)節(jié)點(diǎn)都具有數(shù)據(jù)的完全可見(jiàn)性。

區(qū)塊鏈?zhǔn)侨绾喂ぷ鞯模?br>

現(xiàn)在,讓我們來(lái)了解區(qū)塊鏈?zhǔn)侨绾喂ぷ鞯摹?/p>

區(qū)塊鏈的基本單位是區(qū)塊。單個(gè)塊可以封裝多個(gè)事務(wù)或其他有價(jià)值的數(shù)據(jù):

開(kāi)采區(qū)塊

我們用散列值表示一個(gè)塊。生成塊的哈希值稱(chēng)為“挖掘”塊。開(kāi)采一個(gè)區(qū)塊通常計(jì)算成本很高,因?yàn)樗恰肮ぷ髯C明”。

塊的散列通常由以下數(shù)據(jù)組成:

  • 首先,塊的散列由它封裝的事務(wù)組成
  • 散列還包括塊創(chuàng)建的時(shí)間戳
  • 它還包括一個(gè)nonce,一個(gè)用于密碼學(xué)的任意數(shù)字
  • 最后,當(dāng)前塊的散列還包括前一塊的散列
  • 網(wǎng)絡(luò)中的多個(gè)節(jié)點(diǎn)可以同時(shí)競(jìng)爭(zhēng)挖掘塊。除了生成散列,節(jié)點(diǎn)還必須驗(yàn)證添加到塊中的事務(wù)是否合法。第一個(gè)挖方塊的人贏得比賽!

向區(qū)塊鏈中添加區(qū)塊

雖然挖掘塊的計(jì)算成本很高,但驗(yàn)證塊是否合法相對(duì)容易得多。網(wǎng)絡(luò)中的所有節(jié)點(diǎn)都參與驗(yàn)證新開(kāi)采的區(qū)塊。

因此, 在節(jié)點(diǎn)一致的情況下,一個(gè)新挖掘的區(qū)塊被添加到區(qū)塊鏈中。

現(xiàn)在,有幾種共識(shí)協(xié)議可供我們用于驗(yàn)證。網(wǎng)絡(luò)中的節(jié)點(diǎn)使用相同的協(xié)議來(lái)檢測(cè)鏈的惡意分支。因此,即使引入惡意分支,也會(huì)很快被大多數(shù)節(jié)點(diǎn)拒絕。

Java中的基本區(qū)塊鏈
現(xiàn)在我們已經(jīng)有足夠的上下文開(kāi)始用Java構(gòu)建一個(gè)基本的應(yīng)用程序。

我們這里的簡(jiǎn)單示例將說(shuō)明我們剛才看到的基本概念。生產(chǎn)級(jí)應(yīng)用程序需要考慮很多問(wèn)題,這些問(wèn)題超出了本教程的范圍。不過(guò),我們稍后將討論一些高級(jí)主題。

實(shí)現(xiàn)塊

首先,我們需要定義一個(gè)簡(jiǎn)單的POJO來(lái)保存塊的數(shù)據(jù):

public class Block {
    private String hash;
    private String previousHash;
    private String data;
    private long timeStamp;
    private int nonce;
 
    public Block(String data, String previousHash, long timeStamp) {
        this.data = data;
        this.previousHash = previousHash;
        this.timeStamp = timeStamp;
        this.hash = calculateBlockHash();
    }
    // standard getters and setters
}

讓我們了解一下我們?cè)谶@里打包的東西:

nonce

計(jì)算散列

現(xiàn)在,我們?nèi)绾斡?jì)算塊的散列呢?我們已經(jīng)使用了 calculateBlockHash 方法,但還沒(méi)有看到實(shí)現(xiàn)。在我們實(shí)現(xiàn)這個(gè)方法之前,花點(diǎn)時(shí)間來(lái)理解什么是散列是值得的。

散列是散列函數(shù)的輸出。 哈希函數(shù)將任意大小的輸入數(shù)據(jù)映射為固定大小的輸出數(shù)據(jù)。 哈希對(duì)輸入數(shù)據(jù)中的任何更改都非常敏感,無(wú)論更改多么小。

此外,僅僅從散列中獲取輸入數(shù)據(jù)是不可能的。這些屬性使得哈希函數(shù)在密碼學(xué)中非常有用。

那么,讓我們看看如何在Java中生成塊的哈希:

public String calculateBlockHash() {
    String dataToHash = previousHash 
      + Long.toString(timeStamp) 
      + Integer.toString(nonce) 
      + data;
    MessageDigest digest = null;
    byte[] bytes = null;
    try {
        digest = MessageDigest.getInstance("SHA-256");
        bytes = digest.digest(dataToHash.getBytes(UTF_8));
    } catch (NoSuchAlgorithmException | UnsupportedEncodingException ex) {
        logger.log(Level.SEVERE, ex.getMessage());
    }
    StringBuffer buffer = new StringBuffer();
    for (byte b : bytes) {
        buffer.append(String.format("%02x", b));
    }
    return buffer.toString();
}
public String calculateBlockHash() {
    String dataToHash = previousHash 
      + Long.toString(timeStamp) 
      + Integer.toString(nonce) 
      + data;
    MessageDigest digest = null;
    byte[] bytes = null;
    try {
        digest = MessageDigest.getInstance("SHA-256");
        bytes = digest.digest(dataToHash.getBytes(UTF_8));
    } catch (NoSuchAlgorithmException | UnsupportedEncodingException ex) {
        logger.log(Level.SEVERE, ex.getMessage());
    }
    StringBuffer buffer = new StringBuffer();
    for (byte b : bytes) {
        buffer.append(String.format("%02x", b));
    }
    return buffer.toString();
}

這里發(fā)生了很多事情,讓我們?cè)敿?xì)了解一下:

MessageDigest

我們?cè)谀菈K地上挖礦了嗎?

到目前為止,一切聽(tīng)起來(lái)都簡(jiǎn)單而優(yōu)雅,只是我們還沒(méi)有開(kāi)采這個(gè)區(qū)塊。那么,究竟需要挖掘一個(gè)區(qū)塊,這已經(jīng)吸引了開(kāi)發(fā)人員一段時(shí)間的想象力!

嗯,挖掘一個(gè)區(qū)塊意味著為這個(gè)區(qū)塊解決一個(gè)計(jì)算復(fù)雜的任務(wù)。雖然計(jì)算一個(gè)塊的散列有點(diǎn)瑣碎,但找到以五個(gè)零開(kāi)始的散列卻不是。更復(fù)雜的是找到一個(gè)以十個(gè)零開(kāi)始的散列,我們就得到了一個(gè)大概的想法。

那么,我們到底該怎么做呢?老實(shí)說(shuō),這個(gè)解決方案沒(méi)有那么花哨!我們是用蠻力來(lái)達(dá)到這個(gè)目標(biāo)的。我們?cè)谶@里使用 nonce :

public String mineBlock(int prefix) {
    String prefixString = new String(new char[prefix]).replace('