A Deep Dive into java-tron Chainbase Module (Issue 1)
Blockchain is essentially a tamper-proof distributed ledger and an ideal solution to the problem of trust. In practice, blockchain is used for record-keeping and transactions. For example, crypto-assets like BTC, ETH, and TRX are adopted in economic activities in many applications to ensure transparency of assets.
However, implementing such a tamper-proof distributed ledger is a highly complex engineering project, which involves a wide array of technical aspects including p2p network, smart contract, database, cryptography, consensus mechanism, etc. As the foundation of basic storage, the database is one of the cores that all blockchain project teams are exploring.
The database module of java-tron is also known as chainbase module. As one of the world-leading public chains, java-tron boasts rich experience and expertise in database designing. To make readers fully informed of the bolts and nuts of implementing a blockchain database, a series of articles explaining how chainbase works are going to release. These articles will share insights and optimization on designing the database for java-tron.
As the first issue of the series, this article will focus on the background, and the logic behind handling transactions and state revocation.
Database, an essential part of the blockchain system, stores all data on the blockchain and serves as the foundation for the system to run smoothly. Each Full Node on the chain stores a whole set of data, including block data, transaction data, transaction receipt, etc. And Account-based blockchains also store account information.
In the current blockchain world, there are two popular ways to record and save state:
- UTXO model (Unspent Transaction Output)
- Account model
UTXO model is stateless and performs better in privacy protection, and it is a better fit for concurrent transaction processing. However, it is not equally flexible in its coding.
In comparison, Account model is more intuitive and developer-friendly as user data is stored in corresponding accounts. Taking programmability and flexibility into consideration, java-tron chooses Account model over UTXO model.
For more information：
Currently, mainstream consensuses include PoW, PoS, DPoS, etc. PoW, ie Proof of Work, is a process where all nodes participate in computing an expected hash result, and node coming up with the hash result first has the right to generate blocks. However, as the computing capacity increases, the energy consumption required for computing hash results also grows over time. And major mining nodes dominate a big majority of the computing power, which goes against the original intention of decentralization.
In order to solve the problems that PoW faces, some people have proposed PoS (Proof of Equity), which is simply understood as: the probability of interruption of holding more coins can be obtained, but this will cause monopoly problems, so PoS To improve, DPoS (Delegate Proof of Stake) is proposed: the decentralized characteristics are guaranteed by the elected super representatives, and at the same time, the super representatives take turns responsible for producing blocks to improve the efficiency of producing blocks. java-tron currently uses the DPoS consensus mechanism.
For more details, please visit wiki： Delegated Proof of Stake
Blockchain differs from traditional Internet services in that its processing logic at the database level is not particularly complex, but it does have requirements for data reading and writing capacities because a large amount of key-value reading and writing takes place in the blockchain.
For this reason, java-tron adopts LevelDB by default as its underlying data storage. Also, java-tron features a well-designed architecture of an interface-oriented programming mode, which helps maintain high scalability of chainbase module, enabling any databases that implement chainbase interfaces to be used as the underlying storage engine for java-tron. In chainbase v2, for instance, RocksDB is supported as the underlying data storage.
Java-tron has its data stored in corresponding databases. Full databases can be accessed under the database directory. The table below only lists databases that are relevant to this article:
SR: Super Representative — responsible for producing blocks
FullNode: stores full block data, responsible for broadcasting and validating transactions and blocks, support inquiry
TRX: token issued by TRON
We all know that the blockchain mainly stores transaction data. So before introducing chainbase module, we need to first understand the processing logic of transactions in java-tron.
A transaction will be broadcasted via the network to each node, who will first validate the signature of the transaction upon receiving it, then pre-execute the verified transaction to determine its legitimacy.
The actual implementation of java-tron deviates from the figure demonstrated above, which simplifies the process for the sake of clarity. Please refer to our upcoming related articles for more details at the network level. Also, this article refers to both FullNode and SR as “nodes” for convenience.
For instance, when processing a transfer: account A transfers 100 TRX to account B, it needs to be verified whether account A has a sufficient balance to make the transfer.
The account database stores all users’ account information including their balance data. To judge the legitimacy of a transaction, java-tron applies the following logic: When a transaction is received from the network, it will be immediately executed by changing the account information in the local database: (account A — 100 TRX, account B + 100 TRX). If the operation succeeds, this transaction is deemed legitimate at least in the current status and can be packed into the block.
Please note here: a transaction validated by a node is not yet recorded on the blockchain, because there is still a revocation risk as the transaction has not been packed into a finalized block. Java-tron consensus follows a principle: only transactions in a block confirmed by over 2/3 SRs are recorded on the blockchain. In other words:
- The transaction is packed into a block
- The block is confirmed by over 2/3 SRs
Transactions have to meet the above two conditions to be recorded on the blockchain. In java-tron, a transaction goes through the following three stages before it is finally confirmed:
- The transaction is validated
- The transaction is packed into a block
- The block is received and applied by most nodes in the network
So here is a problem: in the implementation of java-tron, if, after a node has validated a transaction and updated its database state, the transaction does not make it to the block or the block is not confirmed by over 2/3 SRs, the state of the node will be inconsistent with that of the entire network.
Therefore, except for the validation of transaction data in blocks confirmed by over 2/3 SRs, any other state change caused by validating transactions may entail revocation. There are three, not one, cases where such revocation is needed.
- The state change caused by transaction validation when receiving a new block
- The state change caused by transaction validation after producing a block
- The state change caused by transactions in blocks of a fork chain.
State changes due to the above three causes may have to be revoked. Here are the respective reasons:
State Revocation When Receiving a New Block
When receiving a new block, the node needs to return to the ending status of the last block and revoke all later transactions. As is demonstrated in the figure, suppose accountA has a balance of 100 when the node reports a block height of 1000. AccountA receives and validates a transaction (t1) and sends accountB 100 TRX. The node then receives a new block 1001 which includes a new transaction (t2) where accountA sends accountC 50 TRX. Since t2 has been packed into the block, it should be processed before t1. But t2 cannot be validated successfully if there is no revocation because accountA does not have enough balance. Therefore, when the node receives block 1001, the status change caused by t1 must be revoked.
State Revocation After Producing Blocks
First, you might wonder: there shouldn’t be any state change to the database, because a validated transaction is packed into a block directly without changing the state of the database.
But the state may still change because a second validation is conducted when java-tron packs transactions into a block. The validation is done twice because a transaction may expire. To take the figure above as an example, after the node receives block 1001, t1 is revoked and 50 TRX is subtracted from accountA’s balance. But later when the node is to produce the block, t1 will have become illegitimate because account A will not have 100 TRX in its balance. Therefore, t1 cannot be directly packed into the block and needs to be validated again. That’s why we need a second validation when packing transactions into a block.
After transactions are packed into the block, the node will broadcast the block to the network and meanwhile apply the block locally. To apply the block, all transactions within the block will be validated again. In other words, revocation is still needed after transactions are packed into the block.
State Revocation on a Fork Chain
This is the last case of revocation. Forks are inevitable, especially for blockchains with DPoS or other mechanisms that produce blocks at a high speed.
As is shown in the figure above, java-tron maintains a data structure that stores all recent unfinalized blocks. When a fork occurs, the longest chain shall apply: if the fork has a larger block height than the current main chain, the fork will replace it to be the new main chain. To enable the switch, the current main chain needs to revoke all blocks that are inconsistent with the fork and then, starting from the common parent block, apply new blocks one by one.
As is shown in the figure, the dark-colored fork A was the main chain. As the block height of fork B surpasses that of fork A, data of the three blocks on fork A (block 1003, block 1002 and block 1001) need to be revoked and block 1001', block 1002', block1003' and block 1004' on fork B should be applied accordingly.
This article covers the transaction processing and reasons for status revocation in java-tron. In the next article, we will be looking closely at implementing revocation in the chainbase module.
For more information