One morning, I came across an article about on the Waves platform blockchain.
The overall picture was clear, but the method of specific implementation was not. Some codes, signatures, what, where, why?
After several consultations with the oracle's author, I was able to combine the logic of the draw (implemented in PHP) with the algorithm for obtaining a random number.
- At the start of the tournament/round, we request the first part of the code (R-code) from the oracle.
At this point, there is no information about the number of players, the number of prize places, the size of the prize payouts, or even the existence of the lottery. The oracle issues a personal random code via a transaction, which can only be used once and only by the requester. By the way, the R-code can be 'purchased' (referring to the transaction request cost + compensation to the oracle for the response transaction, which totals about $0.015 at the current exchange rate; the code itself is issued for free) in advance, allowing one to avoid waiting for the response transaction. I created a small regularly updated buffer in the database.
- The tournament lasts a standard 60 blocks of the Waves platform blockchain, which is currently about 1 hour. The tournament is considered held and closed if there are at least two tickets after 60 blocks; otherwise, the tournament's active time is extended for another 60 blocks.
- Immediately after closing the tournament, we form and send a data transaction (for which we also pay a fee of about $0.005), and if necessary, several of them, documenting all the conditions of the draw and the ordered list of players (tickets) from which we need to select winners.
- At this stage, we already have the first part of the code (R-code) plus the data transaction ID (TXID). We send them for signing to the oracle as a concatenation (R-code + TXID), again paying the commission + compensation. The oracle checks the received data for uniqueness and ownership, and in response sends us the second part of the code (S-code) in sha256 format, which serves as the starting point for the random number generator.
- To obtain a random number that indicates the serial number of the winning ticket, we convert the S-code from binary SHA256 data to hexadecimal (HEX) representation. Then, from the resulting HEX string, we derive a number. We take the modulo of the resulting number with the number of tickets (all_tickets) and add 1 to the result (to get a number from 1 to all_tickets). In the end, we have the serial number of the winner.
- If there are multiple winners according to the raffle rules, we repeat the previous operations as many times as there are prizes. Each time, we remove the ticket that has already won from the list and decrease all_tickets by 1, using the previously obtained number instead of the S-code.
Let's consider a specific real example, tournament No. 119:
A total of 7 tickets (all_tickets)
Ticket price is 50 coins (Bet)
Game fee is 10% (Fee)
According to lottery rules, 30% goes to prizes, meaning in this case 2 tickets should win a prize, the size of which is calculated using the formula (Bet * all_tickets - Fee) / 2.
1. We obtained R-code:
2. After closing the tournament, we have a list of tickets in the form of pairs: number + address (the wallet address from which the participation fee was paid). Note that addresses may repeat, meaning one participant purchased multiple tickets for one tournament, which is not prohibited by the rules.
Sent transaction data:
3. Requested S-code: with the comment (R-code + TXID):
RdbAiAhKhveAtR4eyTKq75noMxdcEoxbE6BvojJjM13VE 82JTMzhHM5xEA2fQ9Qscd5QAJU3DAd8nShLjdVHTer5S
4. Received S-code:
5. Winners determined.
6.
As a result, we have a step-by-step record of the prize draw process on the blockchain, which can be verified at any time. It is practically impossible to rig the results on the organizer's side; at least, doing so unnoticed is no longer possible.
Determine the winner № 1
All_tickets:
Index: 1 Ticket:139
Index: 2 Ticket:141
Index: 3 Ticket:143
Index: 4 Ticket:145
Index: 5 Ticket:147
Index: 6 Ticket:149
Index: 7 Ticket:151
1. bin -> hex ( bin2hex(sha256(S-code)) ): Ri89jHB4UXZDXY6gT1m4LBDXGMTaYzHozMk4nxiuqVXdC -> 0xdaf5802953dcb27f89972e38e8900b898733f6a613e6e1c6c5491362c1832596
2. hex -> gmp number: 0xdaf5802953dcb27f89972e38e8900b898733f6a613e6e1c6c5491362c1832596 -> 99037963059744689166154019807924045947962565922868104113173478160267437352342
3. gmp -> modulo (mod=7): 99037963059744689166154019807924045947962565922868104113173478160267437352342 -> 4
4. modulo -> ticket: 4 -> 145
determine the winner № 2
All_tickets:
Index: 1 Ticket:139
Index: 2 Ticket:141
Index: 3 Ticket:143
Index: 4 Ticket:147
Index: 5 Ticket:149
Index: 6 Ticket:151
1. bin -> hex ( bin2hex(sha256(previous hex)) ): daf5802953dcb27f89972e38e8900b898733f6a613e6e1c6c5491362c1832596 -> 0x9560e77525e9ea2db92cdb8484dc52046ccafac7c719b8859ff55f0eb92834a0
2. hex -> gmp number: 0x9560e77525e9ea2db92cdb8484dc52046ccafac7c719b8859ff55f0eb92834a0 -> 67565829218838067182838043983962684143266386786567427968312120473742580659360
3. gmp -> modulo (mod=6): 67565829218838067182838043983962684143266386786567427968312120473742580659360 -> 1
4. modulo -> ticket: 1 -> 139
End.Source: habr.com
