Skip to content

Latest commit

 

History

History
779 lines (628 loc) · 31.1 KB

bdpa-elections_irv-part-1.md

File metadata and controls

779 lines (628 loc) · 31.1 KB

BDPA NHSCC 2024 Problem Statement (part 1)

See also: API documentation

Liberty speed your step, developer! BDPA Elections, Inc. has been contracted to once again build a secure electronic election system for democracy. The system will allow certain entities to manage IRV-based elections at scale without requiring voters to appear at a physical voting location.

Summary of requirements (15 total)

The app supports four authenticated user types: voters, moderators, administrators, and reporters. Users can only be one type. Voters are the most common type of user. They vote in elections and can view a complete listing of past election results. Moderators manage elections to determine which voters are eligible to vote in which elections. Administrators can create and manage other users, create and manage elections, and determine which voters/moderators can access which elections. Reporters can view the history of past election results and nothing else.

Note that time-based data in the API is represented as the number of milliseconds elapsed since January 1, 1970 00:00:00 UTC. See the API documentation for details. Further note that you must use the API to complete this problem statement, including interacting with data from other chapters, though you may consider a hybrid approach where you have your own database storing non-API data.

We're looking for feedback! If you have any opinions or ideas, contact us on Slack.

🚧 🚧 To avoid disqualification, please take note of the following:

Expand
  • Any evidence of illegal remote access to AWS WorkSpaces or any other AWS resource will result in a steep penalty or immediate disqualification.

  • Unlike PS2, PS1 is a "chapter-wide problem statement". That is: all students, coaches, and coordinators in the chapter can teach to, talk about, and collaborate on a solution. And then, when the conference comes around, your chapter sends your best five students to finish the job.

  • Your solution’s source code must be located at %USERPROFILE%\Desktop\source on your team's AWS WorkSpace. You can also have other files located elsewhere so long as they are reachable (e.g. junctioned/soft-linked) from %USERPROFILE%\Desktop\source. This is the only location judges are required to access when scoring your source code.

  • Judges must not have to type in anything other than http://127.0.0.1:3000 into the browser to reach your app.

Additionally, observe the following guidelines so your solution doesn't malfunction in front of everyone after being deployed to https://XYZ.submissions.hscc.bdpa.org:

Expand
  • Your solution’s landing page must be available at http://127.0.0.1:3000 (localhost) on your team's AWS WorkSpace.

  • Prefer relative URIs where possible instead of hardcoding your app to use something like 127.0.0.1:3000.

  • Avoid hardcoding the protocol (e.g. use //localhost:3000/some-file.png instead of http://localhost:3000/some-file.png).

  • Avoid loading resources in the browser using ports other than 3000 (e.g. running two separate web-visible servers, one on 3000 and one on 80).

  • If using websockets, stick to port 3000 and ensure your web server can handle Upgrade requests for your websocket-specific routes (e.g. localhost:3000/ws).

  • Consider deploying your solution in production mode instead of development mode, which is slower and results in a degraded UX (e.g. showing errors that would normally be hidden in production, costing you points).

Requirement 1

Your app will support 4 types of users: voter, moderator, administrator, and reporter.

Users can only have a single type at a time. You are free to create other types.

Additionally, all users must be authenticated before being allowed to interact with the system. "Guest" users are not allowed.

Voters

Moderators

  • Are authenticated users (i.e. users that have already logged in)
  • Can access their own personalized Dashboard view
  • Can access the History view

Administrators

  • Are authenticated users (i.e. users that have already logged in)
  • Can access a limited version of the Election view but cannot vote
  • Can access their own personalized Dashboard view
  • Can access the History view
  • Can access any other relevant view

The super administrator user

Note the special super user. The super user is a special administrator that can create other administrator users. The super user must always exist, even when your solution is reset to its initial state. There can only exist one such user.

Reporters

  • Are authenticated users (i.e. users that have already logged in)
  • Can view a complete paginated listing of all past (closed) elections in the system

Requirement 2

Your app will support elections.

Each election in the system has at least the following components:

  • A globally unique identifier automatically generated by the API
  • A title
  • A description
  • A unix epoch timestamp indicating when the election was created in the system
  • A unix epoch timestamp indicating when the election opens
  • A unix epoch timestamp indicating when the election closes
  • A boolean representing if the election was deleted or not
  • An array of options voters must rank from most favored to least favored
    // Example
    ['jelly', 'butter', 'peanut butter'];
  • An array of objects that maps voters to their rankings
    // Example
    // See also: https://hscc18f802d3.docs.apiary.io/#/data-structures/0/ballot
    [
      {
        voter_id: 'someThey425',
        ranking: { 'peanut butter': 1, jelly: 2, butter: 3 }
      },
      {
        voter_id: 'someThem3312',
        ranking: { butter: 1, jelly: 2, 'peanut butter': 3 }
      }
    ];

Warning: all of the above information must be stored using the API. You can cache it locally, but it must also be put into the API. Only accessing your local database without using the API will disqualify your solution.

Feel free to track any other information you deem necessary, but keep in mind extra information cannot be stored using the API.

Owned Elections

Elections that are added to the API by your team are considered "owned" by your team. Necessarily, other elections in the system are considered "unowned" by your team. All unowned elections are considered read-only at the API level. Since your users cannot otherwise interact with these elections, they only need to be shown in the History view and anywhere else explicitly noted.

Election Immutability

Once an election is closed, its results become immutable, which means: none of the information about that election can be modified by administrators and the election itself cannot be deleted by administrators.

The super user is exempt from these restrictions; they can delete closed elections or modify some parts of them; specifically: title, description, opening time, and closure timestamp. Updating the closure timestamp to a time in the future will effectively re-open a closed election, making it no different than any other open election.

Therefore, unless re-opened, the outcomes of closed elections can never be modified. This also implies that if a user voted in an election before their account was deleted or unassigned, their ballot must still count and the outcome of the election cannot change.

When using the API to update owned elections based on the actions of moderators and administrators, you must decide what edge cases like changing an election's available options, opening time, and closing time means after an election has already opened, already closed, and/or has already accepted votes.

Requirement 3

Election view: view and participate in an election.

This view allows a voter to access and participate in an election. A voter cannot use this view to access an election they have not been assigned to. This view can also be used by administrators to view the current state of an election, though they are not able to cast votes.

Similar to the History view, when viewing election results that are closed:

  • The winning option will be emphasized in the frontend UI
  • All eliminated options will be clearly marked
  • The UI will indicate by how many votes the winning option won versus the total number of votes cast
  • If the current user voted in said election, their first choice in the election will be most prominently marked

Warning: whenever you're displaying the results of an election, you must protect the identities of your voters! Do not publicly reveal who voted for which options in the UI.

Additionally, so long as the election remains open, voters are allowed to change their votes or to remove their vote and abstain. Once the election closes, voters can no longer change their minds.

Requirement 4

Dashboard view: view and interact with a personalized user dashboard.

This is the "home page" of your service for users that are authenticated, and is the view to which newly authenticated users are redirected. Each user has their own individualized dashboard.

When accessed by any user, this view will show:

  • The name of the user, like "Ray" or "Ray Tiles"
  • The IP address of the client recorded the previous time this user logged in
  • The timestamp recorded the previous time this user logged in

You are free to display any other relevant information.

The very first time a newly-created user logs in, whereupon they are redirected to this view, said user will be forced to change their password before being allowed to interact with the system.

Note that deleted elections should never be displayed to users that are not administrators. In such cases, deleted elections should be treated as if they don't exist at all.

Additionally:

When accessed by a voter, this view will show:

  • The most recent open elections the user can currently participate in
  • Closed elections they participated in
  • Upcoming elections they will eventually be eligible to participate in

When displaying non-closed elections, they will be sorted in ascending order by their opening time (i.e. elections that opened/will open earlier in time are shown first). When displaying past elections, they will be sorted in descending order by their closing time (i.e. elections that have closed later in time are shown first).

When accessed by a moderator, this view will:

  • Enable adding/removing one or more voters to/from elections they've been assigned to oversee
    • administrators assign moderators to elections

When accessed by an administrator, this view will:

  • Enable adding/removing one or more voters to/from any election
  • Enable assigning/removing one or more moderators to/from any election
  • Allow creating new voters, moderators, and reporters
    • If the administrator is also the super user, they can create new administrators as well
  • Allow viewing existing users and updating/deleting non-administrator users
    • administrators can update the personal data of any other user
    • administrators can view deleted users as well as all others
    • If the administrator is also the super user, they can delete other administrators but cannot delete themselves
  • Allow viewing, creating, updating, and deleting elections in the system
    • Elections that are not owned by your team will always be read-only at the API level. It should be clearly communicated via the UI that unowned elections cannot be updated/deleted by the administrator, only viewed
    • Elections become immutable once they close, after which they can no longer be updated unless they are re-opened

When accessed by a reporter, this view will show:

  • The most recent closed elections in the system, including those not owned by your team.
  • A link to the History view in case the user wants to see more elections.

Requirement 5

History view: view the results of all past (closed) elections.

This view makes available a complete paginated listing of all past (closed) election results in the entire system. This includes elections that are not owned by your team. This does not include deleted elections unless the user accessing this view is an administrator.

Similar to the Election view, when viewing election results:

  • The winning option will be emphasized in the frontend UI
  • All eliminated options will be clearly marked
  • The UI will indicate by how many votes the winning option won versus the total number of votes cast
  • If the current user voted in said election, their choice in the election will be most prominently marked

When displaying past elections, they will be sorted in descending order by their closing time (i.e. elections that have closed later in time are shown first) initially. Users can choose to sort results by at least the following: title, creation time, opening time, closing time.

Consider including other useful metrics and features in this view, such as allowing users to sort elections by ownership (i.e. only showing elections created by your app and not others), sorting by deleted status, etc. You might even allow for full-text searching of titles and descriptions and sort based on the results of users' searches.

To satisfy this requirement, you may find yourself making many multiple calls to the GET /elections endpoint to search through and sort all elections in the system. Consider a proper caching strategy to reduce load on the API and improve your app's performance.

Requirement 6

Auth view: authenticate existing users.

Users can use this view to authenticate (login) using their username and their password. This sensitive information is referred to as a user's credentials.

No part of the system other than this view will be accessible to unauthenticated users.

Authenticating Credentials (Login)

Your app must bring its own authentication system that allows users to login and logout at will. Users that are not logged in cannot interact with the system outside of this view.

Revoking Authentication (Logout)

Authenticated users can choose to logout, after which your app will treat them like any other unauthenticated client. Logging a user out does not require a call to the API.

Registering New Credentials

There is not an open registration feature. Only administrators can create new users. When they do, they must provide at least the following:

  • Desired username <required>
    • Must be alphanumeric (dashes and underscores are also allowed).
  • Email address <required>
  • Password <required>
    • Password strength must be indicated as well. Weak passwords will be rejected. A weak password is ≤10 characters. A moderate password is 11-17 characters. A strong password is above 17 characters.
  • The answer to a simple CAPTCHA challenge of some type <required>
    • Example: what is 2+2=?
    • Teams must not call out to any API for this. Teams must create the CAPTCHA manually.
  • City
  • State
  • Zip
  • Address

Your app must store all user information locally.

Additional Constraints

  • Usernames and email addresses must be unique within your app. That is: no two users can have the same username or email address.
  • Users will be prevented from logging in for 1 hour after 3 failed login attempts. Users will always see how many attempts they have left.
  • Users will have the option to use remember me functionality to maintain long-running authentication state. That is: if a user logs in and wants to be "remembered," they will not be logged out until they manually log out.

Requirement 7

If a user does not remember their password, they can use email to recover their account.

If a user has forgotten their login credentials, they will be able to recover their account by clicking a link in the recovery email sent to their address. Your app will then allow them to set a new password.

The app must not actually send emails out onto the internet. The sending of emails can be simulated however you want, including outputting the would-be email to the console. The app will not use an external API or service for this. For full points, ensure you document how recovery emails are simulated when submitting your solution.

Requirement 8

A navigation element containing the BDPA logo, your app title, and a subset of user data is permanently visible to users.

In every view, a navigation element is permanently visible containing the BDPA logo (downloadable here) and the title of your app.

Additionally, the following must always be visible within the navigation element:

  • The total number of elections in the system
  • The total number of open elections in the system
  • The total number of closed elections in the system

Requirement 9

When voting in an election, a voter must rank all options in order of preference. When an election closes, the winner is determined via Instant-Runoff Voting.

When an eligible voter votes in an Instant-Runoff Voting (IRV) election, they do not just cast a single vote. They must rank each option from most favored to least favored ranked 1 to N, respectively. After the election closes, the system will calculate the winner by the rules of the IRV algorithm:

  1. All voters' top choices (meaning: rank 1) are counted.
  2. If an option gets over 50% of the vote, that option is declared the winner and the election is over.
  3. If no option gets over 50% of the vote, the option with the least rank 1 votes is eliminated.
  4. Voters who had the eliminated option as their rank 1 have their vote go to their next top choice instead (meaning: their rank 2 becomes their new rank 1).
  5. Return to step 1 and repeat the process until only one option remains or an option gets more than 50% of the votes.

For example, suppose an administrator created an election titled What should we eat after the competition? The administrator adds three options to choose from: pizza, chicken, and tacos. Further suppose there were 10 eligible voters. Voter 1 ranks the options according to their tastes:

1 voter's choices
Rank Choice
1 Pizza
2 Tacos
3 Chicken

Clearly, voter 1's favorite option is Pizza, their second favorite is Tacos, and their least favorite is Chicken.

The other nine voters come up with their own ranks for the options as well. Since many of them voted similarly to each other, the 10 different voters' rankings can be summarized as the following:

4 voters voted like this
Rank Choice
1 Chicken
2 Pizza
3 Tacos
4 voters voted like this
Rank Choice
1 Pizza
2 Tacos
3 Chicken
2 voters voted like this
Rank Choice
1 Tacos
2 Pizza
3 Chicken

If we just counted who got the most rank-1 votes (like a normal election), Chicken and Pizza would be tied for first place and no one would win. However, we're using Instant-Runoff Voting!

So, since no one got above 50% of the votes (50% of 10 is 5, so a option needs 6 votes to win), we eliminate the option with the least rank-1 (first place) votes. Since Tacos only got 2 rank-1 votes, Tacos is eliminated. After running step #4 in the IRV algorithm, now the rankings look like this:

4 voters voted like this
Rank Choice
1 Chicken
2 Pizza
4 voters voted like this
Rank Choice
1 Pizza
2 Chicken
2 voters voted like this
Rank Choice
1 Pizza
2 Chicken

Chicken still has 4 votes, but now pizza has 6 votes. Since Pizza has more than 50% of the votes, the system indicates option Pizza wins the election.

There is also a YouTube video explaining Instant-Runoff Voting

Requirement 10

Users view election updates in real time; newly cast votes, open/close status, calculated winner, et cetera will appear without a page refresh.

The Election view must present the most up-to-date election state to users able to view said election. New votes, status updates such as when the election closes and a winner is selected, and any other relevant data must eventually appear within this view without the page refreshing or the user doing anything extra (like pressing a refresh button).

This type of automatic updating/revalidating of data is called asynchronous or "ajaxian" since it occurs outside the usual synchronous event flow. There are many solutions, including interval revalidation, focus revalidation, and visibility-based revalidation (i.e. updating data only for elements that are currently visible). Another solution is to use frontend timers to regularly check a source for new data every now and then.

Requirement 11

The app will be performant.

The amount of time the application takes to load and sort data, display information, and act on input is progressively penalized. That is: the teams with the fastest load times will earn the most points and the teams with the slowest load times will earn zero points from this requirement.

Average (median) is used to calculate load times. Measurements will include initial page load times and, depending on the other requirements, various other frontend UI response times.

Tail latencies (e.g. on startup with a cold cache) are ignored. Only averages are considered.

FOUC may also be penalized.

To maximize performance, consider caching (see also) the result of data processing operations and using range queries to retrieve only the data your app hasn't yet processed.

Requirement 12

Results and lists of items displayed in the frontend UI will be paginated where appropriate.

Pagination is the strategy of showing a limited number of a large set of results and providing a navigation element where users can switch to different "pages" of that large set. A Google search result (which has multiple pages) is a good example of pagination. Infinite scroll, a specific pagination implementation used by the likes of Facebook/Instagram and Twitter, is another good example.

Requirement 13

Security: no XSS, SQLI, insecure database, or other trivial security vulnerabilities.

The app will use modern software engineering practices that protect from common XSS, SQL injection, and other security vulnerabilities. Specifically: form inputs and the like will not be vulnerable to SQL injection attacks. User-generated outputs will not be vulnerable to XSS or similar attacks.

As for database security, any passwords present in the database must be hashed (not encrypted). We recommend using a salted SHA-256 hash construction or something similar. You don't need to do anything fancy. There are many tutorials for how to safely store passwords and other credentials in a database.

Passwords stored in your database in cleartext, hashed incorrectly, or re-encoded (e.g. with base64) will earn your team zero points for this requirement.

Requirement 14

The app will fail gracefully when exceptional conditions are encountered.

This includes handling API errors during fetch, login errors, random exceptions, showing spinners when content needs to load, etc.

Every so often, the API will respond with an HTTP 555 error instead of fulfilling a request. Further, API requests and responses will be manipulated by the judges in an attempt to break the app. If at any time a user is presented with a non-app error page or a completely blank screen for more than a second or so, your solution may lose points on this requirement.

Requirement 15

The frontend UI will be responsive to mobile, tablet, and desktop viewports.

The app will be pleasant to the eye when viewed on a smartphone, tablet, and a desktop viewport. The design and functionality will not "break" across these viewports nor will the app become non-functional.

Judges will view and interact with the app through emulated phone and tablet viewports. If the app breaks when viewed, it will lose points on this and other requirements. We recommend using mobile-first software design principles.