Development and actual combat sharing of Java blockchain voting system

Development and actual combat sharing of Java blockchain voting system

catalogue

1, System introduction

The blockchain voting system developed based on the Java language and the popular Springboot+Mybatis framework on the market can be used for the voting platform in enterprise groups, government agencies and organizational alliances. The system is divided into three layers: the self-developed blockchain as the bottom layer, the voting system as the business layer and the front-end operation interface as the view layer, The functions include complete front and back-end authority management module, voting application function and self-developed blockchain system, with strong scalability. The whole system is developed with springboot framework, which is simple and easy to understand, including complete voting function, authority management function, blockchain model, blockchain wallet, transfer and transaction, UTXO model, data encryption and decryption, mining, database persistence of block data, and P2P distributed network based on netty framework.

2, System background

With the progress of society and the rapid development of economy, voting survey and opinion collection have played a more and more important role in social life. Online voting system replaces offline voting activities with the characteristics of high efficiency, resource saving and easy dissemination. However, because the data of the online voting system is stored in the centralized server, there are many disadvantages. For example:
(1) The voting information of users is at risk of being leaked;
(2) After voters vote, ordinary voters cannot verify whether the voting results are correct;
(3) Voting data and results may be maliciously tampered with
If the voting system uses the characteristics of blockchain technology, such as decentralization, information tamperability, openness and transparency, it can easily build a fair, fair, open and transparent voting system.
The system aims to solve the problems existing in the existing online voting system by using the transfer transaction in the blockchain to replace the voting process. The system generates a blockchain account for each user and voting option, and has a token representing the voting voucher. The voting process is also that voters transfer their own voting token to the virtual blockchain account of the voting option. Finally, the system counts the number of tokens owned by each voting option, records it as the number of votes obtained by the voting option, and publicizes the voting results, At the same time, the voting results are stored in each node of the blockchain network and cannot be tampered with.

3, Main function design

1. Design idea

The system is divided into three layers of organizational structure, which are self-developed blockchain as the bottom layer, voting system as the business layer, and front-end interface as the view layer, so as to facilitate the system background business to flexibly call the bottom blockchain and complete the implementation of voting function and the uplink operation of business logic.

2. Development ideas
  • Firstly, the development and implementation of the underlying blockchain is realized based on SpringBoot+Netty. The test environment can run on a single node or reach a consensus on multiple nodes to ensure the normal operation of the underlying blockchain and always receive the data submitted by the voting application.
  • Secondly, complete the selection and design of the database according to the design to ensure the upper business realization of the blockchain system and the voting system, including the design of table structure and the development of Dao layer.
  • Thirdly, complete the code implementation of the voting system, and use the SpringBoot+SpringMVC+Mybatis framework to develop and implement the business functions of the voting system based on blockchain, including the RBAC permission model design of the voting system application, user management, voting theme management, voting option management, etc

4, Engineering structure

5, Technology selection

(1) Back end technology
technologynameOfficial websiteremarks
springbootspringboot framework
Apache ShiroPermission framework
MyBatis Generatorcode generation
PageHelperMyBatis physical paging plug-in
hikariDatabase connection pool
Thymeleaftemplate engine
Log4JLog component
Swagger2Interface test framework
MavenProject construction management
Nettywebsocket message notification
kaptchagoogle verification code
devtoolsHot deployment
GSONGoogle json
druidAli connection pool
(2) Front end technology
technologynameOfficial websiteremarks
jQuerylibraries
bootstrapFront page frame
Font-awesomeFont Icon
jquery.validatejquery authentication plug-in
ladda.min.jsButton load js
bootstrap-tableTable component
layer.jsPop up window assembly
jquery.blockUI.jsShield assembly
bootstrap-table-export.jsForeground export component
bootstrap-treeviewTree structure component
bootstrap-colorpickerColor component
dropzoneFile upload
bootstrap-wysihtml5Rich text
bootstrap-switchswitch button
(4) Development environment
  • JDK8.0
  • mysql5. More than 7
  • eclipse or Idea
  • redis
(5) Resource download
JDK8: 
http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html
Maven: 
http://maven.apache.org/download.cgi
Redis: 
https://pan.baidu.com/s/1z1_OdNVbtgyEjiktqgB83g password: kdfq
(6) Attachment: installation process of redis windows version
1. First, unzip the downloaded compressed package into a folder, such as C:\Program Files\Redis-x64-3.2.100
2. open cmd Command window, enter the file path just extracted
3. Execute command: redis-server --service-install redis.windows.conf
4. Then on the desktop, right-click My computer→Select management→Access to services and Applications→Service right click to start the service
5. Switch back cmd Windows, executing redis-cli Start the client and connect to the service for testing
6. Execute command: set a 123 Then execute get a ,Output 123

6, Core code

1. Block structure
/**
 * block
 *
 * @author Jared 1245605223@qq.com
 */
public class Block implements Serializable {

    /**
     * Timestamp generated by the block
     */
    private long timestamp;
    /**
     * block height 
     * Redundant field, which can be calculated by the blockchain system
     */
    private long height;
    /**
     * Hash of previous block
     */
    private String previousBlockHash;
    /**
     * Transactions in the block
     */
    private List<Transaction> transactions;
    /**
     * Merkel tree root
     * Generated by transactions.
     */
    private String merkleTreeRoot;
    /**
     * random number
     */
    private String nonce;
    /**
     * Block Hash: generated by timestamp, previousBlockHash, nonce and merkleRoot using Hash algorithm.
     */
    private String hash;

    /**
     * Mining difficulty
     */
    private String difficulty;

    /**
     * Total number of transactions in the block
     */
    private long transactionCount;

    /**
     * The serial number of the first transaction in the block.
     */
    private long startTransactionIndexInBlockchain;

   /**
   * Omit set get method
   */
}
2. Transaction model
/**
 * transaction
 *
 * @author Jared 1245605223@qq.com
 */
public class Transaction implements Serializable {

    /**
     * Transaction hash
     */
    private String transactionHash;
    /**
     * Transaction type
     */
    private TransactionType transactionType;
    /**
     * Transaction input
     */
    private List<TransactionInput> inputs;
    /**
     * Transaction output
     */
    private List<TransactionOutput> outputs;
    /**
     * Serial number of the transaction in the block
     */
    private long transactionIndexInBlock;
    /**
     * Serial number of transaction in blockchain
     */
    private long transactionIndexInBlockchain;
    /**
     * Height of the exchange in the block
     */
    private long blockHeight;
}
3. Build the transaction process
public BuildTransactionResponse buildTransactionDTO(List<String> payerPrivateKeyList,String payerChangeAddress,List<Recipient> recipientList) {
        //Total number of token s to be transferred
        long outputValues = 0;
        if(recipientList != null){
            for(Recipient recipient : recipientList){
                outputValues += recipient.getValue();
            }
        }
        //Create transaction output
        List<TransactionOutputDTO> transactionOutputDtoList = new ArrayList<>();
        //Create transaction output (excluding change transaction output)
        List<BuildTransactionResponse.InnerTransactionOutput> innerTransactionOutputList = new ArrayList<>();

        if(recipientList != null){
            for(Recipient recipient : recipientList){
                TransactionOutputDTO transactionOutputDTO = new TransactionOutputDTO();
                transactionOutputDTO.setValue(recipient.getValue());
                OutputScript outputScript = StackBasedVirtualMachine.createPayToPublicKeyHashOutputScript(recipient.getAddress());
                transactionOutputDTO.setOutputScriptDTO(Model2DtoTool.outputScript2OutputScriptDTO(outputScript));
                transactionOutputDtoList.add(transactionOutputDTO);

                BuildTransactionResponse.InnerTransactionOutput innerTransactionOutput = new BuildTransactionResponse.InnerTransactionOutput();
                innerTransactionOutput.setAddress(recipient.getAddress());
                innerTransactionOutput.setValue(recipient.getValue());
                innerTransactionOutput.setOutputScript(outputScript);
                innerTransactionOutputList.add(innerTransactionOutput);
            }
        }

        //Get enough token s
        //Transaction input list
        List<TransactionOutput> inputs = new ArrayList<>();
        List<String> inputPrivateKeyList = new ArrayList<>();
        //Total number of transaction input token s
        long inputValues = 0;
        long feeValues = 0;
        boolean haveEnoughMoneyToPay = false;
        for(String privateKey : payerPrivateKeyList){
            if(haveEnoughMoneyToPay){
                break;
            }
            String address = AccountUtil.accountFromPrivateKey(privateKey).getAddress();
            long from = 0;
            long size = 100;
            while (true){
                if(haveEnoughMoneyToPay){
                    break;
                }
                //Query the number of token s under the current voter address
                List<TransactionOutput> utxoList = blockchainDataBase.queryUnspendTransactionOutputListByAddress(address,from,size);
                if(utxoList == null || utxoList.isEmpty()){
                    break;
                }
                for(TransactionOutput transactionOutput:utxoList){
                    inputValues += transactionOutput.getValue();
                    //Transaction input
                    inputs.add(transactionOutput);
                    inputPrivateKeyList.add(privateKey);
                    if(inputValues >= outputValues){
                        haveEnoughMoneyToPay = true;
                        break;
                    }
                }
                from += size;
            }
        }

        if(!haveEnoughMoneyToPay){
            BuildTransactionResponse buildTransactionResponse = new BuildTransactionResponse();
            buildTransactionResponse.setBuildTransactionSuccess(false);
            buildTransactionResponse.setMessage("The account does not have enough token Transfer");
            return buildTransactionResponse;
        }

        //Build transaction input
        List<TransactionInputDTO> transactionInputDtoList = new ArrayList<>();
        for(TransactionOutput input:inputs){
            UnspendTransactionOutputDTO unspendTransactionOutputDto = Model2DtoTool.transactionOutput2UnspendTransactionOutputDto(input);
            TransactionInputDTO transactionInputDTO = new TransactionInputDTO();
            transactionInputDTO.setUnspendTransactionOutputDTO(unspendTransactionOutputDto);
            transactionInputDtoList.add(transactionInputDTO);
        }

        //Build transaction
        TransactionDTO transactionDTO = new TransactionDTO();
        transactionDTO.setTransactionInputDtoList(transactionInputDtoList);
        transactionDTO.setTransactionOutputDtoList(transactionOutputDtoList);

        //give change
        long change = inputValues - outputValues - feeValues;
        BuildTransactionResponse.InnerTransactionOutput payerChange = null;
        if(change > 0){
            TransactionOutputDTO transactionOutputDTO = new TransactionOutputDTO();
            transactionOutputDTO.setValue(change);
            OutputScript outputScript = StackBasedVirtualMachine.createPayToPublicKeyHashOutputScript(payerChangeAddress);
            transactionOutputDTO.setOutputScriptDTO(Model2DtoTool.outputScript2OutputScriptDTO(outputScript));
            transactionOutputDtoList.add(transactionOutputDTO);

            payerChange = new BuildTransactionResponse.InnerTransactionOutput();
            payerChange.setAddress(payerChangeAddress);
            payerChange.setValue(change);
            payerChange.setOutputScript(outputScript);
        }

        //autograph
        for(int i=0;i<transactionInputDtoList.size();i++){
            String privateKey = inputPrivateKeyList.get(i);
            String publicKey = AccountUtil.accountFromPrivateKey(privateKey).getPublicKey();
            TransactionInputDTO transactionInputDTO = transactionInputDtoList.get(i);
            String signature = Model2DtoTool.signature(transactionDTO,privateKey);
            InputScript inputScript = StackBasedVirtualMachine.createPayToPublicKeyHashInputScript(signature, publicKey);
            transactionInputDTO.setInputScriptDTO(Model2DtoTool.inputScript2InputScriptDTO(inputScript));
        }


        BuildTransactionResponse buildTransactionResponse = new BuildTransactionResponse();
        buildTransactionResponse.setBuildTransactionSuccess(true);
        buildTransactionResponse.setMessage("Build transaction succeeded");
        buildTransactionResponse.setTransactionHash(TransactionTool.calculateTransactionHash(transactionDTO));
        buildTransactionResponse.setFee(feeValues);
        buildTransactionResponse.setPayerChange(payerChange);
        buildTransactionResponse.setTransactionInputList(inputs);
        buildTransactionResponse.setTransactionOutputList(innerTransactionOutputList);
        buildTransactionResponse.setTransactionDTO(transactionDTO);
        return buildTransactionResponse;
    }
4. Workload proof algorithm judgment

If the computing power of the local computer is too poor, you can change the logic of the algorithm. As long as the first four digits of the hash value of the block are 0, it is considered to meet the calculation result:

public boolean isReachConsensus(BlockchainDatabase blockchainDataBase, Block block) {
        String difficulty = block.getDifficulty();
        if(StringUtil.isNullOrEmpty(difficulty)){
            difficulty = calculateDifficult(blockchainDataBase,block);
            block.setDifficulty(difficulty);
        }

        //Block Hash
        String hash = block.getHash();
        if(hash == null){
            hash = BlockTool.calculateBlockHash(block);
        }
        System.out.println("Mining in progress, block hash: "+hash);
        return new BigInteger(difficulty,16).compareTo(new BigInteger(hash,16)) > 0;
        //return hash.startsWith("0000");
    }
5. Calculate the difficulty of mining
public String calculateDifficult(BlockchainDatabase blockchainDataBase, Block block) {
        long intervalBlockCount = GlobalSetting.MinerConstant.INTERVAL_TIME / GlobalSetting.MinerConstant.BLOCK_TIME;

        String targetDifficult;
        long blockHeight = block.getHeight();
        if(blockHeight == 1){
            targetDifficult = GlobalSetting.GenesisBlock.DIFFICULTY;
            return targetDifficult;
        }
        Block lastBlock = blockchainDataBase.queryBlockByBlockHeight(blockHeight-1);
        long lastBlockHeight = lastBlock.getHeight();
        if (lastBlockHeight % intervalBlockCount != 0){
            targetDifficult = lastBlock.getDifficulty();
            return targetDifficult;
        }
        //At this time, the last block is the last block of the previous cycle.
        Block intervalLastBlock = lastBlock;
        Block intervalFirstBlock = blockchainDataBase.queryBlockByBlockHeight(lastBlockHeight-intervalBlockCount+1);
        long actualTimespan = intervalLastBlock.getTimestamp() - intervalFirstBlock.getTimestamp();
        if (actualTimespan < GlobalSetting.MinerConstant.INTERVAL_TIME /4){
            actualTimespan = GlobalSetting.MinerConstant.INTERVAL_TIME /4;
        }
        if (actualTimespan > GlobalSetting.MinerConstant.INTERVAL_TIME *4){
            actualTimespan = GlobalSetting.MinerConstant.INTERVAL_TIME *4;
        }
        BigInteger bigIntegerTargetDifficult = new BigInteger(intervalLastBlock.getDifficulty(),16).multiply(new BigInteger(String.valueOf(actualTimespan))).divide(new BigInteger(String.valueOf(GlobalSetting.MinerConstant.INTERVAL_TIME)));
        return bigIntegerTargetDifficult.toString(16);
    }
6. Blockchain total account (commonly known as wallet) model

It is used to manage the blockchain accounts owned by the blockchain platform, and to add, delete and query accounts:

public abstract class Wallet {

    public abstract List<Account> queryAllAccount();

    public abstract Account createAccount();

    public abstract void addAccount(Account account);

    public abstract void deleteAccountByAddress(String address);
}

7, Database model

8, Interface style


9, System architecture

10, Technical communication group

Welcome blockchain technology enthusiasts to communicate together. Java blockchain technology exchange group 03 is full. Add group 04 (those who need source code can enter the group and send private letters to the group owner):

Keywords: Java Blockchain Spring Boot

Added by rv20 on Tue, 08 Feb 2022 06:43:47 +0200