Implement a Block Chain prototype with 300 lines of ABAP code
- Version 1: Data structure implementation of “Block” and “Chain”
- Version 2: add support of Proof-of-Work(POW)
- Version 3: Use Block Chain to record transaction, add mine reward
It seems Block Chain turns popular all of a sudden at least in China.
Simply search in Wechat application with keyword “区块链”(Block Chain), there are a tremendous amount of articles found.
In my article, I implement a simple Block Chain prototype via 300 lines of ABAP code.
It consists of three versions where new features are added in an iterative way.
Version 1:Data structure implementation of “Block” and “Chain”
As its name means, a block chain is a chain consisting of blocks.
The diagram below looks like exactly the same as “Linked-list” we learned in Data Structure course in the university.
In the first version, every blocks contains the most fundamental fields: block index, block creation timestamp, block hash the previous block’s hash. The field pHash stores the hash of previous block, constructing a linked list. The first node in the chain ( highlighted as red in the diagram below ), has index as 0 with a null pHash field, is usually called “Genesis block”.
The block itself is implemented by class ZCL_BLOCK. For simplicity reason I set most of fields as public.
The block hash is calculated in method CALCULATE_HASH using SHA1 algorithm, based on all other fields of current block as input and stored in field mv_hash.
The chain is implemented by class ZCL_BLOCKCHAIN.
The following methods are implemented for a chain:
- ADD_BLOCK: an instance of ZCL_BLOCK, io_block_to_insert as importing parameter. First I get the tail block( call it lo_tail_block) in the current chain, and make io_block_to_insert->pHash = lo_tail_block, so that io_block_to_insert becomes the new tail block.
- CONSTRUCTOR: create genesis block.
- GET_BLOCK_BY_INDEX: get block instance with a given index in the chain.
- GET_SIZE: return number of blocks in the chain.
- IS_VALID: chain validity check, to be introduced later.
The source code of this version could be found in my github.
Execute report ZBLOCKCHAIN_V1, specify number of blocks to be created ( I specified as 5 ), and you can see the following output:
Due to miss control supported for Linked List in SAP GUI, I use tree control for simulation.
Now I tamper the content of block with index 1 in line 21 with new content “Change by Jerry”, and call is_valid method in line 23:
Becuase within set_data, the content change will trigger hash recalculation. Say the hash of first block has changed from YYY to CCC, whereas the pHash of second block still points to the old hash YYY, as a result the chain is considered as invalid.
the above output comes from method is_valid: loop at every block in the chain, compare the pHash field of current block with hash field of the block in prior position.
Version 2: add support of Proof-of-Work(POW)
Source code of this version could be found here.
The constructor of ZCL_BLOCKCHAIN_V2 is enhanced with a new importing parameter: iv_difficulty. What’s the purpose of it?
Review the hashes in the printed tree in version 1 carefully: no common prefix exist among those values. It means any one who has changed the content of a given block can simply recalculate the hash of each block and adapt pHash accordingly. In this case the chain is still valid. To avoid such situation, iv_difficulty is introduced to increase the cost of hash calculation. Now a hash is not considered as valid and accepted by the chain until it fulfills some rule. It is iv_difficulty which defines the number of leading zeros a valid hash must own.
For example, when iv_example is set as 3,
we execute the report and find all hashes have three digits’ leading zero.
There are two questions here:
- what’s the meaning of Nonce in the last column?
- how is iv_difficulty involved in hash calculation?
A new member field mv_nonce is introduced in ZCL_BLOCK2:
Since now before a new block is inserted to a chain, an appropriate hash must be calculated first. The calculate-compare-calculate logic is encapsulated into method mine.
There is a loop inside mine: in each loop a hash is calculated. Then check is performed to evaluate whether the leading zeros have specified digits. If not, add 1 to mv_nonce and continue. The field mv_nonce also participates in hash calculation. That is to say, the value of mv_nonce represents how many times of hash calculation have been executed till a valid hash is detected. In fact the logic of method mine in my prototype is called as “mine” in Block Chain area.
In my testing system ,it takes 10 seconds to generate a chain with 10 blocks with iv_difficulty = 4.
Even this prototype is pretty simple, still 10 seconds are consumed. We can realized that POW is actually a protection mechanism to prevent Block Chain from being filled with junk data or juggled by introducing a kind of calculation which normally requires the processing time of service requester.
Version 3: Use Block Chain to record transaction, add mine reward
Source code could be found here.
Class ZCL_TRANSACTION is implement for transaction, consists of mv_from_address, mv_to_address and mv_amount.
In version 3, in the beginning ZCL_BLOCK3 is enhanced: the meaningless fields mv_index and mv_data is eliminated, and a new field mt_transaction is introduced, which is an internal table to store instances of ZCL_TRANSACTION.
All of these three fields will participate in hash calculation.
Class ZCL_BLOCKCHAIN_V3 utilizes a new member variable mt_pending_trans to store transaction passed via importing parameter of method create_transaction.
The field mv_mine_reward stores the mine reward, I hard code as 100.
The pending tasks in mt_pending_trans can only get handled till method mine_pending_trans is called.
After the mine method in line 6 is executed, a valid hash is calculated based on the content of mt_pending_trans. Since now all transaction are recorded via the new block, mt_pending_trans is cleared. A new transaction is created in line 3 to reward the miner whose account specified by iv_award_address.
Since now transaction details are stored in each block, the balance of a given account could simply be calculated by loop over these blocks. The logic is, loop over transaction records stored in each block, if a given account appears as payer in the transaction, subtract balance with current transaction amount(line 5). Accordingly if being as a receiver, add transaction amount to the balance(line 9).
Test report as below.
- In line 12 Tom pays 100 to Jerry.
- In line 13 Jerry pays 10 to Tom.
- In line 15 the mine reward account is set as Jerry, so Jerry gains 100 as mine reward.
So after these transactions Jerry’s balance is 100 – 10 + 100 = 190.
Hope this prototype can help you understand the basis of Block Chain.