DAPP: Ethereum smart contract voting system (solidity) + java background (springboot+mybatis+web3j+thymeleaf)

1. Project background

In order to consolidate the blockchain-related knowledge that we have recently learned, we spent a few days to develop a set of voting systems based on Ethereum smart contracts that can be practically applied .

2. Technical selection

  1. Smart contract writing: solidity
  2. Private chain environment: ganache-cli
  3. Front end: thymeleaf template engine
  4. Backend: springboot+mybatis
  5. Core dependency jar package: web3j

The general framework of the project is as follows (because the front-end content is small, and it is only used to display basic functions, vue is not used in the actual development process):
Insert picture description here

3. Solidity contract code

pragma solidity ^ 0.4.26;
 
contract Ballot {
    string public name; //投票的名称,name of the Ballot
    address public chairman; //合约拥有者的地址
    bool public closed; // 记录投票是否结束
 
    // 获胜票数比例
    uint public proportion; // 获胜时得票数占总票数的最低比例, 例如50%就赋值为50
    uint public totalVotes; // 总票数
 
    // 加投票权集合, 表示哪些人有投票权
    address[] voters;
    struct Proposal { // 候选人信息的结构体
        bytes32 name;
        uint voteCount;  // 候选人的得票数
    }
    Proposal[] public proposals;  // 候选人的结构体(可变长)数组
    mapping(address => bool) voteRecords; // 投票者是否投票(默认false)
    uint[] winningProposals = [0];
    bytes32[] winningProposalsName; // 记录winning候选人名字的数组
 
    constructor(string _name, bytes32[] _proposals, uint _proportion, address[] _voters) public { // 合约的构造函数
        chairman = msg.sender; // 记录合约的拥有者
        closed = false; // 合约默认处于打开
        name = _name; // 投票的名称,如 "选举学生会主席"
        for(uint i = 0; i < _proposals.length; i++) {
            proposals.push(Proposal({
                name: _proposals[i],
                voteCount: 0
            }));
        }
        proportion = _proportion;
        voters = _voters;
        totalVotes = voters.length;
    }
 
 
    function setChairman(address _chairman) external { // 更换合约拥有者时用
        chairman = _chairman;
    }
 
    function vote(uint proposal) public { //给候选人投票, 需传入候选人的序号及投票者的地址
        address voterAddress = msg.sender;
        require(!closed, "the ballot is already closed."); // 保证合约没有关闭,即投票没有结束
        // 看投票者的地址是否在合法名单中
        bool legal = false;
        for(uint i = 0; i < voters.length; i++){
            if(voters[i] == voterAddress){
                legal = true;
                break;
            }
        }
        require(legal, "the voter is illeagal!");
        require(proposal < proposals.length, "the prosal is invalid!");
        require(!voteRecords[voterAddress], "Already voted."); // 保证投票者没有投过票
        voteRecords[voterAddress] = true;
 
        proposals[proposal].voteCount += 1; // 候选人得票数加一
    }
 
    function closeBallot() public { // 关闭投票
        require(chairman == msg.sender, "only the chairman can close the ballot."); // 只有拥有者可以关闭投票
        closed = true;
    }
 
    function getWinningProposals() internal { //统计投票的获胜者
        winningProposals.length = 0;
        uint winningVoteCount = 0;
        for(uint i = 0; i < proposals.length; i++) {
            if(proposals[i].voteCount * 100 / totalVotes >= proportion) {
                winningProposals.push(i);
            }
        }
        if(winningProposals.length == 0){
        for(i = 0; i < proposals.length; i++){
            if(proposals[i].voteCount > winningVoteCount){
                winningVoteCount = proposals[i].voteCount;
            }
        }
        for(i = 0; i < proposals.length; i++){
            if(proposals[i].voteCount == winningVoteCount){
                winningProposals.push(i);
            }
        }
    }
    }
 
    function winnerName() public view returns (bytes32[]) { // 返回得票数最高的候选人的名字
        require(closed, "only when the ballot is closed can you examine the winner");
        getWinningProposals();
        for(uint i = 0; i < winningProposals.length; i++){
            winningProposalsName.push(proposals[winningProposals[i]].name);
        }
        return winningProposalsName;
    }
 
    function getProposalNumber() public view returns (uint){
        return proposals.length;
    }
}

4. Function display

1. Voting list interface

  1. Click "initiate a poll" in the upper left corner to create a new poll;
  2. In the existing voting list, you can vote for unfinished votes;
  3. Only the contract owner can terminate the voting, and this process requires the input of the private key signature verification.
    Insert picture description here

2. Add voting page

You can create a new poll on this page, and you need to provide a private key, deploy and register as the contract owner.
Insert picture description here

3. View the number of votes and the voting page

You need to enter the private key signature when voting, and each account address can only vote once.
Insert picture description here

4. Database tables

In order to simplify the design, a voting collection contract is not designed, but the mysql database is used to store data. This operation will not leak block information.
Insert picture description here

5. Project source code address

Project source code github address

Since this project is only for learning and consolidation, there are many redundancies and deficiencies in the code, but the basic functions have been implemented, and there is no code specification. I hope you understand when you read it.

Guess you like

Origin blog.csdn.net/qq_45656248/article/details/114447375