Raiding the EDO Gold Mine

A 0 to 60 journey on the Tezos Blockchain

diefvandieven
8 min readFeb 15, 2021

I recently had the pleasure of participating in a crypto challenge on the Tezos Blockchain. This was a real 0 to 60 journey for me because I had very little experience with Tezos, smart contracts, or even interacting with any blockchain outside of simple transactions prior to the challenge. So, with hopes that I can spark the same profound experience in others, here is the walk through of how I managed to raid the entire EDO Gold Mine with very little prior experience on the Tezos Blockchain.

The Announcement

The original announcement of the challenge came from Twitter and a Discord server.

CodeCrafting’s original discord message

Finding the Mine

Step one was to find the mine. There were not a whole lot of clues on how to find it other than it was on the Tezos Blockchain. This proved to not be such an issue. There were multiple ways to find the mine.

To start off I exhausted one of the few pieces of prior knowledge I had: I am probably looking for a contract. There are a lot of Tezos Blockchain Explorers out there, if you pick one that lets you look specifically at contracts like tzkt.io you will likely stumble upon the term “origination” which is the creation of the contract. We can look at all the originations in the recent passed at https://tzkt.io/originations, remember we are looking for a contract that originated with a balance of 100 XTZ.

Recent originations on TzKT

And what do we have here? Exactly what we are looking for? That almost never happens.

Another interesting way to find the mine is through a really great Tezos explorer which we will be using from here on out: better-call.dev. BCD does a bit more with its parsing of transactions and contracts and actually tags them with different annotations based on content within the transaction/contract. Super useful! So, if we went to https://better-call.dev/ and just started typing in clue words from CodeCrafting’s original post, say “mine”, we would see a contract with the annotation “%miner”, this is our contract.

So, what’s a contract?

Again, reaching into my slim bag of Tezos knowledge, I knew that a contract was like a small program sitting on the blockchain that took inputs through a transaction against the contract and performed some actions. So, we have to figure out how to interact with this contract. To do this we need to know what the contract does.

While poking around the contract in BCD, you are bound to come across the “Code” tab at the top of the contract page: https://better-call.dev/mainnet/KT1Udix7b2UUnnqSzAk6JsqDy7m1ecwTG1LB/code. This is the actual contract program. Go take a look and come back and tell me what it does! I’ll save you some time, this is Michelson code; a domain specific code used to program smart contracts on Tezos and it is definitely NOT human readable.

I suppose we could have tried to find a Michelson decompiler that would translate this code into some other, more readable language. But I think that would have been like reading through unminified JS: doable in a pinch, but not fun. There is a better option. If you keep poking around the contract, specifically the origination transaction, you will find the contents of the contract storage.

Contract storage on origination contains a pastebin URL

A pastebin URL!? That’s interesting: http://pastebin.com/KPwsQjmW.

I spent awhile here. I had never actually seen contract code before. This was clearly what we wanted, being named “Mining Challenge”, but what do we do with this? At first I thought this was code I had to execute myself to interact with the contract. Later I would figure out that this was the contract code itself.

Still thinking I had to execute this code I began my research into how to do so. <insert flashy research montage here>. Here were the sequence of topics:

  1. Pascal — The code type from pastebin said “Pascal” so let’s figure out how to execute Pascal code. Nope! Don’t do this, this code is not actually Pascal.
  2. Ligo — After more inspecting the code there is a reference to a .ligo dependency. Yes! Do look into this, more specifically PascaLIGO, which is the language of this code: https://ligolang.org/docs/intro/introduction
  3. ligolang/ligo Docker — From the installation section, get the Docker container, you will need this to easily run Ligo code.
  4. Running the whole script — Spent a lot of time trying to run the whole contract code in dry-run mode which allows you to specify the start storage state of the contract, send a transaction, and see the result. Nope! Although this would be great if you were actually developing the contract, it is not really necessary and figuring out how to get the bytes dependency and properly format the whole storage structure is difficult and not necessary.
  5. Deep dive into the script code — Yes! We just need to understand the contract and some of its key points, namely the main, verifySolution, and getMiningReward functions. Each of these will be discussed later.

This is a mining challenge!

This is when it became very clear that this was not a puzzle solving challenge like I was used to. It was a legit mining challenge. Let’s look at the critical functions:

  • main — this is the default entry point for the contract. In other words, this is how we will interact with the contract. It is pretty straight forward, it takes a mineParams parameter which is just a tuple of a nonce and a solution. It calls the mine function which does the brunt of the contract work.
  • verifySolution — this is the critical function. Taking the nonce and the solution that we provide, this function checks if we have a valid solution. If we do, the mine function is going to pay us for our hard work.
  • getMiningReward — lastly, when we have a valid solution we have to get paid. This function determines how much we get paid by looking at how difficult of a solution we submitted.

The first thing I did from here was to try and replicate the verifySolution code in a language I had a better grasp of: Python. The BLAKE2B hashing algorithm is well known and can easily be found in the the hashlib package.

The best way to replicate functionality is to rework an example we know works. When the mine contract was originated, it was quickly followed by two “test” transactions. These transactions showed valid solutions to the contract, but were not difficult enough to payout any of the funds.

A test transaction with valid nonce, solution, and challenge values.

An aside: tz1code...jrtt =? CodeCrafting the coincidence is too much to ignore!

Back to our regular schedule programming:

challenge_bytes = bytes.fromhex("2744c44c00c4d8ce3aa67d036652db22d83048c08c87f472fb8640f95ccdde7f")
nonce_bytes = bytes.fromhex("41b95d97eed87a7c141db4b445eaf489dc2d343af5331655fdd33dee184528ec")
address_bytes = bytes.fromhex("050a000000160000f7011de44aa482aa6a4c9f4bf6e56960c889088a")
print(hashlib.blake2b(challenge_bytes + nonce_bytes + address_bytes, digest_size=32).hexdigest()# 000000d206...33c6 Success!

Notes:

  • challenge_bytes — comes from the current storage value of the contract, every time a prize is mined from the contract, this value changes. So, make sure to always check!
  • nonce_bytes — this is essentially a random number. It is a guess that you check to see if the result from the hash passes the mining reward expectations.
  • address_bytes — this is tricky. This is the equivalent of Bytes.pack(Tezos.sender) from the original contract code. The easiest way to get this value is to run a version of this code in Ligo and steal the result.

Actually mining a reward

Now that we have the ability to mine a solution, there are still a couple steps to actually getting a reward.

The first step is to make sure that our solution is difficult enough to actually merit a reward. This is done by checking a simple ratio of your solution with the target value from the contracts storage.

The solution value you produce with the hashing function can be converted to one large number. You are paid a higher reward if your reward is sufficiently small as compared to the target. Since our solution is a fixed length hex string, what this means in practical terms is that your solution has to start with a certain number of zeros. A safe assumption is that you need a solution hex value that starts with a minimum of 7 zeros.

You can see now why this could be difficult. It is not possible to predict what your hash result will be given any number. So we are essentially guessing and checking our hash results until we get a result starting with 7 or more 0s.

Once you have a valid solution, the fun part starts: submitting your transaction to the contract. There are, of course, multiple ways to do this, but the easiest way that I found was directly through BCD.

  1. You need a Tezos address (and a wallet). I suggest using https://thanoswallet.com/. It is a Chrome extension wallet very similar to Metamask. Create an address (which you will use in your hashing function).
  2. You will need to start your wallet off with a small amount of funds to pay for transaction fees. You can do this how ever you want. I have a Kraken account which I used to convert some ETH to XTZ, then withdrew it to my Thanos wallet.
  3. From the contract page on BCD: https://better-call.dev/mainnet/KT1Udix7b2UUnnqSzAk6JsqDy7m1ecwTG1LB/operations, there is an amazing feature called “Interact” at the top of the page. Not only can you simulate transactions from here, you can create real ones too.
  4. On the default Call settings enter your nonce, solution, address, and a value of 0 (the contract does not require you send it funds).
  5. Click Execute. From here you can select “Wallet” which gives you lots of wallet options including Thanos. Thanos will takeover and ask you to confirm your transaction.
  6. Sit back, wait for your transaction to bake!
  7. Once the operation has been finalized and you can see it in the contract operation list, grab the new challenge value. Rinse and repeat.

Automation

Sending my first transaction to the contract was so thrilling! Seeing the 5 XTZ reward in Thanos was incredible. I had the pattern now and I could start mining as much funds as possible.

To my surprise the solutions were coming pretty quickly at times, anywhere from 5–15 minutes. But sometimes it took longer, 30–40 minutes. After mining a few blocks I realized this was going to go into the night for me, and I wanted to get some sleep. Time to start automating the process.

I found a really great library: https://pypi.org/project/pytezos/, which was easy to install and gave me essentially all the powers of using the tezos-client directly.

Long story short (it might be too late for that), as I sat there waiting for solutions to mine I was also reading into this library and automating the things I was doing by hand which included:

  • Checking the contract for a new challenge value.
  • Submitting my solutions to the contract.
  • Checking the contract totalMined value to know when to stop working.

I was able to fully automate the mining script and the last handful of solutions were mined and submitted as I slept. Sweet…

Acknowledgement

Thanks for listening to me ramble! This was an amazingly informative challenge and I want to send some great praise to CodeCrafting and the Chain of Insight folks for putting this together.

I decided to throw my work up on Github: https://github.com/diefvandieven/raid-the-edo-gold-mine.

Enjoy!

--

--