Just as any other project, smart contracts will need refactoring during their development. In this part, the way the winner is chosen will be refactored.
Everyone, by reading the code, can see that the winning ticket is
407 mod Set.size(store.players). By tampering with the number of bought tickets, it is easy for anyone to get the winning ticket. In this part, we will make it harder to guess the winning ticker number. However, that method is not fully secured as well. This refactoring is for educational purposes, to show some advanced features of LIGO and is NOT to be used in production.
This part is an opportunity to put the emphasis on two modules:
Bytes module handles binary format for serialization, it converts Michelson structures into a binary format (and the reverse), concatenates two bytes. You can find a full reference here
Crypto module performs a few basic operations such as hashing and verifying signatures. You can find a full reference here.
Here is the procedure:
- The administrator will choose a large random number and keep it to himself.
- He hashes it and sends the hash when he calls the
- This hash is saved into the storage.
- The administrator reveals his secret (random large number) when calling the
- The smart contract hashes this number and checks that it matches the storage hash. If it does, it uses this number to pick the winner just as before.
As warned above, this method is still filled with loopholes:
- the administrator knows the secret number and can tamper with the number of bought tickets to get the winning one.
- everyone can try to brute-force the hash in order to find what number yielded this hash.
This method only makes it a little harder to guess the number.
OpenRaffle entrypoint expects a new input: the number hash, that should be saved into the storage. Both the storage and entrypoint have to be modified. The method is very similar to what has been done before:
- Refactoring the storage: it must store a hash. According to the LIGO documentation, a hash has a
- Adding the new input in the openRaffleParameter. The bytes type is added in the tuple:
- Updating the entrypoint function header:
- Refactoring the entrypoint logic. For this change, the only thing to do is to save the hash in the storage:
- The new input has to be processed in the control flow:
You can compile the smart contract with:
The method is the same here. So the step-by-step changes won't be detailed.
Try to do this refactoring as an exercice. The LIGO documentation will tell you how to hash a number and compare it. Once you're done with your smart contract refactoring, you can compare it with our suggested version:
LIGO is meant for smart contract development and always yields a Michelson code. The method for developing such smart contracts is pretty much always the same, and follows an order very close to the Michelson smart contract structure containing:
- the parameter (or entrypoints): the entrypoints are defined into a variant, a type is defined for the input entrypoints.
- the storage: the storage is defined as a type, usually a record.
- the code: the main function dispatches the actions using a pattern matching. The logic for each entrypoint is implemented in a function.
There needs to be a main function, which dispatches the actions of the smart contract.
LIGO syntax was designed to help developers build smart contracts by providing them with a syntax familiar to them: the main difference from other languages is the way the code is built and a few technical limitations due to the particularities of using a blockchain (randomness for instance).
LIGO is only a part of the tools that make the experience of smart contract development easier for developers. Another part, introduced later in this module, is unit testing.
To learn more about LIGO, you can take a look at: