Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to use Java persistence and command line

2025-03-04 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >

Share

Shulou(Shulou.com)06/01 Report--

This article mainly explains "Java persistence and command line use". The explanation in the article is simple and clear, easy to learn and understand. Please follow the editor's train of thought to study and learn "Java persistence and command line use".

Database selection

What we use here is Java to implement, BoltDB does not support Java, here we choose Rocksdb.

Data structure

Before we begin to persist our data, we need to determine how we should store our data. To do this, let's first take a look at how bitcoin is made.

Simply put, Bitcoin uses two "buckets" to store data:

Blocks. Describes the metadata for all blocks on the chain.

Chainstate. The state of the storage chunk chain refers to all the current UTXO (unspent transaction output) and some metadata.

"in the world of Bitcoin, there are neither accounts nor balances, only UTXO scattered into the blockchain."

For details, see Chapter 06 of the second edition of "mastering Bitcoin"-input and output of transactions

In addition, each chunk data is stored on disk as a separate file. This is done for performance reasons: when reading a single chunk of data, there is no need to load all the chunk data into memory.

In the blocks bucket, the stored key-value pair:

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

Index record of the block

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

File information record

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

The file encoding used by the latest block

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

Whether you are in the process of rebuilding the index

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

Various flag flags that can be turned on or off

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

Transaction index record

In the chainstate bucket, the stored key-value pair:

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

The UTXO record of a transaction

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

The UTXO block Hash represented by the database. (sorry, I haven't figured this out yet. )

Since we haven't implemented the transaction-related features yet, we only use block buckets here. In addition, as mentioned earlier, we will not implement that each block data is stored in a separate file, but in a single file. Therefore, we do not store data related to file encoding, so that the key-value pairs we use are simplified to:

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

Key-value pair of block data and block hash

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

The key-value pair of the latest block hash

(see a more detailed explanation)

Serialization

The Key and Value of RocksDB can only be stored in the form of byte []. Here, we need to use the serialization and deserialization library Kryo as follows:

Package one.wangwei.blockchain.util;import com.esotericsoftware.kryo.Kryo;import com.esotericsoftware.kryo.io.Input;import com.esotericsoftware.kryo.io.Output / * sequence chemicals class * * @ author wangwei * @ date 2018-02-07 * / public class SerializeUtils {/ * deserialization * * @ param bytes object corresponding byte array * @ return * / public static Object deserialize (byte [] bytes) {Input input = new Input (bytes); Object obj = new Kryo () .readClassAndObject (input) Input.close (); return obj;} / * * Serialization * * @ param object objects to be serialized * @ 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;}} persistence

As mentioned above, we use RocksDB here. Let's first write a related tool class, RocksDBUtils. The main functions are as follows:

PutLastBlockHash: saves the hash value of the latest block

GetLastBlockHash: query the hash value of the latest block

PutBlock: saving block

GetBlock: query block

Note: BoltDB supports Bucket features, but RocksDB does not, so we need to use Map to do a mapping ourselves.

RocksDBUtilspackage one.wangwei.blockchain.store;import com.google.common.collect.Maps;import one.wangwei.blockchain.block.Block;import one.wangwei.blockchain.util.SerializeUtils;import org.rocksdb.RocksDB;import org.rocksdb.RocksDBException;import java.util.Map / * * Storage utility class * * @ author wangwei * @ date 2018-02-27 * / public class RocksDBUtils {/ * * Block chain data file * / private static final String DB_FILE = "blockchain.db"; / * * Block bucket prefix * / private static final String BLOCKS_BUCKET_KEY = "blocks" / * * latest block * / private static final String LAST_BLOCK_KEY = "l"; private volatile static RocksDBUtils instance; public static RocksDBUtils getInstance () {if (instance = = null) {synchronized (RocksDBUtils.class) {if (instance = = null) {instance = new RocksDBUtils () } return instance;} private RocksDB db; / * block buckets * / private Map blocksBucket; private RocksDBUtils () {openDB (); initBlockBucket () } / * * Open database * / private void openDB () {try {db = RocksDB.open (DB_FILE);} catch (RocksDBException e) {throw new RuntimeException ("Fail to open db!", e) }} / * initialize blocks bucket * / private void initBlockBucket () {try {byte [] blockBucketKey = SerializeUtils.serialize (BLOCKS_BUCKET_KEY); byte [] blockBucketBytes = db.get (blockBucketKey); if (blockBucketBytes! = null) {blocksBucket = (Map) SerializeUtils.deserialize (blockBucketBytes) } else {blocksBucket = Maps.newHashMap (); db.put (blockBucketKey, SerializeUtils.serialize (blocksBucket));}} catch (RocksDBException e) {throw new RuntimeException ("Fail to init block bucket!", e) }} / * Save the hash value of the latest chunk * * @ param tipBlockHash * / public void putLastBlockHash (String tipBlockHash) {try {blocksBucket.put (LAST_BLOCK_KEY, SerializeUtils.serialize (tipBlockHash)); db.put (SerializeUtils.serialize (BLOCKS_BUCKET_KEY), SerializeUtils.serialize (blocksBucket)) } catch (RocksDBException e) {throw new RuntimeException ("Fail to put last block hash!", e);}} / * query the hash value of the latest chunk * * @ return * / public String getLastBlockHash () {byte [] lastBlockHashBytes = blocksBucket.get (LAST_BLOCK_KEY) If (lastBlockHashBytes! = null) {return (String) SerializeUtils.deserialize (lastBlockHashBytes);} return ";} / * Save block * * @ param block * / public void putBlock (Block block) {try {blocksBucket.put (block.getHash (), SerializeUtils.serialize (block)) Db.put (SerializeUtils.serialize (BLOCKS_BUCKET_KEY), SerializeUtils.serialize (blocksBucket));} catch (RocksDBException e) {throw new RuntimeException ("Fail to put block!", e) }} / * query block * * @ param blockHash * @ return * / public Block getBlock (String blockHash) {return (Block) SerializeUtils.deserialize (blocksBucket.get (blockHash));} / * close database * / public void closeDB () {try {db.close () } catch (Exception e) {throw new RuntimeException ("Fail to close db!", e);} create a block chain

Now let's optimize the code logic of the Blockchain.newBlockchain interface to the following logic:

The code is as follows:

/ *

Create a blockchain

* * @ 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 newBlockchain (lastBlockHash);}

Modify the data structure of Blockchain to record only the hash value of the latest block chain

Public class Blockchain {@ Getter private String lastBlockHash; private Blockchain (String lastBlockHash) {this.lastBlockHash = lastBlockHash;}}

After each mining is completed, we also need to save the latest block information and update the latest block chain hash value:

/ *

Add Block

* * @ 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));} / * *

Add Block

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

At this point, the function of the storage part is completed, and we still lack one function:

Retrieve block chain

Now, all our chunks are saved to the database, so we can reopen the existing blockchain and add new chunks to it. But as a result, we can no longer print out information about all the blocks in the block chain, because we don't store the blocks in the array. Let's fix this flaw!

We create an inner class BlockchainIterator in Blockchain as the iterator of the block chain, and iterate the block information through the hash connection before the block. The code is as follows:

Public class Blockchain {.... / * Block chain iterator * / public class BlockchainIterator {private String currentBlockHash; public BlockchainIterator (String currentBlockHash) {this.currentBlockHash = currentBlockHash } / * * is there a next block * * @ return * / public boolean hashNext () throws Exception {if (StringUtils.isBlank (currentBlockHash)) {return false;} Block lastBlock = RocksDBUtils.getInstance () .getBlock (currentBlockHash) If (lastBlock = = null) {return false;} / / Genesis Block directly releases if (lastBlock.getPrevBlockHash (). Length () = 0) {return true;} return RocksDBUtils.getInstance (). GetBlock (lastBlock.getPrevBlockHash ())! = null } / * return block * * @ return * / public Block next () throws Exception {Block currentBlock = RocksDBUtils.getInstance () .getBlock (currentBlockHash); if (currentBlock! = null) {this.currentBlockHash = currentBlock.getPrevBlockHash (); return currentBlock } return null;}}.... } Test / * Test * * @ 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.5more 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 () } / * output * / Block {hash='0000012f87a0510dd0ee7048a6bd52db3002bae7d661126dc28287bd6c23189a', prevBlockHash='0000024b2c23c4fb06c2e2c1349275d415efe17a51db24cd4883da0067300ddf', data='Send 3.5 more BTC to wangwei', timeStamp=1519724875, nonce=369110}, validate = trueBlock {hash='0000024b2c23c4fb06c2e2c1349275d415efe17a51db24cd4883da0067300ddf', prevBlockHash='00000b14fefb51ba2a7428549d469bcf3efae338315e7289d3e6dc4caf589d79', data='Send 2.5 more BTC to wangwei', timeStamp=1519724872, nonce=896348}, validate = trueBlock {hash='00000b14fefb51ba2a7428549d469bcf3efae338315e7289d3e6dc4caf589d79', prevBlockHash='0000099ced1b02f40c750c5468bb8c4fd800ec9f46fea5d8b033e5d054f0f703', data='Send 1.0 BTC to wangwei', timeStamp=1519724869, nonce=673955}, validate = trueBlock {hash='0000099ced1b02f40c750c5468bb8c4fd800ec9f46fea5d8b033e5d054f0f703', prevBlockHash='', data='Genesis Block', timeStamp=1519724866, nonce=840247} Validate = true command line interface

The content of the CLI part will not be described in detail here, but you can check the Github source code link at the end of the article. The general steps are as follows:

Configuration

Add pom.xml configuration

... 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 ... Project Project package $mvn clean & & mvn package execution Command # print help Information $java-jar blockchain-java-jar-with-dependencies.jar-h # add Block $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 "# print Block chain $java-jar blockchain-java-jar-with-dependencies.jar-print Thank you for reading The above is the content of "Java persistence and command line use". After the study of this article, I believe you have a deeper understanding of Java persistence and command line use, and the specific use needs to be verified in practice. Here is, the editor will push for you more related knowledge points of the article, welcome to follow!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Internet Technology

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report