Browse Source

Refactoring, solhint

develop
Ezerous 4 years ago
parent
commit
9d32202d29
  1. 8
      packages/concordia-contracts/.solhint.json
  2. 53
      packages/concordia-contracts/contracts/Forum.sol
  3. 10
      packages/concordia-contracts/contracts/Migrations.sol
  4. 30
      packages/concordia-contracts/contracts/PostVoting.sol
  5. 45
      packages/concordia-contracts/contracts/Voting.sol
  6. 9
      packages/concordia-contracts/migrations/2_deploy_contracts.js
  7. 10
      packages/concordia-contracts/test/TestVoting.sol

8
packages/concordia-contracts/.solhint.json

@ -1,3 +1,9 @@
{ {
"extends": "solhint:default" "extends": "solhint:recommended",
"rules": {
"compiler-version": ["error","^0.7.5"],
"func-visibility": ["warn",{"ignoreConstructors" : true}],
"not-rely-on-time": "off",
"state-visibility": "off"
}
} }

53
packages/concordia-contracts/contracts/Forum.sol

@ -2,6 +2,11 @@
pragma solidity 0.7.5; pragma solidity 0.7.5;
contract Forum { contract Forum {
// Error messages for require()
string public constant USER_HAS_NOT_SIGNED_UP = "User hasn't signed up yet.";
string public constant USERNAME_TAKEN = "Username is already taken.";
string public constant TOPIC_DOES_NOT_EXIST = "Topic doesn't exist.";
string public constant POST_DOES_NOT_EXIST = "Post doesn't exist.";
//----------------------------------------USER---------------------------------------- //----------------------------------------USER----------------------------------------
struct User { struct User {
@ -19,18 +24,17 @@ contract Forum {
event UsernameUpdated(string newName, string oldName,address userAddress); event UsernameUpdated(string newName, string oldName,address userAddress);
function signUp(string memory username) public returns (bool) { function signUp(string memory username) public returns (bool) {
require (!hasUserSignedUp(msg.sender), "User has already signed up."); require (!hasUserSignedUp(msg.sender), USER_HAS_NOT_SIGNED_UP);
require(!isUserNameTaken(username), "Username is already taken."); require(!isUserNameTaken(username), USERNAME_TAKEN);
users[msg.sender] = User(username, users[msg.sender] = User(username, new uint[](0), new uint[](0), block.timestamp, true);
new uint[](0), new uint[](0), block.timestamp, true);
userAddresses[username] = msg.sender; userAddresses[username] = msg.sender;
emit UserSignedUp(username, msg.sender); emit UserSignedUp(username, msg.sender);
return true; return true;
} }
function updateUsername(string memory newUsername) public returns (bool) { function updateUsername(string memory newUsername) public returns (bool) {
require (hasUserSignedUp(msg.sender), "User hasn't signed up yet."); require (hasUserSignedUp(msg.sender), USER_HAS_NOT_SIGNED_UP);
require(!isUserNameTaken(newUsername), "Username is already taken."); require(!isUserNameTaken(newUsername), USERNAME_TAKEN);
string memory oldUsername = getUsername(msg.sender); string memory oldUsername = getUsername(msg.sender);
delete userAddresses[users[msg.sender].username]; delete userAddresses[users[msg.sender].username];
users[msg.sender].username = newUsername; users[msg.sender].username = newUsername;
@ -40,7 +44,7 @@ contract Forum {
} }
function getUsername(address userAddress) public view returns (string memory) { function getUsername(address userAddress) public view returns (string memory) {
require (hasUserSignedUp(userAddress), "User hasn't signed up yet."); require (hasUserSignedUp(userAddress), USER_HAS_NOT_SIGNED_UP);
return users[userAddress].username; return users[userAddress].username;
} }
@ -59,21 +63,20 @@ contract Forum {
} }
function getUserTopics(address userAddress) public view returns (uint[] memory) { function getUserTopics(address userAddress) public view returns (uint[] memory) {
require (hasUserSignedUp(userAddress), "User hasn't signed up yet."); require (hasUserSignedUp(userAddress), USER_HAS_NOT_SIGNED_UP);
return users[userAddress].topicIDs; return users[userAddress].topicIDs;
} }
function getUserPosts(address userAddress) public view returns (uint[] memory) { function getUserPosts(address userAddress) public view returns (uint[] memory) {
require (hasUserSignedUp(userAddress), "User hasn't signed up yet."); require (hasUserSignedUp(userAddress), USER_HAS_NOT_SIGNED_UP);
return users[userAddress].postIDs; return users[userAddress].postIDs;
} }
function getUserDateOfRegister(address userAddress) public view returns (uint) { function getUserDateOfRegister(address userAddress) public view returns (uint) {
require (hasUserSignedUp(userAddress), "User hasn't signed up yet."); require (hasUserSignedUp(userAddress), USER_HAS_NOT_SIGNED_UP);
return users[userAddress].timestamp; return users[userAddress].timestamp;
} }
//----------------------------------------POSTING---------------------------------------- //----------------------------------------POSTING----------------------------------------
struct Topic { struct Topic {
uint topicID; uint topicID;
@ -89,8 +92,8 @@ contract Forum {
uint topicID; uint topicID;
} }
uint numTopics; // Total number of topics uint public numTopics; // Total number of topics
uint numPosts; // Total number of posts uint public numPosts; // Total number of posts
mapping(uint => Topic) topics; mapping(uint => Topic) topics;
mapping(uint => Post) posts; mapping(uint => Post) posts;
@ -99,7 +102,7 @@ contract Forum {
event PostCreated(uint postID, uint topicID); event PostCreated(uint postID, uint topicID);
function createTopic() public returns (uint, uint) { function createTopic() public returns (uint, uint) {
require(hasUserSignedUp(msg.sender)); // Only registered users can create topics require(hasUserSignedUp(msg.sender), USER_HAS_NOT_SIGNED_UP);
//Creates topic //Creates topic
uint topicID = numTopics++; uint topicID = numTopics++;
topics[topicID] = Topic(topicID, msg.sender, block.timestamp, new uint[](0)); topics[topicID] = Topic(topicID, msg.sender, block.timestamp, new uint[](0));
@ -116,8 +119,8 @@ contract Forum {
} }
function createPost(uint topicID) public returns (uint) { function createPost(uint topicID) public returns (uint) {
require(hasUserSignedUp(msg.sender)); // Only registered users can create posts require(hasUserSignedUp(msg.sender), USER_HAS_NOT_SIGNED_UP);
require(topicExists(topicID)); // Only allow posting to a topic that exists require(topicExists(topicID), TOPIC_DOES_NOT_EXIST);
uint postID = numPosts++; uint postID = numPosts++;
posts[postID] = Post(postID, msg.sender, block.timestamp, topicID); posts[postID] = Post(postID, msg.sender, block.timestamp, topicID);
topics[topicID].postIDs.push(postID); topics[topicID].postIDs.push(postID);
@ -126,26 +129,16 @@ contract Forum {
return postID; return postID;
} }
// Verify that topic exists
function topicExists(uint topicID) public view returns (bool) { function topicExists(uint topicID) public view returns (bool) {
return topicID < numTopics; return topicID < numTopics;
} }
// Verify that post exists
function postExists(uint postID) public view returns (bool) { function postExists(uint postID) public view returns (bool) {
return postID < numPosts; return postID < numPosts;
} }
function getNumberOfTopics() public view returns (uint) {
return numTopics;
}
function getNumberOfPosts() public view returns (uint) {
return numPosts;
}
function getTopic(uint topicID) public view returns (address, string memory, uint, uint[] memory) { function getTopic(uint topicID) public view returns (address, string memory, uint, uint[] memory) {
require(topicExists(topicID)); require(topicExists(topicID), TOPIC_DOES_NOT_EXIST);
return ( return (
topics[topicID].author, topics[topicID].author,
users[topics[topicID].author].username, users[topics[topicID].author].username,
@ -155,17 +148,17 @@ contract Forum {
} }
function getTopicPosts(uint topicID) public view returns (uint[] memory) { function getTopicPosts(uint topicID) public view returns (uint[] memory) {
require(topicExists(topicID)); // Topic should exist require(topicExists(topicID), TOPIC_DOES_NOT_EXIST);
return topics[topicID].postIDs; return topics[topicID].postIDs;
} }
function getTopicAuthor(uint topicID) public view returns (address) { function getTopicAuthor(uint topicID) public view returns (address) {
require(topicExists(topicID)); // Topic should exist require(topicExists(topicID), TOPIC_DOES_NOT_EXIST);
return topics[topicID].author; return topics[topicID].author;
} }
function getPost(uint postID) public view returns (address, string memory, uint, uint) { function getPost(uint postID) public view returns (address, string memory, uint, uint) {
require(postExists(postID)); require(postExists(postID), POST_DOES_NOT_EXIST);
return ( return (
posts[postID].author, posts[postID].author,
users[posts[postID].author].username, users[posts[postID].author].username,

10
packages/concordia-contracts/contracts/Migrations.sol

@ -3,7 +3,7 @@ pragma solidity 0.7.5;
contract Migrations { contract Migrations {
address public owner; address public owner;
uint public last_completed_migration; uint public lastCompletedMigration;
constructor() { constructor() {
owner = msg.sender; owner = msg.sender;
@ -14,11 +14,11 @@ contract Migrations {
} }
function setCompleted(uint completed) public restricted { function setCompleted(uint completed) public restricted {
last_completed_migration = completed; lastCompletedMigration = completed;
} }
function upgrade(address new_address) public restricted { function upgrade(address newAddress) public restricted {
Migrations upgraded = Migrations(new_address); Migrations upgraded = Migrations(newAddress);
upgraded.setCompleted(last_completed_migration); upgraded.setCompleted(lastCompletedMigration);
} }
} }

30
packages/concordia-contracts/contracts/PostVoting.sol

@ -10,13 +10,7 @@ contract PostVoting {
forum = Forum(addr); forum = Forum(addr);
} }
enum Option { NONE, UP, DOWN } // NONE -> 0, UP -> 1, DOWN -> 2 enum Option { DEFAULT, UP, DOWN } // DEFAULT -> 0, UP -> 1, DOWN -> 2
Option constant defaultOption = Option.NONE;
function getDefaultChoice() public pure returns (uint) {
return uint(defaultOption);
}
struct PostBallot { struct PostBallot {
mapping(address => Option) votes; mapping(address => Option) votes;
@ -28,13 +22,13 @@ contract PostVoting {
event UserVotedPost(address userAddress, uint postID, Option option); event UserVotedPost(address userAddress, uint postID, Option option);
function getVote(uint postID, address voter) public view returns (Option) { function getVote(uint postID, address voter) public view returns (Option) {
require(forum.postExists(postID)); require(forum.postExists(postID), forum.POST_DOES_NOT_EXIST());
return postBallots[postID].votes[voter]; return postBallots[postID].votes[voter];
} }
// Gets vote count for a specific option (Option.UP/ Option.DOWN) // Gets vote count for a specific option (Option.UP/ Option.DOWN only!)
function getVoteCount(uint postID, Option option) private view returns (uint) { function getVoteCount(uint postID, Option option) private view returns (uint) {
require(forum.postExists(postID)); require(forum.postExists(postID), forum.POST_DOES_NOT_EXIST());
return (postBallots[postID].voters[option].length); return (postBallots[postID].voters[option].length);
} }
@ -48,7 +42,7 @@ contract PostVoting {
// Gets voters for a specific option (Option.UP/ Option.DOWN) // Gets voters for a specific option (Option.UP/ Option.DOWN)
function getVoters(uint postID, Option option) private view returns (address[] memory) { function getVoters(uint postID, Option option) private view returns (address[] memory) {
require(forum.postExists(postID)); require(forum.postExists(postID), forum.POST_DOES_NOT_EXIST());
return (postBallots[postID].voters[option]); return (postBallots[postID].voters[option]);
} }
@ -61,8 +55,8 @@ contract PostVoting {
} }
function getVoterIndex(uint postID, address voter) private view returns (uint) { function getVoterIndex(uint postID, address voter) private view returns (uint) {
require(forum.hasUserSignedUp(voter)); require(forum.hasUserSignedUp(voter), forum.USER_HAS_NOT_SIGNED_UP());
require(forum.postExists(postID)); require(forum.postExists(postID), forum.POST_DOES_NOT_EXIST());
PostBallot storage postBallot = postBallots[postID]; PostBallot storage postBallot = postBallots[postID];
Option votedOption = getVote(postID, voter); Option votedOption = getVote(postID, voter);
@ -76,8 +70,8 @@ contract PostVoting {
} }
function vote(uint postID, Option option) private { function vote(uint postID, Option option) private {
require(forum.hasUserSignedUp(msg.sender)); require(forum.hasUserSignedUp(msg.sender), forum.USER_HAS_NOT_SIGNED_UP());
require(forum.postExists(postID)); // Only allow voting if post exists require(forum.postExists(postID), forum.POST_DOES_NOT_EXIST());
PostBallot storage postBallot = postBallots[postID]; PostBallot storage postBallot = postBallots[postID];
address voter = msg.sender; address voter = msg.sender;
@ -87,7 +81,7 @@ contract PostVoting {
return; return;
// Remove previous vote if exists // Remove previous vote if exists
if(prevOption != Option.NONE){ if(prevOption != Option.DEFAULT){
uint voterIndex = getVoterIndex(postID, voter); uint voterIndex = getVoterIndex(postID, voter);
// Swap with last voter address and delete vote // Swap with last voter address and delete vote
postBallot.voters[prevOption][voterIndex] = postBallot.voters[prevOption][postBallot.voters[prevOption].length - 1]; postBallot.voters[prevOption][voterIndex] = postBallot.voters[prevOption][postBallot.voters[prevOption].length - 1];
@ -95,7 +89,7 @@ contract PostVoting {
} }
// Add new vote // Add new vote
if(option != Option.NONE) if(option != Option.DEFAULT)
postBallot.voters[option].push(voter); postBallot.voters[option].push(voter);
postBallot.votes[voter] = option; postBallot.votes[voter] = option;
emit UserVotedPost(voter, postID, option); emit UserVotedPost(voter, postID, option);
@ -110,6 +104,6 @@ contract PostVoting {
} }
function unvote(uint postID) public{ function unvote(uint postID) public{
vote(postID, Option.NONE); vote(postID, Option.DEFAULT);
} }
} }

45
packages/concordia-contracts/contracts/Voting.sol

@ -4,6 +4,13 @@ pragma solidity 0.7.5;
import "./Forum.sol"; import "./Forum.sol";
contract Voting { contract Voting {
// Error messages for require()
string constant TOPIC_POLL_DIFFERENT_CREATOR = "Only topic's author can create a poll.";
string constant POLL_EXISTS = "Poll already exists.";
string constant POLL_DOES_NOT_EXIST = "Poll does not exist.";
string constant INVALID_OPTION = "Invalid option.";
string constant USER_HAS_NOT_VOTED = "User hasn't voted.";
Forum public forum; Forum public forum;
constructor(Forum addr) { constructor(Forum addr) {
@ -25,7 +32,6 @@ contract Voting {
event PollCreated(uint topicID); event PollCreated(uint topicID);
event UserVotedPoll(address userAddress, uint topicID, uint vote); event UserVotedPoll(address userAddress, uint topicID, uint vote);
// Verifies that a poll exists
function pollExists(uint topicID) public view returns (bool) { function pollExists(uint topicID) public view returns (bool) {
if (polls[topicID].timestamp != 0) if (polls[topicID].timestamp != 0)
return true; return true;
@ -33,10 +39,10 @@ contract Voting {
} }
function createPoll(uint topicID, uint numOptions, string memory dataHash, bool enableVoteChanges) public returns (uint) { function createPoll(uint topicID, uint numOptions, string memory dataHash, bool enableVoteChanges) public returns (uint) {
require(forum.hasUserSignedUp(msg.sender)); // Only registered users can create polls require(forum.hasUserSignedUp(msg.sender), forum.USER_HAS_NOT_SIGNED_UP());
require(forum.topicExists(topicID)); // Only allow poll creation if topic exists require(forum.topicExists(topicID), forum.TOPIC_DOES_NOT_EXIST());
require (forum.getTopicAuthor(topicID) == msg.sender); // Only allow poll creation from the author of the topic require (forum.getTopicAuthor(topicID) == msg.sender, TOPIC_POLL_DIFFERENT_CREATOR);
require(!pollExists(topicID)); // Only allow poll creation if it doesn't already exist require(!pollExists(topicID), POLL_EXISTS);
Poll storage poll = polls[topicID]; Poll storage poll = polls[topicID];
poll.topicID = topicID; poll.topicID = topicID;
@ -50,7 +56,7 @@ contract Voting {
} }
function getPollInfo(uint topicID) public view returns (uint, string memory, uint, uint) { function getPollInfo(uint topicID) public view returns (uint, string memory, uint, uint) {
require(pollExists(topicID)); require(pollExists(topicID), POLL_DOES_NOT_EXIST);
uint totalVotes = getTotalVotes(topicID); uint totalVotes = getTotalVotes(topicID);
@ -63,33 +69,32 @@ contract Voting {
} }
function isOptionValid(uint topicID, uint option) public view returns (bool) { function isOptionValid(uint topicID, uint option) public view returns (bool) {
require(pollExists(topicID)); require(pollExists(topicID), POLL_DOES_NOT_EXIST);
if (option <= polls[topicID].numOptions) // Option 0 is valid as well (no option chosen) if (option <= polls[topicID].numOptions) // Option 0 is valid as well (no option chosen)
return true; return true;
return false; return false;
} }
function hasVoted(uint topicID, address voter) public view returns (bool) { function hasVoted(uint topicID, address voter) public view returns (bool) {
require(pollExists(topicID)); require(pollExists(topicID), POLL_DOES_NOT_EXIST);
if (polls[topicID].votes[voter] != 0) if (polls[topicID].votes[voter] != 0)
return true; return true;
return false; return false;
} }
function getVote(uint topicID, address voter) public view returns (uint) { function getVote(uint topicID, address voter) public view returns (uint) {
require(hasVoted(topicID, voter)); require(hasVoted(topicID, voter), USER_HAS_NOT_VOTED);
return polls[topicID].votes[voter]; return polls[topicID].votes[voter];
} }
// Gets vote count for a specific option
function getVoteCount(uint topicID, uint option) public view returns (uint) { function getVoteCount(uint topicID, uint option) public view returns (uint) {
require(pollExists(topicID)); require(pollExists(topicID), POLL_DOES_NOT_EXIST);
require(isOptionValid(topicID, option)); require(isOptionValid(topicID, option), INVALID_OPTION);
return (polls[topicID].voters[option].length); return (polls[topicID].voters[option].length);
} }
function getTotalVotes(uint topicID) public view returns (uint) { function getTotalVotes(uint topicID) public view returns (uint) {
require(pollExists(topicID)); require(pollExists(topicID), POLL_DOES_NOT_EXIST);
Poll storage poll = polls[topicID]; Poll storage poll = polls[topicID];
uint totalVotes = 0; uint totalVotes = 0;
@ -102,13 +107,13 @@ contract Voting {
// Gets voters for a specific option // Gets voters for a specific option
function getVoters(uint topicID, uint option) public view returns (address[] memory) { function getVoters(uint topicID, uint option) public view returns (address[] memory) {
require(pollExists(topicID)); require(pollExists(topicID), POLL_DOES_NOT_EXIST);
return (polls[topicID].voters[option]); return (polls[topicID].voters[option]);
} }
function getVoterIndex(uint topicID, address voter) public view returns (uint) { function getVoterIndex(uint topicID, address voter) public view returns (uint) {
require(pollExists(topicID)); require(pollExists(topicID), POLL_DOES_NOT_EXIST);
require(hasVoted(topicID, voter)); require(hasVoted(topicID, voter), USER_HAS_NOT_VOTED);
Poll storage poll = polls[topicID]; Poll storage poll = polls[topicID];
uint votedOption = getVote(topicID, voter); uint votedOption = getVote(topicID, voter);
address[] storage optionVoters = poll.voters[votedOption]; address[] storage optionVoters = poll.voters[votedOption];
@ -121,16 +126,16 @@ contract Voting {
} }
function vote(uint topicID, uint option) public { function vote(uint topicID, uint option) public {
require(forum.hasUserSignedUp(msg.sender)); require(forum.hasUserSignedUp(msg.sender), forum.USER_HAS_NOT_SIGNED_UP());
require(pollExists(topicID)); require(pollExists(topicID), POLL_DOES_NOT_EXIST);
require(isOptionValid(topicID, option)); require(isOptionValid(topicID, option), INVALID_OPTION);
Poll storage poll = polls[topicID]; Poll storage poll = polls[topicID];
address voter = msg.sender; address voter = msg.sender;
uint prevOption = poll.votes[voter]; uint prevOption = poll.votes[voter];
if(prevOption == option) if(prevOption == option)
return; return;
// Voter hadn't voted before // Voter hasn't voted before
if(prevOption == 0){ if(prevOption == 0){
poll.voters[option].push(voter); poll.voters[option].push(voter);
poll.votes[voter] = option; poll.votes[voter] = option;

9
packages/concordia-contracts/migrations/2_deploy_contracts.js

@ -4,8 +4,9 @@ const PostVoting = artifacts.require('PostVoting');
// eslint-disable-next-line func-names // eslint-disable-next-line func-names
module.exports = function (deployer) { module.exports = function (deployer) {
deployer.deploy(Forum).then(async (forum) => { return deployer.deploy(Forum)
await deployer.deploy(Voting, forum.address); .then(async (forum) => Promise.all([
await deployer.deploy(PostVoting, forum.address); deployer.deploy(Voting, forum.address),
}); deployer.deploy(PostVoting, forum.address),
]));
}; };

10
packages/concordia-contracts/test/TestVoting.sol

@ -13,7 +13,7 @@ contract TestVoting {
function beforeAll() public { function beforeAll() public {
forum = Forum(DeployedAddresses.Forum()); forum = Forum(DeployedAddresses.Forum());
forum.signUp('testAccount'); forum.signUp("testAccount");
(firstTopicId,) = forum.createTopic(); (firstTopicId,) = forum.createTopic();
} }
@ -28,7 +28,7 @@ contract TestVoting {
function testCreatePoll() public { function testCreatePoll() public {
Voting voting = Voting(DeployedAddresses.Voting()); Voting voting = Voting(DeployedAddresses.Voting());
uint actual = voting.createPoll(firstTopicId, 3, 'asdf', false); uint actual = voting.createPoll(firstTopicId, 3, "asdf", false);
Assert.equal(actual, firstTopicId, "Topic Id should be 1"); Assert.equal(actual, firstTopicId, "Topic Id should be 1");
} }
@ -47,7 +47,7 @@ contract TestVoting {
(uint actualNumberOfOptions, string memory actualDataHash, , uint actualNumberOfVotes) = voting.getPollInfo(firstTopicId); (uint actualNumberOfOptions, string memory actualDataHash, , uint actualNumberOfVotes) = voting.getPollInfo(firstTopicId);
Assert.equal(actualNumberOfOptions, 3, "Number of votes should be 0"); Assert.equal(actualNumberOfOptions, 3, "Number of votes should be 0");
Assert.equal(actualDataHash, 'asdf', "Number of votes should be 0"); Assert.equal(actualDataHash, "asdf", "Number of votes should be 0");
Assert.equal(actualNumberOfVotes, 0, "Number of votes should be 0"); Assert.equal(actualNumberOfVotes, 0, "Number of votes should be 0");
} }
@ -76,7 +76,7 @@ contract TestVoting {
Voting voting = Voting(DeployedAddresses.Voting()); Voting voting = Voting(DeployedAddresses.Voting());
(uint topicId,) = forum.createTopic(); (uint topicId,) = forum.createTopic();
voting.createPoll(topicId, 3, 'asdf', false); voting.createPoll(topicId, 3, "asdf", false);
voting.vote(topicId, 1); voting.vote(topicId, 1);
uint actualVotesOption0 = voting.getVoteCount(topicId, 1); uint actualVotesOption0 = voting.getVoteCount(topicId, 1);
@ -95,7 +95,7 @@ contract TestVoting {
Voting voting = Voting(DeployedAddresses.Voting()); Voting voting = Voting(DeployedAddresses.Voting());
(uint topicId,) = forum.createTopic(); (uint topicId,) = forum.createTopic();
voting.createPoll(topicId, 3, 'asdf', true); voting.createPoll(topicId, 3, "asdf", true);
voting.vote(topicId, 1); voting.vote(topicId, 1);
uint actualVotesOption0 = voting.getVoteCount(topicId, 1); uint actualVotesOption0 = voting.getVoteCount(topicId, 1);

Loading…
Cancel
Save