Skip to content

Commit 0507ca0

Browse files
committed
contracts/ElectionDatabase: isActive (bool) -> status (enum)
NEW, // allows enrolling candidates ACTIVE, // enrolling closed, voting opened COMPLETED, // voting closed ARCHIVED // everything closed Additionally rename some errors Signed-off-by: John Titor <[email protected]>
1 parent 2c9675f commit 0507ca0

File tree

1 file changed

+148
-40
lines changed

1 file changed

+148
-40
lines changed

contracts/ElectionDatabase.sol

Lines changed: 148 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,27 @@ error ElectionDatabase__CandidateNotRegistered();
2525
/// @notice Thrown when a candidate is already registered in the election
2626
error ElectionDatabase__CandidateAlreadyEnrolled();
2727

28-
/// @notice Thrown when a restricted action is attempted during an active election
29-
error ElectionDatabase__ElectionActive();
30-
31-
/// @notice Thrown when an election is not currently accepting votes
32-
error ElectionDatabase__ElectionClosed();
33-
3428
/// @notice Thrown when the requested election does not exist
3529
error ElectionDatabase__ElectionNotFound();
3630

31+
/// @notice Thrown when the election status is not new
32+
error ElectionDatabase__ElectionNotNew();
33+
34+
/// @notice Thrown when a restricted action is attempted during an active election, like enrolling a candidate
35+
error ElectionDatabase__ElectionNotActive();
36+
37+
/// @notice Thrown when an election has already been completed
38+
error ElectionDatabase__ElectionAlreadyCompleted();
39+
40+
/// @notice Thrown when an election is not yet completed
41+
error ElectionDatabase__ElectionNotCompleted();
42+
43+
/// @notice Thrown when trying to perform an action on an archived election
44+
error ElectionDatabase__ElectionAlreadyArchived();
45+
46+
/// @notice Thrown when trying to complete an election without sufficient data
47+
error ElectionDatabase__CannotCompleteElection();
48+
3749
/// @notice Thrown when an election has no contestants/candidates enrolled
3850
error ElectionDatabase__ElectionHasNoContestant();
3951

@@ -51,6 +63,20 @@ error ElectionDatabase__InvalidAddress();
5163
* - Integration with VoterDatabase and CandidateDatabase
5264
*/
5365
contract ElectionDatabase is AdminManagement {
66+
/**
67+
* @notice Election status enumeration
68+
* @dev NEW: Election created, candidates can enroll
69+
* @dev ACTIVE: Election open for voting
70+
* @dev COMPLETED: Election closed, results calculated
71+
* @dev ARCHIVED: Election archived, no further modifications allowed
72+
*/
73+
enum ElectionStatus {
74+
NEW,
75+
ACTIVE,
76+
COMPLETED,
77+
ARCHIVED
78+
}
79+
5480
/**
5581
* @notice Stores details for a single election
5682
* @dev The registrationTimestamp serves as both a timestamp and a registration flag
@@ -67,8 +93,8 @@ contract ElectionDatabase is AdminManagement {
6793
mapping(address => address) voterToChosenCandidate;
6894
// voter -> timestamp when they voted in this specific election (0 if not voted)
6995
mapping(address => uint256) voterToVoteTimestamp;
70-
// used to track whether the election is active or not
71-
bool isActive;
96+
// used to track the current status of the election
97+
ElectionStatus status;
7298
uint256 totalVotes;
7399
// If > 0, election is registered. Acts as creation timestamp
74100
uint256 registrationTimestamp;
@@ -140,8 +166,11 @@ contract ElectionDatabase is AdminManagement {
140166
/// @notice Emitted when an election is opened for voting
141167
event ElectionOpened(uint256 indexed electionId, address indexed admin);
142168

143-
/// @notice Emitted when an election is closed
144-
event ElectionClosed(uint256 indexed electionId, address indexed admin);
169+
/// @notice Emitted when an election is completed
170+
event ElectionCompleted(uint256 indexed electionId, address indexed admin);
171+
172+
/// @notice Emitted when an election is archived
173+
event ElectionArchived(uint256 indexed electionId, address indexed admin);
145174

146175
/**
147176
* @notice Ensures the election exists
@@ -153,23 +182,43 @@ contract ElectionDatabase is AdminManagement {
153182
_;
154183
}
155184

185+
/**
186+
* @notice Ensures the election is in new state
187+
* @param _electionId ID of the election to check
188+
*/
189+
modifier onlyNewElection(uint256 _electionId) {
190+
if (s_elections[_electionId].status != ElectionStatus.NEW)
191+
revert ElectionDatabase__ElectionNotNew();
192+
_;
193+
}
194+
156195
/**
157196
* @notice Ensures the election is open for voting
158197
* @param _electionId ID of the election to check
159198
*/
160-
modifier onlyOpenElection(uint256 _electionId) {
161-
if (!s_elections[_electionId].isActive)
162-
revert ElectionDatabase__ElectionClosed();
199+
modifier onlyActiveElection(uint256 _electionId) {
200+
if (s_elections[_electionId].status != ElectionStatus.ACTIVE)
201+
revert ElectionDatabase__ElectionNotActive();
163202
_;
164203
}
165204

166205
/**
167-
* @notice Ensures the election is closed/inactive
206+
* @notice Ensures the election is not archived
168207
* @param _electionId ID of the election to check
169208
*/
170-
modifier onlyClosedElection(uint256 _electionId) {
171-
if (s_elections[_electionId].isActive)
172-
revert ElectionDatabase__ElectionActive();
209+
modifier onlyNonArchivedElection(uint256 _electionId) {
210+
if (s_elections[_electionId].status == ElectionStatus.ARCHIVED)
211+
revert ElectionDatabase__ElectionAlreadyArchived();
212+
_;
213+
}
214+
215+
/**
216+
* @notice Ensures the election is completed
217+
* @param _electionId ID of the election to check
218+
*/
219+
modifier onlyCompletedElection(uint256 _electionId) {
220+
if (s_elections[_electionId].status != ElectionStatus.COMPLETED)
221+
revert ElectionDatabase__ElectionNotCompleted();
173222
_;
174223
}
175224

@@ -241,7 +290,7 @@ contract ElectionDatabase is AdminManagement {
241290
Election storage newElection = s_elections[electionId];
242291
newElection.name = _name;
243292
newElection.description = _description;
244-
newElection.isActive = false;
293+
newElection.status = ElectionStatus.NEW;
245294
newElection.totalVotes = 0;
246295
newElection.registrationTimestamp = block.timestamp; // Set timestamp to register the election
247296

@@ -262,7 +311,12 @@ contract ElectionDatabase is AdminManagement {
262311
uint256 _electionId,
263312
string memory _name,
264313
string memory _description
265-
) external onlyAdmin onlyRegisteredElection(_electionId) {
314+
)
315+
external
316+
onlyAdmin
317+
onlyRegisteredElection(_electionId)
318+
onlyNonArchivedElection(_electionId)
319+
{
266320
Election storage election = s_elections[_electionId];
267321

268322
election.name = _name;
@@ -305,43 +359,80 @@ contract ElectionDatabase is AdminManagement {
305359
*/
306360
function adminOpenElection(
307361
uint256 _electionId
308-
) external onlyAdmin onlyRegisteredElection(_electionId) {
362+
)
363+
external
364+
onlyAdmin
365+
onlyRegisteredElection(_electionId)
366+
onlyNewElection(_electionId)
367+
{
309368
Election storage election = s_elections[_electionId];
310369

311370
// Prevent opening elections with no candidates
312371
if (election.candidates.length == 0) {
313372
revert ElectionDatabase__ElectionHasNoContestant();
314373
}
315374

316-
election.isActive = true;
375+
election.status = ElectionStatus.ACTIVE;
317376
emit ElectionOpened(_electionId, msg.sender);
318377
}
319378

320379
/**
321-
* @notice Closes an election from voting
380+
* @notice Completes an election and calculates results
381+
* @dev Only owner/admins can call this function
382+
* @dev Election must be active and have candidates enrolled and votes cast
383+
* @param _electionId ID of the election to complete
384+
*/
385+
function adminCompleteElection(
386+
uint256 _electionId
387+
)
388+
external
389+
onlyAdmin
390+
onlyRegisteredElection(_electionId)
391+
onlyActiveElection(_electionId)
392+
{
393+
Election storage election = s_elections[_electionId];
394+
395+
// Ensure election has candidates and votes to calculate a winner
396+
if (election.candidates.length == 0 || election.totalVotes == 0) {
397+
revert ElectionDatabase__CannotCompleteElection();
398+
}
399+
400+
election.status = ElectionStatus.COMPLETED;
401+
emit ElectionCompleted(_electionId, msg.sender);
402+
}
403+
404+
/**
405+
* @notice Archives an election
322406
* @dev Only owner/admins can call this function
323-
* @param _electionId ID of the election to close
407+
* @dev Only completed elections can be archived
408+
* @param _electionId ID of the election to archive
324409
*/
325-
function adminCloseElection(
410+
function adminArchiveElection(
326411
uint256 _electionId
327412
) external onlyAdmin onlyRegisteredElection(_electionId) {
328413
Election storage election = s_elections[_electionId];
329-
election.isActive = false;
330-
emit ElectionClosed(_electionId, msg.sender);
414+
if (election.status == ElectionStatus.ARCHIVED) {
415+
revert ElectionDatabase__ElectionAlreadyArchived();
416+
}
417+
if (election.status == ElectionStatus.COMPLETED) {
418+
revert ElectionDatabase__ElectionAlreadyCompleted();
419+
}
420+
election.status = ElectionStatus.ARCHIVED;
421+
emit ElectionArchived(_electionId, msg.sender);
331422
}
332423

333424
/**
334425
* @notice Allows a candidate to enroll themselves in an election
335426
* @dev Candidate must be registered in CandidateDatabase
336-
* @dev Election must be in closed state
427+
* @dev Election must be in pending state
337428
* @param _electionId ID of the election to enroll in
338429
*/
339430
function enrollCandidate(
340431
uint256 _electionId
341432
)
342433
external
343434
onlyRegisteredElection(_electionId)
344-
onlyClosedElection(_electionId)
435+
onlyNewElection(_electionId)
345436
onlyRegisteredCandidate(msg.sender)
346437
{
347438
Election storage election = s_elections[_electionId];
@@ -360,15 +451,15 @@ contract ElectionDatabase is AdminManagement {
360451

361452
/**
362453
* @notice Allows a candidate to withdraw themselves from an election
363-
* @dev Election must be in closed state
454+
* @dev Election must be in pending state
364455
* @param _electionId ID of the election to withdraw from
365456
*/
366457
function withdrawCandidate(
367458
uint256 _electionId
368459
)
369460
external
370461
onlyRegisteredElection(_electionId)
371-
onlyClosedElection(_electionId)
462+
onlyNewElection(_electionId)
372463
{
373464
Election storage election = s_elections[_electionId];
374465

@@ -404,7 +495,7 @@ contract ElectionDatabase is AdminManagement {
404495
)
405496
external
406497
onlyRegisteredElection(_electionId)
407-
onlyOpenElection(_electionId)
498+
onlyActiveElection(_electionId)
408499
onlyEnrolledCandidate(_electionId, _candidate)
409500
onlyRegisteredVoter
410501
{
@@ -439,6 +530,7 @@ contract ElectionDatabase is AdminManagement {
439530
external
440531
onlyAdmin
441532
onlyRegisteredElection(_electionId)
533+
onlyNonArchivedElection(_electionId)
442534
onlyRegisteredCandidate(_candidate)
443535
{
444536
Election storage election = s_elections[_electionId];
@@ -464,7 +556,12 @@ contract ElectionDatabase is AdminManagement {
464556
function adminWithdrawCandidate(
465557
uint256 _electionId,
466558
address _candidate
467-
) external onlyAdmin onlyRegisteredElection(_electionId) {
559+
)
560+
external
561+
onlyAdmin
562+
onlyRegisteredElection(_electionId)
563+
onlyNonArchivedElection(_electionId)
564+
{
468565
Election storage election = s_elections[_electionId];
469566

470567
// Find and remove the candidate
@@ -502,22 +599,27 @@ contract ElectionDatabase is AdminManagement {
502599
}
503600

504601
/**
505-
* @notice Returns whether the election is currently active
602+
* @notice Returns the current status of the election
506603
* @param _electionId ID of the election
507-
* @return Status of the election (true if active)
604+
* @return Status of the election
508605
*/
509606
function getElectionStatus(
510607
uint256 _electionId
511-
) external view onlyRegisteredElection(_electionId) returns (bool) {
512-
return s_elections[_electionId].isActive;
608+
)
609+
external
610+
view
611+
onlyRegisteredElection(_electionId)
612+
returns (ElectionStatus)
613+
{
614+
return s_elections[_electionId].status;
513615
}
514616

515617
/**
516618
* @notice Returns comprehensive details of an election
517619
* @param _electionId ID of the election
518620
* @return name Name of the election
519621
* @return description Description of the election
520-
* @return isActive Whether the election is currently active
622+
* @return status Current status of the election
521623
* @return candidates Array of candidate addresses enrolled in the election
522624
* @return totalVotes Total number of votes cast in the election
523625
* @return registrationTimestamp When the election was created
@@ -531,7 +633,7 @@ contract ElectionDatabase is AdminManagement {
531633
returns (
532634
string memory name,
533635
string memory description,
534-
bool isActive,
636+
ElectionStatus status,
535637
address[] memory candidates,
536638
uint256 totalVotes,
537639
uint256 registrationTimestamp
@@ -541,7 +643,7 @@ contract ElectionDatabase is AdminManagement {
541643
return (
542644
election.name,
543645
election.description,
544-
election.isActive,
646+
election.status,
545647
election.candidates,
546648
election.totalVotes,
547649
election.registrationTimestamp
@@ -598,7 +700,13 @@ contract ElectionDatabase is AdminManagement {
598700
*/
599701
function getWinner(
600702
uint256 _electionId
601-
) external view onlyRegisteredElection(_electionId) returns (address) {
703+
)
704+
external
705+
view
706+
onlyRegisteredElection(_electionId)
707+
onlyCompletedElection(_electionId)
708+
returns (address)
709+
{
602710
Election storage election = s_elections[_electionId];
603711
uint256 maxVotes = 0;
604712
address winnerAddress = address(0);

0 commit comments

Comments
 (0)