JavaScript: JSHeroes and the making of a crypto challenge

Written by radu.bogdan.gaspar | Published 2017/05/27
Tech Story Tags: javascript | jsheroes | react | nodejs | cryptography

TLDRvia the TL;DR App

If you haven’t heard already, an international JavaScript conference will be taking place in Cluj-Napoca, Romania on the 8th and 9th of June 2017 …and you should be there!

It’s aptly named JSHeroes because all of the speakers have had a positive influence in this ever growing community. The schedule was created so that the topics are in a meaningful order. The time spot they occupy was also chosen based on: subject difficulty, previous talks and possible fatigue the audience might be experiencing at that particular time of day … quite a challenge.

I strongly recommend attending it as the topics chosen are aligned with the current trends and demands of the market. The subjects were also extensively vetted to ensure the best possible know-how gain for our participants. You can get a ticket here or better yet, win a ticket by completing our ongoing crypto challenge.

Conferences are fun but high level conferences which also offer you a chance to get a free ticket are even better. This is the story of the crypto challenge.

The idea first came to me on the 18th of May — 3 weeks before the conference and after clearing it with my department manager I set out to time box this project within a week. Finishing this in a week required some planning so I started doing just that:

  • step 1: go live at least 10 days before the conference …that should be sufficient time to complete all of the challenges
  • step 2: keep the UI simple something that developers in general should be familiar with; avoid complicated UX flows.
  • step 3: since the conferences target audience is technical in nature, that should also be reflected in the crypto challenge
  • step 4: raise the difficulty level incrementally but make them simple enough for someone to actually win and fun enough for them to be worth playing
  • step 5: allow users to see an overall progress of all participants… that should create a sense of urgency / pressure
  • step 6: keep the backend simple and reliable
  • step 7: create all challenges from scratch to ensure genuine solutions
  • step 8: come up with hints that are meaningful but not blatantly obvious

Right… not a big list but testing everything is crucial, I can’t have anything failing after go live and I don’t have a QA team at my disposal. One way to ensure success is to keep everything simple.

Solutions to the steps broken down into tasks:

  • step 1: don’t suck at coding
  • step 2: most developers are familiar with terminals… that’s going to be my main design approach… a terminal should also have some out of the box commands
  • step 3: have some sort of hidden registration that developers are more likely to find … better yet, make it a challenge since that’s the entire point (idea: solve the first challenge to register)
  • step 4: implement challenges around a common theme (no spoilers here)
  • step 5: birth of the first terminal command: status and since you need a way to see all of the default commands, better also implement a help command to go with that. The screen could be cluttered so better add a clear command …but if you clear and you forget the challenge description and hint you’ll need a challenge command to retrieve the current challenge details.
  • step 6: NodeJS and Express should do the trick… add a simple DB to keep track of registrations and users. Create some npm scripts for easy development running and production build and pm2 scripts to easily run it live.
  • steps 7–8: make everything rhyme while you’re at it to make it more fun (might have made things harder on myself)

The technology stack was: React, Redux, Babel, NodeJS, Express, NeDB and Webpack. Based on my plans the backend needed just these:

GET /toc #static page with terms and conditionsGET /status/:id*? # restrict to registered usersGET /challenge/:id*? # retrieves challenge for userPOST /challenge/:id*? # processes users responses

The frontend has only one job… to display the challenge and to accept user input, for that I only needed my redux store to hold backend logs (responses).

Data in the terminal will be displayed in one of the following forms:

  • header (the terminal header populated with the user email if registered)
  • description logs (usually challenges, provided by the backend)
  • status and error logs (just another log but with a status)
  • previously executed commands

We can structure these logs as follows:

{type: 'log', // or 'input', 'vspace'status: null, // or 'success', 'error'text: '',}// where vspace is an empty line (if needed)// a log can be a description or a success/error message// an input is a previously executed command// and text is the actual terminal entry text

So far so good; keeping things simple… everything is a log. Create components for the different types, add CSS for the statuses, create actions for the default known terminal commands: help, clear, status and challenge and the frontend is mostly done.

Moving on to the backend: create controllers, asses and implement the different flows for each of these routes. Let’s see what flows we have in our simple application:

GET Challenge:

The route is defined as (/challenge/:id*?) meaning the request could contain an optional ID which should represent a registered user or a registration ID. If no ID is provided, we can assume the user is unregistered and serve the first (registration) challenge.

If an ID is provided we need to check if the ID is:

  • invalid: we should return an error log,
  • a registration ID: we should return a success log with a registration message,
  • a user ID: we should return the appropriate user challenge and a message to bookmark the URL if the user is on the first challenge

POST Challenge:

The route has the same definition as for GET and the ID has the same meaning but we’re handling other things. If no ID is provided, we can assume the user is answering the first challenge and as such we should check if the answer is correct:

  • if the answer is incorrect: return an error log with a random “don’t give up” message
  • if the answer is correct: return a success message, generate a registration ID and reroute the user to that ID

If an ID is provided we need to check if the ID is:

  • invalid: we should return an error log,
  • a registration ID: here we check that: the ID is valid, the provided email is valid and not already registered; returning error messages for each of these if applicable. The user is then created and we reroute the user to a url containing his ID.
  • a user ID: here we check that: the ID is valid and that the answer is correct; returning error messages for each of these if applicable. We then return the next challenge if there are any remaining, or a winning / completion message.

GET Status:

Like the challenge requests, this one can contain an ID. If it’s a valid user ID we return the total number of players, and the number of players on each of the challenges (not including the registration challenge), otherwise we return an error log as usual.

I’ll leave you with these two diagrams describing the flows:

POST /challenge (flow)

For obvious reasons I won’t go into details on how the challenges and hints were created but that step took just as long as writing the entire code for the app. Besides, you can see them yourself if you just play the game.

We managed to go live ~13 days before the conference and the challenges can still be played after the conference is done… with the exception of the prize of course.

Play here! Enjoy.

More on JSHeroes:Building the agenda for a tech conference by Alex MoldovanJSHeroes, a story written by JavaScript believers… by Paul BrieSimple Ways to Convince Your Manager to Get You a Ticket to JSHeroes 2017 by Alex PausanJSHeroes 2017: Behind the Scenes by Gergely TudorJSHeroes 2017 Transparency Report by Alexandra Retegan


Published by HackerNoon on 2017/05/27