From 3b059fe82dd35819200933c6d51f35fd59347012 Mon Sep 17 00:00:00 2001 From: satyam Date: Thu, 7 Mar 2019 17:35:55 +0530 Subject: [PATCH 01/10] PLCR voting module --- .../Checkpoints/PLCRVotingCheckpoint.sol | 193 ++++++++++++++++++ .../PLCRVotingCheckpointFactory.sol | 58 ++++++ 2 files changed, 251 insertions(+) create mode 100644 contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpoint.sol create mode 100644 contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpointFactory.sol diff --git a/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpoint.sol b/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpoint.sol new file mode 100644 index 000000000..547d17df8 --- /dev/null +++ b/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpoint.sol @@ -0,0 +1,193 @@ +pragma solidity ^0.5.0; + +import "../../Module.sol"; +import "openzeppelin-solidity/contracts/math/SafeMath.sol"; + +contract PLCRVotingCheckpoint is Module { + + using SafeMath for uint256; + + enum Stage { PREP, COMMIT, REVEAL, RESOLVED } + + struct Ballot { + uint64 commitDuration; + uint64 revealDuration; + uint64 startTime; + uint64 noOfProposals; + uint256 quorum; + uint256 totalSupply; + uint256 checkpointId; + mapping(uint256 => uint256) weightedVote; + mapping(address => Vote) proposalToVote; + } + + struct Vote { + uint256 timestamp; + uint256 voteOption; + uint256 weight; + bytes32 secretVote; + bool isRevealed; + } + + Ballot[] ballots; + + event VoteCommited(address indexed _voter, uint256 _weight, uint256 _ballotId, bytes32 _secretVote); + event BallotCreated( + uint256 indexed _ballotId, + uint256 _commitDuration, + uint256 _revealDuration, + uint256 _noOfProposals, + uint256 _startTime, + uint256 _proposedQuorum, + uint256 _totalSupply, + uint256 _checkpointId + ); + event VoteRevealed(address _voter, uint256 _weight, uint256 indexed _ballotId, uint256 _choiceOfProposal, uint256 _salt, bytes32 _secretVote); + + + constructor(address _securityToken, address _polyAddress) + public + Module(_securityToken, _polyAddress) + { + + } + + function createBallot(uint256 _commitDuration, uint256 _revealDuration, uint256 _noOfProposals, uint256 _proposedQuorum) external { + _onlySecurityTokenOwner(); + uint256 startTime = now; + uint256 checkpointId = ISecurityToken(securityToken).createCheckpoint(); + _createBallotWithCheckpoint(_commitDuration, _revealDuration, _noOfProposals, _proposedQuorum, checkpointId, startTime); + } + + function createBallotWithCheckpoint( + uint256 _commitDuration, + uint256 _revealDuration, + uint256 _noOfProposals, + uint256 _proposedQuorum, + uint256 _checkpointId, + uint256 _startTime + ) + external + { + _onlySecurityTokenOwner(); + _createBallotWithCheckpoint(_commitDuration, _revealDuration, _noOfProposals, _proposedQuorum, _checkpointId, _startTime); + } + + function _createBallotWithCheckpoint( + uint256 _commitDuration, + uint256 _revealDuration, + uint256 _noOfProposals, + uint256 _proposedQuorum, + uint256 _checkpointId, + uint256 _startTime + ) + internal + { + // Sanity checks + _validValueCheck(_commitDuration); + _validValueCheck(_revealDuration); + require(_startTime >= now, "Invalid start time"); + require(_noOfProposals > 1, "noOfProposals should be > 1"); + uint256 supplyAtCheckpoint = ISecurityToken(securityToken).totalSupplyAt(_checkpointId); + uint256 _ballotId = ballots.length; + ballots.push(Ballot( + uint64(_commitDuration), + uint64(_revealDuration), + uint64(_startTime), + uint64(_noOfProposals), + _proposedQuorum, + supplyAtCheckpoint, + _checkpointId + )); + emit BallotCreated(_ballotId, _commitDuration, _revealDuration, _noOfProposals, _startTime, _proposedQuorum, supplyAtCheckpoint, _checkpointId); + } + + function commitVote(uint256 _ballotId, bytes32 _secretVote) external { + _validBallotId(_ballotId); + require(getBallotStage(_ballotId) == Stage.COMMIT, "Not in commit stage"); + Ballot storage ballot = ballots[_ballotId]; + require(ballot.proposalToVote[msg.sender].timestamp == uint256(0), "Already voted"); + require(_secretVote != bytes32(0), "Invalid vote"); + uint256 weight = ISecurityToken(securityToken).balanceOfAt(msg.sender, ballot.checkpointId); + ballot.proposalToVote[msg.sender] = Vote(now, weight, 0, _secretVote, false); + emit VoteCommited(msg.sender, weight, _ballotId, _secretVote); + } + + function revealVote(uint256 _ballotId, uint256 _choiceOfProposal, uint256 _salt) external { + _validBallotId(_ballotId); + require(getBallotStage(_ballotId) == Stage.REVEAL, "Not in reveal stage"); + Ballot storage ballot = ballots[_ballotId]; + require(_choiceOfProposal <= ballot.noOfProposals, "Invalid proposal choice"); + require(!ballot.proposalToVote[msg.sender].isRevealed, "Already revealed"); + + // validate the secret vote + require( + bytes32(keccak256(abi.encodePacked(_choiceOfProposal, _salt))) == ballot.proposalToVote[msg.sender].secretVote, + "Invalid vote" + ); + uint256 weight = ballot.proposalToVote[msg.sender].weight; + ballot.weightedVote[_choiceOfProposal] = weight; + ballot.proposalToVote[msg.sender].voteOption = _choiceOfProposal; + ballot.proposalToVote[msg.sender].isRevealed = true; + emit VoteRevealed(msg.sender, weight, _ballotId, _choiceOfProposal, _salt, ballot.proposalToVote[msg.sender].secretVote); + } + + function getBallotStage(uint256 _ballotId) public view returns (Stage) { + Ballot memory ballot = ballots[_ballotId]; + uint256 commitTimeEnd = uint256(ballot.startTime).add(uint256(ballot.commitDuration)); + uint256 revealTimeEnd = uint256(ballot.startTime).add(uint256(ballot.commitDuration)).add(uint256(ballot.revealDuration)); + + if (now < ballot.startTime) + return Stage.PREP; + else if (now <= commitTimeEnd && now >= ballot.startTime) + return Stage.COMMIT; + else if ( now > commitTimeEnd && now <= revealTimeEnd) + return Stage.REVEAL; + else if (now > revealTimeEnd) + return Stage.RESOLVED; + } + + function getTally(uint256 _ballotId) external view returns(uint256, uint256, uint256[] memory) { + if (_ballotId < ballots.length) { + Ballot storage ballot = ballots[_ballotId]; + uint256 maxWeight; + uint256 winningProposal; + uint256[] memory voteTally = new uint256[](ballot.noOfProposals); + for (uint256 i = 0; i < ballot.noOfProposals; i++) { + if (maxWeight < ballot.weightedVote[i + 1]) { + maxWeight = ballot.weightedVote[i + 1]; + winningProposal = i; + } + voteTally[i] = ballot.weightedVote[i + 1]; + } + uint256 quorumWeight = (ballot.totalSupply.mul(ballot.quorum)).div(10 ** 18); + if (maxWeight < quorumWeight) + winningProposal = 0; + return (uint256(ballot.noOfProposals), winningProposal, voteTally); + } else + return (0, 0, new uint256[](0)); + } + + /** + * @notice This function returns the signature of configure function + */ + function getInitFunction() external pure returns(bytes4) { + return bytes4(0); + } + + /** + * @notice Return the permissions flag that are associated with CountTransferManager + */ + function getPermissions() external view returns(bytes32[] memory) { + bytes32[] memory allPermissions = new bytes32[](0); + return allPermissions; + } + + function _validValueCheck(uint256 _value) internal pure { + require(_value > 0, "Invalid value"); + } + + function _validBallotId(uint256 _ballotId) internal view { + require(ballots.length > _ballotId, "Index out of bound"); + } +} \ No newline at end of file diff --git a/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpointFactory.sol b/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpointFactory.sol new file mode 100644 index 000000000..4fc785faa --- /dev/null +++ b/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpointFactory.sol @@ -0,0 +1,58 @@ +pragma solidity ^0.5.0; + +import "./PLCRVotingCheckpoint.sol"; +import "../../ModuleFactory.sol"; + +/** + * @title Factory for deploying PLCRVotingCheckpoint module + */ +contract PLCRVotingCheckpointFactory is ModuleFactory { + + /** + * @notice Constructor + * @param _setupCost Setup cost of the module + * @param _usageCost Usage cost of the module + * @param _polymathRegistry Address of the Polymath registry + */ + constructor (uint256 _setupCost, uint256 _usageCost, address _polymathRegistry) public + ModuleFactory(_setupCost, _usageCost, _polymathRegistry) + { + initialVersion = "3.0.0"; + name = "PLCRVotingCheckpoint"; + title = "PLCR Voting Checkpoint"; + description = "Commit & reveal technique used for voting"; + compatibleSTVersionRange["lowerBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); + compatibleSTVersionRange["upperBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); + + } + + /** + * @notice used to launch the Module with the help of factory + * @return address Contract address of the Module + */ + function deploy(bytes calldata _data) external returns(address) { + address plcrVotingCheckpoint = address(new PLCRVotingCheckpoint(msg.sender, IPolymathRegistry(polymathRegistry).getAddress("PolyToken"))); + _initializeModule(plcrVotingCheckpoint, _data); + return plcrVotingCheckpoint; + } + + /** + * @notice Type of the Module factory + */ + function types() external view returns(uint8[] memory) { + uint8[] memory res = new uint8[](1); + res[0] = 4; + return res; + } + + /** + * @notice Get the tags related to the module factory + */ + function tags() external view returns(bytes32[] memory) { + bytes32[] memory availableTags = new bytes32[](3); + availableTags[0] = "Vote"; + availableTags[1] = "Checkpoint"; + availableTags[2] = "PLCR"; + return availableTags; + } +} \ No newline at end of file From 8a37b79f21106ab21710ed0e0d5ffe8390b0ce74 Mon Sep 17 00:00:00 2001 From: satyam Date: Thu, 14 Mar 2019 18:55:01 +0530 Subject: [PATCH 02/10] improve the PLCR voting --- .../Checkpoints/PLCRVotingCheckpoint.sol | 148 +++++++++++++----- .../PLCRVotingCheckpointFactory.sol | 24 +-- 2 files changed, 111 insertions(+), 61 deletions(-) diff --git a/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpoint.sol b/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpoint.sol index 547d17df8..ad183e7c6 100644 --- a/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpoint.sol +++ b/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpoint.sol @@ -10,21 +10,20 @@ contract PLCRVotingCheckpoint is Module { enum Stage { PREP, COMMIT, REVEAL, RESOLVED } struct Ballot { + uint256 quorum; // Should be a multiple of 10 ** 16 + uint256 totalSupply; + uint256 checkpointId; uint64 commitDuration; uint64 revealDuration; uint64 startTime; - uint64 noOfProposals; - uint256 quorum; - uint256 totalSupply; - uint256 checkpointId; + uint32 totalProposals; + uint32 totalVotes; mapping(uint256 => uint256) weightedVote; mapping(address => Vote) proposalToVote; } struct Vote { - uint256 timestamp; uint256 voteOption; - uint256 weight; bytes32 secretVote; bool isRevealed; } @@ -52,14 +51,29 @@ contract PLCRVotingCheckpoint is Module { } - function createBallot(uint256 _commitDuration, uint256 _revealDuration, uint256 _noOfProposals, uint256 _proposedQuorum) external { - _onlySecurityTokenOwner(); + /** + * @notice Use to create the ballot + * @param _commitDuration Unix time period till the voters commit there vote + * @param _revealDuration Unix time period till the voters reveal there vote starts when commit duration ends + * @param _noOfProposals Total number of proposal used in the ballot. In general it is 2 (For & Against) + * @param _proposedQuorum Minimum number of weight vote requires to win a election. + */ + function createBallot(uint256 _commitDuration, uint256 _revealDuration, uint256 _noOfProposals, uint256 _proposedQuorum) external withPerm(ADMIN) { uint256 startTime = now; uint256 checkpointId = ISecurityToken(securityToken).createCheckpoint(); _createBallotWithCheckpoint(_commitDuration, _revealDuration, _noOfProposals, _proposedQuorum, checkpointId, startTime); } - function createBallotWithCheckpoint( + /** + * @notice Use to create the ballot + * @param _commitDuration Unix time period till the voters commit there vote + * @param _revealDuration Unix time period till the voters reveal there vote starts when commit duration ends + * @param _noOfProposals Total number of proposal used in the ballot. In general it is 2 (For & Against) + * @param _proposedQuorum Minimum number of weight vote requires to win a election. + * @param _checkpointId Valid checkpoint Id + * @param _startTime startTime of the ballot + */ + function createCustomBallot( uint256 _commitDuration, uint256 _revealDuration, uint256 _noOfProposals, @@ -68,15 +82,16 @@ contract PLCRVotingCheckpoint is Module { uint256 _startTime ) external + withPerm(ADMIN) { - _onlySecurityTokenOwner(); + require(_checkpointId <= ISecurityToken(securityToken).currentCheckpointId(), "Invalid checkpoint Id"); _createBallotWithCheckpoint(_commitDuration, _revealDuration, _noOfProposals, _proposedQuorum, _checkpointId, _startTime); } function _createBallotWithCheckpoint( uint256 _commitDuration, uint256 _revealDuration, - uint256 _noOfProposals, + uint256 _totalProposals, uint256 _proposedQuorum, uint256 _checkpointId, uint256 _startTime @@ -87,37 +102,49 @@ contract PLCRVotingCheckpoint is Module { _validValueCheck(_commitDuration); _validValueCheck(_revealDuration); require(_startTime >= now, "Invalid start time"); - require(_noOfProposals > 1, "noOfProposals should be > 1"); + require(_totalProposals > 1, "totalProposals should be > 1"); uint256 supplyAtCheckpoint = ISecurityToken(securityToken).totalSupplyAt(_checkpointId); uint256 _ballotId = ballots.length; ballots.push(Ballot( + _proposedQuorum, + supplyAtCheckpoint, + _checkpointId, uint64(_commitDuration), uint64(_revealDuration), uint64(_startTime), - uint64(_noOfProposals), - _proposedQuorum, - supplyAtCheckpoint, - _checkpointId + uint32(_totalProposals), + uint32(0) )); - emit BallotCreated(_ballotId, _commitDuration, _revealDuration, _noOfProposals, _startTime, _proposedQuorum, supplyAtCheckpoint, _checkpointId); + emit BallotCreated(_ballotId, _commitDuration, _revealDuration, _totalProposals, _startTime, _proposedQuorum, supplyAtCheckpoint, _checkpointId); } + /** + * @notice Used to commit the vote + * @param _ballotId Given ballot Id + * @param _secretVote It is secret hash value (hashed offchain) + */ function commitVote(uint256 _ballotId, bytes32 _secretVote) external { _validBallotId(_ballotId); require(getBallotStage(_ballotId) == Stage.COMMIT, "Not in commit stage"); Ballot storage ballot = ballots[_ballotId]; - require(ballot.proposalToVote[msg.sender].timestamp == uint256(0), "Already voted"); + require(ballot.proposalToVote[msg.sender].secretVote == bytes32(0), "Already voted"); require(_secretVote != bytes32(0), "Invalid vote"); uint256 weight = ISecurityToken(securityToken).balanceOfAt(msg.sender, ballot.checkpointId); - ballot.proposalToVote[msg.sender] = Vote(now, weight, 0, _secretVote, false); + ballot.proposalToVote[msg.sender] = Vote(0, _secretVote, false); emit VoteCommited(msg.sender, weight, _ballotId, _secretVote); } + /** + * @notice Used to reveal the vote + * @param _ballotId Given ballot Id + * @param _choiceOfProposal Proposal chossed by the voter. + * @param _salt used salt for hashing (unique for each user) + */ function revealVote(uint256 _ballotId, uint256 _choiceOfProposal, uint256 _salt) external { _validBallotId(_ballotId); require(getBallotStage(_ballotId) == Stage.REVEAL, "Not in reveal stage"); Ballot storage ballot = ballots[_ballotId]; - require(_choiceOfProposal <= ballot.noOfProposals, "Invalid proposal choice"); + require(_choiceOfProposal < ballot.totalProposals, "Invalid proposal choice"); require(!ballot.proposalToVote[msg.sender].isRevealed, "Already revealed"); // validate the secret vote @@ -125,17 +152,21 @@ contract PLCRVotingCheckpoint is Module { bytes32(keccak256(abi.encodePacked(_choiceOfProposal, _salt))) == ballot.proposalToVote[msg.sender].secretVote, "Invalid vote" ); - uint256 weight = ballot.proposalToVote[msg.sender].weight; - ballot.weightedVote[_choiceOfProposal] = weight; + uint256 weight = ISecurityToken(securityToken).balanceOfAt(msg.sender, ballot.checkpointId); + ballot.weightedVote[_choiceOfProposal] = ballot.weightedVote[_choiceOfProposal].add(weight); ballot.proposalToVote[msg.sender].voteOption = _choiceOfProposal; ballot.proposalToVote[msg.sender].isRevealed = true; emit VoteRevealed(msg.sender, weight, _ballotId, _choiceOfProposal, _salt, ballot.proposalToVote[msg.sender].secretVote); } + /** + * @notice Used to get the current stage of the ballot + * @param _ballotId Given ballot Id + */ function getBallotStage(uint256 _ballotId) public view returns (Stage) { Ballot memory ballot = ballots[_ballotId]; uint256 commitTimeEnd = uint256(ballot.startTime).add(uint256(ballot.commitDuration)); - uint256 revealTimeEnd = uint256(ballot.startTime).add(uint256(ballot.commitDuration)).add(uint256(ballot.revealDuration)); + uint256 revealTimeEnd = commitTimeEnd.add(uint256(ballot.revealDuration)); if (now < ballot.startTime) return Stage.PREP; @@ -147,26 +178,60 @@ contract PLCRVotingCheckpoint is Module { return Stage.RESOLVED; } - function getTally(uint256 _ballotId) external view returns(uint256, uint256, uint256[] memory) { - if (_ballotId < ballots.length) { - Ballot storage ballot = ballots[_ballotId]; - uint256 maxWeight; - uint256 winningProposal; - uint256[] memory voteTally = new uint256[](ballot.noOfProposals); - for (uint256 i = 0; i < ballot.noOfProposals; i++) { - if (maxWeight < ballot.weightedVote[i + 1]) { - maxWeight = ballot.weightedVote[i + 1]; - winningProposal = i; - } - voteTally[i] = ballot.weightedVote[i + 1]; + /** + * @notice Queries the result of a given ballot + * @param _ballotId Id of the target ballot + * @return uint256 voteWeighting + * @return uint256 tieWith + * @return uint256 winningProposal + * @return uint256 totalVotes + */ + function getBallotResult(uint256 _ballotId) external view returns(uint256[] memory, uint256[] memory, uint256, uint256) { + if (_ballotId >= ballots.length) + return (new uint256[](0), new uint256[](0), 0, 0); + + Ballot storage ballot = ballots[_ballotId]; + uint256 i = 0; + uint256 counter = 0; + uint256 maxWeight = 0; + uint256 winningProposal; + for (i = 0; i < ballot.totalProposals; i++) { + if (maxWeight < ballot.weightedVote[i]) { + maxWeight = ballot.weightedVote[i]; + winningProposal = i; } - uint256 quorumWeight = (ballot.totalSupply.mul(ballot.quorum)).div(10 ** 18); - if (maxWeight < quorumWeight) - winningProposal = 0; - return (uint256(ballot.noOfProposals), winningProposal, voteTally); - } else - return (0, 0, new uint256[](0)); + } + for (i = 0; i < ballot.totalProposals; i++) { + if (maxWeight == ballot.weightedVote[i]) + counter ++; + } + uint256[] memory voteWeighting = new uint256[](ballot.totalProposals); + uint256[] memory tieWith = new uint256[](counter); + counter = 0; + for (i = 0; i < ballot.totalProposals; i++) { + voteWeighting[i] = ballot.weightedVote[i]; + if (maxWeight == ballot.weightedVote[i]) { + tieWith[counter] = i; + counter ++; + } + } + uint256 quorumWeight = (ballot.totalSupply.mul(ballot.quorum)).div(10 ** 18); + if (maxWeight < quorumWeight) + winningProposal = 0; + return (voteWeighting, tieWith, winningProposal, ballot.totalVotes); + } + + /** + * @notice Get the voted proposal + * @param _ballotId Id of the ballot + * @param _voter Address of the voter + */ + function getSelectedProposal(uint256 _ballotId, address _voter) external view returns(uint256 proposalId) { + if (_ballotId >= ballots.length) + return 0; + return ballots[_ballotId].proposalToVote[_voter].voteOption; } + /** * @notice This function returns the signature of configure function @@ -180,6 +245,7 @@ contract PLCRVotingCheckpoint is Module { */ function getPermissions() external view returns(bytes32[] memory) { bytes32[] memory allPermissions = new bytes32[](0); + allPermissions[0] = ADMIN; return allPermissions; } diff --git a/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpointFactory.sol b/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpointFactory.sol index 4fc785faa..caaea1e30 100644 --- a/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpointFactory.sol +++ b/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpointFactory.sol @@ -21,6 +21,10 @@ contract PLCRVotingCheckpointFactory is ModuleFactory { name = "PLCRVotingCheckpoint"; title = "PLCR Voting Checkpoint"; description = "Commit & reveal technique used for voting"; + typesData.push(4); + tagsData.push("Vote"); + tagsData.push("Checkpoint"); + tagsData.push("PLCR"); compatibleSTVersionRange["lowerBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); compatibleSTVersionRange["upperBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); @@ -35,24 +39,4 @@ contract PLCRVotingCheckpointFactory is ModuleFactory { _initializeModule(plcrVotingCheckpoint, _data); return plcrVotingCheckpoint; } - - /** - * @notice Type of the Module factory - */ - function types() external view returns(uint8[] memory) { - uint8[] memory res = new uint8[](1); - res[0] = 4; - return res; - } - - /** - * @notice Get the tags related to the module factory - */ - function tags() external view returns(bytes32[] memory) { - bytes32[] memory availableTags = new bytes32[](3); - availableTags[0] = "Vote"; - availableTags[1] = "Checkpoint"; - availableTags[2] = "PLCR"; - return availableTags; - } } \ No newline at end of file From 5cc2fb7f9037a3a4dee2d31ebe1a62c728a851af Mon Sep 17 00:00:00 2001 From: satyam Date: Fri, 15 Mar 2019 18:56:16 +0530 Subject: [PATCH 03/10] add more functionality and add test cases --- .../Checkpoints/PLCRVotingCheckpoint.sol | 57 ++- test/helpers/createInstances.js | 16 + test/zc_plcr_voting_checkpoint.js | 461 ++++++++++++++++++ 3 files changed, 528 insertions(+), 6 deletions(-) create mode 100644 test/zc_plcr_voting_checkpoint.js diff --git a/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpoint.sol b/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpoint.sol index ad183e7c6..4e47ecac1 100644 --- a/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpoint.sol +++ b/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpoint.sol @@ -16,8 +16,9 @@ contract PLCRVotingCheckpoint is Module { uint64 commitDuration; uint64 revealDuration; uint64 startTime; - uint32 totalProposals; + uint24 totalProposals; uint32 totalVotes; + uint8 isActive; mapping(uint256 => uint256) weightedVote; mapping(address => Vote) proposalToVote; } @@ -42,7 +43,7 @@ contract PLCRVotingCheckpoint is Module { uint256 _checkpointId ); event VoteRevealed(address _voter, uint256 _weight, uint256 indexed _ballotId, uint256 _choiceOfProposal, uint256 _salt, bytes32 _secretVote); - + event BallotStatusChanged(uint256 _ballotId, bool _newStatus); constructor(address _securityToken, address _polyAddress) public @@ -101,6 +102,7 @@ contract PLCRVotingCheckpoint is Module { // Sanity checks _validValueCheck(_commitDuration); _validValueCheck(_revealDuration); + _validValueCheck(_proposedQuorum); require(_startTime >= now, "Invalid start time"); require(_totalProposals > 1, "totalProposals should be > 1"); uint256 supplyAtCheckpoint = ISecurityToken(securityToken).totalSupplyAt(_checkpointId); @@ -112,8 +114,9 @@ contract PLCRVotingCheckpoint is Module { uint64(_commitDuration), uint64(_revealDuration), uint64(_startTime), - uint32(_totalProposals), - uint32(0) + uint24(_totalProposals), + uint32(0), + uint8(1) )); emit BallotCreated(_ballotId, _commitDuration, _revealDuration, _totalProposals, _startTime, _proposedQuorum, supplyAtCheckpoint, _checkpointId); } @@ -129,6 +132,7 @@ contract PLCRVotingCheckpoint is Module { Ballot storage ballot = ballots[_ballotId]; require(ballot.proposalToVote[msg.sender].secretVote == bytes32(0), "Already voted"); require(_secretVote != bytes32(0), "Invalid vote"); + require(ballot.isActive == uint8(1), "Inactive ballot"); uint256 weight = ISecurityToken(securityToken).balanceOfAt(msg.sender, ballot.checkpointId); ballot.proposalToVote[msg.sender] = Vote(0, _secretVote, false); emit VoteCommited(msg.sender, weight, _ballotId, _secretVote); @@ -144,6 +148,7 @@ contract PLCRVotingCheckpoint is Module { _validBallotId(_ballotId); require(getBallotStage(_ballotId) == Stage.REVEAL, "Not in reveal stage"); Ballot storage ballot = ballots[_ballotId]; + require(ballot.isActive == uint8(1), "Inactive ballot"); require(_choiceOfProposal < ballot.totalProposals, "Invalid proposal choice"); require(!ballot.proposalToVote[msg.sender].isRevealed, "Already revealed"); @@ -154,11 +159,33 @@ contract PLCRVotingCheckpoint is Module { ); uint256 weight = ISecurityToken(securityToken).balanceOfAt(msg.sender, ballot.checkpointId); ballot.weightedVote[_choiceOfProposal] = ballot.weightedVote[_choiceOfProposal].add(weight); + ballot.totalVotes = ballot.totalVotes + 1; ballot.proposalToVote[msg.sender].voteOption = _choiceOfProposal; ballot.proposalToVote[msg.sender].isRevealed = true; emit VoteRevealed(msg.sender, weight, _ballotId, _choiceOfProposal, _salt, ballot.proposalToVote[msg.sender].secretVote); } + /** + * @notice Allows the token issuer to set the active stats of a ballot + * @param _ballotId The index of the target ballot + * @param _isActive The bool value of the active stats of the ballot + */ + function changeBallotStatus(uint256 _ballotId, bool _isActive) external withPerm(ADMIN) { + _validBallotId(_ballotId); + require( + now < uint256(ballots[_ballotId].startTime) + .add(uint256(ballots[_ballotId].commitDuration) + .add(uint256(ballots[_ballotId].revealDuration))), + "Already ended" + ); + uint8 activeStatus = 0; + if (_isActive) + activeStatus = 1; + require(ballots[_ballotId].isActive != activeStatus, "Active state unchanged"); + ballots[_ballotId].isActive = activeStatus; + emit BallotStatusChanged(_ballotId, _isActive); + } + /** * @notice Used to get the current stage of the ballot * @param _ballotId Given ballot Id @@ -231,7 +258,25 @@ contract PLCRVotingCheckpoint is Module { return 0; return ballots[_ballotId].proposalToVote[_voter].voteOption; } - + + /** + * @notice Get the stats of the ballot + * @param _ballotId The index of the target ballot + */ + function getBallotStats(uint256 _ballotId) external view returns(uint256, uint256, uint256, uint64, uint64, uint64, uint32, uint32, bool) { + Ballot memory ballot = ballots[_ballotId]; + return ( + ballot.quorum, + ballot.totalSupply, + ballot.checkpointId, + ballot.commitDuration, + ballot.revealDuration, + ballot.startTime, + ballot.totalProposals, + ballot.totalVotes, + (ballot.isActive == 1 ? true : false) + ); + } /** * @notice This function returns the signature of configure function @@ -244,7 +289,7 @@ contract PLCRVotingCheckpoint is Module { * @notice Return the permissions flag that are associated with CountTransferManager */ function getPermissions() external view returns(bytes32[] memory) { - bytes32[] memory allPermissions = new bytes32[](0); + bytes32[] memory allPermissions = new bytes32[](1); allPermissions[0] = ADMIN; return allPermissions; } diff --git a/test/helpers/createInstances.js b/test/helpers/createInstances.js index ab5df07fc..3955da82c 100644 --- a/test/helpers/createInstances.js +++ b/test/helpers/createInstances.js @@ -49,6 +49,7 @@ const VolumeRestrictionTMFactory = artifacts.require("./VolumeRestrictionTMFacto const VolumeRestrictionTM = artifacts.require("./VolumeRestrictionTM.sol"); const VestingEscrowWalletFactory = artifacts.require("./VestingEscrowWalletFactory.sol"); const VestingEscrowWallet = artifacts.require("./VestingEscrowWallet.sol"); +const PLCRVotingCheckpointFactory = artifacts.require("./PLCRVotingCheckpointFactory.sol"); const Web3 = require("web3"); let BN = Web3.utils.BN; @@ -107,6 +108,7 @@ let I_SignedTransferManagerFactory; let I_USDOracle; let I_POLYOracle; let I_StablePOLYOracle; +let I_PLCRVotingCheckpointFactory; // Initial fee for ticker registry and security token registry const initRegFee = new BN(web3.utils.toWei("250")); @@ -590,3 +592,17 @@ export async function deploySignedTMAndVerifyed(accountPolymath, MRProxyInstance await registerAndVerifyByMR(I_SignedTransferManagerFactory.address, accountPolymath, MRProxyInstance); return new Array(I_SignedTransferManagerFactory); } + +// Deploy voting modules + +export async function deployPLCRVoteCheckpoint(accountPolymath, MRProxyInstance, setupCost) { + I_PLCRVotingCheckpointFactory = await PLCRVotingCheckpointFactory.new(setupCost, new BN(0), I_PolymathRegistry.address, { from: accountPolymath }); + assert.notEqual( + I_PLCRVotingCheckpointFactory.address.valueOf(), + "0x0000000000000000000000000000000000000000", + "PLCRVotingCheckpointFactory contract was not deployed" + ); + + await registerAndVerifyByMR(I_PLCRVotingCheckpointFactory.address, accountPolymath, MRProxyInstance); + return new Array(I_PLCRVotingCheckpointFactory); +} \ No newline at end of file diff --git a/test/zc_plcr_voting_checkpoint.js b/test/zc_plcr_voting_checkpoint.js new file mode 100644 index 000000000..c64cb6f3d --- /dev/null +++ b/test/zc_plcr_voting_checkpoint.js @@ -0,0 +1,461 @@ +import latestTime from "./helpers/latestTime"; +import {duration, promisifyLogWatch} from "./helpers/utils"; +import { takeSnapshot,increaseTime, revertToSnapshot} from "./helpers/time"; +import {catchRevert} from "./helpers/exceptions"; +import {deployPLCRVoteCheckpoint, setUpPolymathNetwork, deployGPMAndVerifyed} from "./helpers/createInstances"; + +const SecurityToken = artifacts.require("./SecurityToken.sol"); +const GeneralTransferManager = artifacts.require("./GeneralTransferManager"); +const GeneralPermissionManager = artifacts.require("./GeneralPermissionManager"); +const PLCRVotingCheckpoint = artifacts.require("./PLCRVotingCheckpoint"); +const STGetter = artifacts.require("./STGetter.sol"); + +const Web3 = require("web3"); +let BN = Web3.utils.BN; +const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); // Hardcoded development port + +contract("PLCRVotingCheckpoint", async (accounts) => { + // Accounts Variable declaration + let account_polymath; + let account_issuer; + let token_owner; + let account_investor1; + let account_investor2; + let account_investor3; + let account_investor4; + let account_temp; + let account_delegate; + + // Contract Instance Declaration + let I_GeneralPermissionManagerFactory; + let I_SecurityTokenRegistryProxy; + let I_GeneralTransferManagerFactory; + let I_PLCRVotingCheckpointFactory; + let P_PLCRVotingCheckpointFactory; + let I_PLCRVotingCheckpoint; + let I_GeneralTransferManager; + let I_ModuleRegistryProxy; + let I_ModuleRegistry; + let I_FeatureRegistry; + let I_GeneralPermissionManager; + let I_SecurityTokenRegistry; + let I_STRProxied; + let I_STFactory; + let I_SecurityToken; + let I_PolyToken; + let I_MRProxied; + let I_PolymathRegistry; + let I_STRGetter; + let I_STGetter; + let stGetter; + + // SecurityToken Details + const name = "Team"; + const symbol = "SAP"; + const tokenDetails = "This is equity type of issuance"; + const decimals = 18; + let saltArray = new Array(); + // Module key + const delegateManagerKey = 1; + const transferManagerKey = 2; + const stoKey = 3; + const checkpointKey = 4; + const burnKey = 5; + + // Initial fee for ticker registry and security token registry + const initRegFee = new BN(web3.utils.toWei("1000")); + + let currentTime; + const address_zero = "0x0000000000000000000000000000000000000000"; + const one_address = "0x0000000000000000000000000000000000000001"; + + before(async () => { + currentTime = new BN(await latestTime()); + account_polymath = accounts[0]; + account_issuer = accounts[1]; + + token_owner = account_issuer; + + account_investor1 = accounts[6]; + account_investor2 = accounts[7]; + account_investor3 = accounts[8]; + account_investor4 = accounts[9]; + account_temp = accounts[2]; + account_delegate = accounts[3]; + + // ----------- POLYMATH NETWORK Configuration ------------ + + // Step 1: Deploy the genral PM ecosystem + let instances = await setUpPolymathNetwork(account_polymath, token_owner); + + [ + I_PolymathRegistry, + I_PolyToken, + I_FeatureRegistry, + I_ModuleRegistry, + I_ModuleRegistryProxy, + I_MRProxied, + I_GeneralTransferManagerFactory, + I_STFactory, + I_SecurityTokenRegistry, + I_SecurityTokenRegistryProxy, + I_STRProxied, + I_STRGetter, + I_STGetter + ] = instances; + + + // STEP 4: Deploy the WeightedVoteCheckpoint + [I_PLCRVotingCheckpointFactory] = await deployPLCRVoteCheckpoint(account_polymath, I_MRProxied, 0); + [P_PLCRVotingCheckpointFactory] = await deployPLCRVoteCheckpoint(account_polymath, I_MRProxied, new BN(web3.utils.toWei("500"))); + + [I_GeneralPermissionManagerFactory] = await deployGPMAndVerifyed(account_polymath, I_MRProxied, 0); + + // Printing all the contract addresses + console.log(` + --------------------- Polymath Network Smart Contracts: --------------------- + PolymathRegistry: ${I_PolymathRegistry.address} + SecurityTokenRegistryProxy: ${I_SecurityTokenRegistryProxy.address} + SecurityTokenRegistry: ${I_SecurityTokenRegistry.address} + ModuleRegistry: ${I_ModuleRegistry.address} + ModuleRegistryProxy: ${I_ModuleRegistryProxy.address} + FeatureRegistry: ${I_FeatureRegistry.address} + + STFactory: ${I_STFactory.address} + GeneralTransferManagerFactory: ${I_GeneralTransferManagerFactory.address} + + PLCRVotingCheckpointFactory: ${I_PLCRVotingCheckpointFactory.address} + ----------------------------------------------------------------------------- + `); + }); + + function getRandom() { + return Math.floor(Math.random() * 1000000000000000); + } + + describe("\t\t Test case for PLCR Vote Checkpoint module \n", async() => { + + describe("\t\t Attaching the PLCR vote checkpoint module \n", async() => { + + it("\t\t Should register the ticker before the generation of the security token \n", async () => { + await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); + let tx = await I_STRProxied.registerTicker(token_owner, symbol, name, { from: token_owner }); + assert.equal(tx.logs[0].args._owner, token_owner); + assert.equal(tx.logs[0].args._ticker, symbol); + }); + + it("\t\t Should generate the new security token with the same symbol as registered above \n", async () => { + await I_PolyToken.approve(I_STRProxied.address, initRegFee, { from: token_owner }); + + let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, token_owner, 0, { from: token_owner }); + + // Verify the successful generation of the security token + assert.equal(tx.logs[2].args._ticker, symbol, "SecurityToken doesn't get deployed"); + + I_SecurityToken = await SecurityToken.at(tx.logs[2].args._securityTokenAddress); + stGetter = await STGetter.at(I_SecurityToken.address); + const log = (await I_SecurityToken.getPastEvents('ModuleAdded', {filter: {transactionHash: tx.transactionHash}}))[0]; + // Verify that GeneralTransferManager module get added successfully or not + assert.equal(log.args._types[0].toNumber(), transferManagerKey); + assert.equal(web3.utils.hexToString(log.args._name), "GeneralTransferManager"); + }); + + it("\t\t Should intialize the auto attached modules \n", async () => { + let moduleData = (await stGetter.getModulesByType(transferManagerKey))[0]; + I_GeneralTransferManager = await GeneralTransferManager.at(moduleData); + }); + + it("\t\t Should attach the voting module with the ST \n", async() => { + let tx = await I_SecurityToken.addModule(I_PLCRVotingCheckpointFactory.address, "0x0", 0, 0, {from: token_owner}); + assert.equal(tx.logs[2].args._types[0], checkpointKey, "Checkpoint doesn't get deployed"); + assert.equal(web3.utils.hexToString(tx.logs[2].args._name), "PLCRVotingCheckpoint", "PLCRVotingCheckpoint module was not added"); + I_PLCRVotingCheckpoint = await PLCRVotingCheckpoint.at(tx.logs[2].args._module); + }); + + it("\t\t Should fail to attach the voting module because allowance is unsufficent \n", async() => { + await catchRevert( + I_SecurityToken.addModule(P_PLCRVotingCheckpointFactory.address, "0x0", new BN(web3.utils.toWei("500")), 0, {from: token_owner}) + ); + }); + + it("\t\t Should attach the voting module with the ST \n", async() => { + let id = await takeSnapshot(); + await I_PolyToken.transfer(I_SecurityToken.address, new BN(web3.utils.toWei("2000", "ether")), { from: token_owner }); + let tx = await I_SecurityToken.addModule(P_PLCRVotingCheckpointFactory.address, "0x0", new BN(web3.utils.toWei("2000")), 0, {from: token_owner}); + assert.equal(tx.logs[3].args._types[0], checkpointKey, "Checkpoint doesn't get deployed"); + assert.equal(web3.utils.hexToString(tx.logs[3].args._name), "PLCRVotingCheckpoint", "PLCRVotingCheckpoint module was not added"); + await revertToSnapshot(id); + }); + + it("\t\t Should attach the general permission manager", async() => { + let tx = await I_SecurityToken.addModule(I_GeneralPermissionManagerFactory.address, "0x0", 0, 0, {from: token_owner}); + assert.equal(tx.logs[2].args._types[0], delegateManagerKey, "Permission manager doesn't get deployed"); + assert.equal(web3.utils.hexToString(tx.logs[2].args._name), "GeneralPermissionManager", "GeneralPermissionManager module was not added"); + I_GeneralPermissionManager = await GeneralPermissionManager.at(tx.logs[2].args._module); + }); + }); + + describe("\t\t Test for createBallot \n", async() => { + + it("\t\t Should fail to create ballot -- bad owner \n", async() => { + await catchRevert( + I_PLCRVotingCheckpoint.createBallot(new BN(duration.days(5)), new BN(duration.days(10)), new BN(3), new BN(46.57).mul(new BN(10).pow(new BN(16))), {from: account_polymath}) + ); + }); + + it("\t\t Should fail to create ballot -- bad commit duration \n", async() => { + await catchRevert( + I_PLCRVotingCheckpoint.createBallot(new BN(0), new BN(duration.days(10)), new BN(3), new BN(46.57).mul(new BN(10).pow(new BN(16))), {from: account_polymath}) + ); + }); + + it("\t\t Should fail to create ballot -- bad reveal duration \n", async() => { + await catchRevert( + I_PLCRVotingCheckpoint.createBallot(new BN(duration.days(10)), new BN(0), new BN(3), new BN(46.57).mul(new BN(10).pow(new BN(16))), {from: account_polymath}) + ); + }); + + it("\t\t Should fail to create ballot -- bad proposed quorum \n", async() => { + await catchRevert( + I_PLCRVotingCheckpoint.createBallot(new BN(duration.days(10)), new BN(duration.days(10)), new BN(3), new BN(46.57).mul(new BN(10).pow(new BN(16))), {from: account_polymath}) + ); + }); + + it("\t\t Should fail to create ballot -- bad no of proposals \n", async() => { + await catchRevert( + I_PLCRVotingCheckpoint.createBallot(new BN(duration.days(5)), new BN(duration.days(10)), new BN(0), new BN(46.57).mul(new BN(10).pow(new BN(16))), {from: account_polymath}) + ); + }); + + it("\t\t Mint some tokens and transfer to whitelisted investors \n", async() => { + // Whitelist multiple investors + let time = new BN(await latestTime()); + await I_GeneralTransferManager.modifyKYCDataMulti( + [account_investor1, account_investor2, account_investor3, account_investor4], + [time, time, time, time], + [time, time, time, time], + [time + duration.days(200), time + duration.days(200), time + duration.days(200), time + duration.days(200)], + { + from: token_owner + } + ); + + // mint tokens to whitelisted investors + + await I_SecurityToken.issueMulti( + [account_investor1, account_investor2, account_investor3], + [new BN(web3.utils.toWei("500")), new BN(web3.utils.toWei("1000")), new BN(web3.utils.toWei("5000"))], + { + from: token_owner + } + ); + + assert.equal(web3.utils.fromWei((await I_SecurityToken.balanceOf.call(account_investor1)).toString()), 500); + assert.equal(web3.utils.fromWei((await I_SecurityToken.balanceOf.call(account_investor2)).toString()), 1000); + assert.equal(web3.utils.fromWei((await I_SecurityToken.balanceOf.call(account_investor3)).toString()), 5000); + }); + + it("\t\t Should fail to create ballot -- Invalid checkpoint Id \n", async() => { + let startTime = new BN(await latestTime()); + let commitTime = new BN(duration.days(4)); + let revealTime = new BN(duration.days(5)); + await catchRevert( + I_PLCRVotingCheckpoint.createCustomBallot(commitTime, revealTime, new BN(3), new BN(50).mul(new BN(10).pow(new BN(16))), new BN(45), startTime, {from: token_owner}) + ); + }); + + it("\t\t Should fail to create ballot -- Invalid start time \n", async() => { + let startTime = new BN(await latestTime()); + let commitTime = new BN(duration.days(4)); + let revealTime = new BN(duration.days(5)); + await catchRevert( + I_PLCRVotingCheckpoint.createCustomBallot(commitTime, revealTime, new BN(3), new BN(50).mul(new BN(10).pow(new BN(16))), new BN(0), 0, {from: token_owner}) + ); + }); + + it("\t\t SHould give admin permission to the delegate \n", async() => { + await I_GeneralPermissionManager.addDelegate(account_delegate, web3.utils.toHex("I am a delegate"), {from: token_owner}); + await I_GeneralPermissionManager.changePermission(account_delegate, I_PLCRVotingCheckpoint.address, web3.utils.toHex("ADMIN"), true, {from: token_owner}); + }); + + it("\t\t Should create the ballot successfully \n", async() => { + let startTime = new BN(await latestTime()).add(new BN(duration.minutes(5))); + let commitTime = new BN(duration.days(4)); + let revealTime = new BN(duration.days(5)); + await I_SecurityToken.createCheckpoint({from: token_owner}); + let checkpointId = await I_SecurityToken.currentCheckpointId.call(); + let tx = await I_PLCRVotingCheckpoint.createCustomBallot(commitTime, revealTime, new BN(3), new BN(47.8).mul(new BN(10).pow(new BN(16))), checkpointId, startTime, {from: account_delegate}); + assert.equal((tx.logs[0].args._noOfProposals).toString(), 3); + assert.equal((tx.logs[0].args._checkpointId).toString(), 1); + assert.equal((tx.logs[0].args._ballotId).toString(), 0); + }); + }); + + describe("\t\t Test case for commitVote \n", async() => { + + it("\t\t Should fail to commitVote -- bad ballot id \n", async() => { + await catchRevert( + I_PLCRVotingCheckpoint.commitVote(new BN(2), web3.utils.toHex("Some secret"), {from: account_investor1}) + ); + }); + + it("\t\t Should fail to commitVote -- not in the commit stage \n", async() => { + let salt = getRandom(); + await catchRevert( + I_PLCRVotingCheckpoint.commitVote(new BN(0), web3.utils.soliditySha3(1, salt), {from: account_investor1}) + ); + }); + + it("\t\t Should fail to commitVote -- secret vote is 0 \n", async() => { + await increaseTime(duration.minutes(7)); // Increase time to make it under the commit stage + await catchRevert( + I_PLCRVotingCheckpoint.commitVote(new BN(0), "0x0", {from: account_investor1}) + ); + }); + + it("\t\t Should successfully vote by account investor1 \n", async() => { + let salt = getRandom(); + saltArray.push(salt); + let tx = await I_PLCRVotingCheckpoint.commitVote(new BN(0), web3.utils.soliditySha3(1, salt), {from: account_investor1}); + assert.equal(tx.logs[0].args._ballotId, 0); + assert.equal(tx.logs[0].args._secretVote, web3.utils.soliditySha3(1, salt)); + assert.equal(tx.logs[0].args._voter, account_investor1); + + let data = await I_PLCRVotingCheckpoint.getBallotStats.call(new BN(0)); + assert.equal(data[6], 3); + assert.equal(data[8], true); + }); + + // it("\t\t Should successfully vote by account investor2 \n", async() => { + // let tx = await I_WeightedVoteCheckpoint.castVote(new BN(0), new BN(2), {from: account_investor2}); + // assert.equal(tx.logs[0].args._ballotId, 0); + // assert.equal(tx.logs[0].args._proposalId, 2); + // assert.equal(tx.logs[0].args._investor, account_investor2); + + // let data = await I_WeightedVoteCheckpoint.getBallotStats.call(new BN(0)); + // assert.equal(data[4], 2); + // assert.equal(data[5], 3); + // assert.equal(data[6], true); + // }); + + // it("\t\t Should fail to vote again \n", async() => { + // await catchRevert( + // I_WeightedVoteCheckpoint.castVote(new BN(0), new BN(2), {from: account_investor2}) + // ); + // }) + + // it("\t\t Should fail to change the ballot status-- bad owner \n", async() => { + // await catchRevert( + // I_WeightedVoteCheckpoint.changeBallotStatus(new BN(0), false, {from: account_polymath}) + // ); + // }); + + // it("\t\t Should fail to change the ballot status-- no change in the state \n", async() => { + // await catchRevert( + // I_WeightedVoteCheckpoint.changeBallotStatus(new BN(0), true, {from: account_polymath}) + // ); + // }); + + // it("\t\t Should change the status of the ballot with the help of changeBallotStatus \n", async() => { + // let tx = await I_WeightedVoteCheckpoint.changeBallotStatus(new BN(0), false, {from: token_owner}); + // assert.equal(tx.logs[0].args._ballotId, 0); + // assert.equal(tx.logs[0].args._isActive, false); + // }); + + // it("\t\t Should fail to vote because ballot is disabled \n", async() => { + // await catchRevert( + // I_WeightedVoteCheckpoint.castVote(new BN(0), new BN(2), {from: account_investor3}) + // ); + // }); + + // it("\t\t Should turn on the ballot \n", async() => { + // let tx = await I_WeightedVoteCheckpoint.changeBallotStatus(new BN(0), true, {from: token_owner}); + // assert.equal(tx.logs[0].args._ballotId, 0); + // assert.equal(tx.logs[0].args._isActive, true); + // }); + + // it("\t\t Should successfully vote \n", async() => { + // let tx = await I_WeightedVoteCheckpoint.castVote(new BN(0), new BN(1), {from: account_investor3}); + // assert.equal(tx.logs[0].args._ballotId, 0); + // assert.equal(tx.logs[0].args._proposalId, 1); + // assert.equal(tx.logs[0].args._investor, account_investor3); + + // let data = await I_WeightedVoteCheckpoint.getBallotStats.call(new BN(0)); + // assert.equal(data[4], 3); + // assert.equal(data[5], 3); + // assert.equal(data[6], true); + // }); + + // it("\t\t Should fail to vote when the duration of vote is complete \n", async() => { + // await increaseTime(duration.days(6)); + + // // transfer some funds to account_investor4 + // await I_SecurityToken.issue( + // account_investor4, + // new BN(web3.utils.toWei("500")), + // "0x0", + // { + // from: token_owner + // } + // ); + // await catchRevert( + // I_WeightedVoteCheckpoint.castVote(new BN(0), new BN(2), {from: account_investor4}) + // ); + // }); + + // it("\t\t Should fail to change the status of the ballot -- already ended \n", async() => { + // await catchRevert( + // I_WeightedVoteCheckpoint.changeBallotStatus(new BN(0), false, {from: token_owner}) + // ); + // }); + + // it("\t\t Should check who votes whom \n", async() => { + // assert.equal((await I_WeightedVoteCheckpoint.getSelectedProposal.call(new BN(0), account_investor1)).toString(), 1); + // assert.equal((await I_WeightedVoteCheckpoint.getSelectedProposal.call(new BN(0), account_investor2)).toString(), 2); + // assert.equal((await I_WeightedVoteCheckpoint.getSelectedProposal.call(new BN(0), account_investor3)).toString(), 1); + // }); + + // it("\t\t Should get the result of the ballot \n", async() => { + // let data = await I_WeightedVoteCheckpoint.getBallotResults.call(new BN(0)); + // assert.equal(data[4], 3); + // assert.equal(web3.utils.fromWei((data[0][0]).toString()), 5500); + // assert.equal(web3.utils.fromWei((data[0][1]).toString()), 1000); + // assert.equal(data[2], 1); + // assert.equal(data[3], 0); + // }); + }); + + describe("\t\t General function test \n", async() => { + + it("\t\t Should check the permission \n", async() => { + let data = await I_PLCRVotingCheckpoint.getPermissions.call(); + assert.equal(data.length, 1); + }); + + it("\t\t Should check the init function \n", async() => { + assert.equal(await I_PLCRVotingCheckpoint.getInitFunction.call(), "0x00000000"); + }); + }); + + describe("\t\t Factory test cases \n", async() => { + it("\t\t Should get the exact details of the factory \n", async () => { + assert.equal((await I_PLCRVotingCheckpointFactory.setupCost.call()).toNumber(), 0); + assert.equal((await I_PLCRVotingCheckpointFactory.types.call())[0], 4); + assert.equal(await I_PLCRVotingCheckpointFactory.version.call(), "3.0.0"); + assert.equal( + web3.utils.toAscii(await I_PLCRVotingCheckpointFactory.name.call()).replace(/\u0000/g, ""), + "PLCRVotingCheckpoint", + "Wrong Module added" + ); + assert.equal( + await I_PLCRVotingCheckpointFactory.description.call(), + "Commit & reveal technique used for voting", + "Wrong Module added" + ); + assert.equal(await I_PLCRVotingCheckpointFactory.title.call(), "PLCR Voting Checkpoint", "Wrong Module added"); + let tags = await I_PLCRVotingCheckpointFactory.tags.call(); + assert.equal(tags.length, 3); + }); + }); + }); +}); \ No newline at end of file From efdbda4b59c78f96eab3fe47c4942c76a8ba6603 Mon Sep 17 00:00:00 2001 From: satyam Date: Mon, 18 Mar 2019 11:31:06 +0530 Subject: [PATCH 04/10] add more tests --- .../Checkpoints/PLCRVotingCheckpoint.sol | 57 ++-- test/zc_plcr_voting_checkpoint.js | 312 ++++++++++++------ 2 files changed, 250 insertions(+), 119 deletions(-) diff --git a/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpoint.sol b/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpoint.sol index 4e47ecac1..5f06a87a5 100644 --- a/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpoint.sol +++ b/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpoint.sol @@ -128,12 +128,13 @@ contract PLCRVotingCheckpoint is Module { */ function commitVote(uint256 _ballotId, bytes32 _secretVote) external { _validBallotId(_ballotId); - require(getBallotStage(_ballotId) == Stage.COMMIT, "Not in commit stage"); + require(getCurrentBallotStage(_ballotId) == Stage.COMMIT, "Not in commit stage"); Ballot storage ballot = ballots[_ballotId]; require(ballot.proposalToVote[msg.sender].secretVote == bytes32(0), "Already voted"); require(_secretVote != bytes32(0), "Invalid vote"); require(ballot.isActive == uint8(1), "Inactive ballot"); uint256 weight = ISecurityToken(securityToken).balanceOfAt(msg.sender, ballot.checkpointId); + require(weight > 0, "Zero weight is not allowed"); ballot.proposalToVote[msg.sender] = Vote(0, _secretVote, false); emit VoteCommited(msg.sender, weight, _ballotId, _secretVote); } @@ -146,9 +147,10 @@ contract PLCRVotingCheckpoint is Module { */ function revealVote(uint256 _ballotId, uint256 _choiceOfProposal, uint256 _salt) external { _validBallotId(_ballotId); - require(getBallotStage(_ballotId) == Stage.REVEAL, "Not in reveal stage"); + require(getCurrentBallotStage(_ballotId) == Stage.REVEAL, "Not in reveal stage"); Ballot storage ballot = ballots[_ballotId]; require(ballot.isActive == uint8(1), "Inactive ballot"); + require(ballot.proposalToVote[msg.sender].secretVote != bytes32(0), "Should be commit vote first"); require(_choiceOfProposal < ballot.totalProposals, "Invalid proposal choice"); require(!ballot.proposalToVote[msg.sender].isRevealed, "Already revealed"); @@ -190,7 +192,7 @@ contract PLCRVotingCheckpoint is Module { * @notice Used to get the current stage of the ballot * @param _ballotId Given ballot Id */ - function getBallotStage(uint256 _ballotId) public view returns (Stage) { + function getCurrentBallotStage(uint256 _ballotId) public view returns (Stage) { Ballot memory ballot = ballots[_ballotId]; uint256 commitTimeEnd = uint256(ballot.startTime).add(uint256(ballot.commitDuration)); uint256 revealTimeEnd = commitTimeEnd.add(uint256(ballot.revealDuration)); @@ -211,41 +213,48 @@ contract PLCRVotingCheckpoint is Module { * @return uint256 voteWeighting * @return uint256 tieWith * @return uint256 winningProposal + * @return bool isVotingSucceed * @return uint256 totalVotes */ - function getBallotResult(uint256 _ballotId) external view returns(uint256[] memory, uint256[] memory, uint256, uint256) { + function getBallotResults(uint256 _ballotId) external view returns(uint256[] memory, uint256[] memory, uint256, bool, uint256) { if (_ballotId >= ballots.length) - return (new uint256[](0), new uint256[](0), 0, 0); + return (new uint256[](0), new uint256[](0), 0, false, 0); Ballot storage ballot = ballots[_ballotId]; uint256 i = 0; + bool isVotingSucceed = false; uint256 counter = 0; uint256 maxWeight = 0; - uint256 winningProposal; + uint256 winningProposal = 0; + uint256 quorumWeight = (ballot.totalSupply.mul(ballot.quorum)).div(10 ** 18); + uint256[] memory voteWeighting = new uint256[](ballot.totalProposals); for (i = 0; i < ballot.totalProposals; i++) { + voteWeighting[i] = ballot.weightedVote[i]; if (maxWeight < ballot.weightedVote[i]) { maxWeight = ballot.weightedVote[i]; - winningProposal = i; + if (maxWeight >= quorumWeight) + winningProposal = i; } } - for (i = 0; i < ballot.totalProposals; i++) { - if (maxWeight == ballot.weightedVote[i]) - counter ++; + if (maxWeight >= quorumWeight) { + isVotingSucceed = true; + for (i = 0; i < ballot.totalProposals; i++) { + if (maxWeight == ballot.weightedVote[i] && i != winningProposal) + counter ++; + } } - uint256[] memory voteWeighting = new uint256[](ballot.totalProposals); + uint256[] memory tieWith = new uint256[](counter); counter = 0; - for (i = 0; i < ballot.totalProposals; i++) { - voteWeighting[i] = ballot.weightedVote[i]; - if (maxWeight == ballot.weightedVote[i]) { - tieWith[counter] = i; - counter ++; - } + if (counter > 1) { + for (i = 0; i < ballot.totalProposals; i++) { + if (maxWeight == ballot.weightedVote[i]) { + tieWith[counter] = i; + counter ++; + } + } } - uint256 quorumWeight = (ballot.totalSupply.mul(ballot.quorum)).div(10 ** 18); - if (maxWeight < quorumWeight) - winningProposal = 0; - return (voteWeighting, tieWith, winningProposal, ballot.totalVotes); + return (voteWeighting, tieWith, winningProposal, isVotingSucceed, ballot.totalVotes); } /** @@ -253,10 +262,10 @@ contract PLCRVotingCheckpoint is Module { * @param _ballotId Id of the ballot * @param _voter Address of the voter */ - function getSelectedProposal(uint256 _ballotId, address _voter) external view returns(uint256 proposalId) { + function getSelectedProposal(uint256 _ballotId, address _voter) external view returns(uint256 proposalId, bool isRevealed) { if (_ballotId >= ballots.length) - return 0; - return ballots[_ballotId].proposalToVote[_voter].voteOption; + return (0, false); + return (ballots[_ballotId].proposalToVote[_voter].voteOption, ballots[_ballotId].proposalToVote[_voter].isRevealed); } /** diff --git a/test/zc_plcr_voting_checkpoint.js b/test/zc_plcr_voting_checkpoint.js index c64cb6f3d..f1214c968 100644 --- a/test/zc_plcr_voting_checkpoint.js +++ b/test/zc_plcr_voting_checkpoint.js @@ -313,6 +313,25 @@ contract("PLCRVotingCheckpoint", async (accounts) => { ); }); + it("\t\t Should change some ballot status \n", async() => { + await I_PLCRVotingCheckpoint.changeBallotStatus(new BN(0), false, {from: token_owner}); + let data = await I_PLCRVotingCheckpoint.getBallotStats.call(new BN(0)); + assert.isFalse(data[8]); + }); + + it("\t\t Should fail to commitVote because ballot is not active \n", async() => { + let salt = getRandom(); + await catchRevert( + I_PLCRVotingCheckpoint.commitVote(new BN(0), web3.utils.soliditySha3(1, salt), {from: account_investor1}) + ); + }); + + it("\t\t Should change some ballot status \n", async() => { + await I_PLCRVotingCheckpoint.changeBallotStatus(new BN(0), true, {from: token_owner}); + let data = await I_PLCRVotingCheckpoint.getBallotStats.call(new BN(0)); + assert.isTrue(data[8]); + }); + it("\t\t Should successfully vote by account investor1 \n", async() => { let salt = getRandom(); saltArray.push(salt); @@ -326,103 +345,206 @@ contract("PLCRVotingCheckpoint", async (accounts) => { assert.equal(data[8], true); }); - // it("\t\t Should successfully vote by account investor2 \n", async() => { - // let tx = await I_WeightedVoteCheckpoint.castVote(new BN(0), new BN(2), {from: account_investor2}); - // assert.equal(tx.logs[0].args._ballotId, 0); - // assert.equal(tx.logs[0].args._proposalId, 2); - // assert.equal(tx.logs[0].args._investor, account_investor2); + it("\t\t Should failed to vote again \n", async() => { + let salt = getRandom(); + await catchRevert( + I_PLCRVotingCheckpoint.commitVote(new BN(0), web3.utils.soliditySha3(1, salt), {from: account_investor1}) + ) + }); + + it("\t\t Should successfully vote by account investor3 \n", async() => { + let salt = getRandom(); + saltArray.push(salt); + let tx = await I_PLCRVotingCheckpoint.commitVote(new BN(0), web3.utils.soliditySha3(0, salt), {from: account_investor3}); + assert.equal(tx.logs[0].args._ballotId, 0); + assert.equal(tx.logs[0].args._secretVote, web3.utils.soliditySha3(0, salt)); + assert.equal(tx.logs[0].args._voter, account_investor3); - // let data = await I_WeightedVoteCheckpoint.getBallotStats.call(new BN(0)); - // assert.equal(data[4], 2); - // assert.equal(data[5], 3); - // assert.equal(data[6], true); - // }); - - // it("\t\t Should fail to vote again \n", async() => { - // await catchRevert( - // I_WeightedVoteCheckpoint.castVote(new BN(0), new BN(2), {from: account_investor2}) - // ); - // }) - - // it("\t\t Should fail to change the ballot status-- bad owner \n", async() => { - // await catchRevert( - // I_WeightedVoteCheckpoint.changeBallotStatus(new BN(0), false, {from: account_polymath}) - // ); - // }); - - // it("\t\t Should fail to change the ballot status-- no change in the state \n", async() => { - // await catchRevert( - // I_WeightedVoteCheckpoint.changeBallotStatus(new BN(0), true, {from: account_polymath}) - // ); - // }); - - // it("\t\t Should change the status of the ballot with the help of changeBallotStatus \n", async() => { - // let tx = await I_WeightedVoteCheckpoint.changeBallotStatus(new BN(0), false, {from: token_owner}); - // assert.equal(tx.logs[0].args._ballotId, 0); - // assert.equal(tx.logs[0].args._isActive, false); - // }); - - // it("\t\t Should fail to vote because ballot is disabled \n", async() => { - // await catchRevert( - // I_WeightedVoteCheckpoint.castVote(new BN(0), new BN(2), {from: account_investor3}) - // ); - // }); - - // it("\t\t Should turn on the ballot \n", async() => { - // let tx = await I_WeightedVoteCheckpoint.changeBallotStatus(new BN(0), true, {from: token_owner}); - // assert.equal(tx.logs[0].args._ballotId, 0); - // assert.equal(tx.logs[0].args._isActive, true); - // }); - - // it("\t\t Should successfully vote \n", async() => { - // let tx = await I_WeightedVoteCheckpoint.castVote(new BN(0), new BN(1), {from: account_investor3}); - // assert.equal(tx.logs[0].args._ballotId, 0); - // assert.equal(tx.logs[0].args._proposalId, 1); - // assert.equal(tx.logs[0].args._investor, account_investor3); + let data = await I_PLCRVotingCheckpoint.getBallotStats.call(new BN(0)); + assert.equal(data[6], 3); + assert.equal(data[8], true); + }); + + it("\t\t Should successfully vote by account investor2 \n", async() => { + let salt = getRandom(); + saltArray.push(salt); + let tx = await I_PLCRVotingCheckpoint.commitVote(new BN(0), web3.utils.soliditySha3(2, salt), {from: account_investor2}); + assert.equal(tx.logs[0].args._ballotId, 0); + assert.equal(tx.logs[0].args._secretVote, web3.utils.soliditySha3(2, salt)); + assert.equal(tx.logs[0].args._voter, account_investor2); - // let data = await I_WeightedVoteCheckpoint.getBallotStats.call(new BN(0)); - // assert.equal(data[4], 3); - // assert.equal(data[5], 3); - // assert.equal(data[6], true); - // }); - - // it("\t\t Should fail to vote when the duration of vote is complete \n", async() => { - // await increaseTime(duration.days(6)); - - // // transfer some funds to account_investor4 - // await I_SecurityToken.issue( - // account_investor4, - // new BN(web3.utils.toWei("500")), - // "0x0", - // { - // from: token_owner - // } - // ); - // await catchRevert( - // I_WeightedVoteCheckpoint.castVote(new BN(0), new BN(2), {from: account_investor4}) - // ); - // }); - - // it("\t\t Should fail to change the status of the ballot -- already ended \n", async() => { - // await catchRevert( - // I_WeightedVoteCheckpoint.changeBallotStatus(new BN(0), false, {from: token_owner}) - // ); - // }); - - // it("\t\t Should check who votes whom \n", async() => { - // assert.equal((await I_WeightedVoteCheckpoint.getSelectedProposal.call(new BN(0), account_investor1)).toString(), 1); - // assert.equal((await I_WeightedVoteCheckpoint.getSelectedProposal.call(new BN(0), account_investor2)).toString(), 2); - // assert.equal((await I_WeightedVoteCheckpoint.getSelectedProposal.call(new BN(0), account_investor3)).toString(), 1); - // }); - - // it("\t\t Should get the result of the ballot \n", async() => { - // let data = await I_WeightedVoteCheckpoint.getBallotResults.call(new BN(0)); - // assert.equal(data[4], 3); - // assert.equal(web3.utils.fromWei((data[0][0]).toString()), 5500); - // assert.equal(web3.utils.fromWei((data[0][1]).toString()), 1000); - // assert.equal(data[2], 1); - // assert.equal(data[3], 0); - // }); + let data = await I_PLCRVotingCheckpoint.getBallotStats.call(new BN(0)); + assert.equal(data[6], 3); + assert.equal(data[8], true); + }); + + it("\t\t Mint some more tokens and transferred to the tokens holders \n", async() => { + await I_SecurityToken.issueMulti( + [account_investor1, account_investor2, account_investor3, account_investor4], + [new BN(web3.utils.toWei("3000")), + new BN(web3.utils.toWei("2000")), + new BN(web3.utils.toWei("500")), + new BN(web3.utils.toWei("3500")) + ], + { + from: token_owner + } + ); + + assert.equal(web3.utils.fromWei((await I_SecurityToken.balanceOf.call(account_investor1)).toString()), 3500); + assert.equal(web3.utils.fromWei((await I_SecurityToken.balanceOf.call(account_investor2)).toString()), 3000); + assert.equal(web3.utils.fromWei((await I_SecurityToken.balanceOf.call(account_investor3)).toString()), 5500); + assert.equal(web3.utils.fromWei((await I_SecurityToken.balanceOf.call(account_investor4)).toString()), 3500); + }); + + it("\t\t Should fail to vote with a zero weight \n", async() => { + let salt = getRandom(); + await catchRevert( + I_PLCRVotingCheckpoint.commitVote(new BN(0), web3.utils.soliditySha3(0, salt), {from: account_investor4}) + ); + }); + + it("\t\t Should create a new ballot \n", async() => { + let commitTime = new BN(duration.days(10)); + let revealTime = new BN(duration.days(10)); + let tx = await I_PLCRVotingCheckpoint.createBallot(commitTime, revealTime, new BN(4), new BN(51).mul(new BN(10).pow(new BN(16))), {from: account_delegate}); + assert.equal((tx.logs[0].args._noOfProposals).toString(), 4); + assert.equal((tx.logs[0].args._checkpointId).toString(), 2); + assert.equal((tx.logs[0].args._ballotId).toString(), 1); + }); + + it("\t\t Should reveal the vote -- failed because not a valid stage \n", async() => { + await catchRevert( + I_PLCRVotingCheckpoint.revealVote(new BN(0), new BN(0), saltArray[1], {from: account_investor3}) + ); + }) + + it("\t\t Should try to commit vote but failed because commit periods end \n", async() => { + let salt = getRandom(); + await increaseTime(duration.days(4)); + + await catchRevert( + I_PLCRVotingCheckpoint.commitVote(new BN(0), web3.utils.soliditySha3(0, salt), {from: account_investor2}) + ); + }); + + it("\t\t Should fail to reveal vote -- not a valid ballot Id \n", async() => { + await catchRevert( + I_PLCRVotingCheckpoint.revealVote(new BN(4), new BN(0), saltArray[1], {from: account_investor3}) + ); + }); + + it("\t\t Should fali to reveal the vote -- not have the secret vote \n", async() => { + await catchRevert( + I_PLCRVotingCheckpoint.revealVote(new BN(0), new BN(0), saltArray[1], {from: account_investor4}) + ); + }); + + it("\t\t Should fail to reveal vote -- not a valid choice of proposal \n", async() => { + await catchRevert( + I_PLCRVotingCheckpoint.revealVote(new BN(0), new BN(4), saltArray[1], {from: account_investor3}) + ); + }); + + it("\t\t Should successfully reveal the vote by investor 3 \n", async() => { + let tx = await I_PLCRVotingCheckpoint.revealVote(new BN(0), new BN(0), saltArray[1], {from: account_investor3}); + assert.equal(tx.logs[0].args._voter, account_investor3); + assert.equal(tx.logs[0].args._ballotId, 0); + assert.equal(tx.logs[0].args._choiceOfProposal, 0); + assert.equal(tx.logs[0].args._salt, saltArray[1]); + let data = await I_PLCRVotingCheckpoint.getBallotStats.call(new BN(0)); + assert.equal(data[6], 3); + assert.equal(data[7], 1); + assert.equal(data[8], true); + }); + + it("\t\t Should fail to change the ballot status-- bad owner \n", async() => { + await catchRevert( + I_PLCRVotingCheckpoint.changeBallotStatus(new BN(0), false, {from: account_polymath}) + ); + }); + + it("\t\t Should fail to change the ballot status-- no change in the state \n", async() => { + await catchRevert( + I_PLCRVotingCheckpoint.changeBallotStatus(new BN(0), true, {from: token_owner}) + ); + }); + + it("\t\t Should change the status of the ballot with the help of changeBallotStatus \n", async() => { + let tx = await I_PLCRVotingCheckpoint.changeBallotStatus(new BN(0), false, {from: token_owner}); + assert.equal(tx.logs[0].args._ballotId, 0); + assert.isFalse(tx.logs[0].args._newStatus); + }); + + it("\t\t Should fail to reveal vote as ballot status is false \n", async() => { + await catchRevert( + I_PLCRVotingCheckpoint.revealVote(new BN(0), new BN(1), saltArray[0], {from: account_investor1}) + ); + }); + + it("\t\t Should successfully reveal the vote by investor 1 \n", async() => { + let tx = await I_PLCRVotingCheckpoint.changeBallotStatus(new BN(0), true, {from: token_owner}); + assert.equal(tx.logs[0].args._ballotId, 0); + assert.isTrue(tx.logs[0].args._newStatus); + + let txData = await I_PLCRVotingCheckpoint.revealVote(new BN(0), new BN(1), saltArray[0], {from: account_investor1}); + assert.equal(txData.logs[0].args._voter, account_investor1); + assert.equal(txData.logs[0].args._ballotId, 0); + assert.equal(txData.logs[0].args._choiceOfProposal, 1); + assert.equal(txData.logs[0].args._salt, saltArray[0]); + let data = await I_PLCRVotingCheckpoint.getBallotStats.call(new BN(0)); + assert.equal(data[6], 3); + assert.equal(data[7], 2); + assert.equal(data[8], true); + }); + + it("\t\t Should fail to reveal vote again \n", async() => { + await catchRevert( + I_PLCRVotingCheckpoint.revealVote(new BN(0), new BN(1), saltArray[0], {from: account_investor1}) + ); + }); + + it("\t\t Should ge the ballot stage \n", async() => { + let stage1 = await I_PLCRVotingCheckpoint.getCurrentBallotStage.call(new BN(0)); + assert.equal(stage1.toString(), 2); + let stage2 = await I_PLCRVotingCheckpoint.getCurrentBallotStage.call(new BN(1)); + assert.equal(stage2.toString(), 1); + }); + + it("\t\t Should fail to reveal vote when reveal period is over \n", async() => { + await increaseTime(duration.days(5)); + await catchRevert( + I_PLCRVotingCheckpoint.revealVote(new BN(0), new BN(2), saltArray[2], {from: account_investor2}) + ); + }); + + it("\t\t Should check who votes whom \n", async() => { + assert.equal(((await I_PLCRVotingCheckpoint.getSelectedProposal.call(new BN(0), account_investor1))[0]).toString(), 1); + assert.isTrue((await I_PLCRVotingCheckpoint.getSelectedProposal.call(new BN(0), account_investor1))[1]); + assert.equal(((await I_PLCRVotingCheckpoint.getSelectedProposal.call(new BN(0), account_investor2))[0]).toString(), 0); + assert.isFalse((await I_PLCRVotingCheckpoint.getSelectedProposal.call(new BN(0), account_investor2))[1]); + assert.equal(((await I_PLCRVotingCheckpoint.getSelectedProposal.call(new BN(0), account_investor3))[0]).toString(), 0); + assert.isTrue((await I_PLCRVotingCheckpoint.getSelectedProposal.call(new BN(0), account_investor3))[1]); + }); + + it("\t\t Should give the result to 0 because ballot id is not valid", async() => { + let data = await I_PLCRVotingCheckpoint.getBallotResults.call(new BN(5)); + assert.equal(data[0].length, 0); + assert.equal(data[1].length, 0); + assert.equal(data[2].toString(), 0); + assert.isFalse(data[3]); + assert.equal(data[4].toString(), 0); + }); + + it("\t\t Should get the result of the ballot \n", async() => { + let data = await I_PLCRVotingCheckpoint.getBallotResults.call(new BN(0)); + assert.equal(web3.utils.fromWei((data[0][0]).toString()), 5000); + assert.equal(web3.utils.fromWei((data[0][1]).toString()), 500); + assert.equal(data[1].length, 0); + assert.equal(data[2].toString(), 0); + assert.isTrue(data[3]); + assert.equal(data[4].toString(), 2); + }); }); describe("\t\t General function test \n", async() => { From aec7a1d6ed556aedd29c3b70f599c0a6b470e7c4 Mon Sep 17 00:00:00 2001 From: satyam Date: Mon, 18 Mar 2019 11:54:00 +0530 Subject: [PATCH 05/10] add overflow check --- .../Experimental/Checkpoints/PLCRVotingCheckpoint.sol | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpoint.sol b/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpoint.sol index 5f06a87a5..488735b8b 100644 --- a/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpoint.sol +++ b/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpoint.sol @@ -103,6 +103,14 @@ contract PLCRVotingCheckpoint is Module { _validValueCheck(_commitDuration); _validValueCheck(_revealDuration); _validValueCheck(_proposedQuorum); + // Overflow check + require( + uint64(_commitDuration) == _commitDuration && + uint64(_revealDuration) == _revealDuration && + uint64(_startTime) == _startTime && + uint24(_totalProposals) == _totalProposals, + "Uint64 values get overflowed" + ); require(_startTime >= now, "Invalid start time"); require(_totalProposals > 1, "totalProposals should be > 1"); uint256 supplyAtCheckpoint = ISecurityToken(securityToken).totalSupplyAt(_checkpointId); From 92c8d57cc20c36a26c47ec6d1c72829d2ed4c82a Mon Sep 17 00:00:00 2001 From: satyam Date: Mon, 18 Mar 2019 14:51:49 +0530 Subject: [PATCH 06/10] minor fixes --- .../Checkpoints/PLCRVotingCheckpoint.sol | 59 ++++++++----------- test/zc_plcr_voting_checkpoint.js | 5 +- 2 files changed, 27 insertions(+), 37 deletions(-) diff --git a/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpoint.sol b/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpoint.sol index 488735b8b..aeff9ff5c 100644 --- a/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpoint.sol +++ b/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpoint.sol @@ -11,14 +11,13 @@ contract PLCRVotingCheckpoint is Module { struct Ballot { uint256 quorum; // Should be a multiple of 10 ** 16 - uint256 totalSupply; uint256 checkpointId; uint64 commitDuration; uint64 revealDuration; uint64 startTime; uint24 totalProposals; uint32 totalVotes; - uint8 isActive; + bool isActive; mapping(uint256 => uint256) weightedVote; mapping(address => Vote) proposalToVote; } @@ -26,12 +25,11 @@ contract PLCRVotingCheckpoint is Module { struct Vote { uint256 voteOption; bytes32 secretVote; - bool isRevealed; } Ballot[] ballots; - event VoteCommited(address indexed _voter, uint256 _weight, uint256 _ballotId, bytes32 _secretVote); + event VoteCommited(address indexed _voter, uint256 _weight, uint256 indexed _ballotId, bytes32 _secretVote); event BallotCreated( uint256 indexed _ballotId, uint256 _commitDuration, @@ -39,11 +37,10 @@ contract PLCRVotingCheckpoint is Module { uint256 _noOfProposals, uint256 _startTime, uint256 _proposedQuorum, - uint256 _totalSupply, uint256 _checkpointId ); - event VoteRevealed(address _voter, uint256 _weight, uint256 indexed _ballotId, uint256 _choiceOfProposal, uint256 _salt, bytes32 _secretVote); - event BallotStatusChanged(uint256 _ballotId, bool _newStatus); + event VoteRevealed(address indexed _voter, uint256 _weight, uint256 indexed _ballotId, uint256 _choiceOfProposal, uint256 _salt, bytes32 _secretVote); + event BallotStatusChanged(uint256 indexed _ballotId, bool _newStatus); constructor(address _securityToken, address _polyAddress) public @@ -109,24 +106,22 @@ contract PLCRVotingCheckpoint is Module { uint64(_revealDuration) == _revealDuration && uint64(_startTime) == _startTime && uint24(_totalProposals) == _totalProposals, - "Uint64 values get overflowed" + "Parameter values get overflowed" ); require(_startTime >= now, "Invalid start time"); - require(_totalProposals > 1, "totalProposals should be > 1"); - uint256 supplyAtCheckpoint = ISecurityToken(securityToken).totalSupplyAt(_checkpointId); + require(_totalProposals > 1, "Invalid number of totalProposals"); uint256 _ballotId = ballots.length; ballots.push(Ballot( _proposedQuorum, - supplyAtCheckpoint, _checkpointId, uint64(_commitDuration), uint64(_revealDuration), uint64(_startTime), uint24(_totalProposals), uint32(0), - uint8(1) + true )); - emit BallotCreated(_ballotId, _commitDuration, _revealDuration, _totalProposals, _startTime, _proposedQuorum, supplyAtCheckpoint, _checkpointId); + emit BallotCreated(_ballotId, _commitDuration, _revealDuration, _totalProposals, _startTime, _proposedQuorum, _checkpointId); } /** @@ -140,10 +135,10 @@ contract PLCRVotingCheckpoint is Module { Ballot storage ballot = ballots[_ballotId]; require(ballot.proposalToVote[msg.sender].secretVote == bytes32(0), "Already voted"); require(_secretVote != bytes32(0), "Invalid vote"); - require(ballot.isActive == uint8(1), "Inactive ballot"); + require(ballot.isActive, "Inactive ballot"); uint256 weight = ISecurityToken(securityToken).balanceOfAt(msg.sender, ballot.checkpointId); require(weight > 0, "Zero weight is not allowed"); - ballot.proposalToVote[msg.sender] = Vote(0, _secretVote, false); + ballot.proposalToVote[msg.sender] = Vote(0, _secretVote); emit VoteCommited(msg.sender, weight, _ballotId, _secretVote); } @@ -157,10 +152,9 @@ contract PLCRVotingCheckpoint is Module { _validBallotId(_ballotId); require(getCurrentBallotStage(_ballotId) == Stage.REVEAL, "Not in reveal stage"); Ballot storage ballot = ballots[_ballotId]; - require(ballot.isActive == uint8(1), "Inactive ballot"); + require(ballot.isActive, "Inactive ballot"); require(ballot.proposalToVote[msg.sender].secretVote != bytes32(0), "Should be commit vote first"); require(_choiceOfProposal < ballot.totalProposals, "Invalid proposal choice"); - require(!ballot.proposalToVote[msg.sender].isRevealed, "Already revealed"); // validate the secret vote require( @@ -168,11 +162,12 @@ contract PLCRVotingCheckpoint is Module { "Invalid vote" ); uint256 weight = ISecurityToken(securityToken).balanceOfAt(msg.sender, ballot.checkpointId); + bytes32 secretVote = ballot.proposalToVote[msg.sender].secretVote; ballot.weightedVote[_choiceOfProposal] = ballot.weightedVote[_choiceOfProposal].add(weight); ballot.totalVotes = ballot.totalVotes + 1; ballot.proposalToVote[msg.sender].voteOption = _choiceOfProposal; - ballot.proposalToVote[msg.sender].isRevealed = true; - emit VoteRevealed(msg.sender, weight, _ballotId, _choiceOfProposal, _salt, ballot.proposalToVote[msg.sender].secretVote); + ballot.proposalToVote[msg.sender].secretVote = bytes32(0); + emit VoteRevealed(msg.sender, weight, _ballotId, _choiceOfProposal, _salt, secretVote); } /** @@ -188,11 +183,8 @@ contract PLCRVotingCheckpoint is Module { .add(uint256(ballots[_ballotId].revealDuration))), "Already ended" ); - uint8 activeStatus = 0; - if (_isActive) - activeStatus = 1; - require(ballots[_ballotId].isActive != activeStatus, "Active state unchanged"); - ballots[_ballotId].isActive = activeStatus; + require(ballots[_ballotId].isActive != _isActive, "Active state unchanged"); + ballots[_ballotId].isActive = _isActive; emit BallotStatusChanged(_ballotId, _isActive); } @@ -234,7 +226,8 @@ contract PLCRVotingCheckpoint is Module { uint256 counter = 0; uint256 maxWeight = 0; uint256 winningProposal = 0; - uint256 quorumWeight = (ballot.totalSupply.mul(ballot.quorum)).div(10 ** 18); + uint256 supplyAtCheckpoint = ISecurityToken(securityToken).totalSupplyAt(ballot.checkpointId); + uint256 quorumWeight = (supplyAtCheckpoint.mul(ballot.quorum)).div(10 ** 18); uint256[] memory voteWeighting = new uint256[](ballot.totalProposals); for (i = 0; i < ballot.totalProposals; i++) { voteWeighting[i] = ballot.weightedVote[i]; @@ -253,10 +246,10 @@ contract PLCRVotingCheckpoint is Module { } uint256[] memory tieWith = new uint256[](counter); - counter = 0; - if (counter > 1) { + if (counter > 0) { + counter = 0; for (i = 0; i < ballot.totalProposals; i++) { - if (maxWeight == ballot.weightedVote[i]) { + if (maxWeight == ballot.weightedVote[i] && i != winningProposal) { tieWith[counter] = i; counter ++; } @@ -270,10 +263,10 @@ contract PLCRVotingCheckpoint is Module { * @param _ballotId Id of the ballot * @param _voter Address of the voter */ - function getSelectedProposal(uint256 _ballotId, address _voter) external view returns(uint256 proposalId, bool isRevealed) { + function getSelectedProposal(uint256 _ballotId, address _voter) external view returns(uint256 proposalId, bytes32 secretVote) { if (_ballotId >= ballots.length) - return (0, false); - return (ballots[_ballotId].proposalToVote[_voter].voteOption, ballots[_ballotId].proposalToVote[_voter].isRevealed); + return (0, bytes32(0)); + return (ballots[_ballotId].proposalToVote[_voter].voteOption, ballots[_ballotId].proposalToVote[_voter].secretVote); } /** @@ -284,14 +277,14 @@ contract PLCRVotingCheckpoint is Module { Ballot memory ballot = ballots[_ballotId]; return ( ballot.quorum, - ballot.totalSupply, + ISecurityToken(securityToken).totalSupplyAt(ballot.checkpointId), ballot.checkpointId, ballot.commitDuration, ballot.revealDuration, ballot.startTime, ballot.totalProposals, ballot.totalVotes, - (ballot.isActive == 1 ? true : false) + ballot.isActive ); } diff --git a/test/zc_plcr_voting_checkpoint.js b/test/zc_plcr_voting_checkpoint.js index f1214c968..7f495016c 100644 --- a/test/zc_plcr_voting_checkpoint.js +++ b/test/zc_plcr_voting_checkpoint.js @@ -520,11 +520,8 @@ contract("PLCRVotingCheckpoint", async (accounts) => { it("\t\t Should check who votes whom \n", async() => { assert.equal(((await I_PLCRVotingCheckpoint.getSelectedProposal.call(new BN(0), account_investor1))[0]).toString(), 1); - assert.isTrue((await I_PLCRVotingCheckpoint.getSelectedProposal.call(new BN(0), account_investor1))[1]); assert.equal(((await I_PLCRVotingCheckpoint.getSelectedProposal.call(new BN(0), account_investor2))[0]).toString(), 0); - assert.isFalse((await I_PLCRVotingCheckpoint.getSelectedProposal.call(new BN(0), account_investor2))[1]); - assert.equal(((await I_PLCRVotingCheckpoint.getSelectedProposal.call(new BN(0), account_investor3))[0]).toString(), 0); - assert.isTrue((await I_PLCRVotingCheckpoint.getSelectedProposal.call(new BN(0), account_investor3))[1]); + assert.equal(((await I_PLCRVotingCheckpoint.getSelectedProposal.call(new BN(0), account_investor3))[0]).toString(), 0); }); it("\t\t Should give the result to 0 because ballot id is not valid", async() => { From ab117b679d4ed44e6a5cd55c1cb9205d02a695bc Mon Sep 17 00:00:00 2001 From: satyam Date: Mon, 18 Mar 2019 16:59:58 +0530 Subject: [PATCH 07/10] add comments --- .../Experimental/Checkpoints/PLCRVotingCheckpoint.sol | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpoint.sol b/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpoint.sol index aeff9ff5c..05c3e4c1d 100644 --- a/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpoint.sol +++ b/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpoint.sol @@ -109,6 +109,8 @@ contract PLCRVotingCheckpoint is Module { "Parameter values get overflowed" ); require(_startTime >= now, "Invalid start time"); + // Total no of proposals always greater than 2 it means minimum valid + // proposal id will be 0 or 1. Proposal Id should be start from 0 instead of 1. require(_totalProposals > 1, "Invalid number of totalProposals"); uint256 _ballotId = ballots.length; ballots.push(Ballot( @@ -145,7 +147,7 @@ contract PLCRVotingCheckpoint is Module { /** * @notice Used to reveal the vote * @param _ballotId Given ballot Id - * @param _choiceOfProposal Proposal chossed by the voter. + * @param _choiceOfProposal Proposal chossed by the voter. It varies from (0 - totalProposals - 1) * @param _salt used salt for hashing (unique for each user) */ function revealVote(uint256 _ballotId, uint256 _choiceOfProposal, uint256 _salt) external { @@ -153,7 +155,7 @@ contract PLCRVotingCheckpoint is Module { require(getCurrentBallotStage(_ballotId) == Stage.REVEAL, "Not in reveal stage"); Ballot storage ballot = ballots[_ballotId]; require(ballot.isActive, "Inactive ballot"); - require(ballot.proposalToVote[msg.sender].secretVote != bytes32(0), "Should be commit vote first"); + require(ballot.proposalToVote[msg.sender].secretVote != bytes32(0), "Secret vote not available"); require(_choiceOfProposal < ballot.totalProposals, "Invalid proposal choice"); // validate the secret vote From aca36edc7db09f69f76ddba2c749aa1c55425c45 Mon Sep 17 00:00:00 2001 From: satyam Date: Mon, 18 Mar 2019 17:00:28 +0530 Subject: [PATCH 08/10] minor comment --- .../modules/Experimental/Checkpoints/PLCRVotingCheckpoint.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpoint.sol b/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpoint.sol index 05c3e4c1d..e615e1087 100644 --- a/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpoint.sol +++ b/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpoint.sol @@ -147,7 +147,7 @@ contract PLCRVotingCheckpoint is Module { /** * @notice Used to reveal the vote * @param _ballotId Given ballot Id - * @param _choiceOfProposal Proposal chossed by the voter. It varies from (0 - totalProposals - 1) + * @param _choiceOfProposal Proposal chossed by the voter. It varies from (0 to totalProposals - 1) * @param _salt used salt for hashing (unique for each user) */ function revealVote(uint256 _ballotId, uint256 _choiceOfProposal, uint256 _salt) external { From 3b8d567c3f53de1264ae1f883a2a0f1ca3d4ce86 Mon Sep 17 00:00:00 2001 From: satyam Date: Mon, 1 Apr 2019 14:34:57 +0530 Subject: [PATCH 09/10] start proposal Id from 1 instead of 0 --- .../Checkpoints/PLCRVotingCheckpoint.sol | 50 +++++++++++-------- .../PLCRVotingCheckpointFactory.sol | 11 +++- test/zc_plcr_voting_checkpoint.js | 40 +++++++-------- 3 files changed, 58 insertions(+), 43 deletions(-) diff --git a/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpoint.sol b/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpoint.sol index e615e1087..c5001fabd 100644 --- a/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpoint.sol +++ b/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpoint.sol @@ -16,7 +16,7 @@ contract PLCRVotingCheckpoint is Module { uint64 revealDuration; uint64 startTime; uint24 totalProposals; - uint32 totalVotes; + uint32 totalVoters; bool isActive; mapping(uint256 => uint256) weightedVote; mapping(address => Vote) proposalToVote; @@ -29,7 +29,7 @@ contract PLCRVotingCheckpoint is Module { Ballot[] ballots; - event VoteCommited(address indexed _voter, uint256 _weight, uint256 indexed _ballotId, bytes32 _secretVote); + event VoteCommit(address indexed _voter, uint256 _weight, uint256 indexed _ballotId, bytes32 _secretVote); event BallotCreated( uint256 indexed _ballotId, uint256 _commitDuration, @@ -37,7 +37,7 @@ contract PLCRVotingCheckpoint is Module { uint256 _noOfProposals, uint256 _startTime, uint256 _proposedQuorum, - uint256 _checkpointId + uint256 indexed _checkpointId ); event VoteRevealed(address indexed _voter, uint256 _weight, uint256 indexed _ballotId, uint256 _choiceOfProposal, uint256 _salt, bytes32 _secretVote); event BallotStatusChanged(uint256 indexed _ballotId, bool _newStatus); @@ -56,7 +56,15 @@ contract PLCRVotingCheckpoint is Module { * @param _noOfProposals Total number of proposal used in the ballot. In general it is 2 (For & Against) * @param _proposedQuorum Minimum number of weight vote requires to win a election. */ - function createBallot(uint256 _commitDuration, uint256 _revealDuration, uint256 _noOfProposals, uint256 _proposedQuorum) external withPerm(ADMIN) { + function createBallot( + uint256 _commitDuration, + uint256 _revealDuration, + uint256 _noOfProposals, + uint256 _proposedQuorum + ) + external + withPerm(ADMIN) + { uint256 startTime = now; uint256 checkpointId = ISecurityToken(securityToken).createCheckpoint(); _createBallotWithCheckpoint(_commitDuration, _revealDuration, _noOfProposals, _proposedQuorum, checkpointId, startTime); @@ -141,7 +149,7 @@ contract PLCRVotingCheckpoint is Module { uint256 weight = ISecurityToken(securityToken).balanceOfAt(msg.sender, ballot.checkpointId); require(weight > 0, "Zero weight is not allowed"); ballot.proposalToVote[msg.sender] = Vote(0, _secretVote); - emit VoteCommited(msg.sender, weight, _ballotId, _secretVote); + emit VoteCommit(msg.sender, weight, _ballotId, _secretVote); } /** @@ -156,7 +164,7 @@ contract PLCRVotingCheckpoint is Module { Ballot storage ballot = ballots[_ballotId]; require(ballot.isActive, "Inactive ballot"); require(ballot.proposalToVote[msg.sender].secretVote != bytes32(0), "Secret vote not available"); - require(_choiceOfProposal < ballot.totalProposals, "Invalid proposal choice"); + require(_choiceOfProposal < ballot.totalProposals && _choiceOfProposal >= 1, "Invalid proposal choice"); // validate the secret vote require( @@ -166,7 +174,7 @@ contract PLCRVotingCheckpoint is Module { uint256 weight = ISecurityToken(securityToken).balanceOfAt(msg.sender, ballot.checkpointId); bytes32 secretVote = ballot.proposalToVote[msg.sender].secretVote; ballot.weightedVote[_choiceOfProposal] = ballot.weightedVote[_choiceOfProposal].add(weight); - ballot.totalVotes = ballot.totalVotes + 1; + ballot.totalVoters = ballot.totalVoters + 1; ballot.proposalToVote[msg.sender].voteOption = _choiceOfProposal; ballot.proposalToVote[msg.sender].secretVote = bytes32(0); emit VoteRevealed(msg.sender, weight, _ballotId, _choiceOfProposal, _salt, secretVote); @@ -180,7 +188,7 @@ contract PLCRVotingCheckpoint is Module { function changeBallotStatus(uint256 _ballotId, bool _isActive) external withPerm(ADMIN) { _validBallotId(_ballotId); require( - now < uint256(ballots[_ballotId].startTime) + now <= uint256(ballots[_ballotId].startTime) .add(uint256(ballots[_ballotId].commitDuration) .add(uint256(ballots[_ballotId].revealDuration))), "Already ended" @@ -216,7 +224,7 @@ contract PLCRVotingCheckpoint is Module { * @return uint256 tieWith * @return uint256 winningProposal * @return bool isVotingSucceed - * @return uint256 totalVotes + * @return uint256 totalVoters */ function getBallotResults(uint256 _ballotId) external view returns(uint256[] memory, uint256[] memory, uint256, bool, uint256) { if (_ballotId >= ballots.length) @@ -232,17 +240,17 @@ contract PLCRVotingCheckpoint is Module { uint256 quorumWeight = (supplyAtCheckpoint.mul(ballot.quorum)).div(10 ** 18); uint256[] memory voteWeighting = new uint256[](ballot.totalProposals); for (i = 0; i < ballot.totalProposals; i++) { - voteWeighting[i] = ballot.weightedVote[i]; - if (maxWeight < ballot.weightedVote[i]) { - maxWeight = ballot.weightedVote[i]; + voteWeighting[i] = ballot.weightedVote[i + 1]; + if (maxWeight < ballot.weightedVote[i + 1]) { + maxWeight = ballot.weightedVote[i + 1]; if (maxWeight >= quorumWeight) - winningProposal = i; + winningProposal = i + 1; } } if (maxWeight >= quorumWeight) { isVotingSucceed = true; for (i = 0; i < ballot.totalProposals; i++) { - if (maxWeight == ballot.weightedVote[i] && i != winningProposal) + if (maxWeight == ballot.weightedVote[i + 1] && (i + 1) != winningProposal) counter ++; } } @@ -251,13 +259,13 @@ contract PLCRVotingCheckpoint is Module { if (counter > 0) { counter = 0; for (i = 0; i < ballot.totalProposals; i++) { - if (maxWeight == ballot.weightedVote[i] && i != winningProposal) { - tieWith[counter] = i; + if (maxWeight == ballot.weightedVote[i + 1] && (i + 1) != winningProposal) { + tieWith[counter] = i + 1; counter ++; } } } - return (voteWeighting, tieWith, winningProposal, isVotingSucceed, ballot.totalVotes); + return (voteWeighting, tieWith, winningProposal, isVotingSucceed, ballot.totalVoters); } /** @@ -265,10 +273,10 @@ contract PLCRVotingCheckpoint is Module { * @param _ballotId Id of the ballot * @param _voter Address of the voter */ - function getSelectedProposal(uint256 _ballotId, address _voter) external view returns(uint256 proposalId, bytes32 secretVote) { + function getSelectedProposal(uint256 _ballotId, address _voter) external view returns(uint256 proposalId) { if (_ballotId >= ballots.length) - return (0, bytes32(0)); - return (ballots[_ballotId].proposalToVote[_voter].voteOption, ballots[_ballotId].proposalToVote[_voter].secretVote); + return 0; + return (ballots[_ballotId].proposalToVote[_voter].voteOption); } /** @@ -285,7 +293,7 @@ contract PLCRVotingCheckpoint is Module { ballot.revealDuration, ballot.startTime, ballot.totalProposals, - ballot.totalVotes, + ballot.totalVoters, ballot.isActive ); } diff --git a/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpointFactory.sol b/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpointFactory.sol index caaea1e30..00639c4be 100644 --- a/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpointFactory.sol +++ b/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpointFactory.sol @@ -13,9 +13,16 @@ contract PLCRVotingCheckpointFactory is ModuleFactory { * @param _setupCost Setup cost of the module * @param _usageCost Usage cost of the module * @param _polymathRegistry Address of the Polymath registry + * @param _isCostInPoly true = cost in Poly, false = USD */ - constructor (uint256 _setupCost, uint256 _usageCost, address _polymathRegistry) public - ModuleFactory(_setupCost, _usageCost, _polymathRegistry) + constructor ( + uint256 _setupCost, + uint256 _usageCost, + address _polymathRegistry, + bool _isCostInPoly + ) + public + ModuleFactory(_setupCost, _usageCost, _polymathRegistry, _isCostInPoly) { initialVersion = "3.0.0"; name = "PLCRVotingCheckpoint"; diff --git a/test/zc_plcr_voting_checkpoint.js b/test/zc_plcr_voting_checkpoint.js index 7f495016c..a489bb821 100644 --- a/test/zc_plcr_voting_checkpoint.js +++ b/test/zc_plcr_voting_checkpoint.js @@ -335,9 +335,9 @@ contract("PLCRVotingCheckpoint", async (accounts) => { it("\t\t Should successfully vote by account investor1 \n", async() => { let salt = getRandom(); saltArray.push(salt); - let tx = await I_PLCRVotingCheckpoint.commitVote(new BN(0), web3.utils.soliditySha3(1, salt), {from: account_investor1}); + let tx = await I_PLCRVotingCheckpoint.commitVote(new BN(0), web3.utils.soliditySha3(2, salt), {from: account_investor1}); assert.equal(tx.logs[0].args._ballotId, 0); - assert.equal(tx.logs[0].args._secretVote, web3.utils.soliditySha3(1, salt)); + assert.equal(tx.logs[0].args._secretVote, web3.utils.soliditySha3(2, salt)); assert.equal(tx.logs[0].args._voter, account_investor1); let data = await I_PLCRVotingCheckpoint.getBallotStats.call(new BN(0)); @@ -355,9 +355,9 @@ contract("PLCRVotingCheckpoint", async (accounts) => { it("\t\t Should successfully vote by account investor3 \n", async() => { let salt = getRandom(); saltArray.push(salt); - let tx = await I_PLCRVotingCheckpoint.commitVote(new BN(0), web3.utils.soliditySha3(0, salt), {from: account_investor3}); + let tx = await I_PLCRVotingCheckpoint.commitVote(new BN(0), web3.utils.soliditySha3(1, salt), {from: account_investor3}); assert.equal(tx.logs[0].args._ballotId, 0); - assert.equal(tx.logs[0].args._secretVote, web3.utils.soliditySha3(0, salt)); + assert.equal(tx.logs[0].args._secretVote, web3.utils.soliditySha3(1, salt)); assert.equal(tx.logs[0].args._voter, account_investor3); let data = await I_PLCRVotingCheckpoint.getBallotStats.call(new BN(0)); @@ -400,7 +400,7 @@ contract("PLCRVotingCheckpoint", async (accounts) => { it("\t\t Should fail to vote with a zero weight \n", async() => { let salt = getRandom(); await catchRevert( - I_PLCRVotingCheckpoint.commitVote(new BN(0), web3.utils.soliditySha3(0, salt), {from: account_investor4}) + I_PLCRVotingCheckpoint.commitVote(new BN(0), web3.utils.soliditySha3(2, salt), {from: account_investor4}) ); }); @@ -424,33 +424,33 @@ contract("PLCRVotingCheckpoint", async (accounts) => { await increaseTime(duration.days(4)); await catchRevert( - I_PLCRVotingCheckpoint.commitVote(new BN(0), web3.utils.soliditySha3(0, salt), {from: account_investor2}) + I_PLCRVotingCheckpoint.commitVote(new BN(0), web3.utils.soliditySha3(1, salt), {from: account_investor2}) ); }); it("\t\t Should fail to reveal vote -- not a valid ballot Id \n", async() => { await catchRevert( - I_PLCRVotingCheckpoint.revealVote(new BN(4), new BN(0), saltArray[1], {from: account_investor3}) + I_PLCRVotingCheckpoint.revealVote(new BN(4), new BN(1), saltArray[1], {from: account_investor3}) ); }); it("\t\t Should fali to reveal the vote -- not have the secret vote \n", async() => { await catchRevert( - I_PLCRVotingCheckpoint.revealVote(new BN(0), new BN(0), saltArray[1], {from: account_investor4}) + I_PLCRVotingCheckpoint.revealVote(new BN(0), new BN(1), saltArray[1], {from: account_investor4}) ); }); it("\t\t Should fail to reveal vote -- not a valid choice of proposal \n", async() => { await catchRevert( - I_PLCRVotingCheckpoint.revealVote(new BN(0), new BN(4), saltArray[1], {from: account_investor3}) + I_PLCRVotingCheckpoint.revealVote(new BN(0), new BN(5), saltArray[1], {from: account_investor3}) ); }); it("\t\t Should successfully reveal the vote by investor 3 \n", async() => { - let tx = await I_PLCRVotingCheckpoint.revealVote(new BN(0), new BN(0), saltArray[1], {from: account_investor3}); + let tx = await I_PLCRVotingCheckpoint.revealVote(new BN(0), new BN(1), saltArray[1], {from: account_investor3}); assert.equal(tx.logs[0].args._voter, account_investor3); assert.equal(tx.logs[0].args._ballotId, 0); - assert.equal(tx.logs[0].args._choiceOfProposal, 0); + assert.equal(tx.logs[0].args._choiceOfProposal, 1); assert.equal(tx.logs[0].args._salt, saltArray[1]); let data = await I_PLCRVotingCheckpoint.getBallotStats.call(new BN(0)); assert.equal(data[6], 3); @@ -478,7 +478,7 @@ contract("PLCRVotingCheckpoint", async (accounts) => { it("\t\t Should fail to reveal vote as ballot status is false \n", async() => { await catchRevert( - I_PLCRVotingCheckpoint.revealVote(new BN(0), new BN(1), saltArray[0], {from: account_investor1}) + I_PLCRVotingCheckpoint.revealVote(new BN(0), new BN(2), saltArray[0], {from: account_investor1}) ); }); @@ -487,10 +487,10 @@ contract("PLCRVotingCheckpoint", async (accounts) => { assert.equal(tx.logs[0].args._ballotId, 0); assert.isTrue(tx.logs[0].args._newStatus); - let txData = await I_PLCRVotingCheckpoint.revealVote(new BN(0), new BN(1), saltArray[0], {from: account_investor1}); + let txData = await I_PLCRVotingCheckpoint.revealVote(new BN(0), new BN(2), saltArray[0], {from: account_investor1}); assert.equal(txData.logs[0].args._voter, account_investor1); assert.equal(txData.logs[0].args._ballotId, 0); - assert.equal(txData.logs[0].args._choiceOfProposal, 1); + assert.equal(txData.logs[0].args._choiceOfProposal, 2); assert.equal(txData.logs[0].args._salt, saltArray[0]); let data = await I_PLCRVotingCheckpoint.getBallotStats.call(new BN(0)); assert.equal(data[6], 3); @@ -500,7 +500,7 @@ contract("PLCRVotingCheckpoint", async (accounts) => { it("\t\t Should fail to reveal vote again \n", async() => { await catchRevert( - I_PLCRVotingCheckpoint.revealVote(new BN(0), new BN(1), saltArray[0], {from: account_investor1}) + I_PLCRVotingCheckpoint.revealVote(new BN(0), new BN(2), saltArray[0], {from: account_investor1}) ); }); @@ -514,14 +514,14 @@ contract("PLCRVotingCheckpoint", async (accounts) => { it("\t\t Should fail to reveal vote when reveal period is over \n", async() => { await increaseTime(duration.days(5)); await catchRevert( - I_PLCRVotingCheckpoint.revealVote(new BN(0), new BN(2), saltArray[2], {from: account_investor2}) + I_PLCRVotingCheckpoint.revealVote(new BN(0), new BN(3), saltArray[2], {from: account_investor2}) ); }); it("\t\t Should check who votes whom \n", async() => { - assert.equal(((await I_PLCRVotingCheckpoint.getSelectedProposal.call(new BN(0), account_investor1))[0]).toString(), 1); - assert.equal(((await I_PLCRVotingCheckpoint.getSelectedProposal.call(new BN(0), account_investor2))[0]).toString(), 0); - assert.equal(((await I_PLCRVotingCheckpoint.getSelectedProposal.call(new BN(0), account_investor3))[0]).toString(), 0); + assert.equal(((await I_PLCRVotingCheckpoint.getSelectedProposal.call(new BN(0), account_investor1))).toString(), 2); + assert.equal(((await I_PLCRVotingCheckpoint.getSelectedProposal.call(new BN(0), account_investor2))).toString(), 0); + assert.equal(((await I_PLCRVotingCheckpoint.getSelectedProposal.call(new BN(0), account_investor3))).toString(), 1); }); it("\t\t Should give the result to 0 because ballot id is not valid", async() => { @@ -538,7 +538,7 @@ contract("PLCRVotingCheckpoint", async (accounts) => { assert.equal(web3.utils.fromWei((data[0][0]).toString()), 5000); assert.equal(web3.utils.fromWei((data[0][1]).toString()), 500); assert.equal(data[1].length, 0); - assert.equal(data[2].toString(), 0); + assert.equal(data[2].toString(), 1); assert.isTrue(data[3]); assert.equal(data[4].toString(), 2); }); From fa292dfb943857e61d6887e79b31bdd17b671e68 Mon Sep 17 00:00:00 2001 From: satyam Date: Wed, 10 Apr 2019 13:55:56 +0530 Subject: [PATCH 10/10] minor test fixes --- .../Checkpoints/PLCRVotingCheckpointFactory.sol | 4 ++-- test/zc_plcr_voting_checkpoint.js | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpointFactory.sol b/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpointFactory.sol index 00639c4be..173ed0c5b 100644 --- a/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpointFactory.sol +++ b/contracts/modules/Experimental/Checkpoints/PLCRVotingCheckpointFactory.sol @@ -32,8 +32,8 @@ contract PLCRVotingCheckpointFactory is ModuleFactory { tagsData.push("Vote"); tagsData.push("Checkpoint"); tagsData.push("PLCR"); - compatibleSTVersionRange["lowerBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); - compatibleSTVersionRange["upperBound"] = VersionUtils.pack(uint8(0), uint8(0), uint8(0)); + compatibleSTVersionRange["lowerBound"] = VersionUtils.pack(uint8(3), uint8(0), uint8(0)); + compatibleSTVersionRange["upperBound"] = VersionUtils.pack(uint8(3), uint8(0), uint8(0)); } diff --git a/test/zc_plcr_voting_checkpoint.js b/test/zc_plcr_voting_checkpoint.js index a489bb821..262e6accd 100644 --- a/test/zc_plcr_voting_checkpoint.js +++ b/test/zc_plcr_voting_checkpoint.js @@ -150,9 +150,9 @@ contract("PLCRVotingCheckpoint", async (accounts) => { let tx = await I_STRProxied.generateSecurityToken(name, symbol, tokenDetails, false, token_owner, 0, { from: token_owner }); // Verify the successful generation of the security token - assert.equal(tx.logs[2].args._ticker, symbol, "SecurityToken doesn't get deployed"); + assert.equal(tx.logs[1].args._ticker, symbol, "SecurityToken doesn't get deployed"); - I_SecurityToken = await SecurityToken.at(tx.logs[2].args._securityTokenAddress); + I_SecurityToken = await SecurityToken.at(tx.logs[1].args._securityTokenAddress); stGetter = await STGetter.at(I_SecurityToken.address); const log = (await I_SecurityToken.getPastEvents('ModuleAdded', {filter: {transactionHash: tx.transactionHash}}))[0]; // Verify that GeneralTransferManager module get added successfully or not @@ -166,7 +166,7 @@ contract("PLCRVotingCheckpoint", async (accounts) => { }); it("\t\t Should attach the voting module with the ST \n", async() => { - let tx = await I_SecurityToken.addModule(I_PLCRVotingCheckpointFactory.address, "0x0", 0, 0, {from: token_owner}); + let tx = await I_SecurityToken.addModule(I_PLCRVotingCheckpointFactory.address, "0x0", new BN(0), new BN(0), false, {from: token_owner}); assert.equal(tx.logs[2].args._types[0], checkpointKey, "Checkpoint doesn't get deployed"); assert.equal(web3.utils.hexToString(tx.logs[2].args._name), "PLCRVotingCheckpoint", "PLCRVotingCheckpoint module was not added"); I_PLCRVotingCheckpoint = await PLCRVotingCheckpoint.at(tx.logs[2].args._module); @@ -174,21 +174,21 @@ contract("PLCRVotingCheckpoint", async (accounts) => { it("\t\t Should fail to attach the voting module because allowance is unsufficent \n", async() => { await catchRevert( - I_SecurityToken.addModule(P_PLCRVotingCheckpointFactory.address, "0x0", new BN(web3.utils.toWei("500")), 0, {from: token_owner}) + I_SecurityToken.addModule(P_PLCRVotingCheckpointFactory.address, "0x0", new BN(web3.utils.toWei("500")), 0, false, {from: token_owner}) ); }); it("\t\t Should attach the voting module with the ST \n", async() => { let id = await takeSnapshot(); await I_PolyToken.transfer(I_SecurityToken.address, new BN(web3.utils.toWei("2000", "ether")), { from: token_owner }); - let tx = await I_SecurityToken.addModule(P_PLCRVotingCheckpointFactory.address, "0x0", new BN(web3.utils.toWei("2000")), 0, {from: token_owner}); + let tx = await I_SecurityToken.addModule(P_PLCRVotingCheckpointFactory.address, "0x0", new BN(web3.utils.toWei("2000")), 0, false, {from: token_owner}); assert.equal(tx.logs[3].args._types[0], checkpointKey, "Checkpoint doesn't get deployed"); assert.equal(web3.utils.hexToString(tx.logs[3].args._name), "PLCRVotingCheckpoint", "PLCRVotingCheckpoint module was not added"); await revertToSnapshot(id); }); it("\t\t Should attach the general permission manager", async() => { - let tx = await I_SecurityToken.addModule(I_GeneralPermissionManagerFactory.address, "0x0", 0, 0, {from: token_owner}); + let tx = await I_SecurityToken.addModule(I_GeneralPermissionManagerFactory.address, "0x0", 0, 0, false, {from: token_owner}); assert.equal(tx.logs[2].args._types[0], delegateManagerKey, "Permission manager doesn't get deployed"); assert.equal(web3.utils.hexToString(tx.logs[2].args._name), "GeneralPermissionManager", "GeneralPermissionManager module was not added"); I_GeneralPermissionManager = await GeneralPermissionManager.at(tx.logs[2].args._module);