Skip to content

Commit 9531fbc

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 9531fbc

File tree

1 file changed

+128
-39
lines changed

1 file changed

+128
-39
lines changed

contracts/ElectionDatabase.sol

Lines changed: 128 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,24 @@ 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__ElectionCompleted();
39+
40+
/// @notice Thrown when trying to perform an action on an archived election
41+
error ElectionDatabase__ElectionAlreadyArchived();
42+
43+
/// @notice Thrown when trying to complete an election without sufficient data
44+
error ElectionDatabase__CannotCompleteElection();
45+
3746
/// @notice Thrown when an election has no contestants/candidates enrolled
3847
error ElectionDatabase__ElectionHasNoContestant();
3948

@@ -51,6 +60,20 @@ error ElectionDatabase__InvalidAddress();
5160
* - Integration with VoterDatabase and CandidateDatabase
5261
*/
5362
contract ElectionDatabase is AdminManagement {
63+
/**
64+
* @notice Election status enumeration
65+
* @dev NEW: Election created, candidates can enroll
66+
* @dev ACTIVE: Election open for voting
67+
* @dev COMPLETED: Election closed, results calculated
68+
* @dev ARCHIVED: Election archived, no further modifications allowed
69+
*/
70+
enum ElectionStatus {
71+
NEW,
72+
ACTIVE,
73+
COMPLETED,
74+
ARCHIVED
75+
}
76+
5477
/**
5578
* @notice Stores details for a single election
5679
* @dev The registrationTimestamp serves as both a timestamp and a registration flag
@@ -67,8 +90,8 @@ contract ElectionDatabase is AdminManagement {
6790
mapping(address => address) voterToChosenCandidate;
6891
// voter -> timestamp when they voted in this specific election (0 if not voted)
6992
mapping(address => uint256) voterToVoteTimestamp;
70-
// used to track whether the election is active or not
71-
bool isActive;
93+
// used to track the current status of the election
94+
ElectionStatus status;
7295
uint256 totalVotes;
7396
// If > 0, election is registered. Acts as creation timestamp
7497
uint256 registrationTimestamp;
@@ -140,8 +163,11 @@ contract ElectionDatabase is AdminManagement {
140163
/// @notice Emitted when an election is opened for voting
141164
event ElectionOpened(uint256 indexed electionId, address indexed admin);
142165

143-
/// @notice Emitted when an election is closed
144-
event ElectionClosed(uint256 indexed electionId, address indexed admin);
166+
/// @notice Emitted when an election is completed
167+
event ElectionCompleted(uint256 indexed electionId, address indexed admin);
168+
169+
/// @notice Emitted when an election is archived
170+
event ElectionArchived(uint256 indexed electionId, address indexed admin);
145171

146172
/**
147173
* @notice Ensures the election exists
@@ -157,19 +183,29 @@ contract ElectionDatabase is AdminManagement {
157183
* @notice Ensures the election is open for voting
158184
* @param _electionId ID of the election to check
159185
*/
160-
modifier onlyOpenElection(uint256 _electionId) {
161-
if (!s_elections[_electionId].isActive)
162-
revert ElectionDatabase__ElectionClosed();
186+
modifier onlyActiveElection(uint256 _electionId) {
187+
if (s_elections[_electionId].status != ElectionStatus.ACTIVE)
188+
revert ElectionDatabase__ElectionNotActive();
189+
_;
190+
}
191+
192+
/**
193+
* @notice Ensures the election is in new state
194+
* @param _electionId ID of the election to check
195+
*/
196+
modifier onlyNewElection(uint256 _electionId) {
197+
if (s_elections[_electionId].status != ElectionStatus.NEW)
198+
revert ElectionDatabase__ElectionNotNew();
163199
_;
164200
}
165201

166202
/**
167-
* @notice Ensures the election is closed/inactive
203+
* @notice Ensures the election is not archived
168204
* @param _electionId ID of the election to check
169205
*/
170-
modifier onlyClosedElection(uint256 _electionId) {
171-
if (s_elections[_electionId].isActive)
172-
revert ElectionDatabase__ElectionActive();
206+
modifier onlyNonArchivedElection(uint256 _electionId) {
207+
if (s_elections[_electionId].status == ElectionStatus.ARCHIVED)
208+
revert ElectionDatabase__ElectionAlreadyArchived();
173209
_;
174210
}
175211

@@ -241,7 +277,7 @@ contract ElectionDatabase is AdminManagement {
241277
Election storage newElection = s_elections[electionId];
242278
newElection.name = _name;
243279
newElection.description = _description;
244-
newElection.isActive = false;
280+
newElection.status = ElectionStatus.NEW;
245281
newElection.totalVotes = 0;
246282
newElection.registrationTimestamp = block.timestamp; // Set timestamp to register the election
247283

@@ -262,7 +298,12 @@ contract ElectionDatabase is AdminManagement {
262298
uint256 _electionId,
263299
string memory _name,
264300
string memory _description
265-
) external onlyAdmin onlyRegisteredElection(_electionId) {
301+
)
302+
external
303+
onlyAdmin
304+
onlyRegisteredElection(_electionId)
305+
onlyNonArchivedElection(_electionId)
306+
{
266307
Election storage election = s_elections[_electionId];
267308

268309
election.name = _name;
@@ -305,43 +346,80 @@ contract ElectionDatabase is AdminManagement {
305346
*/
306347
function adminOpenElection(
307348
uint256 _electionId
308-
) external onlyAdmin onlyRegisteredElection(_electionId) {
349+
)
350+
external
351+
onlyAdmin
352+
onlyRegisteredElection(_electionId)
353+
onlyNewElection(_electionId)
354+
{
309355
Election storage election = s_elections[_electionId];
310356

311357
// Prevent opening elections with no candidates
312358
if (election.candidates.length == 0) {
313359
revert ElectionDatabase__ElectionHasNoContestant();
314360
}
315361

316-
election.isActive = true;
362+
election.status = ElectionStatus.ACTIVE;
317363
emit ElectionOpened(_electionId, msg.sender);
318364
}
319365

320366
/**
321-
* @notice Closes an election from voting
367+
* @notice Completes an election and calculates results
322368
* @dev Only owner/admins can call this function
323-
* @param _electionId ID of the election to close
369+
* @dev Election must be active and have candidates enrolled and votes cast
370+
* @param _electionId ID of the election to complete
324371
*/
325-
function adminCloseElection(
372+
function adminCompleteElection(
373+
uint256 _electionId
374+
)
375+
external
376+
onlyAdmin
377+
onlyRegisteredElection(_electionId)
378+
onlyActiveElection(_electionId)
379+
{
380+
Election storage election = s_elections[_electionId];
381+
382+
// Ensure election has candidates and votes to calculate a winner
383+
if (election.candidates.length == 0 || election.totalVotes == 0) {
384+
revert ElectionDatabase__CannotCompleteElection();
385+
}
386+
387+
election.status = ElectionStatus.COMPLETED;
388+
emit ElectionCompleted(_electionId, msg.sender);
389+
}
390+
391+
/**
392+
* @notice Archives an election
393+
* @dev Only owner/admins can call this function
394+
* @dev Only completed elections can be archived
395+
* @param _electionId ID of the election to archive
396+
*/
397+
function adminArchiveElection(
326398
uint256 _electionId
327399
) external onlyAdmin onlyRegisteredElection(_electionId) {
328400
Election storage election = s_elections[_electionId];
329-
election.isActive = false;
330-
emit ElectionClosed(_electionId, msg.sender);
401+
if (election.status == ElectionStatus.ARCHIVED) {
402+
revert ElectionDatabase__ElectionAlreadyArchived();
403+
}
404+
if (election.status == ElectionStatus.COMPLETED) {
405+
revert ElectionDatabase__ElectionCompleted();
406+
}
407+
election.status = ElectionStatus.ARCHIVED;
408+
emit ElectionArchived(_electionId, msg.sender);
331409
}
332410

333411
/**
334412
* @notice Allows a candidate to enroll themselves in an election
335413
* @dev Candidate must be registered in CandidateDatabase
336-
* @dev Election must be in closed state
414+
* @dev Election must be in pending state
337415
* @param _electionId ID of the election to enroll in
338416
*/
339417
function enrollCandidate(
340418
uint256 _electionId
341419
)
342420
external
343421
onlyRegisteredElection(_electionId)
344-
onlyClosedElection(_electionId)
422+
onlyNewElection(_electionId)
345423
onlyRegisteredCandidate(msg.sender)
346424
{
347425
Election storage election = s_elections[_electionId];
@@ -360,15 +438,15 @@ contract ElectionDatabase is AdminManagement {
360438

361439
/**
362440
* @notice Allows a candidate to withdraw themselves from an election
363-
* @dev Election must be in closed state
441+
* @dev Election must be in pending state
364442
* @param _electionId ID of the election to withdraw from
365443
*/
366444
function withdrawCandidate(
367445
uint256 _electionId
368446
)
369447
external
370448
onlyRegisteredElection(_electionId)
371-
onlyClosedElection(_electionId)
449+
onlyNewElection(_electionId)
372450
{
373451
Election storage election = s_elections[_electionId];
374452

@@ -404,7 +482,7 @@ contract ElectionDatabase is AdminManagement {
404482
)
405483
external
406484
onlyRegisteredElection(_electionId)
407-
onlyOpenElection(_electionId)
485+
onlyActiveElection(_electionId)
408486
onlyEnrolledCandidate(_electionId, _candidate)
409487
onlyRegisteredVoter
410488
{
@@ -439,6 +517,7 @@ contract ElectionDatabase is AdminManagement {
439517
external
440518
onlyAdmin
441519
onlyRegisteredElection(_electionId)
520+
onlyNonArchivedElection(_electionId)
442521
onlyRegisteredCandidate(_candidate)
443522
{
444523
Election storage election = s_elections[_electionId];
@@ -464,7 +543,12 @@ contract ElectionDatabase is AdminManagement {
464543
function adminWithdrawCandidate(
465544
uint256 _electionId,
466545
address _candidate
467-
) external onlyAdmin onlyRegisteredElection(_electionId) {
546+
)
547+
external
548+
onlyAdmin
549+
onlyRegisteredElection(_electionId)
550+
onlyNonArchivedElection(_electionId)
551+
{
468552
Election storage election = s_elections[_electionId];
469553

470554
// Find and remove the candidate
@@ -502,22 +586,27 @@ contract ElectionDatabase is AdminManagement {
502586
}
503587

504588
/**
505-
* @notice Returns whether the election is currently active
589+
* @notice Returns the current status of the election
506590
* @param _electionId ID of the election
507-
* @return Status of the election (true if active)
591+
* @return Status of the election
508592
*/
509593
function getElectionStatus(
510594
uint256 _electionId
511-
) external view onlyRegisteredElection(_electionId) returns (bool) {
512-
return s_elections[_electionId].isActive;
595+
)
596+
external
597+
view
598+
onlyRegisteredElection(_electionId)
599+
returns (ElectionStatus)
600+
{
601+
return s_elections[_electionId].status;
513602
}
514603

515604
/**
516605
* @notice Returns comprehensive details of an election
517606
* @param _electionId ID of the election
518607
* @return name Name of the election
519608
* @return description Description of the election
520-
* @return isActive Whether the election is currently active
609+
* @return status Current status of the election
521610
* @return candidates Array of candidate addresses enrolled in the election
522611
* @return totalVotes Total number of votes cast in the election
523612
* @return registrationTimestamp When the election was created
@@ -531,7 +620,7 @@ contract ElectionDatabase is AdminManagement {
531620
returns (
532621
string memory name,
533622
string memory description,
534-
bool isActive,
623+
ElectionStatus status,
535624
address[] memory candidates,
536625
uint256 totalVotes,
537626
uint256 registrationTimestamp
@@ -541,7 +630,7 @@ contract ElectionDatabase is AdminManagement {
541630
return (
542631
election.name,
543632
election.description,
544-
election.isActive,
633+
election.status,
545634
election.candidates,
546635
election.totalVotes,
547636
election.registrationTimestamp

0 commit comments

Comments
 (0)