IITKBucks
I am mentoring a project titled IITKBucks, as a part of Programming Club’s Summer Camp 2020. In this project, we’ll explore blockchains and cryptocurrencies by building our own cryptocurrency.
Last night, I gave the students an assignment - a very simplified mining problem. The task was to take a string input from the user and find an integer nonce value such that the SHA256 hash of the concatenation of the input and the nonce is less than a fixed target.
I had been planning to start with functional programming since long, and with this assignment I had an opportunity.
Why aren’t Arch and Haskell friends?
Sadly, we can’t just do ghc filename.hs
to compile Haskell code on Arch. We need to mandatorily pass a -dynamic
flag to GHC. I haven’t compiled Haskell code on other OSes, but from what I’ve read, I believe this is an Arch-only issue.
Also, due to this same reason, cabal install
fails by default. Cabal is unable to build the libraries. So we need to update the Cabal config file. Thankfully, everything is very well documented in the ArchWiki, and I didn’t have to turn to other sources.
Beginner’s notes
There was nothing particularly despicable about Haskell (for comparison, read my blog post on Rust). Everything seemed logical. Or maybe it was the fact that since this entire paradigm of functional programming was new to me, I chose to explore and appreciate it like a maze, instead of nitpicking over minor inconveniences.
It took me some time to understand why functions with multiple parameters are declared the way they are, but the reason seemed to fit perfectly into the general ideology of the language.
I absolutely loved the show
function, perhaps because I’ve been irritated by the way other languages handle integer to string conversions :p. I also fell in love with the apply operator $
, and would like to admit that I tried to overuse it. :p
I was able to easily build two functions - getSHA256Hash
to calculate the SHA256 hash of a string, and isValid
to check if the hash is below the target. I now needed to iterate over the nonce values to try to find one that worked.
Running back to imperative languages
The above task would be pretty simple in an imperative language. I needed something like the following:
for i := 1; i <= 10000000; i++ {
if isValid(getSHA256Hash(input + to_string(i))) {
print(i)
break
}
}
But apparently, Haskell doesn’t have loops. However, searching for “for loop in haskell” led me to the functions forM
and forM_
of Control.Monad
. And I ended up writing some code like this:
forM_ [1..1000000] $ \nonce -> do
let hsh = getSHA256Hash $ input ++ show nonce
if isValid hsh
then putStrLn $ "Nonce = " ++ show nonce
else return ()
This worked well. However, since there was no break statement, it wouldn’t stop after finding a valid nonce value, and would print as many valid values as it could find, in that range. So I started looking for a break
equivalent in Haskell. Dum Dum. I could find nothing.
Eureka
Instead, somewhere on StackOverflow or Reddit, I found a comment, which asked the OP to change their code to use a recursive function. And at that moment it struck me. It seemed as if everything was crystal clear. I quickly created a recursive function:
findNonce :: String -> Integer -> Integer
findNonce input currentNonce
| isValid hsh = currentNonce
| otherwise = findNonce input (currentNonce+1)
where hsh = getSHA256Hash $ input ++ show currentNonce
Bonus 1
It uses guards !!!
Bonus 2
It compiled without an error, in the very first try. And it worked exactly as expected.
This was too much happiness for me to handle, so I took a tour of my entire house, at 4 in the morning. At that moment, it seemed to me as if these 5 lines were the most beautiful piece of code I’d ever written. :D
Conclusion
No conclusion. It’s 10 in the morning, and it’s time for me to sleep. More FP blog posts coming up later, perhaps. :p