国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

基于Java語(yǔ)言構(gòu)建區(qū)塊鏈(三)—— 持久化 & 命令行

李世贊 / 3025人閱讀

摘要:我們?cè)撨x擇哪一款數(shù)據(jù)庫(kù)呢事實(shí)上,在比特幣白皮書中并沒有明確指定使用哪一種的數(shù)據(jù)庫(kù),因此這個(gè)由開發(fā)人員自己決定。詳見精通比特幣第二版第章節(jié)交易的輸入與輸出此外,每個(gè)區(qū)塊數(shù)據(jù)都是以多帶帶的文件形式存儲(chǔ)在磁盤上。資料源代碼精通比特幣第二版

最終內(nèi)容請(qǐng)以原文為準(zhǔn):https://wangwei.one/posts/35c...
引言

上一篇 文章我們實(shí)現(xiàn)了區(qū)塊鏈的工作量證明機(jī)制(Pow),盡可能地實(shí)現(xiàn)了挖礦。但是距離真正的區(qū)塊鏈應(yīng)用還有很多重要的特性沒有實(shí)現(xiàn)。今天我們來實(shí)現(xiàn)區(qū)塊鏈數(shù)據(jù)的存儲(chǔ)機(jī)制,將每次生成的區(qū)塊鏈數(shù)據(jù)保存下來。有一點(diǎn)需要注意,區(qū)塊鏈本質(zhì)上是一款分布式的數(shù)據(jù)庫(kù),我們這里不實(shí)現(xiàn)"分布式",只聚焦于數(shù)據(jù)存儲(chǔ)部分。

數(shù)據(jù)庫(kù)選擇

到目前為止,我們的實(shí)現(xiàn)機(jī)制中還沒有區(qū)塊存儲(chǔ)這一環(huán)節(jié),導(dǎo)致我們的區(qū)塊每次生成之后都保存在了內(nèi)存中。這樣不便于我們重新使用區(qū)塊鏈,每次都要從頭開始生成區(qū)塊,也不能夠跟他人共享我們的區(qū)塊鏈,因此,我們需要將其存儲(chǔ)在磁盤上。

我們?cè)撨x擇哪一款數(shù)據(jù)庫(kù)呢?事實(shí)上,在《比特幣白皮書》中并沒有明確指定使用哪一種的數(shù)據(jù)庫(kù),因此這個(gè)由開發(fā)人員自己決定。中本聰 開發(fā)的 Bitcoin Core 中使用的是LevelDB。原文 Building Blockchain in Go. Part 3: Persistence and CLI 中使用的是 BoltDB ,對(duì)Go語(yǔ)言支持比較好。

但是我們這里使用的是Java來實(shí)現(xiàn),BoltDB不支持Java,這里我們選用 Rocksdb

RocksDB是由Facebook數(shù)據(jù)庫(kù)工程團(tuán)隊(duì)開發(fā)和維護(hù)的一款key-value存儲(chǔ)引擎,比LevelDB性能更加強(qiáng)大,有關(guān)Rocksdb的詳細(xì)介紹,請(qǐng)移步至官方文檔:https://github.com/facebook/r... ,這里不多做介紹。
數(shù)據(jù)結(jié)構(gòu)

在我們開始實(shí)現(xiàn)數(shù)據(jù)持久化之前,我們先要確定我們?cè)撊绾稳ゴ鎯?chǔ)我們的數(shù)據(jù)。為此,我們先來看看比特幣是怎么做的。

簡(jiǎn)單來講,比特幣使用了兩個(gè)"buckets(桶)"來存儲(chǔ)數(shù)據(jù):

blocks. 描述鏈上所有區(qū)塊的元數(shù)據(jù).

chainstate. 存儲(chǔ)區(qū)塊鏈的狀態(tài),指的是當(dāng)前所有的UTXO(未花費(fèi)交易輸出)以及一些元數(shù)據(jù).

“在比特幣的世界里既沒有賬戶,也沒有余額,只有分散到區(qū)塊鏈里的UTXO。”

詳見:《精通比特幣》第二版 第06章節(jié) —— 交易的輸入與輸出

此外,每個(gè)區(qū)塊數(shù)據(jù)都是以多帶帶的文件形式存儲(chǔ)在磁盤上。這樣做是出于性能的考慮:當(dāng)讀取某一個(gè)多帶帶的區(qū)塊數(shù)據(jù)時(shí),不需要加載所有的區(qū)塊數(shù)據(jù)到內(nèi)存中來。

blocks 這個(gè)桶中,存儲(chǔ)的鍵值對(duì):

"b" + 32-byte block hash -> block index record

區(qū)塊的索引記錄

"f" + 4-byte file number -> file information record

文件信息記錄

"l" -> 4-byte file number: the last block file number used

最新的一個(gè)區(qū)塊所使用的文件編碼

"R" -> 1-byte boolean: whether we"re in the process of reindexing

是否處于重建索引的進(jìn)程當(dāng)中

"F" + 1-byte flag name length + flag name string -> 1 byte boolean: various flags that can be on or off

各種可以打開或關(guān)閉的flag標(biāo)志

"t" + 32-byte transaction hash -> transaction index record

交易索引記錄

chainstate 這個(gè)桶中,存儲(chǔ)的鍵值對(duì):

"c" + 32-byte transaction hash -> unspent transaction output record for that transaction

某筆交易的UTXO記錄

"B" -> 32-byte block hash: the block hash up to which the database represents the unspent transaction outputs

數(shù)據(jù)庫(kù)所表示的UTXO的區(qū)塊Hash(抱歉,這一點(diǎn)我還沒弄明白……)

由于我們還沒有實(shí)現(xiàn)交易相關(guān)的特性,因此,我們這里只使用 block 桶。另外,前面提到過的,這里我們不會(huì)實(shí)現(xiàn)各個(gè)區(qū)塊數(shù)據(jù)各自存儲(chǔ)在獨(dú)立的文件上,而是統(tǒng)一存放在一個(gè)文件里面。因此,我們不要存儲(chǔ)和文件編碼相關(guān)的數(shù)據(jù),這樣一來,我們所用到的鍵值對(duì)就簡(jiǎn)化為:

32-byte block-hash -> Block structure (serialized)

區(qū)塊數(shù)據(jù)與區(qū)塊hash的鍵值對(duì)

"l" -> the hash of the last block in a chain

最新一個(gè)區(qū)塊hash的鍵值對(duì)

序列化

RocksDB的Key與Value只能以byte[]的形式進(jìn)行存儲(chǔ),這里我們需要用到序列化與反序列化庫(kù) Kryo,代碼如下:

package one.wangwei.blockchain.util;

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;

/**
 * 序列化工具類
 *
 * @author wangwei
 * @date 2018/02/07
 */
public class SerializeUtils {

    /**
     * 反序列化
     *
     * @param bytes 對(duì)象對(duì)應(yīng)的字節(jié)數(shù)組
     * @return
     */
    public static Object deserialize(byte[] bytes) {
        Input input = new Input(bytes);
        Object obj = new Kryo().readClassAndObject(input);
        input.close();
        return obj;
    }

    /**
     * 序列化
     *
     * @param object 需要序列化的對(duì)象
     * @return
     */
    public static byte[] serialize(Object object) {
        Output output = new Output(4096, -1);
        new Kryo().writeClassAndObject(output, object);
        byte[] bytes = output.toBytes();
        output.close();
        return bytes;
    }
}
持久化

上面已經(jīng)說過,我們這里使用RocksDB,我們先寫一個(gè)相關(guān)的工具類RocksDBUtils,主要的功能如下:

putLastBlockHash:保存最新一個(gè)區(qū)塊的Hash值

getLastBlockHash:查詢最新一個(gè)區(qū)塊的Hash值

putBlock:保存區(qū)塊

getBlock:查詢區(qū)塊

注意:BoltDB 支持 Bucket 的特性,而RocksDB 不支持,我們這里采用統(tǒng)一前綴的方式進(jìn)行處理。
RocksDBUtils
package one.wangwei.blockchain.util;

import lombok.Getter;
import one.wangwei.blockchain.block.Block;
import org.rocksdb.Options;
import org.rocksdb.RocksDB;
import org.rocksdb.RocksDBException;

/**
 * RocksDB 工具類
 *
 * @author wangwei
 * @date 2018/02/27
 */
public class RocksDBUtils {

    /**
     * 區(qū)塊鏈數(shù)據(jù)文件
     */
    private static final String DB_FILE = "blockchain.db";
    /**
     * 區(qū)塊桶前綴
     */
    private static final String BLOCKS_BUCKET_PREFIX = "blocks_";

    private volatile static RocksDBUtils instance;

    public static RocksDBUtils getInstance() {
        if (instance == null) {
            synchronized (RocksDBUtils.class) {
                if (instance == null) {
                    instance = new RocksDBUtils();
                }
            }
        }
        return instance;
    }

    @Getter
    private RocksDB rocksDB;

    private RocksDBUtils() {
        initRocksDB();
    }

    /**
     * 初始化RocksDB
     */
    private void initRocksDB() {
        try {
            rocksDB = RocksDB.open(new Options().setCreateIfMissing(true), DB_FILE);
        } catch (RocksDBException e) {
            e.printStackTrace();
        }
    }

    /**
     * 保存最新一個(gè)區(qū)塊的Hash值
     *
     * @param tipBlockHash
     */
    public void putLastBlockHash(String tipBlockHash) throws Exception {
        rocksDB.put(SerializeUtils.serialize(BLOCKS_BUCKET_PREFIX + "l"), SerializeUtils.serialize(tipBlockHash));
    }

    /**
     * 查詢最新一個(gè)區(qū)塊的Hash值
     *
     * @return
     */
    public String getLastBlockHash() throws Exception {
        byte[] lastBlockHashBytes = rocksDB.get(SerializeUtils.serialize(BLOCKS_BUCKET_PREFIX + "l"));
        if (lastBlockHashBytes != null) {
            return (String) SerializeUtils.deserialize(lastBlockHashBytes);
        }
        return "";
    }

    /**
     * 保存區(qū)塊
     *
     * @param block
     */
    public void putBlock(Block block) throws Exception {
        byte[] key = SerializeUtils.serialize(BLOCKS_BUCKET_PREFIX + block.getHash());
        rocksDB.put(key, SerializeUtils.serialize(block));
    }

    /**
     * 查詢區(qū)塊
     *
     * @param blockHash
     * @return
     */
    public Block getBlock(String blockHash) throws Exception {
        byte[] key = SerializeUtils.serialize(BLOCKS_BUCKET_PREFIX + blockHash);
        return (Block) SerializeUtils.deserialize(rocksDB.get(key));
    }

}
創(chuàng)建區(qū)塊鏈

現(xiàn)在我們來優(yōu)化 Blockchain.newBlockchain 接口的代碼邏輯,改為如下邏輯:

代碼如下:

/**
  * 

創(chuàng)建區(qū)塊鏈

* * @return */ public static Blockchain newBlockchain() throws Exception { String lastBlockHash = RocksDBUtils.getInstance().getLastBlockHash(); if (StringUtils.isBlank(lastBlockHash)) { Block genesisBlock = Block.newGenesisBlock(); lastBlockHash = genesisBlock.getHash(); RocksDBUtils.getInstance().putBlock(genesisBlock); RocksDBUtils.getInstance().putLastBlockHash(lastBlockHash); } return new Blockchain(lastBlockHash); }

修改 Blockchain 的數(shù)據(jù)結(jié)構(gòu),只記錄最新一個(gè)區(qū)塊鏈的Hash值

public class Blockchain {
    
    @Getter
    private String lastBlockHash;

    private Blockchain(String lastBlockHash) {
        this.lastBlockHash = lastBlockHash;
    }
}

每次挖礦完成后,我們也需要將最新的區(qū)塊信息保存下來,并且更新最新區(qū)塊鏈Hash值:

/**
 * 

添加區(qū)塊

* * @param data */ public void addBlock(String data) throws Exception { String lastBlockHash = RocksDBUtils.getInstance().getLastBlockHash(); if (StringUtils.isBlank(lastBlockHash)) { throw new Exception("Fail to add block into blockchain ! "); } this.addBlock(Block.newBlock(lastBlockHash, data)); } /** *

添加區(qū)塊

* * @param block */ public void addBlock(Block block) throws Exception { RocksDBUtils.getInstance().putLastBlockHash(block.getHash()); RocksDBUtils.getInstance().putBlock(block); this.lastBlockHash = block.getHash(); }

到此,存儲(chǔ)部分的功能就實(shí)現(xiàn)完畢,我們還缺少一個(gè)功能:

檢索區(qū)塊鏈

現(xiàn)在,我們所有的區(qū)塊都保存到了數(shù)據(jù)庫(kù),因此,我們能夠重新打開已有的區(qū)塊鏈并且向其添加新的區(qū)塊。但這也導(dǎo)致我們?cè)僖矡o法打印出區(qū)塊鏈中所有區(qū)塊的信息,因?yàn)椋覀儧]有將區(qū)塊存儲(chǔ)在數(shù)組當(dāng)中。讓我們來修復(fù)這個(gè)瑕疵!

我們?cè)贐lockchain中創(chuàng)建一個(gè)內(nèi)部類 BlockchainIterator ,作為區(qū)塊鏈的迭代器,通過區(qū)塊之前的hash連接來依次迭代輸出區(qū)塊信息,代碼如下:

public class Blockchain {
 
    ....
    
    /**
     * 區(qū)塊鏈迭代器
     */
    public class BlockchainIterator {

        private String currentBlockHash;

        public BlockchainIterator(String currentBlockHash) {
            this.currentBlockHash = currentBlockHash;
        }

        /**
         * 是否有下一個(gè)區(qū)塊
         *
         * @return
         */
        public boolean hashNext() throws Exception {
            if (StringUtils.isBlank(currentBlockHash)) {
                return false;
            }
            Block lastBlock = RocksDBUtils.getInstance().getBlock(currentBlockHash);
            if (lastBlock == null) {
                return false;
            }
            // 創(chuàng)世區(qū)塊直接放行
            if (lastBlock.getPrevBlockHash().length() == 0) {
                return true;
            }
            return RocksDBUtils.getInstance().getBlock(lastBlock.getPrevBlockHash()) != null;
        }

        
        /**
         * 返回區(qū)塊
         *
         * @return
         */
        public Block next() throws Exception {
            Block currentBlock = RocksDBUtils.getInstance().getBlock(currentBlockHash);
            if (currentBlock != null) {
                this.currentBlockHash = currentBlock.getPrevBlockHash();
                return currentBlock;
            }
            return null;
        }
    }   
    
    ....    
}
測(cè)試
/**
 * 測(cè)試
 *
 * @author wangwei
 * @date 2018/02/05
 */
public class BlockchainTest {

    public static void main(String[] args) {
        try {
            Blockchain blockchain = Blockchain.newBlockchain();

            blockchain.addBlock("Send 1.0 BTC to wangwei");
            blockchain.addBlock("Send 2.5 more BTC to wangwei");
            blockchain.addBlock("Send 3.5 more BTC to wangwei");

            for (Blockchain.BlockchainIterator iterator = blockchain.getBlockchainIterator(); iterator.hashNext(); ) {
                Block block = iterator.next();

                if (block != null) {
                    boolean validate = ProofOfWork.newProofOfWork(block).validate();
                    System.out.println(block.toString() + ", validate = " + validate);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


/*輸出*/

Block{hash="0000012f87a0510dd0ee7048a6bd52db3002bae7d661126dc28287bd6c23189a", prevBlockHash="0000024b2c23c4fb06c2e2c1349275d415efe17a51db24cd4883da0067300ddf", data="Send 3.5 more BTC to wangwei", timeStamp=1519724875, nonce=369110}, validate = true
Block{hash="0000024b2c23c4fb06c2e2c1349275d415efe17a51db24cd4883da0067300ddf", prevBlockHash="00000b14fefb51ba2a7428549d469bcf3efae338315e7289d3e6dc4caf589d79", data="Send 2.5 more BTC to wangwei", timeStamp=1519724872, nonce=896348}, validate = true
Block{hash="00000b14fefb51ba2a7428549d469bcf3efae338315e7289d3e6dc4caf589d79", prevBlockHash="0000099ced1b02f40c750c5468bb8c4fd800ec9f46fea5d8b033e5d054f0f703", data="Send 1.0 BTC to wangwei", timeStamp=1519724869, nonce=673955}, validate = true
Block{hash="0000099ced1b02f40c750c5468bb8c4fd800ec9f46fea5d8b033e5d054f0f703", prevBlockHash="", data="Genesis Block", timeStamp=1519724866, nonce=840247}, validate = true
命令行界面

CLI 部分的內(nèi)容,這里不做詳細(xì)介紹,具體可以去查看文末的Github源碼鏈接。大致步驟如下:

配置

添加pom.xml配置


   
    ...
    
    
        commons-cli
        commons-cli
        1.4
    
    
    ...
    
    
        org.apache.maven.plugins
        maven-assembly-plugin
        3.1.0
        
            
                
                    true
                    lib/
                    one.wangwei.blockchain.cli.Main
                
            
            
                jar-with-dependencies
            
        
        
            
                make-assembly
                
                package
                
                
                    single
                
            
        
    
    
    ...
   
項(xiàng)目工程打包
$ mvn clean && mvn package
執(zhí)行命令
# 打印幫助信息
$ java -jar blockchain-java-jar-with-dependencies.jar -h 

# 添加區(qū)塊
$ java -jar blockchain-java-jar-with-dependencies.jar -add "Send 1.5 BTC to wangwei"
$ java -jar blockchain-java-jar-with-dependencies.jar -add "Send 2.5 BTC to wangwei"
$ java -jar blockchain-java-jar-with-dependencies.jar -add "Send 3.5 BTC to wangwei"

# 打印區(qū)塊鏈
$ java -jar blockchain-java-jar-with-dependencies.jar -print
總結(jié)

本篇我們實(shí)現(xiàn)了區(qū)塊鏈的存儲(chǔ)功能,接下來我們將實(shí)現(xiàn)地址、交易、錢包這一些列的功能。

資料

源代碼:https://github.com/wangweiX/b...

https://jeiwan.cc/posts/build...

《精通比特幣》第二版

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/76357.html

相關(guān)文章

  • 基于Java語(yǔ)言構(gòu)建區(qū)塊)—— 久化 & 命令

    摘要:我們?cè)撨x擇哪一款數(shù)據(jù)庫(kù)呢事實(shí)上,在比特幣白皮書中并沒有明確指定使用哪一種的數(shù)據(jù)庫(kù),因此這個(gè)由開發(fā)人員自己決定。詳見精通比特幣第二版第章節(jié)交易的輸入與輸出此外,每個(gè)區(qū)塊數(shù)據(jù)都是以單獨(dú)的文件形式存儲(chǔ)在磁盤上。資料源代碼精通比特幣第二版 showImg(https://segmentfault.com/img/remote/1460000013923488?w=1200&h=627); 最...

    asoren 評(píng)論0 收藏0
  • 基于Java語(yǔ)言構(gòu)建區(qū)塊(六)—— 交易(Merkle Tree)

    摘要:截止年月號(hào),比特幣中有個(gè)區(qū)塊,并且這些數(shù)據(jù)占據(jù)了的磁盤空間。每個(gè)比特幣節(jié)點(diǎn)都是路由區(qū)塊鏈數(shù)據(jù)庫(kù)挖礦錢包服務(wù)的功能集合。是比特幣的輕量級(jí)節(jié)點(diǎn),它不需要下載所有的區(qū)塊鏈數(shù)據(jù),也不需要驗(yàn)證區(qū)塊和交易數(shù)據(jù)。 showImg(https://img.i7years.com/blog/pexels-photo-38136.jpeg); 最終內(nèi)容請(qǐng)以原文為準(zhǔn):https://wangwei.one/...

    liuhh 評(píng)論0 收藏0
  • 基于Java語(yǔ)言構(gòu)建區(qū)塊(六)—— 交易(Merkle Tree)

    摘要:截止年月號(hào),比特幣中有個(gè)區(qū)塊,并且這些數(shù)據(jù)占據(jù)了的磁盤空間。每個(gè)比特幣節(jié)點(diǎn)都是路由區(qū)塊鏈數(shù)據(jù)庫(kù)挖礦錢包服務(wù)的功能集合。是比特幣的輕量級(jí)節(jié)點(diǎn),它不需要下載所有的區(qū)塊鏈數(shù)據(jù),也不需要驗(yàn)證區(qū)塊和交易數(shù)據(jù)。 showImg(https://img.i7years.com/blog/pexels-photo-38136.jpeg); 最終內(nèi)容請(qǐng)以原文為準(zhǔn):https://wangwei.one/...

    KevinYan 評(píng)論0 收藏0
  • 以太坊DApp開發(fā)入門教程——Node.js和truffle框架打造區(qū)塊投票系統(tǒng)

    摘要:第一節(jié)課程概述本課程面向初學(xué)者,內(nèi)容涵蓋以太坊開發(fā)相關(guān)的基本概念,并將手把手地教大家如何構(gòu)建一個(gè)基于以太坊的完整去中心化應(yīng)用區(qū)塊鏈投票系統(tǒng)。第七節(jié)以太坊世界計(jì)算機(jī)以太坊是一種區(qū)塊鏈的實(shí)現(xiàn)。交易數(shù)據(jù)以太坊中每筆交易都存儲(chǔ)在區(qū)塊鏈上。 第一節(jié) 課程概述 本課程面向初學(xué)者,內(nèi)容涵蓋以太坊開發(fā)相關(guān)的基本概念,并將手把手地教大家如何構(gòu)建一個(gè) 基于以太坊的完整去中心化應(yīng)用 —— 區(qū)塊鏈投票系統(tǒng)。 ...

    zebrayoung 評(píng)論0 收藏0
  • 以太坊DApp開發(fā)入門教程——Node.js和truffle框架打造區(qū)塊投票系統(tǒng)

    摘要:第一節(jié)課程概述本課程面向初學(xué)者,內(nèi)容涵蓋以太坊開發(fā)相關(guān)的基本概念,并將手把手地教大家如何構(gòu)建一個(gè)基于以太坊的完整去中心化應(yīng)用區(qū)塊鏈投票系統(tǒng)。第七節(jié)以太坊世界計(jì)算機(jī)以太坊是一種區(qū)塊鏈的實(shí)現(xiàn)。交易數(shù)據(jù)以太坊中每筆交易都存儲(chǔ)在區(qū)塊鏈上。 第一節(jié) 課程概述 本課程面向初學(xué)者,內(nèi)容涵蓋以太坊開發(fā)相關(guān)的基本概念,并將手把手地教大家如何構(gòu)建一個(gè) 基于以太坊的完整去中心化應(yīng)用 —— 區(qū)塊鏈投票系統(tǒng)。 ...

    MASAILA 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<